From 3de35623bd7b23b475542e6cd85a4330175723f6 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:14:57 -0400 Subject: [PATCH] GP-3958 - Update how un-maximized window bounds are tracked --- .../src/main/java/docking/RootNode.java | 129 +++++++++++++----- 1 file changed, 98 insertions(+), 31 deletions(-) diff --git a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java index 722554719e..42e58839f5 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java @@ -28,6 +28,7 @@ import org.jdom.Element; import generic.util.WindowUtilities; import ghidra.framework.OperatingSystem; import ghidra.framework.Platform; +import ghidra.util.Swing; import ghidra.util.bean.GGlassPane; import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; @@ -427,16 +428,13 @@ class RootNode extends WindowNode { Element saveToXML() { Element root = new Element(ROOT_NODE_ELEMENT_NAME); JFrame frame = windowWrapper.getParentFrame(); - // Save un-maximized/un-iconified bounds rather than e.g. whole screen if maximized - int state = frame.getExtendedState(); // Remember current state - frame.setExtendedState(JFrame.NORMAL); // Un-maximize & un-iconify - Rectangle r = frame.getBounds(); // The un-maximized & un-iconified bounds - frame.setExtendedState(state); // Revert to original state + Rectangle r = getSaveableBounds(); root.setAttribute("X_POS", "" + r.x); root.setAttribute("Y_POS", "" + r.y); root.setAttribute("WIDTH", "" + r.width); root.setAttribute("HEIGHT", "" + r.height); - root.setAttribute("EX_STATE", "" + state); + root.setAttribute("EX_STATE", "" + frame.getExtendedState()); + if (child != null) { root.addContent(child.saveToXML()); } @@ -448,6 +446,18 @@ class RootNode extends WindowNode { return root; } + private Rectangle getSaveableBounds() { + + Rectangle bounds = windowWrapper.getLastBounds(); + if (bounds != null) { + return bounds; + } + + // This implies the user has never maximized the window; just use the window bounds. + JFrame frame = windowWrapper.getParentFrame(); + return frame.getBounds(); + } + /** * Restores the component hierarchy from the given XML JDOM element. *

@@ -478,7 +488,15 @@ class RootNode extends WindowNode { Rectangle bounds = new Rectangle(x, y, width, height); WindowUtilities.ensureOnScreen(frame, bounds); frame.setBounds(bounds); - frame.setExtendedState(extendedState); + windowWrapper.setLastBounds(bounds); + + Swing.runLater(() -> { + // On some systems setting the bounds will interfere with setting the extended state. + // Run this later to ensure the extended state is applied after setting the bounds. + // Executing in this order allows the bounds we set above to be used when the user + // transitions out of the maximized state. + frame.setExtendedState(extendedState); + }); List restoredPlaceholders = new ArrayList<>(); Iterator elementIterator = rootNodeElement.getChildren().iterator(); @@ -611,29 +629,64 @@ class RootNode extends WindowNode { //================================================================================================== /** Interface to wrap JDialog and JFrame so that they can be used by one handle */ - private interface SwingWindowWrapper { - boolean isVisible(); + private abstract class SwingWindowWrapper { - boolean isModal(); + /** + * The last known non-maximized window bounds + */ + private Rectangle lastBounds; - void validate(); + abstract boolean isVisible(); - Container getContentPane(); + abstract boolean isModal(); - void setJMenuBar(JMenuBar menuBar); + abstract void validate(); - void dispose(); + abstract Container getContentPane(); - Window getWindow(); + abstract void setJMenuBar(JMenuBar menuBar); - JFrame getParentFrame(); + abstract void dispose(); - void setTitle(String title); + abstract Window getWindow(); - String getTitle(); + abstract JFrame getParentFrame(); + + abstract void setTitle(String title); + + abstract String getTitle(); + + /** + * Stores the given bounds if they are not the maximized bounds + * @param bounds the bounds + */ + public void setLastBounds(Rectangle bounds) { + Rectangle screenBounds = WindowUtilities.getScreenBounds(getWindow()); + if (screenBounds == null) { + return; + } + + Rectangle boundsSize = new Rectangle(bounds.getSize()); + Rectangle screenSize = new Rectangle(screenBounds.getSize()); + if (boundsSize.contains(screenSize)) { + // This can happen when the bounds being set are the full screen bounds. We only + // wish to save the non-maximized bounds. + return; + } + + this.lastBounds = bounds; + } + + /** + * Returns the last non-maximized frame bounds + * @return the bounds + */ + public Rectangle getLastBounds() { + return lastBounds; + } } - private class JDialogWindowWrapper implements SwingWindowWrapper { + private class JDialogWindowWrapper extends SwingWindowWrapper { private final JDialog wrappedDialog; private final SwingWindowWrapper parentFrame; @@ -663,9 +716,16 @@ class RootNode extends WindowNode { public void windowActivated(WindowEvent e) { winMgr.setActive(wrappedDialog, true); } + + @Override + public void windowStateChanged(WindowEvent e) { + // this is called when transitioning in and out of the full-screen state + setLastBounds(wrappedDialog.getBounds()); + } }; dialog.addWindowListener(windowListener); + dialog.addWindowStateListener(windowListener); } @Override @@ -686,6 +746,11 @@ class RootNode extends WindowNode { return wrappedDialog; } + @Override + public JFrame getParentFrame() { + return parentFrame.getParentFrame(); + } + @Override public boolean isVisible() { return wrappedDialog.isVisible(); @@ -701,11 +766,6 @@ class RootNode extends WindowNode { wrappedDialog.validate(); } - @Override - public JFrame getParentFrame() { - return parentFrame.getParentFrame(); - } - @Override public void setTitle(String title) { wrappedDialog.setTitle(title); @@ -722,7 +782,7 @@ class RootNode extends WindowNode { } } - private class JFrameWindowWrapper implements SwingWindowWrapper { + private class JFrameWindowWrapper extends SwingWindowWrapper { private final JFrame wrappedFrame; private WindowAdapter windowListener; @@ -763,8 +823,16 @@ class RootNode extends WindowNode { public void windowDeiconified(WindowEvent e) { winMgr.deIconify(); } + + @Override + public void windowStateChanged(WindowEvent e) { + // this is called when transitioning in and out of the full-screen state + setLastBounds(wrappedFrame.getBounds()); + } }; + wrappedFrame.addWindowListener(windowListener); + wrappedFrame.addWindowStateListener(windowListener); wrappedFrame.setSize(800, 400); } @@ -786,6 +854,11 @@ class RootNode extends WindowNode { return wrappedFrame; } + @Override + public JFrame getParentFrame() { + return wrappedFrame; + } + @Override public boolean isVisible() { return wrappedFrame.isVisible(); @@ -801,11 +874,6 @@ class RootNode extends WindowNode { wrappedFrame.validate(); } - @Override - public JFrame getParentFrame() { - return wrappedFrame; - } - @Override public void setTitle(String title) { wrappedFrame.setTitle(title); @@ -820,7 +888,6 @@ class RootNode extends WindowNode { public boolean isModal() { return false; } - } public void addDockingWindowListener(DockingWindowListener listener) {