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) {