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 13b1f5dee6..2c4cf3597a 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
@@ -26,6 +26,7 @@ package net.runelite.client.config;
import java.awt.Dimension;
import net.runelite.api.Constants;
+import net.runelite.client.ui.ContainableFrame;
@ConfigGroup("runelite")
public interface RuneLiteConfig extends Config
@@ -64,14 +65,14 @@ public interface RuneLiteConfig extends Config
}
@ConfigItem(
- keyName = "containInScreen",
+ keyName = "containInScreen2",
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.",
+ description = "Makes the client stay contained in the screen when attempted to move out of it.
Note: 'Always' only works if custom chrome is enabled.",
position = 13
)
- default boolean containInScreen()
+ default ContainableFrame.Mode containInScreen()
{
- return false;
+ return ContainableFrame.Mode.RESIZING;
}
@ConfigItem(
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 247e6a9dff..5a175716d4 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
@@ -858,7 +858,15 @@ public class ClientUI
}
frame.setExpandResizeType(config.automaticResizeType());
- frame.setContainedInScreen(config.containInScreen() && withTitleBar);
+
+ ContainableFrame.Mode containMode = config.containInScreen();
+ if (containMode == ContainableFrame.Mode.ALWAYS && !withTitleBar)
+ {
+ // When native window decorations are enabled we don't have a way to receive window move events
+ // so we can't contain to screen always.
+ containMode = ContainableFrame.Mode.RESIZING;
+ }
+ frame.setContainedInScreen(containMode);
if (!config.rememberScreenBounds())
{
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 fdfca6e89c..e65d6c9fa7 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
@@ -32,18 +32,25 @@ import net.runelite.client.config.ExpandResizeType;
public class ContainableFrame extends JFrame
{
+ public enum Mode
+ {
+ ALWAYS,
+ RESIZING,
+ NEVER;
+ }
+
private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40;
@Setter
private ExpandResizeType expandResizeType;
- private boolean containedInScreen;
+ private Mode containedInScreen;
private boolean expandedClientOppositeDirection;
- public void setContainedInScreen(boolean value)
+ public void setContainedInScreen(Mode value)
{
this.containedInScreen = value;
- if (value)
+ if (this.containedInScreen == Mode.ALWAYS)
{
// Reposition the frame if it is intersecting with the bounds
this.setLocation(this.getX(), this.getY());
@@ -54,13 +61,13 @@ public class ContainableFrame extends JFrame
@Override
public void setLocation(int x, int y)
{
- if (containedInScreen)
+ if (this.containedInScreen == Mode.ALWAYS)
{
Rectangle bounds = this.getGraphicsConfiguration().getBounds();
- x = Math.max(x, (int)bounds.getX());
- x = Math.min(x, (int)(bounds.getX() + bounds.getWidth() - this.getWidth()));
- y = Math.max(y, (int)bounds.getY());
- y = Math.min(y, (int)(bounds.getY() + bounds.getHeight() - this.getHeight()));
+ x = Math.max(x, (int) bounds.getX());
+ x = Math.min(x, (int) (bounds.getX() + bounds.getWidth() - this.getWidth()));
+ y = Math.max(y, (int) bounds.getY());
+ y = Math.min(y, (int) (bounds.getY() + bounds.getHeight() - this.getHeight()));
}
super.setLocation(x, y);
@@ -69,15 +76,17 @@ public class ContainableFrame extends JFrame
@Override
public void setBounds(int x, int y, int width, int height)
{
- if (containedInScreen)
+ if (this.containedInScreen == Mode.ALWAYS)
{
+ // XXX: this is wrong if setSize/resize is called because Component::resize sets private state that is read
+ // in Window::setBounds
Rectangle bounds = this.getGraphicsConfiguration().getBounds();
- width = Math.min(width, width - (int)bounds.getX() + x);
- x = Math.max(x, (int)bounds.getX());
- height = Math.min(height, height - (int)bounds.getY() + y);
- y = Math.max(y, (int)bounds.getY());
- width = Math.min(width, (int)(bounds.getX() + bounds.getWidth()) - x);
- height = Math.min(height, (int)(bounds.getY() + bounds.getHeight()) - y);
+ width = Math.min(width, width - (int) bounds.getX() + x);
+ x = Math.max(x, (int) bounds.getX());
+ height = Math.min(height, height - (int) bounds.getY() + y);
+ y = Math.max(y, (int) bounds.getY());
+ 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);
@@ -115,7 +124,7 @@ public class ContainableFrame extends JFrame
final int newWindowWidth = getWidth() + increment;
int newWindowX = getX();
- if (containedInScreen)
+ if (this.containedInScreen != Mode.NEVER)
{
final Rectangle screenBounds = getGraphicsConfiguration().getBounds();
final boolean wouldExpandThroughEdge = getX() + newWindowWidth > screenBounds.getX() + screenBounds.getWidth();