10

I'm trying to create a kind of toolbar in an undecorated alwaysOnTop frame. Thus, I want my frame to be on top of my main frame, but not on top of frames from other programs. I tried this code :

public class Test {
    private static JFrame mainFrame;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame = new JFrame("test");
                mainFrame.setSize(800,600);
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setVisible(true);

                A a = new A();
            }
        });
    }

    public static class A extends JDialog {

        public A() {
            super(mainFrame);
            setAlwaysOnTop(true);
            setFocusable(false);
            setSize(80,60);
            setVisible(true);
        }
    }
}

But despite the use of JDialog and precising the owner, the frame stay on top of other applications (at least with Ubuntu. Maybe the result is different with other OS ?)

EDIT : Ok, I tried this code for my dialog :

public static class A extends JDialog {

    public A(String name) {
        super(mainFrame, name);
        setAlwaysOnTop(true);
        setFocusable(false);
        setSize(80, 60);
        setVisible(true);

        mainFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowActivated(WindowEvent e) {
                A.this.setAlwaysOnTop(true);
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
                // A.this.setAlwaysOnTop(false);
                A.this.toBack();
            }
        });
    }
}

The issue now is that when the main window loose focus, the dialog steals the focus back and I don't understand why. For instance, I run my app, I try to switch to Firefox, Firefox appears and covers the mainFrame, but the A dialog gets the focus and stays in the screen. Now, if I select Firefox again, the dialog will at last correctly disappear. Could you explain me why the dialog gets the focus?

Thanks

almightyGOSU
  • 3,731
  • 6
  • 31
  • 41
Sharcoux
  • 5,546
  • 7
  • 45
  • 78

2 Answers2

4

You should make your window to always on top only when the parent window goes activated. Something like this:

public class Test {
    private static JFrame mainFrame;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame = new JFrame("test");
                mainFrame.setSize(800,600);
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setVisible(true);

                final A a = new A();
                mainFrame.addWindowListener(new WindowAdapter() {
                    /**
                     * {@inheritDoc}
                     */
                    @Override
                    public void windowDeactivated(WindowEvent e) {
                        a.setAlwaysOnTop(false);
                    }

                    /**
                     * {@inheritDoc}
                     */
                    @Override
                    public void windowActivated(WindowEvent e) {
                        a.setAlwaysOnTop(true);
                    }
                });
            }
        });
    }

    public static class A extends JDialog {

        public A() {
            super(mainFrame);
            setAlwaysOnTop(true);
            setFocusable(false);
            setSize(80,60);
            setVisible(true);
        }
    }
}
Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • Sounds good. Let's try that in my real program. I'll let you know. – Sharcoux Jun 23 '14 at 09:16
  • Ok. I tried your approach. it doesn't work with setAlwaysOnTop(false), but it does with toBack(). Nevertheless, I still have a little issue. Please, see my edit. And thanks for your help ! – Sharcoux Jun 23 '14 at 11:29
  • @user1967800 when the frame is on top it steals the focus. So you must set the alwaysOnTop to false. – Sergiy Medvynskyy Jun 23 '14 at 11:41
  • toBack calls setAlwaysOnTop(false). Anyway, I tried, but the bug remains. I think that it's because the call to toBack() is made too late. The focus is probably transferred from my app to the other one before the event is thrown ? – Sharcoux Jun 23 '14 at 12:21
4

Ok, I found a solution (don't know if it is THE solution, but it's working, so...)

I discovered setFocusableWindowState(), that is perfect for toolbars. By the way, I don't know if my previous setFocusable(false) had any effect.

The next issue was that the focus gets very weird behaviour with this code : If I switch from MyApp to Firefox, here is what happens :

focus : MyApp -> Firefox
execution of MyDialog.toFront()
focus : Firefox -> MyDialog
MyDialog not focusable !
focus : MyDialog -> MyApp !!!

result : nothing changed !

So I finally got the tricks : just after MyDialog.toFront(), you give back the focus to the previous owner. And the only way I found to do this with no error was : mainFrame.toBack()

Final code :

public class Test {
    private static JFrame mainFrame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                mainFrame = new JFrame("test");
                mainFrame.setSize(800,600);
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setVisible(true);

                A a = new A();
            }
        });
    }

    public static class A extends JDialog {

        public A() {
            super(mainFrame);
            setAlwaysOnTop(true);
            setFocusableWindowState(false);
            setSize(80,60);
            setVisible(true);

            mainFrame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowActivated(WindowEvent e) {
                    A.this.setAlwaysOnTop(true);
                    A.this.toFront();
                }
                @Override
                public void windowDeactivated(WindowEvent e) {
                    A.this.setAlwaysOnTop(false);
                }
            });
        }
    }
}
Sharcoux
  • 5,546
  • 7
  • 45
  • 78