diff --git a/runelite-client/src/main/java/net/runelite/client/config/ExpandResizeType.java b/runelite-client/src/main/java/net/runelite/client/config/ExpandResizeType.java new file mode 100644 index 0000000000..8e04c9476d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/config/ExpandResizeType.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Woox + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.config; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ExpandResizeType +{ + KEEP_WINDOW_SIZE("Keep window size"), + KEEP_GAME_SIZE("Keep game size"); + + private final String type; + + @Override + public String toString() + { + return type; + } +} 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 52c66f295d..2fe1671b57 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 @@ -46,11 +46,22 @@ public interface RuneLiteConfig extends Config return Constants.GAME_FIXED_SIZE; } + @ConfigItem( + keyName = "automaticResizeType", + name = "Resize type", + description = "Choose how the window should resize when opening and closing panels", + position = 2 + ) + default ExpandResizeType automaticResizeType() + { + return ExpandResizeType.KEEP_GAME_SIZE; + } + @ConfigItem( keyName = "lockWindowSize", name = "Lock window size", description = "Determines if the window resizing is allowed or not", - position = 2 + position = 3 ) default boolean lockWindowSize() { @@ -62,7 +73,7 @@ public interface RuneLiteConfig extends Config name = "Enable custom window chrome", description = "Use Runelite's custom window title and borders.", warning = "Please restart your client after changing this setting", - position = 3 + position = 4 ) default boolean enableCustomChrome() { @@ -73,7 +84,7 @@ public interface RuneLiteConfig extends Config keyName = "gameAlwaysOnTop", name = "Enable client always on top", description = "The game will always be on the top of the screen", - position = 4 + position = 5 ) default boolean gameAlwaysOnTop() { @@ -84,7 +95,7 @@ public interface RuneLiteConfig extends Config keyName = "notificationMode", name = "Notification mode", description = "Determines mode of notifications", - position = 5 + position = 6 ) default Notifier.NotificationMode notificationMode() { @@ -95,7 +106,7 @@ public interface RuneLiteConfig extends Config keyName = "notificationFocused", name = "Send notifications when focused", description = "Toggles idle notifications for when the client is focused", - position = 6 + position = 7 ) default boolean sendNotificationsWhenFocused() { @@ -106,7 +117,7 @@ public interface RuneLiteConfig extends Config keyName = "notificationRequestFocus", name = "Request focus on notification", description = "Toggles window focus request", - position = 7 + position = 8 ) default boolean requestFocusOnNotification() { @@ -117,7 +128,7 @@ public interface RuneLiteConfig extends Config keyName = "fontType", name = "Dynamic Overlay Font", description = "Configures what font type is used for in-game overlays such as player name, ground items, etc.", - position = 8 + position = 9 ) default FontType fontType() { @@ -128,7 +139,7 @@ public interface RuneLiteConfig extends Config keyName = "infoBoxVertical", name = "Display infoboxes vertically", description = "Toggles the infoboxes to display vertically", - position = 9 + position = 10 ) default boolean infoBoxVertical() { @@ -139,7 +150,7 @@ public interface RuneLiteConfig extends Config keyName = "infoBoxWrap", name = "Infobox wrap count", description = "Configures the amount of infoboxes shown before wrapping", - position = 10 + position = 11 ) default int infoBoxWrap() { @@ -150,7 +161,7 @@ public interface RuneLiteConfig extends Config keyName = "containInScreen", name = "Contain in screen", description = "Makes the client stay contained in the screen when attempted to move out of it.
Note: Only works if custom chrome is enabled.", - position = 11 + position = 12 ) default boolean containInScreen() { @@ -161,7 +172,7 @@ public interface RuneLiteConfig extends Config keyName = "rememberScreenBounds", name = "Remember client position", description = "Save the position and size of the client after exiting", - position = 12 + position = 13 ) default boolean rememberScreenBounds() { 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 de848eb318..919d80bbb1 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 @@ -33,7 +33,6 @@ import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; -import java.awt.Frame; import java.awt.Graphics; import java.awt.LayoutManager; import java.awt.Rectangle; @@ -180,6 +179,11 @@ public class ClientUI frame.setResizable(!config.lockWindowSize()); } + if (event.getKey().equals("automaticResizeType")) + { + frame.setExpandResizeType(config.automaticResizeType()); + } + if (event.getKey().equals("containInScreen") || event.getKey().equals("uiEnableCustomChrome")) { @@ -433,7 +437,8 @@ public class ClientUI // Show frame frame.pack(); - revalidateMinimumSize(); + frame.revalidateMinimumSize(); + if (config.rememberScreenBounds()) { try @@ -464,6 +469,7 @@ public class ClientUI { frame.setLocationRelativeTo(frame.getOwner()); } + frame.setVisible(true); frame.toFront(); requestFocus(); @@ -620,11 +626,11 @@ public class ClientUI if (sidebarOpen) { - expandFrameBy(pluginToolbar.getWidth()); + frame.expandBy(pluginToolbar.getWidth()); } else { - contractFrameBy(pluginToolbar.getWidth()); + frame.contractBy(pluginToolbar.getWidth()); } } @@ -640,8 +646,11 @@ public class ClientUI toggleSidebar(); } + int expandBy = panel.getWrappedPanel().getPreferredSize().width; + if (pluginPanel != null) { + expandBy = pluginPanel.getWrappedPanel().getPreferredSize().width - expandBy; navContainer.remove(0); } @@ -657,7 +666,16 @@ public class ClientUI giveClientFocus(); panel.onActivate(); wrappedPanel.repaint(); - expandFrameBy(pluginPanel.getWrappedPanel().getPreferredSize().width); + + // Check if frame was really expanded or contracted + if (expandBy > 0) + { + frame.expandBy(expandBy); + } + else if (expandBy < 0) + { + frame.contractBy(expandBy); + } } private void contract() @@ -673,57 +691,10 @@ public class ClientUI navContainer.setMaximumSize(new Dimension(0, 0)); navContainer.revalidate(); giveClientFocus(); - contractFrameBy(pluginPanel.getWrappedPanel().getPreferredSize().width); + frame.contractBy(pluginPanel.getWrappedPanel().getPreferredSize().width); pluginPanel = null; } - private void expandFrameBy(final int value) - { - if (isFullScreen()) - { - return; - } - - final int result = getValidatedResult(value); - - if (result != -1) - { - frame.setSize(result, frame.getHeight()); - } - } - - private void contractFrameBy(final int value) - { - if (isFullScreen()) - { - return; - } - - final int result = getValidatedResult(-value); - - if (result != -1) - { - frame.setSize(result, frame.getHeight()); - } - } - - private int getValidatedResult(final int value) - { - revalidateMinimumSize(); - final int result = frame.getWidth() + value; - return result <= frame.getMinimumSize().width ? result : -1; - } - - private void revalidateMinimumSize() - { - frame.setMinimumSize(frame.getLayout().minimumLayoutSize(frame)); - } - - private boolean isFullScreen() - { - return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH; - } - private void giveClientFocus() { if (client instanceof Client) @@ -745,8 +716,22 @@ public class ClientUI } else { + final Rectangle bounds = frame.getBounds(); + + // Try to contract sidebar + if (sidebarOpen) + { + bounds.width -= pluginToolbar.getWidth(); + } + + // Try to contract plugin panel + if (pluginPanel != null) + { + bounds.width -= pluginPanel.getWrappedPanel().getPreferredSize().width; + } + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_CLIENT_MAXIMIZED); - configManager.setConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS, frame.getBounds()); + configManager.setConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS, bounds); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java index 4060d28a7e..f168baffa0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java @@ -24,18 +24,25 @@ */ package net.runelite.client.ui; +import java.awt.Frame; import java.awt.Rectangle; import javax.swing.JFrame; -import lombok.Getter; +import lombok.Setter; +import net.runelite.client.config.ExpandResizeType; public class ContainableFrame extends JFrame { - @Getter + private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40; + + @Setter + private ExpandResizeType expandResizeType; private boolean containedInScreen; + private boolean expandedClientOppositeDirection; public void setContainedInScreen(boolean value) { this.containedInScreen = value; + if (value) { // Reposition the frame if it is intersecting with the bounds @@ -55,6 +62,7 @@ public class ContainableFrame extends JFrame y = Math.max(y, (int)bounds.getY()); y = Math.min(y, (int)(bounds.getY() + bounds.getHeight() - this.getHeight())); } + super.setLocation(x, y); } @@ -71,6 +79,116 @@ public class ContainableFrame extends JFrame width = Math.min(width, (int)(bounds.getX() + bounds.getWidth()) - x); height = Math.min(height, (int)(bounds.getY() + bounds.getHeight()) - y); } + super.setBounds(x, y, width, height); } + + /** + * Expand frame by specified value. If the frame is going to be expanded outside of screen push the frame to + * the side. + * @param value size to expand frame by + */ + public void expandBy(final int value) + { + if (isFullScreen()) + { + return; + } + + int increment = value; + boolean forcedWidthIncrease = false; + + if (expandResizeType == ExpandResizeType.KEEP_WINDOW_SIZE) + { + final int minimumWidth = getLayout().minimumLayoutSize(this).width; + final int currentWidth = getWidth(); + + if (minimumWidth > currentWidth) + { + forcedWidthIncrease = true; + increment = minimumWidth - currentWidth; + } + } + + if (forcedWidthIncrease || expandResizeType == ExpandResizeType.KEEP_GAME_SIZE) + { + final int newWindowWidth = getWidth() + increment; + final Rectangle screenBounds = getGraphicsConfiguration().getBounds(); + final boolean wouldExpandThroughEdge = getX() + newWindowWidth > screenBounds.getX() + screenBounds.getWidth(); + int newWindowX = getX(); + + if (wouldExpandThroughEdge) + { + if (!isFrameCloseToRightEdge()) + { + // Move the window to the edge + newWindowX = (int)(screenBounds.getX() + screenBounds.getWidth()) - getWidth(); + } + + // Expand the window to the left as the user probably don't want the + // window to go through the screen + newWindowX -= increment; + + expandedClientOppositeDirection = true; + } + + setBounds(newWindowX, getY(), newWindowWidth, getHeight()); + } + + revalidateMinimumSize(); + } + + /** + * Contract frame by specified value. If new frame size is less than it's minimum size, force the minimum size. + * If the frame was pushed from side before, restore it's original position. + * @param value value to contract frame by + */ + public void contractBy(final int value) + { + if (isFullScreen()) + { + return; + } + + revalidateMinimumSize(); + final Rectangle screenBounds = getGraphicsConfiguration().getBounds(); + final boolean wasCloseToLeftEdge = Math.abs(getX() - screenBounds.getX()) <= SCREEN_EDGE_CLOSE_DISTANCE; + int newWindowX = getX(); + int newWindowWidth = getWidth() - value; + + if (isFrameCloseToRightEdge() && (expandedClientOppositeDirection || !wasCloseToLeftEdge)) + { + // Keep the distance to the right edge + newWindowX += value; + } + + if (expandResizeType == ExpandResizeType.KEEP_WINDOW_SIZE && newWindowWidth > getMinimumSize().width) + { + // The sidebar fits inside the window, do not resize and move + newWindowWidth = getWidth(); + newWindowX = getX(); + } + + setBounds(newWindowX, getY(), newWindowWidth, getHeight()); + expandedClientOppositeDirection = false; + } + + /** + * Force minimum size of frame to be it's layout manager's minimum size + */ + public void revalidateMinimumSize() + { + setMinimumSize(getLayout().minimumLayoutSize(this)); + } + + private boolean isFullScreen() + { + return (getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH; + } + + private boolean isFrameCloseToRightEdge() + { + Rectangle screenBounds = getGraphicsConfiguration().getBounds(); + return Math.abs((getX() + getWidth()) - (screenBounds.getX() + screenBounds.getWidth())) <= SCREEN_EDGE_CLOSE_DISTANCE; + } }