Merge pull request #1977 from WooxSolo/window-confine-to-screen

Improve client window functionality
This commit is contained in:
Adam
2018-05-07 17:37:16 -04:00
committed by GitHub
4 changed files with 225 additions and 67 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
* 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;
}
}

View File

@@ -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.<br>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()
{

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}