2

I'm able to create a system tray application in Java, but I'm having trouble with positioning. The program will simply need to handle a few inputs/outputs, so I would like it to be easily accessible.

My question is, when I click the system tray icon for my application how can I set it's position elegantly above the system tray? The requirements being that it does this regardless of display settings (resolution, multi monitors, etc) and Taskbar location. Is there a way to tell it to open near the tray, rather than positioning it at all?

I want it to do exactly what the "Network" settings button does in Windows. Similar to the following:

Network System Tray Image

Is this possible in Java?

Community
  • 1
  • 1
Dustin Fain
  • 120
  • 8

2 Answers2

9

As Vulcan has pointed out, yes it's possible.

Also, it's very possible to take into consideration where to place the popup (with relevance to the location where the task icon is)

Show me the popup

public class TestTrayIcon {

    protected static final PopupFrame POPUP_FRAME = new PopupFrame();

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                try {
                    TrayIcon trayIcon = new TrayIcon(ImageIO.read(new File("D:/DevWork/common/icons/iconex_v_bundle_png/iconexperience/v_collections_png/basic_foundation/16x16/plain/about.png")));
                    trayIcon.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e) {

                            Point pos = e.getLocationOnScreen();
                            Rectangle screen = getScreenBoundsAt(pos);

                            if (pos.x + POPUP_FRAME.getWidth() > screen.x + screen.width) {
                                pos.x = screen.x + screen.width - POPUP_FRAME.getWidth();
                            }
                            if (pos.x < screen.x) {
                                pos.x = screen.x;
                            }

                            if (pos.y + POPUP_FRAME.getHeight() > screen.y + screen.height) {
                                pos.y = screen.y + screen.height - POPUP_FRAME.getHeight();
                            }
                            if (pos.y < screen.y) {
                                pos.y = screen.y;
                            }

                            POPUP_FRAME.setLocation(pos);
                            POPUP_FRAME.setVisible(true);

                        }
                    });
                    SystemTray.getSystemTray().add(trayIcon);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static class PopupFrame extends JFrame {

        public PopupFrame() throws HeadlessException {
            setLayout(new BorderLayout());
            add(new JLabel("Hello world"));
            pack();
        }
    }

    public static Rectangle getScreenBoundsAt(Point pos) {
        GraphicsDevice gd = getGraphicsDeviceAt(pos);
        Rectangle bounds = null;

        if (gd != null) {
            bounds = gd.getDefaultConfiguration().getBounds();
            Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration());

            bounds.x += insets.left;
            bounds.y += insets.top;
            bounds.width -= (insets.left + insets.right);
            bounds.height -= (insets.top + insets.bottom);
        }
        return bounds;
    }

    public static GraphicsDevice getGraphicsDeviceAt(Point pos) {
        GraphicsDevice device = null;

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();

        ArrayList<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(lstGDs.length);

        for (GraphicsDevice gd : lstGDs) {
            GraphicsConfiguration gc = gd.getDefaultConfiguration();
            Rectangle screenBounds = gc.getBounds();

            if (screenBounds.contains(pos)) {
                lstDevices.add(gd);
            }
        }

        if (lstDevices.size() == 1) {
            device = lstDevices.get(0);
        }
        return device;
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Fancy and quite an intelligent approach; more comprehensive than mine by far. Kudos to doing it so early in the morning as well. +1 – FThompson Aug 26 '12 at 20:06
  • I did for my answer too, unfortunately :P My application doesn't pop up a window on click though, and rather upon a keycombo being pressed (it's a clipboard upload application where the window shows upload progress), but I'll be rewriting that later with consideration for the cases you've pointed out here, thanks. – FThompson Aug 26 '12 at 20:12
  • @MadProgrammer I tried your code and works partially, and only when the taskbar is in the top, in the bottom the `JFrame` shows but behind it, also the same if the taskbar is in right/left of the screen... I'm facing a similar problem to that of @DustinFain, but with a `JDialog`. – FiroKun May 08 '16 at 16:50
  • Maybe [this version](http://stackoverflow.com/questions/14431467/how-do-i-determine-the-position-of-the-system-tray-on-the-screen/14431536#14431536) – MadProgrammer May 08 '16 at 20:10
5

Yes, it is possible.

To set a window's location relative to the lower right corner of the screen, you can get the graphic environment's maximum window bounds and then perform simple math.

Rectangle screen = GraphicsEnvironment.getLocalGraphicsEnvironment()
        .getMaximumWindowBounds();
String os = System.getProperty("os.name");
if (os.contains("Windows")) {
    setLocation(screen.width - windowSize.width - 6, screen.height - windowSize.height - 6);
} else if (os.contains("Mac")) {
    setLocation(screen.width - windowSize.width + 6, 6);
}

The - 6 on both width and height accounts for the small gap between the window and the taskbar, and is easily customizable.

However, with this approach note that Windows taskbars located on the top or left side will still have a taskbar-sized gap on the right/bottom side of the window. I don't believe it is possible to find the taskbar orientation from the Java API alone.

FThompson
  • 28,352
  • 13
  • 60
  • 93
  • 2
    What if the task bar isn't at the bottom of the screen? – MadProgrammer Aug 26 '12 at 19:54
  • @MadProgrammer A very small percentage of people place Windows taskbar in locations other than the bottom; I'm not sure if there is a way in the Java API to detect which side it is on. With the taskbar on top in Windows 7, the window will still appear at the bottom right, with a taskbar height gap under the window. – FThompson Aug 26 '12 at 19:59
  • Guess who's in that small percentage ;) - Also, you're discounting Mac's ;) – MadProgrammer Aug 26 '12 at 20:00
  • Thanks for the quick responses. Would this work for horizontal dual-monitors, or does MaximumWindowBounds() equal the length of both monitors? – Dustin Fain Aug 26 '12 at 20:06
  • @DustinFain `MaximumWindowBounds` should work just fine for horizontal monitors. – MadProgrammer Aug 26 '12 at 20:07
  • @DustinFain `getMaximumWindowsBounds()` includes the size of both monitors. – FThompson Aug 26 '12 at 20:07