diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index 41677620d6..6d840f754b 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -40,6 +40,7 @@ public class RuneLiteProperties private static final String RUNELITE_VERSION = "runelite.version"; private static final String RUNESCAPE_VERSION = "runescape.version"; private static final String DISCORD_APP_ID = "runelite.discord.appid"; + private static final String DISCORD_INVITE = "runelite.discord.invite"; private final Properties properties = new Properties(); @@ -76,4 +77,9 @@ public class RuneLiteProperties { return properties.getProperty(DISCORD_APP_ID); } + + public String getDiscordInvite() + { + return properties.getProperty(DISCORD_INVITE); + } } 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 e58c12c5f6..c3d6030631 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,11 +28,15 @@ 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.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; +import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.LayoutStyle; +import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; @@ -40,6 +44,9 @@ import net.runelite.api.events.SessionClose; import net.runelite.api.events.SessionOpen; 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.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.util.RunnableExceptionLogger; @@ -53,6 +60,12 @@ public class InfoPanel extends PluginPanel @Nullable private Client client; + @Inject + private ClientUI clientUI; + + @Inject + private RuneLiteConfig runeliteConfig; + @Inject private RuneLiteProperties runeLiteProperties; @@ -67,6 +80,8 @@ public class InfoPanel extends PluginPanel private final GroupLayout layout = new GroupLayout(this); + private final JPanel toolbarPanelPlaceholder = new JPanel(); + private final JLabel usernameHeader = new JLabel(); private final JRichTextPane username = new JRichTextPane(); @@ -76,6 +91,8 @@ public class InfoPanel extends PluginPanel final Font smallFont = FontManager.getRunescapeSmallFont(); + toolbarPanelPlaceholder.setVisible(false); + final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); runeliteVersionHeader.setFont(smallFont); final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion()); @@ -112,7 +129,11 @@ public class InfoPanel extends PluginPanel + "" ); + setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(toolbarPanelPlaceholder) + .addGap(3) .addGroup(layout.createParallelGroup() .addComponent(runeliteVersionHeader) .addComponent(runescapeVersionHeader) @@ -130,6 +151,9 @@ 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(runeliteVersionHeader) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addComponent(runescapeVersionHeader) @@ -154,7 +178,29 @@ public class InfoPanel extends PluginPanel } @Subscribe - private void onSessionOpen(SessionOpen sessionOpen) + private 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); + } + } + } + + @Subscribe + public void onSessionOpen(SessionOpen sessionOpen) { String name = sessionManager.getAccountSession().getUsername(); if (name != null) 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 c7a5dc3a63..376df371c9 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 @@ -51,7 +51,6 @@ import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.inject.Inject; import javax.swing.JButton; -import javax.swing.JComponent; import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; @@ -69,7 +68,6 @@ import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.Notifier; import net.runelite.client.RuneLite; import net.runelite.client.config.ConfigManager; -import net.runelite.client.events.ClientUILoaded; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest; @@ -85,7 +83,6 @@ import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; @PluginDescriptor( name = "Screenshot plugin" @@ -133,7 +130,6 @@ public class ScreenshotPlugin extends Plugin @Override protected void startUp() throws Exception { - // prior to UI loading this does nothing addButtonToTitleBar(); } @@ -143,12 +139,6 @@ public class ScreenshotPlugin extends Plugin removeButtonFromTitlebar(); } - @Subscribe - public void clientUiLoaded(ClientUILoaded e) - { - addButtonToTitleBar(); - } - private void addButtonToTitleBar() { try @@ -170,7 +160,7 @@ public class ScreenshotPlugin extends Plugin } }); - clientUi.addButtonToTitleBar(titleBarButton, iconImage, invertedIconImage, 130); + clientUi.getTitleToolbar().addButton(titleBarButton, iconImage, invertedIconImage); }); } catch (IOException ex) @@ -183,14 +173,7 @@ public class ScreenshotPlugin extends Plugin { SwingUtilities.invokeLater(() -> { - JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(clientUi); - - if (titleBar != null) - { - titleBar.remove(titleBarButton); - clientUi.revalidate(); - clientUi.repaint(); - } + clientUi.getTitleToolbar().remove(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 e7c3ea3f6d..d618480fe2 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 @@ -29,10 +29,8 @@ import java.awt.AWTException; import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Cursor; -import java.awt.Desktop; import java.awt.Dimension; import java.awt.Frame; -import java.awt.Image; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.ComponentAdapter; @@ -43,13 +41,9 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; import java.util.Enumeration; import javax.imageio.ImageIO; import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JOptionPane; @@ -69,7 +63,6 @@ import net.runelite.api.GameState; import net.runelite.api.events.ConfigChanged; import net.runelite.client.RuneLiteProperties; 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; @@ -78,7 +71,6 @@ public class ClientUI extends JFrame { private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH; private static final BufferedImage ICON; - private static final String DISCORD_INVITE = "https://discord.gg/R4BQ8tU"; @Getter private TrayIcon trayIcon; @@ -89,6 +81,9 @@ public class ClientUI extends JFrame private PluginToolbar pluginToolbar; private PluginPanel pluginPanel; + @Getter + private TitleToolbar titleToolbar; + static { BufferedImage icon = null; @@ -158,46 +153,29 @@ public class ClientUI extends JFrame if (customChrome) { getRootPane().setWindowDecorationStyle(JRootPane.FRAME); + + JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(this); + titleToolbar.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING); + titleBar.add(titleToolbar); + + // The title bar doesn't have a real layout manager, so we have to do it manually + titleBar.addComponentListener(new ComponentAdapter() + { + @Override + public void componentResized(ComponentEvent e) + { + super.componentResized(e); + final int width = titleToolbar.getPreferredSize().width; + titleToolbar.setBounds(titleBar.getWidth() - 75 - width, 0, width, titleBar.getHeight()); + titleToolbar.revalidate(); + } + }); } pack(); revalidateMinimumSize(); setLocationRelativeTo(getOwner()); - if (customChrome) - { - try - { - BufferedImage discordIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord.png")); - BufferedImage 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(DISCORD_INVITE).toURI()); - } - catch (IOException | URISyntaxException ex) - { - log.warn("error opening browser", ex); - } - } - }); - - addButtonToTitleBar(discordButton, discordIcon, invertedIcon, 100); - } - catch (IOException ex) - { - log.warn("unable to load discord button", ex); - } - } - setVisible(true); toFront(); requestFocus(); @@ -217,44 +195,6 @@ public class ClientUI extends JFrame } } - public void addButtonToTitleBar(JButton button, Image iconImage, Image invertedIconImage, int xOffset) - { - JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(this); - - if (titleBar == null) - { - return; - } - - int size = titleBar.getHeight() - 6; - - ImageIcon icon = new ImageIcon(iconImage.getScaledInstance(size, size, Image.SCALE_SMOOTH)); - ImageIcon invertedIcon = new ImageIcon(invertedIconImage.getScaledInstance(size, size, Image.SCALE_SMOOTH)); - - button.setIcon(icon); - button.setRolloverIcon(invertedIcon); - button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); - button.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING); - button.setFocusable(false); - button.setBounds(titleBar.getWidth() - xOffset, 2, - icon.getIconWidth() + 4, icon.getIconHeight() + 2); - - titleBar.addComponentListener(new ComponentAdapter() - { - @Override - public void componentResized(ComponentEvent e) - { - super.componentResized(e); - button.setBounds(titleBar.getWidth() - xOffset, 1, button.getWidth(), button.getHeight()); - } - }); - - titleBar.add(button); - - revalidate(); - repaint(); - } - @Subscribe public void onConfigChanged(ConfigChanged event) { @@ -382,6 +322,8 @@ public class ClientUI extends JFrame pluginToolbar = new PluginToolbar(this); container.add(pluginToolbar); + titleToolbar = new TitleToolbar(properties); + add(container); } 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 new file mode 100644 index 0000000000..c9c74e05d6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/TitleToolbar.java @@ -0,0 +1,118 @@ +/* + * 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.Desktop; +import java.awt.Image; +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.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JPanel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLiteProperties; +import org.pushingpixels.substance.internal.SubstanceSynapse; + +@Slf4j +public class TitleToolbar extends JPanel +{ + private static final int TITLEBAR_SIZE = 23; + + @Getter + private final GroupLayout.SequentialGroup horizontal; + + @Getter + private final GroupLayout.ParallelGroup vertical; + + public TitleToolbar(RuneLiteProperties properties) + { + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + horizontal = layout.createSequentialGroup(); + layout.setHorizontalGroup(horizontal); + + vertical = layout.createParallelGroup(); + layout.setVerticalGroup(vertical); + + try + { + BufferedImage discordIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord.png")); + BufferedImage 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); + } + catch (IOException ex) + { + log.warn("unable to load discord button", ex); + } + } + + public void addButton(JButton button, Image iconImage, Image invertedIconImage) + { + final int iconSize = TITLEBAR_SIZE - 6; + ImageIcon icon = new ImageIcon(iconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH)); + ImageIcon invertedIcon = new ImageIcon(invertedIconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH)); + + button.setIcon(icon); + button.setRolloverIcon(invertedIcon); + button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); + button.setFocusable(false); + + horizontal.addGap(6); + horizontal.addComponent(button, 0, TITLEBAR_SIZE, TITLEBAR_SIZE); + vertical.addComponent(button, 0, TITLEBAR_SIZE, TITLEBAR_SIZE); + revalidate(); + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index a904c2d233..4860325109 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -2,3 +2,4 @@ runelite.title=RuneLite runelite.version=${project.version} runescape.version=${rs.version} runelite.discord.appid=409416265891971072 +runelite.discord.invite=https://discord.gg/R4BQ8tU \ No newline at end of file