From 20fcaf8d81bf9f0aceba9075e935fe796d2578a5 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Fri, 27 Apr 2018 19:53:07 +0200 Subject: [PATCH] Add support for hiding the sidebar --- .../java/net/runelite/client/ui/ClientUI.java | 229 ++++++++++++++---- .../net/runelite/client/ui/PluginPanel.java | 2 +- .../net/runelite/client/ui/UiKeyListener.java | 58 +++++ .../net/runelite/client/util/SwingUtil.java | 11 - .../net/runelite/client/ui/close.png | Bin 0 -> 242 bytes .../resources/net/runelite/client/ui/open.png | Bin 0 -> 241 bytes 6 files changed, 237 insertions(+), 63 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/UiKeyListener.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/close.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/open.png 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 fbe616c79d..e0ec06009f 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,6 @@ import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.LayoutManager; -import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.image.BufferedImage; import java.io.IOException; @@ -45,6 +44,7 @@ import javax.imageio.ImageIO; import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.BoxLayout; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; @@ -65,6 +65,7 @@ import net.runelite.client.events.PluginToolbarButtonAdded; import net.runelite.client.events.PluginToolbarButtonRemoved; import net.runelite.client.events.TitleToolbarButtonAdded; import net.runelite.client.events.TitleToolbarButtonRemoved; +import net.runelite.client.input.KeyManager; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; @@ -79,26 +80,33 @@ import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @Singleton public class ClientUI { - private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH; public static final BufferedImage ICON; + private static final BufferedImage SIDEBAR_OPEN; + private static final BufferedImage SIDEBAR_CLOSE; static { - BufferedImage icon = null; + BufferedImage icon; + BufferedImage sidebarOpen; + BufferedImage sidebarClose; try { synchronized (ImageIO.class) { icon = ImageIO.read(ClientUI.class.getResourceAsStream("/runelite.png")); + sidebarOpen = ImageIO.read(ClientUI.class.getResourceAsStream("open.png")); + sidebarClose = ImageIO.read(ClientUI.class.getResourceAsStream("close.png")); } } catch (IOException e) { - log.warn("Client icon failed to load", e); + throw new RuntimeException(e); } ICON = icon; + SIDEBAR_OPEN = sidebarOpen; + SIDEBAR_CLOSE = sidebarClose; } @Getter @@ -108,6 +116,7 @@ public class ClientUI private final RuneLiteProperties properties; private final RuneLiteConfig config; private final EventBus eventBus; + private final KeyManager keyManager; private Applet client; private JFrame frame; private JPanel navContainer; @@ -116,25 +125,27 @@ public class ClientUI private ClientTitleToolbar titleToolbar; private JButton currentButton; private NavigationButton currentNavButton; + private boolean sidebarOpen; + private JPanel container; + private PluginPanel lastPluginPanel; + private NavigationButton sidebarNavigationButton; + private JButton sidebarNavigationJButton; @Inject private ClientUI( RuneLite runelite, RuneLiteProperties properties, RuneLiteConfig config, - EventBus eventBus) + EventBus eventBus, + KeyManager keyManager) { this.runelite = runelite; this.properties = properties; this.config = config; this.eventBus = eventBus; + this.keyManager = keyManager; } - /** - * On config changed. - * - * @param event the event - */ @Subscribe public void onConfigChanged(ConfigChanged event) { @@ -212,24 +223,28 @@ public class ClientUI return; } - if (currentButton != null) - { - currentButton.setSelected(false); - } + boolean doClose = currentButton != null && currentButton == jButton && currentButton.isSelected(); - if (currentNavButton != null) - { - currentNavButton.setSelected(false); - } - - if (currentButton == jButton && currentNavButton == navButton) + if (doClose) { contract(); + currentButton.setSelected(false); + currentNavButton.setSelected(false); currentButton = null; currentNavButton = null; } else { + if (currentButton != null) + { + currentButton.setSelected(false); + } + + if (currentNavButton != null) + { + currentNavButton.setSelected(false); + } + currentButton = jButton; currentNavButton = navButton; currentButton.setSelected(true); @@ -319,7 +334,7 @@ public class ClientUI && client instanceof Client && ((Client) client).getGameState() != GameState.LOGIN_SCREEN); - final JPanel container = new JPanel(); + container = new JPanel(); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); container.add(new ClientPanel(client)); @@ -330,10 +345,13 @@ public class ClientUI container.add(navContainer); pluginToolbar = new ClientPluginToolbar(); - container.add(pluginToolbar); - titleToolbar = new ClientTitleToolbar(); frame.add(container); + + // Add key listener + final UiKeyListener uiKeyListener = new UiKeyListener(this); + frame.addKeyListener(uiKeyListener); + keyManager.registerKeyListener(uiKeyListener); }); } @@ -397,15 +415,30 @@ public class ClientUI }); } + // Show frame frame.pack(); - SwingUtil.revalidateMinimumSize(frame); + revalidateMinimumSize(); frame.setLocationRelativeTo(frame.getOwner()); frame.setVisible(true); frame.toFront(); requestFocus(); giveClientFocus(); - trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), frame); + + // Create hide sidebar button + sidebarNavigationButton = NavigationButton + .builder() + .icon(SIDEBAR_CLOSE) + .onClick(this::toggleSidebar) + .build(); + + sidebarNavigationJButton = SwingUtil.createSwingButton( + sidebarNavigationButton, + 0, + null); + + titleToolbar.addComponent(sidebarNavigationButton, sidebarNavigationJButton); + toggleSidebar(); }); eventBus.post(new ClientUILoaded()); @@ -480,25 +513,82 @@ public class ClientUI return new Point(0, 0); } - private void expand(PluginPanel panel) + void toggleSidebar() { + // Toggle sidebar open + boolean isSidebarOpen = sidebarOpen; + sidebarOpen = !sidebarOpen; + + // Select/deselect buttons + if (currentButton != null) + { + currentButton.setSelected(sidebarOpen); + } + + if (currentNavButton != null) + { + currentNavButton.setSelected(sidebarOpen); + } + + if (isSidebarOpen) + { + sidebarNavigationJButton.setIcon(new ImageIcon(SIDEBAR_OPEN)); + sidebarNavigationJButton.setToolTipText("Open SideBar"); + + // Save last panel and close current one + lastPluginPanel = pluginPanel; + contract(); + + // Remove plugin toolbar + container.remove(pluginToolbar); + } + else + { + sidebarNavigationJButton.setIcon(new ImageIcon(SIDEBAR_CLOSE)); + sidebarNavigationJButton.setToolTipText("Close SideBar"); + + // Try to restore last panel + expand(lastPluginPanel); + + // Add plugin toolbar back + container.add(pluginToolbar); + } + + // Revalidate sizes of affected Swing components + container.revalidate(); + container.repaint(); + giveClientFocus(); + + if (sidebarOpen) + { + expandFrameBy(pluginToolbar.getWidth()); + } + else + { + contractFrameBy(pluginToolbar.getWidth()); + } + } + + private void expand(@Nullable PluginPanel panel) + { + if (panel == null) + { + return; + } + + if (!sidebarOpen) + { + toggleSidebar(); + } + if (pluginPanel != null) { navContainer.remove(0); } - else - { - if (SwingUtil.isInScreenBounds( - frame.getLocationOnScreen().y + frame.getWidth() + PANEL_EXPANDED_WIDTH, - frame.getLocationOnScreen().y)) - { - frame.setSize(frame.getWidth() + PANEL_EXPANDED_WIDTH, frame.getHeight()); - } - } pluginPanel = panel; - navContainer.setMinimumSize(new Dimension(PANEL_EXPANDED_WIDTH, 0)); - navContainer.setMaximumSize(new Dimension(PANEL_EXPANDED_WIDTH, Integer.MAX_VALUE)); + navContainer.setMinimumSize(new Dimension(pluginPanel.getWrappedPanel().getPreferredSize().width, 0)); + navContainer.setMaximumSize(new Dimension(pluginPanel.getWrappedPanel().getPreferredSize().width, Integer.MAX_VALUE)); final JPanel wrappedPanel = panel.getWrappedPanel(); navContainer.add(wrappedPanel); @@ -507,35 +597,72 @@ public class ClientUI // panel.onActivate has to go after giveClientFocus so it can get focus if it needs. giveClientFocus(); panel.onActivate(); - wrappedPanel.repaint(); - SwingUtil.revalidateMinimumSize(frame); + expandFrameBy(pluginPanel.getWrappedPanel().getPreferredSize().width); } private void contract() { - boolean wasMinimumWidth = frame.getWidth() == frame.getMinimumSize().width; + if (pluginPanel == null) + { + return; + } + pluginPanel.onDeactivate(); navContainer.remove(0); navContainer.setMinimumSize(new Dimension(0, 0)); navContainer.setMaximumSize(new Dimension(0, 0)); navContainer.revalidate(); giveClientFocus(); - SwingUtil.revalidateMinimumSize(frame); + contractFrameBy(pluginPanel.getWrappedPanel().getPreferredSize().width); + pluginPanel = null; + } - if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != Frame.MAXIMIZED_BOTH) + private void expandFrameBy(final int value) + { + if (isFullScreen()) { - if (wasMinimumWidth) - { - frame.setSize(frame.getMinimumSize().width, frame.getHeight()); - } - else if (frame.getWidth() < Toolkit.getDefaultToolkit().getScreenSize().getWidth()) - { - frame.setSize(frame.getWidth() - PANEL_EXPANDED_WIDTH, frame.getHeight()); - } + return; } - pluginPanel = null; + 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() diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java index 3a1401e56a..36dd220fcc 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java @@ -37,7 +37,7 @@ import lombok.Getter; public abstract class PluginPanel extends JPanel { public static final int PANEL_WIDTH = 225; - static final int SCROLLBAR_WIDTH = 17; + private static final int SCROLLBAR_WIDTH = 17; private static final int OFFSET = 6; private static final EmptyBorder BORDER_PADDING = new EmptyBorder(OFFSET, OFFSET, OFFSET, OFFSET); private static final Dimension OUTER_PREFERRED_SIZE = new Dimension(PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH, 0); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/UiKeyListener.java b/runelite-client/src/main/java/net/runelite/client/ui/UiKeyListener.java new file mode 100644 index 0000000000..494acc94e9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/UiKeyListener.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, Adam + * 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.ui; + +import java.awt.event.KeyEvent; +import javax.swing.SwingUtilities; +import net.runelite.client.input.KeyListener; + +class UiKeyListener implements KeyListener +{ + private final ClientUI clientUi; + + UiKeyListener(ClientUI clientUi) + { + this.clientUi = clientUi; + } + + @Override + public void keyTyped(KeyEvent e) + { + } + + @Override + public void keyPressed(KeyEvent e) + { + if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_F11) + { + SwingUtilities.invokeLater(clientUi::toggleSidebar); + } + } + + @Override + public void keyReleased(KeyEvent e) + { + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index 05fee3993b..ff5015917e 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -228,17 +228,6 @@ public class SwingUtil }); } - /** - * Revalidate minimum frame size. - * - * @param frame the frame - */ - public static void revalidateMinimumSize(final JFrame frame) - { - // The JFrame only respects minimumSize if it was set by setMinimumSize, for some reason. (atleast on windows/native) - frame.setMinimumSize(frame.getLayout().minimumLayoutSize(frame)); - } - private static BufferedImage resizeImage(BufferedImage image, int newWidth, int newHeight) { final Image tmp = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/close.png b/runelite-client/src/main/resources/net/runelite/client/ui/close.png new file mode 100644 index 0000000000000000000000000000000000000000..4e016c48728095037691967874724f504d0a7143 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^Ahrkx8<5=cZcP}F;wCIk-rFM@cev91o|b5G mPwl1;{Qf(B{0sgQ#=rN1O6cZmt8V}u$KdJe=d#Wzp$Pyg3Sg`N literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/open.png b/runelite-client/src/main/resources/net/runelite/client/ui/open.png new file mode 100644 index 0000000000000000000000000000000000000000..6d5ca35ad434528a527c80ce3896817b6df5bc34 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^Ahrkx8<5=cZcP}F;wir^p)|Gc8&FSnlu|vi9QFT03&@ua(xl&Uh>EfgQu&X%Q~loCIH41U8w*7 literal 0 HcmV?d00001