Creating an offscreen frame in Java (or: how to avoid a blank menu on a Mac, when all application windows are closed)?

后端 未结 6 1849
囚心锁ツ
囚心锁ツ 2020-12-19 08:55

I am making a Mac application, and I want my menu bar to look right.

Any Mac user knows the menu bar should be in the top screen menu. Setting apple.laf.useSc

相关标签:
6条回答
  • 2020-12-19 09:15

    Not a direct solution, but I think some create a 1-pixel window instead. That yields complaints though, like one described at Super User: Chaotic behavior of a dead pixel on my iMac 24"...

    0 讨论(0)
  • 2020-12-19 09:15

    This code works in Java 7:

    if( isMac ) {
        //This creates an invisible frame so that we always have a menu bar visible
        JFrame menuFrame = new JFrame();
        menuFrame.setUndecorated( true );
        menuFrame.setJMenuBar( defaultMenuBar );
        AWTUtilities.setWindowOpaque( menuFrame, false );
        menuFrame.setBounds( 0,0,1,1 );
        menuFrame.setVisible( true );
    }
    

    Just call this before you open any other windows, and it will stay in the background and automatically become the focused window when others are closed. You can still use the com.apple.eawt.Application.getApplication().setDefaultMenuBar(menuBar) method in your application so that you don't need to call setJMenuBar() on each JFrame.

    0 讨论(0)
  • 2020-12-19 09:21

    You should definitely consider WizardOfOdds' very helpful answer. Using "The Application Menu" correctly will help, and it's easy to set up a minimal Info.plist to get started. A persistent File menu will allow your application to open a new window when others are closed. This answer links to a simple example.

    Although Apple's Human Interface Guidelines are an excellent guide to what your users will expect, you can certainly experiment with the approach you suggested in your question. In particular, you might try setLocation(Short.MIN_VALUE, Short.MIN_VALUE) on the invisible window. In addition, you might want to respond to a WindowEvent in some special way if it signals the close of the last visible window.

    Addendum: When your listener sees the last visible window close, create a new, empty application window. Alternatively, move the invisible window onscreen and make it visible until the user decides how to proceed.

    Addendum: Mac OS X helpfully prevents a visible window form being moved offscreen, but it's easy to put an invisible window in limbo, as shown below.

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.event.ItemEvent;
    import java.awt.event.ItemListener;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JToggleButton;
    
    public class FrameTest extends JFrame {
    
        private static FrameTest marco;
        private static FrameTest polo;
    
        private static class MyPanel extends JPanel {
    
            public MyPanel() {
                super(true);
                final JToggleButton b = new JToggleButton("Test");
                b.addItemListener(new ItemListener() {
                    @Override
                    public void itemStateChanged(ItemEvent e) {
                        if (b.isSelected()) {
                            polo.setLocation(100, 100);
                            polo.setVisible(true);
                        }
                        else {
                            polo.setVisible(false);
                            polo.setLocation(Short.MIN_VALUE, Short.MIN_VALUE);
                        }
                    }
                });
                this.add(b);
            }
        }
    
        public FrameTest(String title) {
            super(title);
            this.setLayout(new BorderLayout());
            this.add(new MyPanel());
            this.pack();
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    
        public static void main(final String[] args) {
    
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    marco = new FrameTest("Marco");
                    marco.setLocationRelativeTo(null);
                    marco.setVisible(true);
                    polo = new FrameTest("Polo");
                    polo.setLocation(Short.MIN_VALUE, Short.MIN_VALUE);
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-19 09:25

    I know that this post is quite old, anyway, I had the same problem and found the solution. Actually it's quite simple. Just don't add the JMenuBar to your main frame when running on mac os x, but to your application using

    com.apple.eawt.Application.getApplication().setDefaultMenuBar(menuBar);

    Now the MenuBar is still displayed even if you set all frame's visibility to false.

    0 讨论(0)
  • 2020-12-19 09:29

    First a note: your question seems really to be "How to have a Window menu following the Apple Human Interface Guidelines" and not "creating an offscreen frame in Java", which seems like a monstrous hack.

    I suggest checking Apple's "Mac OS X Integration for Java", which, under "Window menu", shows apparently exactly what you're trying to achieve:

    Apple Human Interface Guidelines suggests that all Mac OS X applications should provide a Window menu to keep track of all currently open windows. A Window menu should contain a list of windows, with a checkmark next to the active window.

    0 讨论(0)
  • 2020-12-19 09:29

    This is a comment to trashgod's answer. It's too big so I have to move it here >.<


    More problems!

    I'm getting 2 errors:

    line 23: The method itemStateChanged(ItemEvent) of type new ItemListener(){} must override a superclass method

    line 50:The method run() of type new Runnable(){} must override a superclass method

    Any help? I've never encountered this before. I don't know what it means.

    EDIT: I have more problems! I need the offscreen window to be visible while it is offscreen for it to produce a menu to move to the screen menu bar. When I use the code

    offScreen = new JFrame("WTF?!  You can see me?");
    offScreen.setSize(400,300);
    offScreen.setLocation(0, java.awt.Toolkit.getDefaultToolkit().getScreenSize().height+50);
    System.out.println(offScreen.getLocation());
    offScreen.setVisible(true);
    System.out.println(offScreen.getLocation());
    

    I get the output:

    java.awt.Point[x=0,y=1100]
    java.awt.Point[x=0,y=961]
    

    It moves it back once it's made visible again!

    I've searched for ages and I can't find anything.

    0 讨论(0)
提交回复
热议问题