diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index f98b2f16ce..47d94e5bac 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -155,4 +155,15 @@ public interface RuneLiteConfig extends Config { return 4; } + + @ConfigItem( + keyName = "rememberScreenBounds", + name = "Remember client position", + description = "Save the position and size of the client after exiting", + position = 13 + ) + default boolean rememberScreenBounds() + { + return true; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index e0ec06009f..44eb4f58dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -36,7 +36,10 @@ import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.LayoutManager; +import java.awt.Rectangle; import java.awt.TrayIcon; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.IOException; import javax.annotation.Nullable; @@ -59,6 +62,7 @@ import net.runelite.api.Point; import net.runelite.api.events.ConfigChanged; import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; +import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.events.ClientUILoaded; import net.runelite.client.events.PluginToolbarButtonAdded; @@ -80,6 +84,11 @@ import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @Singleton public class ClientUI { + private static final String CONFIG_GROUP = "runelite"; + private static final String CONFIG_CLIENT_BOUNDS = "clientBounds"; + private static final String CONFIG_CLIENT_MAXIMIZED = "clientMaximized"; + private static final int CLIENT_WELL_HIDDEN_MARGIN = 160; + private static final int CLIENT_WELL_HIDDEN_MARGIN_TOP = 10; public static final BufferedImage ICON; private static final BufferedImage SIDEBAR_OPEN; private static final BufferedImage SIDEBAR_CLOSE; @@ -131,6 +140,9 @@ public class ClientUI private NavigationButton sidebarNavigationButton; private JButton sidebarNavigationJButton; + @Inject + private ConfigManager configManager; + @Inject private ClientUI( RuneLite runelite, @@ -169,6 +181,12 @@ public class ClientUI frame.setResizable(!config.lockWindowSize()); } + if (event.getKey().equals("rememberScreenBounds") && event.getNewValue().equals("false")) + { + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_CLIENT_MAXIMIZED); + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS); + } + if (!event.getKey().equals("gameSize")) { return; @@ -329,7 +347,12 @@ public class ClientUI frame.setLocationRelativeTo(frame.getOwner()); frame.setResizable(true); - SwingUtil.addGracefulExitCallback(frame, runelite::shutdown, + SwingUtil.addGracefulExitCallback(frame, + () -> + { + saveClientBoundsConfig(); + runelite.shutdown(); + }, () -> client != null && client instanceof Client && ((Client) client).getGameState() != GameState.LOGIN_SCREEN); @@ -418,11 +441,54 @@ public class ClientUI // Show frame frame.pack(); revalidateMinimumSize(); - frame.setLocationRelativeTo(frame.getOwner()); + if (config.rememberScreenBounds()) + { + try + { + Rectangle clientBounds = configManager.getConfiguration( + CONFIG_GROUP, CONFIG_CLIENT_BOUNDS, Rectangle.class); + if (clientBounds != null) + { + frame.setBounds(clientBounds); + } + else + { + frame.setLocationRelativeTo(frame.getOwner()); + } + + if (configManager.getConfiguration(CONFIG_GROUP, CONFIG_CLIENT_MAXIMIZED) != null) + { + frame.setExtendedState(JFrame.MAXIMIZED_BOTH); + } + } + catch (Exception ex) + { + log.warn("Failed to set window bounds", ex); + frame.setLocationRelativeTo(frame.getOwner()); + } + } + else + { + frame.setLocationRelativeTo(frame.getOwner()); + } frame.setVisible(true); frame.toFront(); requestFocus(); giveClientFocus(); + + // If the frame is well hidden (e.g. unplugged 2nd screen), + // we want to move it back to default position as it can be + // hard for the user to reposition it themselves otherwise. + Rectangle clientBounds = frame.getBounds(); + Rectangle screenBounds = frame.getGraphicsConfiguration().getBounds(); + if (clientBounds.x + clientBounds.width - CLIENT_WELL_HIDDEN_MARGIN < screenBounds.getX() || + clientBounds.x + CLIENT_WELL_HIDDEN_MARGIN > screenBounds.getX() + screenBounds.getWidth() || + clientBounds.y + CLIENT_WELL_HIDDEN_MARGIN_TOP < screenBounds.getY() || + clientBounds.y + CLIENT_WELL_HIDDEN_MARGIN > screenBounds.getY() + screenBounds.getHeight()) + { + frame.setLocationRelativeTo(frame.getOwner()); + } + trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), frame); // Create hide sidebar button @@ -677,4 +743,17 @@ public class ClientUI client.requestFocusInWindow(); } } + + private void saveClientBoundsConfig() + { + if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) != 0) + { + configManager.setConfiguration(CONFIG_GROUP, CONFIG_CLIENT_MAXIMIZED, true); + } + else + { + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_CLIENT_MAXIMIZED); + configManager.setConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS, frame.getBounds()); + } + } }