From 445c5baf99cb73fad93ba60a8ce1258ba3fa789a Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 14:59:37 +0100 Subject: [PATCH] Remove need to extend JToolBar in PluginToolbar - Create PluginToolbar through Guice - Create separate component class for toolbar layout - Create custom navigation button component to not depend on swing - Create new events that are fired on new toolbar button addition and removal Signed-off-by: Tomas Slusny --- .../events/PluginToolbarButtonAdded.java | 35 ++++++ .../events/PluginToolbarButtonRemoved.java | 35 ++++++ .../client/ui/ClientPluginToolbar.java | 76 +++++++++++++ .../java/net/runelite/client/ui/ClientUI.java | 74 ++++++++++--- .../runelite/client/ui/NavigationButton.java | 65 +++++++---- .../net/runelite/client/ui/PluginToolbar.java | 101 +++++++----------- 6 files changed, 286 insertions(+), 100 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonAdded.java create mode 100644 runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonRemoved.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/ClientPluginToolbar.java diff --git a/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonAdded.java b/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonAdded.java new file mode 100644 index 0000000000..925a05e485 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonAdded.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.events; + +import lombok.Value; +import net.runelite.client.ui.NavigationButton; + +@Value +public class PluginToolbarButtonAdded +{ + private NavigationButton button; + private int index; +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonRemoved.java b/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonRemoved.java new file mode 100644 index 0000000000..67f2c5386d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/PluginToolbarButtonRemoved.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.events; + +import lombok.Value; +import net.runelite.client.ui.NavigationButton; + +@Value +public class PluginToolbarButtonRemoved +{ + private NavigationButton button; + private int index; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientPluginToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientPluginToolbar.java new file mode 100644 index 0000000000..88bad70430 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientPluginToolbar.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2018, Adam + * Copyright (c) 2018, Tomas Slusny + * 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.Component; +import java.awt.Dimension; +import java.util.HashMap; +import java.util.Map; +import javax.swing.JToolBar; + +/** + * Client plugin toolbar. + */ +public class ClientPluginToolbar extends JToolBar +{ + private static final int TOOLBAR_WIDTH = 36, TOOLBAR_HEIGHT = 503; + private final Map componentMap = new HashMap<>(); + + /** + * Instantiates a new Client plugin toolbar. + */ + ClientPluginToolbar() + { + super(JToolBar.VERTICAL); + setFloatable(false); + setSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); + setMinimumSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); + setPreferredSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); + setMaximumSize(new Dimension(TOOLBAR_WIDTH, Integer.MAX_VALUE)); + } + + public void addComponent(final int index, final NavigationButton button, final Component component) + { + if (componentMap.put(button, component) == null) + { + add(component, index); + revalidate(); + repaint(); + } + } + + public void removeComponent(final NavigationButton button) + { + final Component component = componentMap.remove(button); + + if (component != null) + { + remove(component); + revalidate(); + repaint(); + } + } +} 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 3ac19d8a81..9b15018d90 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 @@ -39,11 +39,14 @@ import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.function.Supplier; import javax.annotation.Nullable; 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; import javax.swing.JPanel; @@ -58,6 +61,8 @@ import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.events.ClientUILoaded; +import net.runelite.client.events.PluginToolbarButtonAdded; +import net.runelite.client.events.PluginToolbarButtonRemoved; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; @@ -97,9 +102,6 @@ public class ClientUI @Getter private TrayIcon trayIcon; - @Getter - private PluginToolbar pluginToolbar; - @Getter private TitleToolbar titleToolbar; @@ -111,6 +113,8 @@ public class ClientUI private JFrame frame; private JPanel navContainer; private PluginPanel pluginPanel; + private ClientPluginToolbar pluginToolbar; + private JButton currentButton; @Inject private ClientUI( @@ -193,6 +197,56 @@ public class ClientUI }); } + @Subscribe + public void onPluginToolbarButtonAdded(final PluginToolbarButtonAdded event) + { + final JButton button = new JButton(); + button.setName(event.getButton().getName()); + button.setToolTipText(event.getButton().getTooltip()); + button.setToolTipText(event.getButton().getTooltip()); + button.setIcon(new ImageIcon(event.getButton().getIcon())); + button.addActionListener(e -> + { + final Supplier panelSupplier = event.getButton().getPanel(); + + if (panelSupplier == null) + { + return; + } + + if (currentButton != null) + { + currentButton.setSelected(false); + } + + if (currentButton == button) + { + contract(); + currentButton = null; + } + else + { + currentButton = button; + currentButton.setSelected(true); + expand(panelSupplier.get()); + } + + if (event.getButton().getOnClick() != null) + { + event.getButton().getOnClick().run(); + } + }); + + event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); + SwingUtilities.invokeLater(() -> pluginToolbar.addComponent(event.getIndex(), event.getButton(), button)); + } + + @Subscribe + public void onPluginToolbarButtonRemoved(final PluginToolbarButtonRemoved event) + { + SwingUtilities.invokeLater(() -> pluginToolbar.removeComponent(event.getButton())); + } + /** * Initialize UI. * @@ -243,7 +297,7 @@ public class ClientUI navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); container.add(navContainer); - pluginToolbar = new PluginToolbar(this); + pluginToolbar = new ClientPluginToolbar(); container.add(pluginToolbar); titleToolbar = new TitleToolbar(properties); @@ -378,12 +432,7 @@ public class ClientUI giveClientFocus(); } - /** - * Expand panel. - * - * @param panel the panel - */ - public void expand(PluginPanel panel) + private void expand(PluginPanel panel) { if (pluginPanel != null) { @@ -415,10 +464,7 @@ public class ClientUI SwingUtil.revalidateMinimumSize(frame); } - /** - * Contract. panel. - */ - public void contract() + private void contract() { boolean wasMinimumWidth = frame.getWidth() == frame.getMinimumSize().width; pluginPanel.onDeactivate(); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java index df6cff5320..f85e0cf215 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2017-2018, Adam + * Copyright (c) 2018, Tomas Slusny * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,30 +25,50 @@ */ package net.runelite.client.ui; -import java.awt.Image; +import java.awt.image.BufferedImage; import java.util.function.Supplier; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; +import lombok.Builder; +import lombok.Data; -@Slf4j -public class NavigationButton extends JButton +/** + * UI navigation button. + */ +@Data +@Builder +public class NavigationButton { - @Getter - private final Supplier panelSupplier; + /** + * Button name. + */ + private final String name; - public NavigationButton(String name, Image icon) - { - this(name, icon, null); - } + /** + * Icon of button. + */ + private final BufferedImage icon; - public NavigationButton(String name, Image icon, Supplier panelSupplier) - { - super(); - setName(name); - setToolTipText(name); - setIcon(new ImageIcon(icon)); - this.panelSupplier = panelSupplier; - } + /** + * Tooltip to show when hovered. + */ + private String tooltip; + + /** + * Button selection state + */ + private boolean selected; + + /** + * On select action of the button. + */ + private Runnable onSelect; + + /** + * On click action of the button. + */ + private Runnable onClick; + + /** + * Supplier for plugin panel, used when expanding and contracting sidebar. + */ + private Supplier panel; } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java index 134a278a0f..4ef441f7a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java @@ -24,87 +24,60 @@ */ package net.runelite.client.ui; -import java.awt.Component; -import java.awt.Dimension; +import com.google.common.eventbus.EventBus; import java.util.Comparator; import java.util.TreeSet; -import java.util.function.Supplier; -import javax.swing.JToolBar; -import lombok.extern.slf4j.Slf4j; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.client.events.PluginToolbarButtonAdded; +import net.runelite.client.events.PluginToolbarButtonRemoved; -@Slf4j -public class PluginToolbar extends JToolBar +/** + * Plugin toolbar buttons holder. + */ +@Singleton +public class PluginToolbar { - public static final int TOOLBAR_WIDTH = 36, TOOLBAR_HEIGHT = 503; + private final EventBus eventBus; + private final TreeSet buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getName)); - private final ClientUI ui; - private final TreeSet buttons = new TreeSet<>(Comparator.comparing(Component::getName)); - - private NavigationButton current; - - public PluginToolbar(ClientUI ui) + @Inject + private PluginToolbar(final EventBus eventBus) { - super(JToolBar.VERTICAL); - this.ui = ui; - - super.setFloatable(false); - super.setSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); - super.setMinimumSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); - super.setPreferredSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT)); - super.setMaximumSize(new Dimension(TOOLBAR_WIDTH, Integer.MAX_VALUE)); + this.eventBus = eventBus; } - public void addNavigation(NavigationButton button) + /** + * Add navigation. + * + * @param button the button + */ + public void addNavigation(final NavigationButton button) { - button.addActionListener((ae) -> onClick(button)); - button.setToolTipText(button.getName()); - if (buttons.contains(button)) { - log.warn("Button already in container '{}'", button.getName()); return; } - buttons.add(button); + if (buttons.add(button)) + { + int index = buttons.headSet(button).size(); + eventBus.post(new PluginToolbarButtonAdded(button, index)); + } + } + + /** + * Remove navigation. + * + * @param button the button + */ + public void removeNavigation(final NavigationButton button) + { int index = buttons.headSet(button).size(); - add(button, index); - revalidate(); - repaint(); - } - public void removeNavigation(NavigationButton button) - { - buttons.remove(button); - remove(button); - revalidate(); - repaint(); - } - - private void onClick(NavigationButton button) - { - Supplier panelSupplier = button.getPanelSupplier(); - if (panelSupplier == null) + if (buttons.remove(button)) { - return; - } - - if (current != null) - { - current.setSelected(false); - } - - if (current == button) - { - ui.contract(); - current = null; - } - else - { - current = button; - current.setSelected(true); - - PluginPanel pluginPanel = panelSupplier.get(); - ui.expand(pluginPanel); + eventBus.post(new PluginToolbarButtonRemoved(button, index)); } } }