From 571f5f50c64b0d1dce81f0cdc56d58bd6850fb55 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 12:21:44 +0100 Subject: [PATCH 01/11] Extract utlity methods to SwingUtil class - Extract common utlity methods from ClientUI to SwingUtil utlity class. - Make OSXUtil accept JFrame instead of ClientUI Signed-off-by: Tomas Slusny --- .../java/net/runelite/client/ui/ClientUI.java | 138 +------- .../net/runelite/client/util/OSXUtil.java | 4 +- .../net/runelite/client/util/SwingUtil.java | 296 ++++++++++++++++++ 3 files changed, 315 insertions(+), 123 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java 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 64b1ded59a..06a4287f6b 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 @@ -26,38 +26,24 @@ package net.runelite.client.ui; import com.google.common.eventbus.Subscribe; import java.applet.Applet; -import java.awt.AWTException; import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; -import java.awt.Frame; import java.awt.LayoutManager; -import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.IOException; -import java.util.Enumeration; import javax.imageio.ImageIO; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JFrame; -import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.SwingUtilities; -import javax.swing.ToolTipManager; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.plaf.FontUIResource; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; @@ -67,6 +53,7 @@ import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; +import net.runelite.client.util.SwingUtil; import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @@ -111,34 +98,20 @@ public class ClientUI extends JFrame public static ClientUI create(RuneLite runelite, RuneLiteProperties properties, Applet client) { - // Force heavy-weight popups/tooltips. - // Prevents them from being obscured by the game applet. - ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); - JPopupMenu.setDefaultLightWeightPopupEnabled(false); - - // Do not render shadows under popups/tooltips. - // Fixes black boxes under popups that are above the game applet. - System.setProperty("jgoodies.popupDropShadowEnabled", "false"); - - // Do not fill in background on repaint. Reduces flickering when - // the applet is resized. - System.setProperty("sun.awt.noerasebackground", "true"); + // Set some sensible swing defaults + SwingUtil.setupDefaults(); // Use substance look and feel - try - { - UIManager.setLookAndFeel(new SubstanceGraphiteLookAndFeel()); - } - catch (UnsupportedLookAndFeelException ex) - { - log.warn("unable to set look and feel", ex); - } + SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel()); // Use custom UI font - setUIFont(new FontUIResource(FontManager.getRunescapeFont())); + SwingUtil.setFont(FontManager.getRunescapeFont()); - ClientUI gui = new ClientUI(runelite, properties, client); + final ClientUI gui = new ClientUI(runelite, properties, client); + + // Try to enable fullscreen on OSX OSXUtil.tryEnableFullscreen(gui); + return gui; } @@ -147,7 +120,7 @@ public class ClientUI extends JFrame this.runelite = runelite; this.properties = properties; this.client = client; - this.trayIcon = setupTrayIcon(); + this.trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), this); init(); setTitle(properties.getTitle()); @@ -297,70 +270,14 @@ public class ClientUI extends JFrame }); } - private static void setUIFont(FontUIResource f) - { - final Enumeration keys = UIManager.getDefaults().keys(); - - while (keys.hasMoreElements()) - { - final Object key = keys.nextElement(); - final Object value = UIManager.get(key); - - if (value instanceof FontUIResource) - { - UIManager.put(key, f); - } - } - } - - private TrayIcon setupTrayIcon() - { - if (!SystemTray.isSupported()) - { - return null; - } - - SystemTray systemTray = SystemTray.getSystemTray(); - TrayIcon trayIcon = new TrayIcon(ICON, properties.getTitle()); - trayIcon.setImageAutoSize(true); - - try - { - systemTray.add(trayIcon); - } - catch (AWTException ex) - { - log.debug("Unable to add system tray icon", ex); - return trayIcon; - } - - // bring to front when tray icon is clicked - trayIcon.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - setVisible(true); - setState(Frame.NORMAL); // unminimize - } - }); - - return trayIcon; - } - private void init() { assert SwingUtilities.isEventDispatchThread(); - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent e) - { - checkExit(); - } - }); + SwingUtil.addGracefulExitCallback(this, runelite::shutdown, + () -> client != null + && client instanceof Client + && ((Client) client).getGameState() != GameState.LOGIN_SCREEN); final JPanel container = new JPanel(); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); @@ -406,7 +323,9 @@ public class ClientUI extends JFrame } else { - if (isInScreenBounds((int) getLocationOnScreen().getX() + getWidth() + PANEL_EXPANDED_WIDTH, (int) getLocationOnScreen().getY())) + if (SwingUtil.isInScreenBounds( + getLocationOnScreen().y + getWidth() + PANEL_EXPANDED_WIDTH, + getLocationOnScreen().y)) { this.setSize(getWidth() + PANEL_EXPANDED_WIDTH, getHeight()); } @@ -450,29 +369,6 @@ public class ClientUI extends JFrame pluginPanel = null; } - private boolean isInScreenBounds(int x, int y) - { - Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); - return x >= 0 && x <= size.getWidth() && y >= 0 && y <= size.getHeight(); - } - - private void checkExit() - { - int result = JOptionPane.OK_OPTION; - - // only ask if not logged out - if (client != null && client instanceof Client && ((Client) client).getGameState() != GameState.LOGIN_SCREEN) - { - result = JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "Exit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); - } - - if (result == JOptionPane.OK_OPTION) - { - runelite.shutdown(); - System.exit(0); - } - } - public PluginToolbar getPluginToolbar() { return pluginToolbar; diff --git a/runelite-client/src/main/java/net/runelite/client/util/OSXUtil.java b/runelite-client/src/main/java/net/runelite/client/util/OSXUtil.java index 393e57445b..f521349b94 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/OSXUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/OSXUtil.java @@ -26,8 +26,8 @@ package net.runelite.client.util; import com.apple.eawt.Application; import com.apple.eawt.FullScreenUtilities; +import javax.swing.JFrame; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.ClientUI; /** * A class with OSX-specific functions to improve integration. @@ -40,7 +40,7 @@ public class OSXUtil * * @param gui The gui to enable the fullscreen on. */ - public static void tryEnableFullscreen(ClientUI gui) + public static void tryEnableFullscreen(JFrame gui) { if (OSType.getOSType() == OSType.MacOS) { 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 new file mode 100644 index 0000000000..e5b9a29444 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -0,0 +1,296 @@ +/* + * 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.util; + +import java.awt.AWTException; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.awt.image.LookupOp; +import java.awt.image.LookupTable; +import java.util.Enumeration; +import java.util.concurrent.Callable; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.LookAndFeel; +import javax.swing.ToolTipManager; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; +import javax.swing.plaf.FontUIResource; +import lombok.extern.slf4j.Slf4j; + +/** + * Various Swing utilities. + */ +@Slf4j +public class SwingUtil +{ + /** + * Sets some sensible defaults for swing. + * IMPORTANT! Needs to be called before main frame creation + */ + public static void setupDefaults() + { + // Force heavy-weight popups/tooltips. + // Prevents them from being obscured by the game applet. + ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + + // Do not render shadows under popups/tooltips. + // Fixes black boxes under popups that are above the game applet. + System.setProperty("jgoodies.popupDropShadowEnabled", "false"); + + // Do not fill in background on repaint. Reduces flickering when + // the applet is resized. + System.setProperty("sun.awt.noerasebackground", "true"); + } + + /** + * Safely sets Swing theme + * + * @param laf the swing look and feel + */ + public static void setTheme(@Nonnull final LookAndFeel laf) + { + try + { + UIManager.setLookAndFeel(laf); + } + catch (UnsupportedLookAndFeelException ex) + { + log.warn("Unable to set look and feel", ex); + } + } + + /** + * Sets default Swing font. + * IMPORTANT! Needs to be called before main frame creation + * + * @param font the new font to use + */ + public static void setFont(@Nonnull final Font font) + { + final FontUIResource f = new FontUIResource(font); + final Enumeration keys = UIManager.getDefaults().keys(); + + while (keys.hasMoreElements()) + { + final Object key = keys.nextElement(); + final Object value = UIManager.get(key); + + if (value instanceof FontUIResource) + { + UIManager.put(key, f); + } + } + } + + /** + * Create tray icon. + * + * @param icon the icon + * @param title the title + * @param frame the frame + * @return the tray icon + */ + @Nullable + public static TrayIcon createTrayIcon(@Nonnull final Image icon, @Nonnull final String title, @Nonnull final Frame frame) + { + if (!SystemTray.isSupported()) + { + return null; + } + + final SystemTray systemTray = SystemTray.getSystemTray(); + final TrayIcon trayIcon = new TrayIcon(icon, title); + trayIcon.setImageAutoSize(true); + + try + { + systemTray.add(trayIcon); + } + catch (AWTException ex) + { + log.debug("Unable to add system tray icon", ex); + return trayIcon; + } + + // Bring to front when tray icon is clicked + trayIcon.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + frame.setVisible(true); + frame.setState(Frame.NORMAL); // Restore + } + }); + + return trayIcon; + } + + /** + * Check if point is in screen bounds. + * + * @param x the x + * @param y the y + * @return the boolean + */ + public static boolean isInScreenBounds(final int x, final int y) + { + final Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); + final Rectangle bounds = new Rectangle(size); + return bounds.contains(x, y); + } + + /** + * Add graceful exit callback. + * + * @param frame the frame + * @param callback the callback + * @param confirmRequired the confirm required + */ + public static void addGracefulExitCallback(@Nonnull final JFrame frame, @Nonnull final Runnable callback, @Nonnull final Callable confirmRequired) + { + frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent event) + { + int result = JOptionPane.OK_OPTION; + + try + { + if (confirmRequired.call()) + { + result = JOptionPane.showConfirmDialog( + frame, + "Are you sure you want to exit?", "Exit", + JOptionPane .OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + } + } + catch (Exception e) + { + log.warn("Unexpected exception occurred while check for confirm required", e); + } + + if (result == JOptionPane.OK_OPTION) + { + callback.run(); + System.exit(0); + } + } + }); + } + + /** + * 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)); + } + + /** + * Create inverted buffered image + * + * @param image buffered image + * @return inverted buffered image + */ + public static BufferedImage createInvertedImage(BufferedImage image) + { + if (image.getType() != BufferedImage.TYPE_INT_ARGB) + { + image = convertToARGB(image); + } + + final LookupTable lookup = new LookupTable(0, 4) + { + @Override + public int[] lookupPixel(int[] src, int[] dest) + { + dest[0] = 255 - src[0]; + dest[1] = 255 - src[1]; + dest[2] = 255 - src[2]; + return dest; + } + }; + + final LookupOp op = new LookupOp(lookup, new RenderingHints(null)); + return op.filter(image, null); + } + + /** + * Resize buffered image. + * + * @param image the image + * @param newWidth the new width + * @param newHeight the new height + * @return the buffered image + */ + public static BufferedImage resizeImage(BufferedImage image, int newWidth, int newHeight) + { + final Image tmp = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); + final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); + + final Graphics2D g2d = dimg.createGraphics(); + g2d.drawImage(tmp, 0, 0, null); + g2d.dispose(); + return dimg; + } + + private static BufferedImage convertToARGB(final BufferedImage image) + { + final BufferedImage newImage = new BufferedImage( + image.getWidth(), image.getHeight(), + BufferedImage.TYPE_INT_ARGB); + + final Graphics2D g = newImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return newImage; + } +} From 31048d2632a11b17b2ab8bfe3c47cc62cd5b3558 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 13:18:27 +0100 Subject: [PATCH 02/11] Remove the need to extend JFrame in ClientUI - Make ClientUI wrap JFrame inside - Do not make RuneLite class depend on Swing API at all - Create ClientUI through Guice - Simplify initialization and showing of UI Signed-off-by: Tomas Slusny --- .../java/net/runelite/client/RuneLite.java | 48 +- .../net/runelite/client/RuneLiteModule.java | 7 - .../java/net/runelite/client/ui/ClientUI.java | 479 ++++++++++-------- .../client/plugins/PluginManagerTest.java | 5 - 4 files changed, 287 insertions(+), 252 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index db09d65a93..5dfe7e7f90 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -35,7 +35,6 @@ import java.applet.Applet; import java.io.File; import java.util.Optional; import javax.inject.Singleton; -import javax.swing.SwingUtilities; import joptsimple.OptionParser; import joptsimple.OptionSet; import lombok.extern.slf4j.Slf4j; @@ -43,9 +42,7 @@ import net.runelite.api.Client; import net.runelite.client.account.SessionManager; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.discord.DiscordService; -import net.runelite.client.events.ClientUILoaded; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientUI; @@ -66,9 +63,6 @@ public class RuneLite private static Injector injector; private static OptionSet options; - @Inject - private RuneLiteProperties properties; - @Inject private PluginManager pluginManager; @@ -90,17 +84,16 @@ public class RuneLite @Inject private SessionManager sessionManager; - @Inject - private RuneLiteConfig runeliteConfig; - @Inject private DiscordService discordService; @Inject private ClientSessionManager clientSessionManager; + @Inject + private ClientUI clientUI; + Client client; - ClientUI gui; public static void main(String[] args) throws Exception { @@ -147,8 +140,8 @@ public class RuneLite this.client = (Client) client; } - // Load swing UI - SwingUtilities.invokeAndWait(() -> setGui(ClientUI.create(this, properties, client))); + // Initialize UI + clientUI.init(client); // Initialize Discord service discordService.init(); @@ -157,10 +150,10 @@ public class RuneLite configManager.load(); // Register event listeners + eventBus.register(clientUI); eventBus.register(overlayRenderer); eventBus.register(menuManager); eventBus.register(chatMessageManager); - eventBus.register(gui); eventBus.register(pluginManager); // Tell the plugin manager if client is outdated or not @@ -183,28 +176,8 @@ public class RuneLite // Load the session, including saved configuration sessionManager.loadSession(); - SwingUtilities.invokeAndWait(() -> - { - if (client != null) - { - client.setSize(runeliteConfig.gameSize()); - client.setPreferredSize(runeliteConfig.gameSize()); - - client.getParent().setPreferredSize(runeliteConfig.gameSize()); - client.getParent().setSize(runeliteConfig.gameSize()); - } - - gui.showWithChrome(runeliteConfig.enableCustomChrome()); - - if (gui.isAlwaysOnTopSupported()) - { - gui.setAlwaysOnTop(runeliteConfig.gameAlwaysOnTop()); - } - - gui.setResizable(!runeliteConfig.lockWindowSize()); - }); - - eventBus.post(new ClientUILoaded()); + // Show UI after all plugins are loaded + clientUI.show(); } public void shutdown() @@ -213,11 +186,6 @@ public class RuneLite discordService.close(); } - public void setGui(ClientUI gui) - { - this.gui = gui; - } - @VisibleForTesting public void setClient(Client client) { diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 24c49fd308..3110e16736 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -41,7 +41,6 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; import net.runelite.client.task.Scheduler; -import net.runelite.client.ui.ClientUI; import net.runelite.client.util.QueryRunner; @Slf4j @@ -67,12 +66,6 @@ public class RuneLiteModule extends AbstractModule return runeLite.client; } - @Provides - ClientUI provideClientUi(RuneLite runelite) - { - return runelite.gui; - } - @Provides @Singleton RuneLiteConfig provideConfig(ConfigManager configManager) 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 06a4287f6b..3ac19d8a81 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 @@ -24,6 +24,7 @@ */ package net.runelite.client.ui; +import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import java.applet.Applet; import java.awt.BorderLayout; @@ -32,12 +33,16 @@ import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; +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; +import javax.annotation.Nullable; import javax.imageio.ImageIO; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JFrame; @@ -51,6 +56,8 @@ import net.runelite.api.GameState; import net.runelite.api.events.ConfigChanged; 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.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; @@ -58,25 +65,16 @@ import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; +/** + * Client UI. + */ @Slf4j -public class ClientUI extends JFrame +@Singleton +public class ClientUI { private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH; public static final BufferedImage ICON; - @Getter - private TrayIcon trayIcon; - - private final RuneLite runelite; - private final Applet client; - private final RuneLiteProperties properties; - private JPanel navContainer; - private PluginToolbar pluginToolbar; - private PluginPanel pluginPanel; - - @Getter - private TitleToolbar titleToolbar; - static { BufferedImage icon = null; @@ -96,115 +94,42 @@ public class ClientUI extends JFrame ICON = icon; } - public static ClientUI create(RuneLite runelite, RuneLiteProperties properties, Applet client) - { - // Set some sensible swing defaults - SwingUtil.setupDefaults(); + @Getter + private TrayIcon trayIcon; - // Use substance look and feel - SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel()); + @Getter + private PluginToolbar pluginToolbar; - // Use custom UI font - SwingUtil.setFont(FontManager.getRunescapeFont()); + @Getter + private TitleToolbar titleToolbar; - final ClientUI gui = new ClientUI(runelite, properties, client); + private final RuneLite runelite; + private final RuneLiteProperties properties; + private final RuneLiteConfig config; + private final EventBus eventBus; + private Applet client; + private JFrame frame; + private JPanel navContainer; + private PluginPanel pluginPanel; - // Try to enable fullscreen on OSX - OSXUtil.tryEnableFullscreen(gui); - - return gui; - } - - private ClientUI(RuneLite runelite, RuneLiteProperties properties, Applet client) + @Inject + private ClientUI( + RuneLite runelite, + RuneLiteProperties properties, + RuneLiteConfig config, + EventBus eventBus) { this.runelite = runelite; this.properties = properties; - this.client = client; - this.trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), this); - - init(); - setTitle(properties.getTitle()); - setIconImage(ICON); - // Prevent substance from using a resize cursor for pointing - getLayeredPane().setCursor(Cursor.getDefaultCursor()); - setLocationRelativeTo(getOwner()); - setResizable(true); - } - - public void showWithChrome(boolean customChrome) - { - setUndecorated(customChrome); - - if (customChrome) - { - getRootPane().setWindowDecorationStyle(JRootPane.FRAME); - - JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(this); - titleToolbar.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING); - titleBar.add(titleToolbar); - - // Substance's default layout manager for the title bar only lays out substance's components - // This wraps the default manager and lays out the TitleToolbar as well. - LayoutManager delegate = titleBar.getLayout(); - titleBar.setLayout(new LayoutManager() - { - @Override - public void addLayoutComponent(String name, Component comp) - { - delegate.addLayoutComponent(name, comp); - } - - @Override - public void removeLayoutComponent(Component comp) - { - delegate.removeLayoutComponent(comp); - } - - @Override - public Dimension preferredLayoutSize(Container parent) - { - return delegate.preferredLayoutSize(parent); - } - - @Override - public Dimension minimumLayoutSize(Container parent) - { - return delegate.minimumLayoutSize(parent); - } - - @Override - public void layoutContainer(Container parent) - { - delegate.layoutContainer(parent); - final int width = titleToolbar.getPreferredSize().width; - titleToolbar.setBounds(titleBar.getWidth() - 75 - width, 0, width, titleBar.getHeight()); - } - }); - } - - pack(); - revalidateMinimumSize(); - setLocationRelativeTo(getOwner()); - - setVisible(true); - toFront(); - requestFocus(); - giveClientFocus(); - } - - private void giveClientFocus() - { - if (client instanceof Client) - { - final Canvas c = ((Client) client).getCanvas(); - c.requestFocusInWindow(); - } - else if (client != null) - { - client.requestFocusInWindow(); - } + this.config = config; + this.eventBus = eventBus; } + /** + * On config changed. + * + * @param event the event + */ @Subscribe public void onConfigChanged(ConfigChanged event) { @@ -213,91 +138,235 @@ public class ClientUI extends JFrame return; } - if (event.getKey().equals("gameAlwaysOnTop")) - { - if (this.isAlwaysOnTopSupported()) - { - this.setAlwaysOnTop(Boolean.valueOf(event.getNewValue())); - } - } - - if (event.getKey().equals("lockWindowSize")) - { - SwingUtilities.invokeLater(() -> setResizable(!Boolean.valueOf(event.getNewValue()))); - } - - if (!event.getKey().equals("gameSize")) - { - return; - } - - if (client == null) - { - return; - } - - String[] splitStr = event.getNewValue().split("x"); - int width = Integer.parseInt(splitStr[0]); - int height = Integer.parseInt(splitStr[1]); - - // The upper bounds are defined by the applet's max size - // The lower bounds are taken care of by ClientPanel's setMinimumSize - - if (width > 7680) - { - width = 7680; - } - - if (height > 2160) - { - height = 2160; - } - - Dimension size = new Dimension(width, height); - SwingUtilities.invokeLater(() -> { + if (event.getKey().equals("gameAlwaysOnTop")) + { + if (frame.isAlwaysOnTopSupported()) + { + frame.setAlwaysOnTop(config.gameAlwaysOnTop()); + } + } + + if (event.getKey().equals("lockWindowSize")) + { + SwingUtilities.invokeLater(() -> frame.setResizable(!config.lockWindowSize())); + } + + if (!event.getKey().equals("gameSize")) + { + return; + } + + if (client == null) + { + return; + } + + int width = config.gameSize().width; + int height = config.gameSize().height; + + // The upper bounds are defined by the applet's max size + // The lower bounds are taken care of by ClientPanel's setMinimumSize + + if (width > 7680) + { + width = 7680; + } + + if (height > 2160) + { + height = 2160; + } + + final Dimension size = new Dimension(width, height); + client.setSize(size); client.setPreferredSize(size); - client.getParent().setPreferredSize(size); client.getParent().setSize(size); - if (isVisible()) + if (frame.isVisible()) { - pack(); + frame.pack(); } }); } - private void init() + /** + * Initialize UI. + * + * @param client the client + * @throws Exception exception that can occur during creation of the UI + */ + public void init(@Nullable final Applet client) throws Exception { - assert SwingUtilities.isEventDispatchThread(); + this.client = client; - SwingUtil.addGracefulExitCallback(this, runelite::shutdown, - () -> client != null - && client instanceof Client - && ((Client) client).getGameState() != GameState.LOGIN_SCREEN); + SwingUtilities.invokeAndWait(() -> + { + // Set some sensible swing defaults + SwingUtil.setupDefaults(); - final JPanel container = new JPanel(); - container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); - container.add(new ClientPanel(client)); + // Use substance look and feel + SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel()); - navContainer = new JPanel(); - navContainer.setLayout(new BorderLayout(0, 0)); - navContainer.setMinimumSize(new Dimension(0, 0)); - navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); - container.add(navContainer); + // Use custom UI font + SwingUtil.setFont(FontManager.getRunescapeFont()); - pluginToolbar = new PluginToolbar(this); - container.add(pluginToolbar); + // Create main window + frame = new JFrame(); - titleToolbar = new TitleToolbar(properties); + // Try to enable fullscreen on OSX + OSXUtil.tryEnableFullscreen(frame); - add(container); + trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), frame); + + frame.setTitle(properties.getTitle()); + frame.setIconImage(ICON); + frame.getLayeredPane().setCursor(Cursor.getDefaultCursor()); // Prevent substance from using a resize cursor for pointing + frame.setLocationRelativeTo(frame.getOwner()); + frame.setResizable(true); + + SwingUtil.addGracefulExitCallback(frame, runelite::shutdown, + () -> client != null + && client instanceof Client + && ((Client) client).getGameState() != GameState.LOGIN_SCREEN); + + final JPanel container = new JPanel(); + container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); + container.add(new ClientPanel(client)); + + navContainer = new JPanel(); + navContainer.setLayout(new BorderLayout(0, 0)); + navContainer.setMinimumSize(new Dimension(0, 0)); + navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); + container.add(navContainer); + + pluginToolbar = new PluginToolbar(this); + container.add(pluginToolbar); + + titleToolbar = new TitleToolbar(properties); + + frame.add(container); + }); } - @Override + /** + * Show client UI after everything else is done. + * + * @throws Exception exception that can occur during modification of the UI + */ + public void show() throws Exception + { + final boolean withTitleBar = config.enableCustomChrome(); + + SwingUtilities.invokeAndWait(() -> + { + frame.setUndecorated(withTitleBar); + + if (withTitleBar) + { + frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); + + JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(frame); + titleToolbar.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING); + titleBar.add(titleToolbar); + + // Substance's default layout manager for the title bar only lays out substance's components + // This wraps the default manager and lays out the TitleToolbar as well. + LayoutManager delegate = titleBar.getLayout(); + titleBar.setLayout(new LayoutManager() + { + @Override + public void addLayoutComponent(String name, Component comp) + { + delegate.addLayoutComponent(name, comp); + } + + @Override + public void removeLayoutComponent(Component comp) + { + delegate.removeLayoutComponent(comp); + } + + @Override + public Dimension preferredLayoutSize(Container parent) + { + return delegate.preferredLayoutSize(parent); + } + + @Override + public Dimension minimumLayoutSize(Container parent) + { + return delegate.minimumLayoutSize(parent); + } + + @Override + public void layoutContainer(Container parent) + { + delegate.layoutContainer(parent); + final int width = titleToolbar.getPreferredSize().width; + titleToolbar.setBounds(titleBar.getWidth() - 75 - width, 0, width, titleBar.getHeight()); + } + }); + } + + frame.pack(); + SwingUtil.revalidateMinimumSize(frame); + frame.setLocationRelativeTo(frame.getOwner()); + frame.setVisible(true); + frame.toFront(); + requestFocus(); + giveClientFocus(); + }); + + eventBus.post(new ClientUILoaded()); + } + + /** + * Paint this component to target graphics + * + * @param graphics the graphics + */ + public void paint(final Graphics graphics) + { + frame.paint(graphics); + } + + /** + * Gets component width. + * + * @return the width + */ + public int getWidth() + { + return frame.getWidth(); + } + + /** + * Gets component height. + * + * @return the height + */ + public int getHeight() + { + return frame.getHeight(); + } + + /** + * Returns true if this component has focus. + * + * @return true if component has focus + */ + public boolean isFocused() + { + return frame.isFocused(); + } + + /** + * Request focus on this component and then on client component + */ public void requestFocus() { if (OSType.getOSType() == OSType.MacOS) @@ -305,17 +374,16 @@ public class ClientUI extends JFrame OSXUtil.requestFocus(); } - super.requestFocus(); + frame.requestFocus(); giveClientFocus(); } - private void revalidateMinimumSize() - { - // The JFrame only respects minimumSize if it was set by setMinimumSize, for some reason. (atleast on windows/native) - this.setMinimumSize(this.getLayout().minimumLayoutSize(this)); - } - - void expand(PluginPanel panel) + /** + * Expand panel. + * + * @param panel the panel + */ + public void expand(PluginPanel panel) { if (pluginPanel != null) { @@ -324,10 +392,10 @@ public class ClientUI extends JFrame else { if (SwingUtil.isInScreenBounds( - getLocationOnScreen().y + getWidth() + PANEL_EXPANDED_WIDTH, - getLocationOnScreen().y)) + frame.getLocationOnScreen().y + frame.getWidth() + PANEL_EXPANDED_WIDTH, + frame.getLocationOnScreen().y)) { - this.setSize(getWidth() + PANEL_EXPANDED_WIDTH, getHeight()); + frame.setSize(frame.getWidth() + PANEL_EXPANDED_WIDTH, frame.getHeight()); } } @@ -344,34 +412,45 @@ public class ClientUI extends JFrame panel.onActivate(); wrappedPanel.repaint(); - revalidateMinimumSize(); + SwingUtil.revalidateMinimumSize(frame); } - void contract() + /** + * Contract. panel. + */ + public void contract() { - boolean wasMinimumWidth = this.getWidth() == (int) this.getMinimumSize().getWidth(); + boolean wasMinimumWidth = frame.getWidth() == frame.getMinimumSize().width; pluginPanel.onDeactivate(); navContainer.remove(0); navContainer.setMinimumSize(new Dimension(0, 0)); - navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); + navContainer.setMaximumSize(new Dimension(0, 0)); navContainer.revalidate(); giveClientFocus(); - revalidateMinimumSize(); + SwingUtil.revalidateMinimumSize(frame); + if (wasMinimumWidth) { - this.setSize((int) this.getMinimumSize().getWidth(), getHeight()); + frame.setSize(frame.getMinimumSize().width, frame.getHeight()); } - else if (getWidth() < Toolkit.getDefaultToolkit().getScreenSize().getWidth()) + else if (frame.getWidth() < Toolkit.getDefaultToolkit().getScreenSize().getWidth()) { - this.setSize(getWidth() - PANEL_EXPANDED_WIDTH, getHeight()); + frame.setSize(frame.getWidth() - PANEL_EXPANDED_WIDTH, frame.getHeight()); } pluginPanel = null; } - public PluginToolbar getPluginToolbar() + private void giveClientFocus() { - return pluginToolbar; + if (client instanceof Client) + { + final Canvas c = ((Client) client).getCanvas(); + c.requestFocusInWindow(); + } + else if (client != null) + { + client.requestFocusInWindow(); + } } - } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java index da87c58bc3..efd2ce9f24 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -45,7 +45,6 @@ import joptsimple.OptionSet; import net.runelite.api.Client; import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteModule; -import net.runelite.client.ui.ClientUI; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Rule; @@ -67,9 +66,6 @@ public class PluginManagerTest private RuneLite runelite; private Set pluginClasses; - @Mock - ClientUI clientUi; - @Mock Client client; @@ -83,7 +79,6 @@ public class PluginManagerTest RuneLite.setInjector(injector); runelite = injector.getInstance(RuneLite.class); - runelite.setGui(clientUi); // Find plugins we expect to have pluginClasses = new HashSet<>(); From 9bcc646d889b04ed6c72933217cfb01a129cbfae Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 13:46:36 +0100 Subject: [PATCH 03/11] Make plugins work with new ClientUI - Change LinkBrowser to not use ClientUI as parent for message box (not required at all) and change it from Guice service to static utility class - Set screenshot plugin offsets statically without recalculating them based on swing component Signed-off-by: Tomas Slusny --- .../client/account/SessionManager.java | 6 ++--- .../client/plugins/feed/FeedPanel.java | 6 ++--- .../client/plugins/feed/FeedPlugin.java | 6 +---- .../grandexchange/GrandExchangeItemPanel.java | 9 ++++--- .../grandexchange/GrandExchangePanel.java | 5 ++-- .../GrandExchangeSearchPanel.java | 7 ++---- .../plugins/screenshot/ScreenshotPlugin.java | 6 ++--- .../net/runelite/client/util/LinkBrowser.java | 24 +++---------------- 8 files changed, 18 insertions(+), 51 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java index 584583c5ca..f5a84d7fee 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java @@ -60,16 +60,14 @@ public class SessionManager private final EventBus eventBus; private ConfigManager configManager; private ScheduledExecutorService executor; - private final LinkBrowser browser; private final AccountClient loginClient = new AccountClient(); @Inject - public SessionManager(ConfigManager configManager, EventBus eventBus, ScheduledExecutorService executor, LinkBrowser browser) + public SessionManager(ConfigManager configManager, EventBus eventBus, ScheduledExecutorService executor) { this.configManager = configManager; this.eventBus = eventBus; this.executor = executor; - this.browser = browser; eventBus.register(this); } @@ -213,7 +211,7 @@ public class SessionManager openSession(new AccountSession(login.getUid(), Instant.now())); // Navigate to login link - browser.browse(login.getOauthUrl()); + LinkBrowser.browse(login.getOauthUrl()); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java index f09ec8be62..19e2e94c30 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPanel.java @@ -120,13 +120,11 @@ class FeedPanel extends PluginPanel private final FeedConfig config; private final Supplier feedSupplier; - private final LinkBrowser linkBrowser; - FeedPanel(FeedConfig config, Supplier feedSupplier, LinkBrowser linkBrowser) + FeedPanel(FeedConfig config, Supplier feedSupplier) { this.config = config; this.feedSupplier = feedSupplier; - this.linkBrowser = linkBrowser; } void rebuildFeed() @@ -294,7 +292,7 @@ class FeedPanel extends PluginPanel public void mouseReleased(MouseEvent e) { avatarAndRight.setBackground(hoverColor); - linkBrowser.browse(item.getUrl()); + LinkBrowser.browse(item.getUrl()); } }); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java index 5ba1a07745..8f4151763f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java @@ -43,7 +43,6 @@ import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.task.Schedule; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; -import net.runelite.client.util.LinkBrowser; import net.runelite.http.api.feed.FeedClient; import net.runelite.http.api.feed.FeedResult; @@ -63,9 +62,6 @@ public class FeedPlugin extends Plugin @Inject private ScheduledExecutorService executorService; - @Inject - private LinkBrowser linkBrowser; - private FeedPanel feedPanel; private NavigationButton navButton; @@ -86,7 +82,7 @@ public class FeedPlugin extends Plugin @Override protected void startUp() throws Exception { - feedPanel = new FeedPanel(config, feedSupplier, linkBrowser); + feedPanel = new FeedPanel(config, feedSupplier); BufferedImage icon; synchronized (ImageIO.class) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java index 4428c17abc..9a33a42fda 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeItemPanel.java @@ -45,10 +45,9 @@ import net.runelite.client.util.LinkBrowser; class GrandExchangeItemPanel extends JPanel { private static final NumberFormat NUMBER_FORMATTER = NumberFormat.getInstance(); - private static final Dimension ICON_SIZE = new Dimension(32, 32); - GrandExchangeItemPanel(LinkBrowser linkBrowser, BufferedImage icon, String name, int itemID, int gePrice, Double + GrandExchangeItemPanel(BufferedImage icon, String name, int itemID, int gePrice, Double haPrice) { BorderLayout layout = new BorderLayout(); @@ -75,7 +74,7 @@ class GrandExchangeItemPanel extends JPanel @Override public void mouseReleased(MouseEvent e) { - geLink(linkBrowser, name, itemID); + geLink(name, itemID); } }); @@ -125,13 +124,13 @@ class GrandExchangeItemPanel extends JPanel add(rightPanel, BorderLayout.CENTER); } - private void geLink(LinkBrowser linkBrowser, String name, int itemID) + private void geLink(String name, int itemID) { final String url = "http://services.runescape.com/m=itemdb_oldschool/" + name.replaceAll(" ", "_") + "/viewitem?obj=" + itemID; - linkBrowser.browse(url); + LinkBrowser.browse(url); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java index b43e1e42b5..1c956029cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePanel.java @@ -37,7 +37,6 @@ import net.runelite.api.Client; import net.runelite.api.GrandExchangeOffer; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.PluginPanel; -import net.runelite.client.util.LinkBrowser; @Slf4j class GrandExchangePanel extends PluginPanel @@ -54,7 +53,7 @@ class GrandExchangePanel extends PluginPanel private JTabbedPane tabbedPane = new JTabbedPane(); @Inject - GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor, LinkBrowser linkBrowser) + GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor) { setLayout(new BorderLayout()); add(tabbedPane, BorderLayout.NORTH); @@ -68,7 +67,7 @@ class GrandExchangePanel extends PluginPanel } // Search Panel - searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor, linkBrowser); + searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor); tabbedPane.addTab("Offers", offerPanel); tabbedPane.addTab("Search", searchPanel); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java index aa5ff86e63..dfeb0966ea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java @@ -46,7 +46,6 @@ import net.runelite.api.Client; import net.runelite.api.ItemComposition; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.hiscore.IconTextField; -import net.runelite.client.util.LinkBrowser; import net.runelite.http.api.item.Item; import net.runelite.http.api.item.ItemClient; import net.runelite.http.api.item.ItemPrice; @@ -60,7 +59,6 @@ class GrandExchangeSearchPanel extends JPanel private final Client client; private final ItemManager itemManager; private final ScheduledExecutorService executor; - private final LinkBrowser linkBrowser; private ItemClient itemClient; @@ -71,12 +69,11 @@ class GrandExchangeSearchPanel extends JPanel private JPanel searchItemsPanel = new JPanel(); private JLabel searchingLabel = new JLabel(); - GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor, LinkBrowser linkBrowser) + GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor) { this.client = client; this.itemManager = itemManager; this.executor = executor; - this.linkBrowser = linkBrowser; init(); } @@ -203,7 +200,7 @@ class GrandExchangeSearchPanel extends JPanel { for (GrandExchangeItems item : ITEMS_LIST) { - GrandExchangeItemPanel panel = new GrandExchangeItemPanel(linkBrowser, item.getIcon(), item.getName(), + GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(), item.getItemId(), item.getGePrice(), item.getHaPrice()); searchItemsPanel.add(panel); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index 981803c119..c047cbdfdb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -31,7 +31,6 @@ import java.awt.Color; import java.awt.Desktop; import java.awt.FontMetrics; import java.awt.Graphics; -import java.awt.Point; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.datatransfer.Clipboard; @@ -410,9 +409,8 @@ public class ScreenshotPlugin extends Plugin clientUi.paint(graphics); // Evaluate the position of the game inside the frame - Point gamePoint = SwingUtilities.convertPoint(client.getCanvas(), 0, 0, clientUi); - gameOffsetX = gamePoint.x; - gameOffsetY = gamePoint.y; + gameOffsetX = 6; + gameOffsetY = 0; } // Draw the game onto the screenshot diff --git a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java index 0d0d8b7313..6f6444ae9c 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java +++ b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java @@ -30,13 +30,10 @@ import java.awt.datatransfer.StringSelection; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import javax.inject.Inject; -import javax.inject.Provider; import javax.inject.Singleton; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.ClientUI; /** * Utility class used for browser navigation @@ -45,21 +42,13 @@ import net.runelite.client.ui.ClientUI; @Slf4j public class LinkBrowser { - private final Provider clientUIProvider; - - @Inject - private LinkBrowser(final Provider clientUIProvider) - { - this.clientUIProvider = clientUIProvider; - } - /** * Tries to navigate to specified URL in browser. In case operation fails, displays message box with message * and copies link to clipboard to navigate to. * @param url url to open * @return true if operation was successful */ - public boolean browse(final String url) + public static boolean browse(final String url) { if (!Desktop.isDesktopSupported()) { @@ -93,18 +82,11 @@ public class LinkBrowser * Open swing message box with specified message and copy data to clipboard * @param message message to show */ - private void showMessageBox(final String message, final String data) + private static void showMessageBox(final String message, final String data) { - final ClientUI clientUI = clientUIProvider.get(); - - if (clientUI == null) - { - return; - } - SwingUtilities.invokeLater(() -> { - final int result = JOptionPane.showConfirmDialog(clientUI, message, "Message", + final int result = JOptionPane.showConfirmDialog(null, message, "Message", JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) From 445c5baf99cb73fad93ba60a8ce1258ba3fa789a Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 14:59:37 +0100 Subject: [PATCH 04/11] 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)); } } } From 716c05224729ddc70fb05dec05362227e810765b Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 15:17:26 +0100 Subject: [PATCH 05/11] Make plugins work with the new PluginToolbar - Use updated construction for NavigationButton - Replace ClientUI injections with PluginToolbar injections Signed-off-by: Tomas Slusny --- .../client/plugins/config/ConfigPlugin.java | 17 ++++++------ .../plugins/devtools/DevToolsPlugin.java | 17 ++++++------ .../client/plugins/feed/FeedPlugin.java | 18 ++++++------- .../grandexchange/GrandExchangePlugin.java | 27 ++++++++++--------- .../client/plugins/hiscore/HiscorePlugin.java | 21 ++++++++------- .../client/plugins/info/InfoPlugin.java | 18 ++++++------- .../kourendlibrary/KourendLibraryPlugin.java | 18 ++++++------- .../client/plugins/notes/NotesPlugin.java | 25 +++++++++-------- .../plugins/xptracker/XpTrackerPlugin.java | 20 +++++++------- 9 files changed, 94 insertions(+), 87 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 9e3a42fab1..83838f3d01 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -36,8 +36,8 @@ import net.runelite.client.events.PluginChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginManager; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; @PluginDescriptor( name = "Configuration", @@ -47,7 +47,7 @@ import net.runelite.client.ui.NavigationButton; public class ConfigPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private ConfigManager configManager; @@ -75,18 +75,19 @@ public class ConfigPlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("config_icon.png")); } - navButton = new NavigationButton( - "Configuration", - icon, - () -> configPanel); + navButton = NavigationButton.builder() + .name("Configuration") + .icon(icon) + .panel(() -> configPanel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); } @Override protected void shutDown() throws Exception { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index f46f2bb90b..84a406b3c6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -35,9 +35,9 @@ import net.runelite.api.widgets.Widget; import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; import net.runelite.client.ui.overlay.Overlay; @PluginDescriptor( @@ -47,7 +47,7 @@ import net.runelite.client.ui.overlay.Overlay; public class DevToolsPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private DevToolsOverlay overlay; @@ -89,12 +89,13 @@ public class DevToolsPlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("devtools_icon.png")); } - navButton = new NavigationButton( - "Developer Tools", - icon, - () -> panel); + navButton = NavigationButton.builder() + .name("Developer Tools") + .icon(icon) + .panel(() -> panel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); font = FontManager.getRunescapeFont() .deriveFont(Font.BOLD, 16); @@ -103,7 +104,7 @@ public class DevToolsPlugin extends Plugin @Override protected void shutDown() throws Exception { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java index 8f4151763f..3bd072e431 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java @@ -41,8 +41,8 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.task.Schedule; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; import net.runelite.http.api.feed.FeedClient; import net.runelite.http.api.feed.FeedResult; @@ -54,7 +54,7 @@ import net.runelite.http.api.feed.FeedResult; public class FeedPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private FeedConfig config; @@ -90,20 +90,20 @@ public class FeedPlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("icon.png")); } - navButton = new NavigationButton( - "News Feed", - icon, - () -> feedPanel); - - ui.getPluginToolbar().addNavigation(navButton); + navButton = NavigationButton.builder() + .name("News Feed") + .icon(icon) + .panel(() -> feedPanel) + .build(); + pluginToolbar.addNavigation(navButton); executorService.submit(this::updateFeed); } @Override protected void shutDown() throws Exception { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } private void updateFeed() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 6fdf42708c..b2a1ddcfe0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -48,8 +48,8 @@ import net.runelite.client.input.MouseListener; import net.runelite.client.input.MouseManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; @PluginDescriptor( name = "Grand Exchange" @@ -69,7 +69,7 @@ public class GrandExchangePlugin extends Plugin private Client client; @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private GrandExchangeConfig config; @@ -84,13 +84,20 @@ public class GrandExchangePlugin extends Plugin protected void startUp() throws IOException { panel = injector.getInstance(GrandExchangePanel.class); + BufferedImage icon; synchronized (ImageIO.class) { icon = ImageIO.read(getClass().getResourceAsStream("ge_icon.png")); } - button = new NavigationButton("GE Offers", icon, () -> panel); - ui.getPluginToolbar().addNavigation(button); + + button = NavigationButton.builder() + .name("GE Offers") + .icon(icon) + .panel(() -> panel) + .build(); + + pluginToolbar.addNavigation(button); itemClick = new MouseListener() { @@ -119,7 +126,8 @@ public class GrandExchangePlugin extends Plugin if (!button.isSelected()) { - button.doClick(); + button.setSelected(true); + button.getOnSelect().run(); } panel.getSearchPanel().priceLookup(itemComp.getName()); @@ -145,8 +153,7 @@ public class GrandExchangePlugin extends Plugin @Override protected void shutDown() { - ui.getPluginToolbar().removeNavigation(button); - + pluginToolbar.removeNavigation(button); mouseManager.unregisterMouseListener(itemClick); } @@ -172,10 +179,6 @@ public class GrandExchangePlugin extends Plugin @Subscribe public void onGrandExchangeOfferChanged(GrandExchangeOfferChanged offerEvent) { - SwingUtilities.invokeLater(() -> - { - panel.updateOffer(offerEvent.getOffer(), offerEvent.getSlot()); - }); + SwingUtilities.invokeLater(() -> panel.updateOffer(offerEvent.getOffer(), offerEvent.getSlot())); } - } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java index 1c806ba30d..ac43c3a2d3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java @@ -38,8 +38,8 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; @PluginDescriptor( name = "HiScore", @@ -50,7 +50,7 @@ public class HiscorePlugin extends Plugin private static final String LOOKUP = "Lookup"; @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private MenuManager menuManager; @@ -81,12 +81,13 @@ public class HiscorePlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("hiscore.gif")); } - navButton = new NavigationButton( - "Hiscore", - icon, - () -> hiscorePanel); + navButton = NavigationButton.builder() + .name("Hiscore") + .icon(icon) + .panel(() -> hiscorePanel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); if (config.playerOption()) { @@ -97,8 +98,7 @@ public class HiscorePlugin extends Plugin @Override protected void shutDown() throws Exception { - ui.getPluginToolbar().removeNavigation(navButton); - + pluginToolbar.removeNavigation(navButton); menuManager.removePlayerMenuItem(LOOKUP); } @@ -129,7 +129,8 @@ public class HiscorePlugin extends Plugin { if (!navButton.isSelected()) { - navButton.doClick(); + navButton.setSelected(true); + navButton.getOnSelect().run(); } }); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java index ad1c730903..43abd0ddc0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java @@ -29,8 +29,8 @@ import javax.imageio.ImageIO; import javax.inject.Inject; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; @PluginDescriptor( name = "Info Panel", @@ -39,7 +39,7 @@ import net.runelite.client.ui.NavigationButton; public class InfoPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; private NavigationButton navButton; @@ -55,18 +55,18 @@ public class InfoPlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("info_icon.png")); } - navButton = new NavigationButton( - "Info", - icon, - () -> panel - ); + navButton = NavigationButton.builder() + .name("Info") + .icon(icon) + .panel(() -> panel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); } @Override protected void shutDown() { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index 965efee38f..5ed9759b58 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -44,8 +44,8 @@ import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; import net.runelite.client.ui.overlay.Overlay; @PluginDescriptor( @@ -57,7 +57,7 @@ public class KourendLibraryPlugin extends Plugin final static boolean debug = false; @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private Client client; @@ -86,19 +86,19 @@ public class KourendLibraryPlugin extends Plugin icon = ImageIO.read(Book.class.getResourceAsStream("panel_icon.png")); } - navButton = new NavigationButton( - "Kourend Library", - icon, - () -> panel - ); + navButton = NavigationButton.builder() + .name("Kourend Library") + .icon(icon) + .panel(() -> panel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); } @Override protected void shutDown() { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java index 4e9e3ecd97..c5f7984e3a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java @@ -24,19 +24,18 @@ */ package net.runelite.client.plugins.notes; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import javax.inject.Inject; - -import com.google.common.eventbus.Subscribe; -import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.events.SessionOpen; import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; -import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.PluginToolbar; @PluginDescriptor( name = "Notes", @@ -46,7 +45,7 @@ import lombok.extern.slf4j.Slf4j; public class NotesPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private NotesConfig config; @@ -72,19 +71,19 @@ public class NotesPlugin extends Plugin icon = ImageIO.read(getClass().getResourceAsStream("notes_icon.png")); } - navButton = new NavigationButton( - "Notes", - icon, - () -> panel - ); + navButton = NavigationButton.builder() + .name("Notes") + .icon(icon) + .panel(() -> panel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); } @Override protected void shutDown() { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index fcc3a1f54c..34bfcee6db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -47,8 +47,8 @@ import net.runelite.client.game.SkillIconManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.PluginToolbar; import net.runelite.http.api.worlds.World; import net.runelite.http.api.worlds.WorldClient; import net.runelite.http.api.worlds.WorldResult; @@ -62,7 +62,7 @@ import net.runelite.http.api.xp.XpClient; public class XpTrackerPlugin extends Plugin { @Inject - private ClientUI ui; + private PluginToolbar pluginToolbar; @Inject private Client client; @@ -104,25 +104,27 @@ public class XpTrackerPlugin extends Plugin log.warn("Error looking up worlds list", e); } + xpPanel = new XpPanel(this, client, skillIconManager); + BufferedImage icon; synchronized (ImageIO.class) { icon = ImageIO.read(getClass().getResourceAsStream("xp.png")); } - xpPanel = new XpPanel(this, client, skillIconManager); - navButton = new NavigationButton( - "XP Tracker", - icon, - () -> xpPanel); + navButton = NavigationButton.builder() + .name("XP Tracker") + .icon(icon) + .panel(() -> xpPanel) + .build(); - ui.getPluginToolbar().addNavigation(navButton); + pluginToolbar.addNavigation(navButton); } @Override protected void shutDown() throws Exception { - ui.getPluginToolbar().removeNavigation(navButton); + pluginToolbar.removeNavigation(navButton); } @Subscribe From 381b111b4a50c1564ce6883cff748a43f82a2e80 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 15:44:07 +0100 Subject: [PATCH 06/11] Remove the need for custom TitleToolbar component - Extract title toolbar layout to separate component - Create new events for adding and removing title toolbar buttons - Create TitleToolbar through Guice Signed-off-by: Tomas Slusny --- .../java/net/runelite/client/RuneLite.java | 7 + .../events/TitleToolbarButtonAdded.java | 35 +++ .../events/TitleToolbarButtonRemoved.java | 35 +++ .../client/ui/ClientTitleToolbar.java | 145 ++++++++++++ .../java/net/runelite/client/ui/ClientUI.java | 54 ++++- .../runelite/client/ui/NavigationButton.java | 2 + .../net/runelite/client/ui/TitleToolbar.java | 215 +++++------------- 7 files changed, 332 insertions(+), 161 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonAdded.java create mode 100644 runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonRemoved.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/ClientTitleToolbar.java diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 5dfe7e7f90..6fdff16707 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -46,6 +46,7 @@ import net.runelite.client.discord.DiscordService; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientUI; +import net.runelite.client.ui.TitleToolbar; import net.runelite.client.ui.overlay.OverlayRenderer; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -93,6 +94,9 @@ public class RuneLite @Inject private ClientUI clientUI; + @Inject + private TitleToolbar titleToolbar; + Client client; public static void main(String[] args) throws Exception @@ -176,6 +180,9 @@ public class RuneLite // Load the session, including saved configuration sessionManager.loadSession(); + // Refresh title toolbar + titleToolbar.refresh(); + // Show UI after all plugins are loaded clientUI.show(); } diff --git a/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonAdded.java b/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonAdded.java new file mode 100644 index 0000000000..a053963a6e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonAdded.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 TitleToolbarButtonAdded +{ + private NavigationButton button; + private int index; +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonRemoved.java b/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonRemoved.java new file mode 100644 index 0000000000..b0f39f05cb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/TitleToolbarButtonRemoved.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 TitleToolbarButtonRemoved +{ + private NavigationButton button; + private int index; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientTitleToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientTitleToolbar.java new file mode 100644 index 0000000000..1f1e1b4dd9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientTitleToolbar.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Abex + * 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.Container; +import java.awt.Dimension; +import java.awt.LayoutManager2; +import java.util.HashMap; +import java.util.Map; +import javax.swing.JPanel; + +/** + * Client title toolbar component. + */ +public class ClientTitleToolbar extends JPanel +{ + public static final int TITLEBAR_SIZE = 23; + private static final int ITEM_PADDING = 4; + + private final Map componentMap = new HashMap<>(); + + /** + * Instantiates a new Client title toolbar. + */ + public ClientTitleToolbar() + { + // The only other layout manager that would manage it's preferred size without padding + // was the GroupLayout manager, which doesn't work with dynamic layouts like this one. + // Primarily, it would not remove components unless it was immediately repainted. + setLayout(new LayoutManager2() + { + @Override + public void addLayoutComponent(String name, Component comp) + { + } + + @Override + public void addLayoutComponent(Component comp, Object constraints) + { + } + + @Override + public void removeLayoutComponent(Component comp) + { + } + + @Override + public Dimension preferredLayoutSize(Container parent) + { + int width = parent.getComponentCount() * (TITLEBAR_SIZE + ITEM_PADDING); + return new Dimension(width, TITLEBAR_SIZE); + } + + @Override + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + @Override + public Dimension maximumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + @Override + public float getLayoutAlignmentX(Container target) + { + return 0; + } + + @Override + public float getLayoutAlignmentY(Container target) + { + return 0; + } + + @Override + public void invalidateLayout(Container target) + { + } + + @Override + public void layoutContainer(Container parent) + { + int x = 0; + for (Component c : parent.getComponents()) + { + x += ITEM_PADDING; + int height = c.getPreferredSize().height; + if (height > TITLEBAR_SIZE) + { + height = TITLEBAR_SIZE; + } + c.setBounds(x, (TITLEBAR_SIZE - height) / 2, TITLEBAR_SIZE, height); + x += TITLEBAR_SIZE; + } + } + }); + } + + public void addComponent(NavigationButton button, Component c) + { + if (componentMap.put(button, c) == null) + { + add(c); + revalidate(); + repaint(); + } + } + + public void removeComponent(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 9b15018d90..d9b518849b 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 @@ -63,10 +63,13 @@ 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.events.TitleToolbarButtonAdded; +import net.runelite.client.events.TitleToolbarButtonRemoved; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; +import org.pushingpixels.substance.internal.SubstanceSynapse; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @@ -102,9 +105,6 @@ public class ClientUI @Getter private TrayIcon trayIcon; - @Getter - private TitleToolbar titleToolbar; - private final RuneLite runelite; private final RuneLiteProperties properties; private final RuneLiteConfig config; @@ -114,6 +114,7 @@ public class ClientUI private JPanel navContainer; private PluginPanel pluginPanel; private ClientPluginToolbar pluginToolbar; + private ClientTitleToolbar titleToolbar; private JButton currentButton; @Inject @@ -247,6 +248,48 @@ public class ClientUI SwingUtilities.invokeLater(() -> pluginToolbar.removeComponent(event.getButton())); } + @Subscribe + public void onTitleToolbarButtonAdded(final TitleToolbarButtonAdded event) + { + if (!config.enableCustomChrome()) + { + return; + } + + SwingUtilities.invokeLater(() -> + { + + final int iconSize = ClientTitleToolbar.TITLEBAR_SIZE - 6; + final BufferedImage scaledImage = SwingUtil.resizeImage(event.getButton().getIcon(), iconSize, iconSize); + final JButton button = new JButton(); + button.setName(event.getButton().getName()); + button.setToolTipText(event.getButton().getTooltip()); + button.setIcon(new ImageIcon(scaledImage)); + button.setRolloverIcon(new ImageIcon(SwingUtil.createInvertedImage(scaledImage))); + button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); + button.setFocusable(false); + + if (event.getButton().getOnClick() != null) + { + button.addActionListener(e -> event.getButton().getOnClick().run()); + } + + event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); + titleToolbar.addComponent(event.getButton(), button); + }); + } + + @Subscribe + public void onTitleToolbarButtonRemoved(final TitleToolbarButtonRemoved event) + { + if (!config.enableCustomChrome()) + { + return; + } + + SwingUtilities.invokeLater(() -> titleToolbar.removeComponent(event.getButton())); + } + /** * Initialize UI. * @@ -300,8 +343,7 @@ public class ClientUI pluginToolbar = new ClientPluginToolbar(); container.add(pluginToolbar); - titleToolbar = new TitleToolbar(properties); - + titleToolbar = new ClientTitleToolbar(); frame.add(container); }); } @@ -323,7 +365,7 @@ public class ClientUI { frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); - JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(frame); + final JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(frame); titleToolbar.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING); titleBar.add(titleToolbar); 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 f85e0cf215..8ebb330b48 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 @@ -29,12 +29,14 @@ import java.awt.image.BufferedImage; import java.util.function.Supplier; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; /** * UI navigation button. */ @Data @Builder +@EqualsAndHashCode(of = {"name", "tooltip"}) public class NavigationButton { /** diff --git a/runelite-client/src/main/java/net/runelite/client/ui/TitleToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/TitleToolbar.java index 9a5ffc1be4..80333c2da6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/TitleToolbar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/TitleToolbar.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018 Abex + * 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,174 +25,78 @@ */ package net.runelite.client.ui; -import java.awt.Component; -import java.awt.Container; -import java.awt.Desktop; -import java.awt.Dimension; -import java.awt.Image; -import java.awt.LayoutManager2; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import javax.imageio.ImageIO; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JPanel; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLiteProperties; -import org.pushingpixels.substance.internal.SubstanceSynapse; +import com.google.common.eventbus.EventBus; +import java.util.Comparator; +import java.util.Iterator; +import java.util.TreeSet; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.client.events.TitleToolbarButtonAdded; +import net.runelite.client.events.TitleToolbarButtonRemoved; -@Slf4j -public class TitleToolbar extends JPanel +/** + * Title toolbar buttons holder. + */ +@Singleton +public class TitleToolbar { - private static final int TITLEBAR_SIZE = 23; - private static final int ITEM_PADDING = 4; + private final EventBus eventBus; + private final TreeSet buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getTooltip)); - public TitleToolbar(RuneLiteProperties properties) + @Inject + private TitleToolbar(final EventBus eventBus) { - // The only other layout manager that would manage it's preferred size without padding - // was the GroupLayout manager, which doesn't work with dynamic layouts like this one. - // Primarily, it would not remove components unless it was immediately repainted. - setLayout(new LayoutManager2() + this.eventBus = eventBus; + } + + /** + * Add navigation. + * + * @param button the button + */ + public void addNavigation(final NavigationButton button) + { + if (buttons.contains(button)) { - @Override - public void addLayoutComponent(String name, Component comp) - { - } - - @Override - public void addLayoutComponent(Component comp, Object constraints) - { - } - - @Override - public void removeLayoutComponent(Component comp) - { - } - - @Override - public Dimension preferredLayoutSize(Container parent) - { - int width = parent.getComponentCount() * (TITLEBAR_SIZE + ITEM_PADDING); - return new Dimension(width, TITLEBAR_SIZE); - } - - @Override - public Dimension minimumLayoutSize(Container parent) - { - return preferredLayoutSize(parent); - } - - @Override - public Dimension maximumLayoutSize(Container parent) - { - return preferredLayoutSize(parent); - } - - @Override - public float getLayoutAlignmentX(Container target) - { - return 0; - } - - @Override - public float getLayoutAlignmentY(Container target) - { - return 0; - } - - @Override - public void invalidateLayout(Container target) - { - } - - @Override - public void layoutContainer(Container parent) - { - int x = 0; - for (Component c : parent.getComponents()) - { - x += ITEM_PADDING; - int height = c.getPreferredSize().height; - if (height > TITLEBAR_SIZE) - { - height = TITLEBAR_SIZE; - } - c.setBounds(x, (TITLEBAR_SIZE - height) / 2, TITLEBAR_SIZE, height); - x += TITLEBAR_SIZE; - } - } - }); - - try - { - BufferedImage discordIcon; - BufferedImage invertedIcon; - synchronized (ImageIO.class) - { - discordIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord.png")); - invertedIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord_inverted.png")); - } - - JButton discordButton = new JButton(); - discordButton.setToolTipText("Join Discord"); - discordButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - super.mouseClicked(e); - try - { - Desktop.getDesktop().browse(new URL(properties.getDiscordInvite()).toURI()); - } - catch (IOException | URISyntaxException ex) - { - log.warn("error opening browser", ex); - } - } - }); - - addButton(discordButton, discordIcon, invertedIcon); + return; } - catch (IOException ex) + + if (buttons.add(button)) { - log.warn("unable to load discord button", ex); + int index = buttons.headSet(button).size(); + eventBus.post(new TitleToolbarButtonAdded(button, index)); } } - public void addButton(JButton button, Image iconImage, Image invertedIconImage) + /** + * Remove navigation. + * + * @param button the button + */ + public void removeNavigation(final NavigationButton button) { - final int iconSize = TITLEBAR_SIZE - 6; - ImageIcon icon = new ImageIcon(iconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH)); - ImageIcon invertedIcon; - if (invertedIconImage == null) - { - invertedIcon = icon; - } - else - { - invertedIcon = new ImageIcon(invertedIconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH)); - } + int index = buttons.headSet(button).size(); - button.setIcon(icon); - button.setRolloverIcon(invertedIcon); - button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); - button.setFocusable(false); - - add(button); - revalidate(); - repaint(); + if (buttons.remove(button)) + { + eventBus.post(new TitleToolbarButtonRemoved(button, index)); + } } - @Override - public void remove(Component c) + /** + * Refresh all buttons + */ + public void refresh() { - super.remove(c); - revalidate(); - repaint(); + final Iterator iterator = buttons.iterator(); + int index = 0; + + while (iterator.hasNext()) + { + final NavigationButton button = iterator.next(); + eventBus.post(new TitleToolbarButtonRemoved(button, index)); + eventBus.post(new TitleToolbarButtonAdded(button, index)); + index++; + } } } From 059f92ad6cc769dbbd83728d58e358ed0e6d9c80 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 19:02:18 +0100 Subject: [PATCH 07/11] Make plugins work with new title toolbar - Change info panel to support new hooks - Change plugins to use NavigationButton for adding buttons to titleToolbar Signed-off-by: Tomas Slusny --- .../client/plugins/account/AccountPlugin.java | 54 ++++----- .../client/plugins/info/InfoPanel.java | 107 ++++++++++++------ .../plugins/screenshot/ScreenshotPlugin.java | 80 +++---------- 3 files changed, 116 insertions(+), 125 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java index d7c367177a..01ceaf4a72 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java @@ -25,22 +25,20 @@ package net.runelite.client.plugins.account; import com.google.common.eventbus.Subscribe; -import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; import javax.imageio.ImageIO; import javax.inject.Inject; -import javax.swing.JButton; import javax.swing.JOptionPane; import lombok.extern.slf4j.Slf4j; import net.runelite.api.events.SessionClose; +import net.runelite.api.events.SessionOpen; import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; -import net.runelite.api.events.SessionOpen; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.ClientUI; +import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.TitleToolbar; import net.runelite.client.util.RunnableExceptionLogger; @@ -55,13 +53,13 @@ public class AccountPlugin extends Plugin private SessionManager sessionManager; @Inject - private ClientUI ui; + private TitleToolbar titleToolbar; @Inject private ScheduledExecutorService executor; - private JButton loginButton; - private JButton logoutButton; + private NavigationButton loginButton; + private NavigationButton logoutButton; private static final BufferedImage LOGIN_IMAGE, LOGOUT_IMAGE; @@ -84,47 +82,45 @@ public class AccountPlugin extends Plugin @Override protected void startUp() throws Exception { - loginButton = new JButton(); - loginButton.setToolTipText("Login"); - loginButton.addActionListener(this::loginClick); + loginButton = NavigationButton.builder() + .icon(LOGIN_IMAGE) + .tooltip("Login") + .onClick(this::loginClick) + .build(); - logoutButton = new JButton(); - logoutButton.setToolTipText("Logout"); - logoutButton.addActionListener(this::logoutClick); + logoutButton = NavigationButton.builder() + .icon(LOGOUT_IMAGE) + .tooltip("Logout") + .onClick(this::logoutClick) + .build(); addAndRemoveButtons(); } private void addAndRemoveButtons() { - TitleToolbar tb = ui.getTitleToolbar(); - tb.remove(loginButton); - tb.remove(logoutButton); - if (sessionManager.getAccountSession() == null) - { - tb.addButton(loginButton, LOGIN_IMAGE, LOGIN_IMAGE); - } - else - { - tb.addButton(logoutButton, LOGOUT_IMAGE, LOGOUT_IMAGE); - } + titleToolbar.removeNavigation(loginButton); + titleToolbar.removeNavigation(logoutButton); + titleToolbar.addNavigation(sessionManager.getAccountSession() == null + ? loginButton + : logoutButton); } @Override protected void shutDown() throws Exception { - ui.getTitleToolbar().remove(loginButton); - ui.getTitleToolbar().remove(logoutButton); + titleToolbar.removeNavigation(loginButton); + titleToolbar.removeNavigation(logoutButton); } - private void loginClick(ActionEvent ae) + private void loginClick() { executor.execute(RunnableExceptionLogger.wrap(sessionManager::login)); } - private void logoutClick(ActionEvent ae) + private void logoutClick() { - if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(ui, + if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, "Are you sure you want to logout?", "Logout Confirmation", JOptionPane.YES_NO_OPTION)) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java index c3d6030631..76c1a7bdbd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -26,15 +26,16 @@ package net.runelite.client.plugins.info; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import java.awt.Font; import com.google.inject.Inject; -import java.lang.reflect.InvocationTargetException; +import java.awt.Font; +import java.awt.image.BufferedImage; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; import javax.swing.BorderFactory; import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JPanel; import javax.swing.LayoutStyle; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; @@ -46,22 +47,28 @@ import net.runelite.client.RuneLiteProperties; import net.runelite.client.account.SessionManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.events.ClientUILoaded; -import net.runelite.client.ui.ClientUI; +import net.runelite.client.events.TitleToolbarButtonAdded; +import net.runelite.client.events.TitleToolbarButtonRemoved; +import net.runelite.client.ui.ClientTitleToolbar; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.TitleToolbar; import net.runelite.client.util.RunnableExceptionLogger; +import net.runelite.client.util.SwingUtil; +import org.pushingpixels.substance.internal.SubstanceSynapse; @Slf4j public class InfoPanel extends PluginPanel { - private final static String RUNELITE_LOGIN = "https://runelite_login/"; + private static final int TITLEBAR_SIZE = 23; + private static final String RUNELITE_LOGIN = "https://runelite_login/"; @Inject @Nullable private Client client; @Inject - private ClientUI clientUI; + private TitleToolbar titleToolbar; @Inject private RuneLiteConfig runeliteConfig; @@ -79,9 +86,7 @@ public class InfoPanel extends PluginPanel private ScheduledExecutorService executor; private final GroupLayout layout = new GroupLayout(this); - - private final JPanel toolbarPanelPlaceholder = new JPanel(); - + private final ClientTitleToolbar titleBar = new ClientTitleToolbar(); private final JLabel usernameHeader = new JLabel(); private final JRichTextPane username = new JRichTextPane(); @@ -90,8 +95,7 @@ public class InfoPanel extends PluginPanel setLayout(layout); final Font smallFont = FontManager.getRunescapeSmallFont(); - - toolbarPanelPlaceholder.setVisible(false); + titleBar.setVisible(false); final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); runeliteVersionHeader.setFont(smallFont); @@ -132,7 +136,7 @@ public class InfoPanel extends PluginPanel setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6)); layout.setVerticalGroup(layout.createSequentialGroup() - .addComponent(toolbarPanelPlaceholder) + .addComponent(titleBar) .addGap(3) .addGroup(layout.createParallelGroup() .addComponent(runeliteVersionHeader) @@ -152,16 +156,15 @@ public class InfoPanel extends PluginPanel layout.setHorizontalGroup(layout.createParallelGroup() .addGroup(layout.createSequentialGroup() .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) - .addComponent(toolbarPanelPlaceholder) - ).addGroup(layout.createSequentialGroup() + .addComponent(titleBar)) + .addGroup(layout.createSequentialGroup() .addComponent(runeliteVersionHeader) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) - .addComponent(runescapeVersionHeader) - ).addGroup(layout.createSequentialGroup() + .addComponent(runescapeVersionHeader)) + .addGroup(layout.createSequentialGroup() .addComponent(runeliteVersion) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) - .addComponent(runescapeVersion) - ) + .addComponent(runescapeVersion)) .addComponent(usernameHeader) .addComponent(username) .addComponent(issueLink) @@ -178,24 +181,12 @@ public class InfoPanel extends PluginPanel } @Subscribe - private void onClientUILoaded(ClientUILoaded e) + public void onClientUILoaded(ClientUILoaded e) { // Add the title toolbar to the infopanel if the custom chrome is disabled if (!runeliteConfig.enableCustomChrome()) { - try - { - SwingUtilities.invokeAndWait(() -> - { - JPanel toolbar = clientUI.getTitleToolbar(); - layout.replace(toolbarPanelPlaceholder, toolbar); - toolbar.revalidate(); - }); - } - catch (InterruptedException | InvocationTargetException ex) - { - throw new RuntimeException(ex); - } + titleBar.setVisible(true); } } @@ -212,8 +203,58 @@ public class InfoPanel extends PluginPanel } @Subscribe - private void onSessionClose(SessionClose e) + public void onSessionClose(SessionClose e) { setNotLoggedIn(); } + + @Subscribe + public void onTitleToolbarButtonAdded(TitleToolbarButtonAdded event) + { + if (runeliteConfig.enableCustomChrome()) + { + return; + } + + SwingUtilities.invokeLater(() -> + { + + final int iconSize = TITLEBAR_SIZE - 6; + final BufferedImage scaledImage = SwingUtil.resizeImage(event.getButton().getIcon(), iconSize, iconSize); + + final JButton button = new JButton(); + button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); + button.setName(event.getButton().getName()); + button.setToolTipText(event.getButton().getTooltip()); + button.setIcon(new ImageIcon(scaledImage)); + button.setRolloverIcon(new ImageIcon(SwingUtil.createInvertedImage(scaledImage))); + + if (event.getButton().getOnClick() != null) + { + button.addActionListener(e -> event.getButton().getOnClick().run()); + } + + event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); + + titleBar.addComponent(event.getButton(), button); + titleBar.revalidate(); + titleBar.repaint(); + }); + } + + @Subscribe + public void onTitleToolbarButtonRemoved(TitleToolbarButtonRemoved event) + { + if (runeliteConfig.enableCustomChrome()) + { + return; + } + + SwingUtilities.invokeLater(() -> + { + titleBar.removeComponent(event.getButton()); + titleBar.revalidate(); + titleBar.repaint(); + }); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index c047cbdfdb..d8f37c9556 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -28,15 +28,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; import java.awt.Color; -import java.awt.Desktop; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -51,10 +48,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.inject.Inject; -import javax.swing.JButton; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; @@ -79,6 +72,8 @@ import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest; import net.runelite.client.plugins.screenshot.imgur.ImageUploadResponse; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.TitleToolbar; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.util.Text; import net.runelite.http.api.RuneLiteAPI; @@ -124,13 +119,16 @@ public class ScreenshotPlugin extends Plugin @Inject private ClientUI clientUi; + @Inject + private TitleToolbar titleToolbar; + @Inject private OverlayRenderer overlayRenderer; @Inject private ScheduledExecutorService executor; - private JButton titleBarButton; + private NavigationButton titleBarButton; @Provides ScreenshotConfig getConfig(ConfigManager configManager) @@ -140,66 +138,24 @@ public class ScreenshotPlugin extends Plugin @Override protected void startUp() throws Exception - { - addButtonToTitleBar(); - } - - @Override - protected void shutDown() throws Exception - { - removeButtonFromTitlebar(); - } - - private void addButtonToTitleBar() { try { BufferedImage iconImage; - BufferedImage invertedIconImage; synchronized (ImageIO.class) { iconImage = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("screenshot.png")); - invertedIconImage = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("screenshot_inverted.png")); } - SwingUtilities.invokeLater(() -> - { - titleBarButton = new JButton(); - titleBarButton.setToolTipText("Take screenshot"); - titleBarButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - super.mouseClicked(e); + titleBarButton = NavigationButton.builder() + .tooltip("Take screenshot") + .icon(iconImage) + .onClick(() -> takeScreenshot( + TIME_FORMAT.format(new Date()), + client.getLocalPlayer() != null)) + .build(); - if (SwingUtilities.isLeftMouseButton(e)) - { - takeScreenshot(TIME_FORMAT.format(new Date()), client.getLocalPlayer() != null); - } - } - }); - - JPopupMenu popupMenu = new JPopupMenu(); - - JMenuItem folderItem = new JMenuItem("Open screenshot folder..."); - folderItem.addActionListener(e -> - { - try - { - Desktop.getDesktop().open(RuneLite.SCREENSHOT_DIR); - } - catch (IOException ex) - { - log.warn("Error opening screenshot directory", ex); - } - }); - popupMenu.add(folderItem); - - titleBarButton.setComponentPopupMenu(popupMenu); - - clientUi.getTitleToolbar().addButton(titleBarButton, iconImage, invertedIconImage); - }); + titleToolbar.addNavigation(titleBarButton); } catch (IOException ex) { @@ -207,12 +163,10 @@ public class ScreenshotPlugin extends Plugin } } - private void removeButtonFromTitlebar() + @Override + protected void shutDown() throws Exception { - SwingUtilities.invokeLater(() -> - { - clientUi.getTitleToolbar().remove(titleBarButton); - }); + titleToolbar.removeNavigation(titleBarButton); } @Subscribe From f1d5c4ef72171f45152e6b670e5d2250a56d9433 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 7 Mar 2018 18:59:10 +0100 Subject: [PATCH 08/11] Add Discord button through DiscordPlugin Signed-off-by: Tomas Slusny --- .../client/plugins/discord/DiscordPlugin.java | 27 ++++++++++++++++++ .../{ui => plugins/discord}/discord.png | Bin .../runelite/client/ui/discord_inverted.png | Bin 68458 -> 0 bytes 3 files changed, 27 insertions(+) rename runelite-client/src/main/resources/net/runelite/client/{ui => plugins/discord}/discord.png (100%) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/discord_inverted.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java index d6d886e801..defce8acb8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java @@ -27,19 +27,25 @@ package net.runelite.client.plugins.discord; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Provides; +import java.awt.image.BufferedImage; import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; +import javax.imageio.ImageIO; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.Skill; import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.GameStateChanged; +import net.runelite.client.RuneLiteProperties; import net.runelite.client.config.ConfigManager; import net.runelite.client.discord.DiscordService; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.task.Schedule; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.TitleToolbar; +import net.runelite.client.util.LinkBrowser; @PluginDescriptor( name = "Discord" @@ -55,9 +61,16 @@ public class DiscordPlugin extends Plugin @Inject private DiscordService discordService; + @Inject + private TitleToolbar titleToolbar; + + @Inject + private RuneLiteProperties properties; + private final DiscordState discordState = new DiscordState(); private Map skillExp = new HashMap<>(); private boolean loggedIn = false; + private NavigationButton discordButton; @Provides private DiscordConfig provideConfig(ConfigManager configManager) @@ -68,12 +81,26 @@ public class DiscordPlugin extends Plugin @Override protected void startUp() throws Exception { + BufferedImage icon; + synchronized (ImageIO.class) + { + icon = ImageIO.read(getClass().getResourceAsStream("discord.png")); + } + + discordButton = NavigationButton.builder() + .tooltip("Join Discord") + .icon(icon) + .onClick(() -> LinkBrowser.browse(properties.getDiscordInvite())) + .build(); + + titleToolbar.addNavigation(discordButton); updateGameStatus(client.getGameState(), true); } @Override protected void shutDown() throws Exception { + titleToolbar.removeNavigation(discordButton); discordService.clearPresence(); discordState.reset(); } diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/discord.png b/runelite-client/src/main/resources/net/runelite/client/plugins/discord/discord.png similarity index 100% rename from runelite-client/src/main/resources/net/runelite/client/ui/discord.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/discord/discord.png diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/discord_inverted.png b/runelite-client/src/main/resources/net/runelite/client/ui/discord_inverted.png deleted file mode 100644 index ab5fccaf293efb2ba60cb4aee921f7d37afc003a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68458 zcmeHP2|QH$7r&meZ!J_k9^`ets_4G- z(6BkJBQC2-us}%cxTUt;On*j(eL_f&kNX_GlMh;QR6&|AAsmt)wKmiY@|XjqRV~^-7jl^n zjdr&#(SshTL({FC-7TT>m!X16DbY+wNDP{87aBAb8nzQ!c~enw8?-kX8g1V0tp9#m zhE%g^Z>FL%^>dW;<_0VgQr{%#?5r_SrP4}f+Gt(3pWI@MHHx-JkDC#oBl*1J4g@8H zOZP_G(Y38oBBQcW_fWRP3iUVF1$ri}TGi0qRF&#&3_&k^{HnXvG_%J0&k^wV>^?QQ zZJ5V$(S+YR54dMa8ef7ETAny}coT9n_vGQ6$B&yD8z0RpFzrRP|iz?rqcScA;5-YhqXC(A27ilP!sR5*8o$^4l<3_x zK3XwhV-jNTZoW9);Gpiw=DVpLl25`6ab?pBUXK;jF8Ou3MrpC>g8eIHj>UGNI{m9E%_4JN8_Z&DnAH z4vZ^voIXqIZB%o#WVDx)i)yy&@6zG7MUKrorhH6RRnsv!# zGR&Q{9U78_|304TloGe_t%IxsK6y@3ND4m5%hBjBKi95(0=G`lXviy- z7hOFrYnz(F2JzJ3-vVZqC|{^}{QFkb+^8*Zm#RM z011<_c~h27vA~B_4v%YgcV`5uW{I%u&@itzG z&$j0`f1R*Pv&*r|^K0Tq3o#dgEHNd4TCvKp`Zs27^3s>nEUSHQhg()`j636Y%q{Z8 z-te4i{nPi&|1g(4xUO_*gHnTMsb}eFWdUUcW$XEG=C{Sz&(AqK_3Rv*IW}b#cPdU* zJcyndYoPUTMssX)?8Dgl%PSXeTdbCylznBbRc7Pjv#xE~c^iWk>$#qBaaz1A%RD_U zee#3c^xVV|i8%`*7No{Mz!#r?pM5Jk`SQ5)O^*a>GqOFice&wL$>1;F$S7`$$dA}{ zb=MzV27=-tk0ny&7R&i_xMm#Z;bXGun0I!|@hPyi1i-)g$eO>`~=YOV-6X z#NM;O%iwdi1a^cT*k`?`TqDv@BUZNb>CODcc|IREEqk$}=3`uJR%}sgQti1m-L`dY zA$5P=(fM;~ysP$>yTvzg&+VG3jYR)EHhn{Vnx8GsHUNLASy6MfX1H_u!}FKV_@;Ea zy^}HgWVrdy2EzvZS9?)m&m-5} z`n`T|SJ3U1+ks*~u_-|zLC(QOhrB~w##d`a&h)LW!)>WI`)YRF>{%>c%U184`n;K{ zhAA5B^`FeTs&mz-RKwl%&V83S7pq^bC+KOYX6SDG^;-S+){c;uaW6$1cFII)-dyIr z{bXg)hoHnU$r>ZoW*;}U30?5Z(f^r9sV0p%IJte4@8m-w+l%Ci@RKyFb*n=k;g+gS zT+nW*I)BHJf5P)-ZdbaYeMYS*XYZ8c!+AF1Hcw+`$B&8GD(^YNV0OM%;F0jdn@m#_ z=0{}X-{4=lZ!J$-80u= z5=hg(vOinZ^S*6rt8-+AU%v8=`5OY37vA1ec!_VrTTv&Grl4JK>mIf)vvtvQt-s{< zH8@VzPk%rSEO%khKbS4Z8pnx~iC z9)8|f-6p#;?~~3y_iLBrb#@uvx>oDN=QRJm`~9`wtiEM+8fENDY59Eoo9D9h1)iH) z%lK>fy95;_vLELBeP6D|z|lVJ!L+v)5@kPM>yCTpt1hl9?)1lr(wBy zCyLL0vi0E3v=@T+e%>mFm(yHt8q*$Q9_tYEZON)7S8eYYRzG<2Y{z$t+Mgv)=;*s{ zpV#4dYu%HUJ@+E;?NtZkj>YZr)A&-ivi4N=&gRvX_3I_Q-d&aQc~LXB=RotEyg8Li z+pqYvXVx~2a=zjmb8hwu&o@4A^t&VO*_P#=$c@R!S-K&oZAbp4o$j42vhlMjpMJ>S z{(el)gZuG%22b-lc6fE(==@ke=BeN2DP5K2xsQFPw58|S=B@Z(+-mX|_cY|b(UR`E zNAK!(uP^XT=%}u<=oAPFtm+Q`7$%7u9u^%|9xNXGHvShwJ-wGko4Z=RC*rEsR4K>H zbf4-j&yX3T>G%2m+lQ6aQPn3S+an{cd;Wf=>rKJiap!7Fi%KV6yR}VQYx##o-&zZO z>wM+jovxR(ZTww+w@Q!4mkS>q?k0YV7#9)IGmJl~*06 zLjxh`OHc2;1%iH?4nc2LLeT6e2$JzSvf_q0#0MR-GBa`9#s~HEF6#Z#1719w15kV( zExoI%#!=aCAFB-%j=oNRvrAW*;Kj zo=GuM8q?m5@hrCxfKuWJI&QnD#X-~$dOVy=0Af<$_d!n*Mh@X|!DWoxlAGm30>BeN zl*y5^CJ}A2dS?yAzq{#SK*I-B8=OP{YKfobaP1>}O~Vy9hw7ShPS>+r#a8S}_ym zXvIfwFWq@|5P(uZw184tKU8(p@<1zIYI_*c4<-Sq1wjcYwY9vWr<4TlU5D);}D<0hS#A6oS2vB8IvERs;aWXK(R|`KL<)Kmg7t6tQ4v-vI%7 zGk|tdxyc?|2*6DV;svZUNr1&8fESX=O)&xjfX4uJ4xrruxanl@5>~1V00iKTH;I4% z;4wg%188;tUTUe_7%N2v00QvFBY;3yiWeXNAOP8K1(6AGb`WGL7dN&S?E+POKa25qt5=gq2 zMHqeCNo@|sxZg1#0Mp+V_9YRe!C(}g=%WuZt8W5{2C=^P0Y+(n240k)^qD~e6Qefz zopMPB=mU)6fYD@8T8AQxK2wzZV5RGK$^rqf0!PViA7B&*l>9KNLlH)wDMo#xweE*w zRvrOp0b#^WUzSiDFyfcm8WdrWnWE+o3;hqrfB?+>Y=Eq4C<<5quovx%RXoh>NBryup+GR?->{i+#>yT}Og1NC8fcCY^+qT-gaCxV(7Xnk zC`#)&4G$%Z^mK@NBNRiB0Q8bFka@I7Fc6<;u0_-vq2M3_5CTWzLNkGAU_u+uKNn3!GQ!IBx0Z!7RiI#4NV7=ykT%fBk-dR!28KS;$1zhg+j%Kll!4U3f z+8v6vFv%67zHkLx#-LE7ff2?-v3RV+1kVquIYQJIu7JxJ6$sNHF~ZDBQ5o4wDJKLS zBYPQMgQzcD0hiGRKw^&&9HoolV0t$Xg|(D&K-8fgAELf+1zi3Q3F<)pD;_aWYIm&s za)MoIF%$JzU_^ZGgHQ}I0SG}da$%7ScGzThtcZFe6jTU60FHIS7)j1x;J_kIPKfOX z21I?~3b+hIjzw}vZNl8q6-oDU(wHuA3Jb8r-{%fdU$_D;_wDQ-U zxDO0z|7xtU_Iv6bA?gcPz-6-C{Q}mYiT3+FvNj^xL*vdGlK$OjV4-KD-`js9M1A23 zxQwvSub}4~&G~+>+&w}wC$wx)%@U%%a0OhZYN>yM77(=O`n_`32<<%3vPUyBi2A}6 zaG9pH{z+PZ(46b{$Xz2eb3n@$-K-$$3s=Bpx)#Y7&;rNm9Jwd%lUR+P)_#Vwfv7KB z0hbxJOump7SZ3zPt#hx$OzgDw3^)^r`oa}(dB7gXH_!sl=p4C4?w1&io7Os}vw)~C zTmhGv_C&ssRxq^Y$gKPXzUqLt}<5@gnLAR{*&O@p0);&IwNiJCqB_Y;1r4uz#G$Y$CaJA0PlA z0B=3>xb>uBg&TAL&;dXP!0L2ygI&-8KnDOFfE!LKRv1AC0385y0IW_IH`oOo0CWJ* z0l49$VucZO0MG$I2f*rdaf4mZ0YC=;9e^87DpnXl2LK%abO5YQ7dO}i9RPFy&;hvN zq+*2;bO6u+KnK9;ba8`S&;i)o0RkXD_MbpL!ha5le_-Uh_ Date: Fri, 9 Mar 2018 11:58:05 +0100 Subject: [PATCH 09/11] Add support for popups to navigation button - Add support for popup map to navigation button - Re-add popup to ScreenshotPlugin Signed-off-by: Tomas Slusny --- .../runelite/client/plugins/info/InfoPanel.java | 16 ++++++++++++++++ .../plugins/screenshot/ScreenshotPlugin.java | 17 +++++++++++++++++ .../java/net/runelite/client/ui/ClientUI.java | 16 ++++++++++++++++ .../runelite/client/ui/NavigationButton.java | 6 ++++++ 4 files changed, 55 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java index 76c1a7bdbd..a90f2a73c3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -36,6 +36,8 @@ import javax.swing.GroupLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; import javax.swing.LayoutStyle; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; @@ -234,6 +236,20 @@ public class InfoPanel extends PluginPanel button.addActionListener(e -> event.getButton().getOnClick().run()); } + if (event.getButton().getPopup() != null) + { + final JPopupMenu popupMenu = new JPopupMenu(); + + event.getButton().getPopup().forEach((name, callback) -> + { + final JMenuItem menuItem = new JMenuItem(name); + menuItem.addActionListener((e) -> callback.run()); + popupMenu.add(menuItem); + }); + + button.setComponentPopupMenu(popupMenu); + } + event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); titleBar.addComponent(event.getButton(), button); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index d8f37c9556..ab776dbb93 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -25,9 +25,11 @@ package net.runelite.client.plugins.screenshot; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; import java.awt.Color; +import java.awt.Desktop; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Toolkit; @@ -153,6 +155,21 @@ public class ScreenshotPlugin extends Plugin .onClick(() -> takeScreenshot( TIME_FORMAT.format(new Date()), client.getLocalPlayer() != null)) + .popup(ImmutableMap + .builder() + .put("Open screenshot folder...", () -> + { + try + { + Desktop.getDesktop().open(RuneLite.SCREENSHOT_DIR); + } + catch (IOException ex) + { + log.warn("Error opening screenshot dir", ex); + + } + }) + .build()) .build(); titleToolbar.addNavigation(titleBarButton); 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 d9b518849b..6053663f59 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 @@ -49,7 +49,9 @@ import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.SwingUtilities; import lombok.Getter; @@ -274,6 +276,20 @@ public class ClientUI button.addActionListener(e -> event.getButton().getOnClick().run()); } + if (event.getButton().getPopup() != null) + { + final JPopupMenu popupMenu = new JPopupMenu(); + + event.getButton().getPopup().forEach((name, callback) -> + { + final JMenuItem menuItem = new JMenuItem(name); + menuItem.addActionListener((e) -> callback.run()); + popupMenu.add(menuItem); + }); + + button.setComponentPopupMenu(popupMenu); + } + event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); titleToolbar.addComponent(event.getButton(), button); }); 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 8ebb330b48..608f365455 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 @@ -26,6 +26,7 @@ package net.runelite.client.ui; import java.awt.image.BufferedImage; +import java.util.Map; import java.util.function.Supplier; import lombok.Builder; import lombok.Data; @@ -73,4 +74,9 @@ public class NavigationButton * Supplier for plugin panel, used when expanding and contracting sidebar. */ private Supplier panel; + + /** + * Map of key-value pairs for setting the popup menu + */ + private Map popup; } From d1cbf181e3573d831adf0373e02d907813b7cced Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Fri, 9 Mar 2018 14:44:14 +0100 Subject: [PATCH 10/11] Change panel supplier to just getter for panel As none of the plugins are creating the panels on demand anymore, supplier for panel is not needed. Signed-off-by: Tomas Slusny --- .../net/runelite/client/plugins/config/ConfigPlugin.java | 2 +- .../runelite/client/plugins/devtools/DevToolsPlugin.java | 2 +- .../java/net/runelite/client/plugins/feed/FeedPlugin.java | 2 +- .../client/plugins/grandexchange/GrandExchangePlugin.java | 2 +- .../net/runelite/client/plugins/hiscore/HiscorePlugin.java | 2 +- .../java/net/runelite/client/plugins/info/InfoPlugin.java | 2 +- .../plugins/kourendlibrary/KourendLibraryPlugin.java | 2 +- .../net/runelite/client/plugins/notes/NotesPlugin.java | 2 +- .../runelite/client/plugins/xptracker/XpTrackerPlugin.java | 2 +- .../src/main/java/net/runelite/client/ui/ClientUI.java | 7 +++---- .../main/java/net/runelite/client/ui/NavigationButton.java | 5 ++--- 11 files changed, 14 insertions(+), 16 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 83838f3d01..08a379875b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -78,7 +78,7 @@ public class ConfigPlugin extends Plugin navButton = NavigationButton.builder() .name("Configuration") .icon(icon) - .panel(() -> configPanel) + .panel(configPanel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index 84a406b3c6..d7e071c48b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -92,7 +92,7 @@ public class DevToolsPlugin extends Plugin navButton = NavigationButton.builder() .name("Developer Tools") .icon(icon) - .panel(() -> panel) + .panel(panel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java index 3bd072e431..7f011e4fca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/feed/FeedPlugin.java @@ -93,7 +93,7 @@ public class FeedPlugin extends Plugin navButton = NavigationButton.builder() .name("News Feed") .icon(icon) - .panel(() -> feedPanel) + .panel(feedPanel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index b2a1ddcfe0..e78e4f50ca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -94,7 +94,7 @@ public class GrandExchangePlugin extends Plugin button = NavigationButton.builder() .name("GE Offers") .icon(icon) - .panel(() -> panel) + .panel(panel) .build(); pluginToolbar.addNavigation(button); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java index ac43c3a2d3..c30c4c1669 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java @@ -84,7 +84,7 @@ public class HiscorePlugin extends Plugin navButton = NavigationButton.builder() .name("Hiscore") .icon(icon) - .panel(() -> hiscorePanel) + .panel(hiscorePanel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java index 43abd0ddc0..bb14b4ea3e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java @@ -58,7 +58,7 @@ public class InfoPlugin extends Plugin navButton = NavigationButton.builder() .name("Info") .icon(icon) - .panel(() -> panel) + .panel(panel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index 5ed9759b58..50213e05e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -89,7 +89,7 @@ public class KourendLibraryPlugin extends Plugin navButton = NavigationButton.builder() .name("Kourend Library") .icon(icon) - .panel(() -> panel) + .panel(panel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java index c5f7984e3a..0cd89cfa66 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/notes/NotesPlugin.java @@ -74,7 +74,7 @@ public class NotesPlugin extends Plugin navButton = NavigationButton.builder() .name("Notes") .icon(icon) - .panel(() -> panel) + .panel(panel) .build(); pluginToolbar.addNavigation(navButton); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index 34bfcee6db..6820c2e55d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -115,7 +115,7 @@ public class XpTrackerPlugin extends Plugin navButton = NavigationButton.builder() .name("XP Tracker") .icon(icon) - .panel(() -> xpPanel) + .panel(xpPanel) .build(); pluginToolbar.addNavigation(navButton); 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 6053663f59..cbd14b89b2 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,7 +39,6 @@ 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; @@ -210,9 +209,9 @@ public class ClientUI button.setIcon(new ImageIcon(event.getButton().getIcon())); button.addActionListener(e -> { - final Supplier panelSupplier = event.getButton().getPanel(); + final PluginPanel panel = event.getButton().getPanel(); - if (panelSupplier == null) + if (panel == null) { return; } @@ -231,7 +230,7 @@ public class ClientUI { currentButton = button; currentButton.setSelected(true); - expand(panelSupplier.get()); + expand(panel); } if (event.getButton().getOnClick() != null) 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 608f365455..18205a89ca 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 @@ -27,7 +27,6 @@ package net.runelite.client.ui; import java.awt.image.BufferedImage; import java.util.Map; -import java.util.function.Supplier; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; @@ -71,9 +70,9 @@ public class NavigationButton private Runnable onClick; /** - * Supplier for plugin panel, used when expanding and contracting sidebar. + * Plugin panel, used when expanding and contracting sidebar. */ - private Supplier panel; + private PluginPanel panel; /** * Map of key-value pairs for setting the popup menu From 981855a14dd5c0c344579908038bb6acf75c8276 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Sat, 10 Mar 2018 12:36:45 +0100 Subject: [PATCH 11/11] Consistent behaviour between ClientUI and InfoPane - Move method for creation of all buttons from NavigationButton to swing util - Correctly update InfoPanel when plugin is started or needs title bar refresh - Correctly update ClientUI title bar when custom one is visible Signed-off-by: Tomas Slusny --- .../client/plugins/info/InfoPanel.java | 97 +++++--------- .../client/plugins/info/InfoPlugin.java | 13 ++ .../java/net/runelite/client/ui/ClientUI.java | 97 ++++---------- .../net/runelite/client/util/SwingUtil.java | 120 +++++++++++------- 4 files changed, 147 insertions(+), 180 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java index a90f2a73c3..8465bbbef8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -28,16 +28,13 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import java.awt.Font; -import java.awt.image.BufferedImage; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; +import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.GroupLayout; -import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; import javax.swing.LayoutStyle; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; @@ -54,12 +51,11 @@ import net.runelite.client.events.TitleToolbarButtonRemoved; import net.runelite.client.ui.ClientTitleToolbar; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; -import net.runelite.client.ui.TitleToolbar; import net.runelite.client.util.RunnableExceptionLogger; import net.runelite.client.util.SwingUtil; -import org.pushingpixels.substance.internal.SubstanceSynapse; @Slf4j +@Singleton public class InfoPanel extends PluginPanel { private static final int TITLEBAR_SIZE = 23; @@ -69,9 +65,6 @@ public class InfoPanel extends PluginPanel @Nullable private Client client; - @Inject - private TitleToolbar titleToolbar; - @Inject private RuneLiteConfig runeliteConfig; @@ -97,7 +90,7 @@ public class InfoPanel extends PluginPanel setLayout(layout); final Font smallFont = FontManager.getRunescapeSmallFont(); - titleBar.setVisible(false); + updateTitleBar(); final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); runeliteVersionHeader.setFont(smallFont); @@ -125,7 +118,8 @@ public class InfoPanel extends PluginPanel } } }); - setNotLoggedIn(); + + updateLoggedIn(); final JRichTextPane issueLink = new JRichTextPane("text/html", "RuneLite is open source!
" @@ -175,39 +169,48 @@ public class InfoPanel extends PluginPanel eventBus.register(this); } - private void setNotLoggedIn() + private void updateLoggedIn() { - username.setContentType("text/html"); - username.setText("Login to sync settings to the cloud."); - usernameHeader.setText("Not logged in"); - } + final String name = sessionManager.getAccountSession() != null + ? sessionManager.getAccountSession().getUsername() + : null; - @Subscribe - public void onClientUILoaded(ClientUILoaded e) - { - // Add the title toolbar to the infopanel if the custom chrome is disabled - if (!runeliteConfig.enableCustomChrome()) - { - titleBar.setVisible(true); - } - } - - @Subscribe - public void onSessionOpen(SessionOpen sessionOpen) - { - String name = sessionManager.getAccountSession().getUsername(); if (name != null) { username.setContentType("text/plain"); username.setText(name); usernameHeader.setText("Logged in as"); } + else + { + username.setContentType("text/html"); + username.setText("Login to sync settings to the cloud."); + usernameHeader.setText("Not logged in"); + } + } + + private void updateTitleBar() + { + titleBar.setVisible(!runeliteConfig.enableCustomChrome()); + } + + @Subscribe + public void onClientUILoaded(ClientUILoaded e) + { + // Add the title toolbar to the infopanel if the custom chrome is disabled + updateTitleBar(); + } + + @Subscribe + public void onSessionOpen(SessionOpen sessionOpen) + { + updateLoggedIn(); } @Subscribe public void onSessionClose(SessionClose e) { - setNotLoggedIn(); + updateLoggedIn(); } @Subscribe @@ -220,38 +223,8 @@ public class InfoPanel extends PluginPanel SwingUtilities.invokeLater(() -> { - final int iconSize = TITLEBAR_SIZE - 6; - final BufferedImage scaledImage = SwingUtil.resizeImage(event.getButton().getIcon(), iconSize, iconSize); - - final JButton button = new JButton(); - button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); - button.setName(event.getButton().getName()); - button.setToolTipText(event.getButton().getTooltip()); - button.setIcon(new ImageIcon(scaledImage)); - button.setRolloverIcon(new ImageIcon(SwingUtil.createInvertedImage(scaledImage))); - - if (event.getButton().getOnClick() != null) - { - button.addActionListener(e -> event.getButton().getOnClick().run()); - } - - if (event.getButton().getPopup() != null) - { - final JPopupMenu popupMenu = new JPopupMenu(); - - event.getButton().getPopup().forEach((name, callback) -> - { - final JMenuItem menuItem = new JMenuItem(name); - menuItem.addActionListener((e) -> callback.run()); - popupMenu.add(menuItem); - }); - - button.setComponentPopupMenu(popupMenu); - } - - event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); - + final JButton button = SwingUtil.createSwingButton(event.getButton(), iconSize, null); titleBar.addComponent(event.getButton(), button); titleBar.revalidate(); titleBar.repaint(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java index bb14b4ea3e..a79bf2bec5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java @@ -27,10 +27,12 @@ package net.runelite.client.plugins.info; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import javax.inject.Inject; +import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.PluginToolbar; +import net.runelite.client.ui.TitleToolbar; @PluginDescriptor( name = "Info Panel", @@ -41,6 +43,12 @@ public class InfoPlugin extends Plugin @Inject private PluginToolbar pluginToolbar; + @Inject + private TitleToolbar titleToolbar; + + @Inject + private RuneLiteConfig runeLiteConfig; + private NavigationButton navButton; @Override @@ -62,6 +70,11 @@ public class InfoPlugin extends Plugin .build(); pluginToolbar.addNavigation(navButton); + + if (!runeLiteConfig.enableCustomChrome()) + { + titleToolbar.refresh(); + } } @Override 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 cbd14b89b2..39d3d40ee8 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 @@ -44,13 +44,10 @@ 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.JMenuItem; import javax.swing.JPanel; -import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.SwingUtilities; import lombok.Getter; @@ -70,7 +67,6 @@ import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; -import org.pushingpixels.substance.internal.SubstanceSynapse; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; @@ -202,45 +198,37 @@ 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 -> + SwingUtilities.invokeLater(() -> { - final PluginPanel panel = event.getButton().getPanel(); + final JButton button = SwingUtil.createSwingButton(event.getButton(), 0, (jButton) -> + { + final PluginPanel panel = event.getButton().getPanel(); - if (panel == null) - { - return; - } + if (panel == null) + { + return; + } - if (currentButton != null) - { - currentButton.setSelected(false); - } + if (currentButton != null) + { + currentButton.setSelected(false); + } - if (currentButton == button) - { - contract(); - currentButton = null; - } - else - { - currentButton = button; - currentButton.setSelected(true); - expand(panel); - } + if (currentButton == jButton) + { + contract(); + currentButton = null; + } + else + { + currentButton = jButton; + currentButton.setSelected(true); + expand(panel); + } + }); - if (event.getButton().getOnClick() != null) - { - event.getButton().getOnClick().run(); - } + pluginToolbar.addComponent(event.getIndex(), event.getButton(), button); }); - - event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); - SwingUtilities.invokeLater(() -> pluginToolbar.addComponent(event.getIndex(), event.getButton(), button)); } @Subscribe @@ -252,44 +240,15 @@ public class ClientUI @Subscribe public void onTitleToolbarButtonAdded(final TitleToolbarButtonAdded event) { - if (!config.enableCustomChrome()) + if (!config.enableCustomChrome() && !SwingUtil.isCustomTitlePanePresent(frame)) { return; } SwingUtilities.invokeLater(() -> { - final int iconSize = ClientTitleToolbar.TITLEBAR_SIZE - 6; - final BufferedImage scaledImage = SwingUtil.resizeImage(event.getButton().getIcon(), iconSize, iconSize); - final JButton button = new JButton(); - button.setName(event.getButton().getName()); - button.setToolTipText(event.getButton().getTooltip()); - button.setIcon(new ImageIcon(scaledImage)); - button.setRolloverIcon(new ImageIcon(SwingUtil.createInvertedImage(scaledImage))); - button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); - button.setFocusable(false); - - if (event.getButton().getOnClick() != null) - { - button.addActionListener(e -> event.getButton().getOnClick().run()); - } - - if (event.getButton().getPopup() != null) - { - final JPopupMenu popupMenu = new JPopupMenu(); - - event.getButton().getPopup().forEach((name, callback) -> - { - final JMenuItem menuItem = new JMenuItem(name); - menuItem.addActionListener((e) -> callback.run()); - popupMenu.add(menuItem); - }); - - button.setComponentPopupMenu(popupMenu); - } - - event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected())); + final JButton button = SwingUtil.createSwingButton(event.getButton(), iconSize, null); titleToolbar.addComponent(event.getButton(), button); }); } @@ -297,7 +256,7 @@ public class ClientUI @Subscribe public void onTitleToolbarButtonRemoved(final TitleToolbarButtonRemoved event) { - if (!config.enableCustomChrome()) + if (!config.enableCustomChrome() && !SwingUtil.isCustomTitlePanePresent(frame)) { return; } 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 e5b9a29444..0eaac05395 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 @@ -31,22 +31,24 @@ import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; -import java.awt.RenderingHints; import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; +import java.awt.Window; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; -import java.awt.image.LookupOp; -import java.awt.image.LookupTable; import java.util.Enumeration; import java.util.concurrent.Callable; +import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JFrame; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.LookAndFeel; @@ -56,6 +58,9 @@ import javax.swing.UnsupportedLookAndFeelException; import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE; import javax.swing.plaf.FontUIResource; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.NavigationButton; +import org.pushingpixels.substance.internal.SubstanceSynapse; +import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; /** * Various Swing utilities. @@ -234,44 +239,7 @@ public class SwingUtil frame.setMinimumSize(frame.getLayout().minimumLayoutSize(frame)); } - /** - * Create inverted buffered image - * - * @param image buffered image - * @return inverted buffered image - */ - public static BufferedImage createInvertedImage(BufferedImage image) - { - if (image.getType() != BufferedImage.TYPE_INT_ARGB) - { - image = convertToARGB(image); - } - - final LookupTable lookup = new LookupTable(0, 4) - { - @Override - public int[] lookupPixel(int[] src, int[] dest) - { - dest[0] = 255 - src[0]; - dest[1] = 255 - src[1]; - dest[2] = 255 - src[2]; - return dest; - } - }; - - final LookupOp op = new LookupOp(lookup, new RenderingHints(null)); - return op.filter(image, null); - } - - /** - * Resize buffered image. - * - * @param image the image - * @param newWidth the new width - * @param newHeight the new height - * @return the buffered image - */ - public static BufferedImage resizeImage(BufferedImage image, int newWidth, int newHeight) + private static BufferedImage resizeImage(BufferedImage image, int newWidth, int newHeight) { final Image tmp = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); @@ -282,15 +250,69 @@ public class SwingUtil return dimg; } - private static BufferedImage convertToARGB(final BufferedImage image) + /** + * Create swing button from navigation button. + * + * @param navigationButton the navigation button + * @param iconSize the icon size (in case it is 0 default icon size will be used) + * @param specialCallback the special callback + * @return the swing button + */ + public static JButton createSwingButton( + @Nonnull final NavigationButton navigationButton, + int iconSize, + @Nullable final Consumer specialCallback) { - final BufferedImage newImage = new BufferedImage( - image.getWidth(), image.getHeight(), - BufferedImage.TYPE_INT_ARGB); - final Graphics2D g = newImage.createGraphics(); - g.drawImage(image, 0, 0, null); - g.dispose(); - return newImage; + final BufferedImage scaledImage = iconSize > 0 + ? resizeImage(navigationButton.getIcon(), iconSize, iconSize) + : navigationButton.getIcon(); + + final JButton button = new JButton(); + button.setName(navigationButton.getName()); + button.setToolTipText(navigationButton.getTooltip()); + button.setIcon(new ImageIcon(scaledImage)); + button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); + button.setFocusable(false); + button.addActionListener(e -> + { + if (specialCallback != null) + { + specialCallback.accept(button); + } + + if (navigationButton.getOnClick() != null) + { + navigationButton.getOnClick().run(); + } + }); + + if (navigationButton.getPopup() != null) + { + final JPopupMenu popupMenu = new JPopupMenu(); + + navigationButton.getPopup().forEach((name, callback) -> + { + final JMenuItem menuItem = new JMenuItem(name); + menuItem.addActionListener((e) -> callback.run()); + popupMenu.add(menuItem); + }); + + button.setComponentPopupMenu(popupMenu); + } + + navigationButton.setOnSelect(() -> button.setSelected(navigationButton.isSelected())); + return button; + } + + /** + * Checks if custom substance title pane is present. + * + * @param frame the parent frame + * @return true if title pane is present + */ + public static boolean isCustomTitlePanePresent(final Window frame) + { + return SubstanceCoreUtilities.getTitlePaneComponent(frame) != null; } }