Search code examples
javaswingwindows-10

Maximized JFrame's location is [-8,-8]. Can it be fixed?


My JFrame is misbehaving. When I maximize it (that is, call frame.setExtendedState(JFrame.MAXIMIZED_BOTH)), its location becomes [-8,-8] — even though it's not askew visually

I'm tasked with implementing a maximizable dialog in Swing. Many of our dialogs are "fat": they contain tables, forms, etc. So users want an option to maximize dialogs (and they don't want to manually resize them). I'm aware that Swing is not designed to operate on a native level (because the library belongs to Java, a cross-platform language). Still, it's a job I have to finish. I'm kind of stuck as once the dialog is maximized, its location is in a weird place, with noticeable gaps on the sides. I suspect it's because of the weird [-8,-8] location of its window ancestor which is set natively (in sun.awt.windows.WComponentPeer#pShow). It's as if -8 for a frame actually means 0, while it means -8 for a dialog

Java 8. Windows 10

MRE:

package demos.panel;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;

public class FrameDemo {
    private static JFrame frame;

    public static void main(String[] args) {
        Container mainPanel = createMainPanel();
        frame = new JFrame("Frame Demo");
        frame.setContentPane(mainPanel);
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setVisible(true);
    }

    private static JPanel createMainPanel() {
        JPanel panel = new JPanel();
        panel.add(createDebugButton());
        panel.add(createDialogButton());
        return panel;
    }

    private static JButton createDebugButton() {
        JButton button = new JButton("Print frame location");
        button.addActionListener(e -> {
            System.out.println("Frame's location: " + frame.getLocation());
        });
        return button;
    }

    private static JButton createDialogButton() {
        JButton button = new JButton("Show dialog");
        button.addActionListener(e -> {
            createDialog().setVisible(true);
        });
        return button;
    }

    private static JDialog createDialog() {
        JDialog dialog = new JDialog();
        dialog.setContentPane(createDialogPane(dialog));
        dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        return dialog;
    }

    private static JPanel createDialogPane(JDialog dialog) {
        JPanel dialogPane = new JPanel();
        dialogPane.add(createMaximizeButton(dialog));
        return dialogPane;
    }

    private static JButton createMaximizeButton(JDialog dialog) {
        JButton button = new JButton("Maximize dialog");
        button.addActionListener(e -> {
            maximizeAndDisableResizing(dialog);
            // I won't make it a toggle, for the sake of simplicity,
            // so you'll have to rerun, sorry
            button.setEnabled(false);
        });
        return button;
    }

    private static void maximizeAndDisableResizing(Window window) {
        maximize(window);
        disableResizing(window);
    }

    private static void maximize(Window window) {
        Rectangle maximumPossibleBounds = getMaximumPossibleBounds();
        maximumPossibleBounds.setLocation(getTopmostContainerLocation(window));
        window.setBounds(maximumPossibleBounds);
    }

    private static Rectangle getMaximumPossibleBounds() {
        GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
        return graphicsEnvironment.getMaximumWindowBounds();
    }

    private static Point getTopmostContainerLocation(Window window) {
        Window topmostContainer = SwingUtilities.getWindowAncestor(window);
        Point location = (topmostContainer == null) ? new Point(0, 0) : topmostContainer.getLocation();
        return location;
    }

    private static void disableResizing(Window window) {
        ((Dialog) window).setResizable(false);
    }
}

dialog is maximized, gaps persist


Solution

  • Here is my effort.
    (Notes after the code.)

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Point;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    
    import javax.swing.JButton;
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class SwngTst0 implements Runnable {
        private JFrame  applicationWindow;
    
        public void run() {
            createAndDisplayGui();
        }
    
        private void createAndDisplayGui() {
            applicationWindow = new JFrame("Application Window");
            applicationWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            applicationWindow.add(createButton(), BorderLayout.PAGE_START);
            applicationWindow.pack();
            applicationWindow.setExtendedState(JFrame.MAXIMIZED_BOTH);
            applicationWindow.setVisible(true);
        }
    
        private JPanel createButton() {
            JPanel panel = new JPanel();
            JButton button = new JButton("Show Dialog");
            button.addActionListener(this::showDialog);
            panel.add(button);
            return panel;
        }
    
        private JPanel createButton2() {
            JPanel panel = new JPanel();
            JButton button = new JButton("Maximize");
            button.addActionListener(this::maximizeDialog);
            panel.add(button);
            return panel;
        }
    
        private void maximizeDialog(ActionEvent event) {
            JButton button = (JButton) event.getSource();
            JDialog dlg = (JDialog) button.getTopLevelAncestor();
            Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
    
            // Returned size is slightly less than actual [screen] size.
            // Hence explicitly increase as suitable.
            size.width += 8;
    
            dlg.setPreferredSize(size);
            dlg.pack();
        }
    
        private void showDialog(ActionEvent event) {
            JDialog dlg = new JDialog(applicationWindow, "Dialog");
            dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            dlg.add(createButton2(), BorderLayout.PAGE_START);
            dlg.pack();
    
            // By default, location of [top, left corner of] 'dlg' is (0,0).
            // 'dlg' should appear at top, left corner of screen.
            // However it does not.
            // Hence explicitly set location of 'dlg'.
            dlg.setLocation(new Point(-4, 0));
    
            dlg.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new SwngTst0());
        }
    }
    

    Here is how it looks with JDialog maximized.

    screen capture

    This question gave me code for getting screen size and this question showed me how to set JDialog size.

    Granted, it's not exactly what [I believe] you want but I assume that you will be able to modify it to suit your needs. I think you just need to experiment with parameter values for methods setLocation and setPreferredSize.

    EDIT

    Played around with the above code a bit more and realized the simple solution:- set the bounds of the JDialog to the bounds of its owner window.

    To do this, I rewrote method maximizeDialog as:

    private void maximizeDialog(ActionEvent event) {
        JButton button = (JButton) event.getSource();
        JDialog dlg = (JDialog) button.getTopLevelAncestor();
        dlg.setBounds(dlg.getOwner().getBounds());
    }
    

    Note that dlg has an owner window since I supplied one in the invocation of the JDialog constructor, i.e.

    JDialog dlg = new JDialog(applicationWindow, "Dialog");
    

    (Refer to method showDialog in the above code.)