Allow titlebar buttons to be moved out of the titlebar

This commit is contained in:
Abex
2018-02-08 16:42:12 -07:00
committed by Adam
parent acb2bce8fa
commit c754677413
6 changed files with 196 additions and 100 deletions

View File

@@ -40,6 +40,7 @@ public class RuneLiteProperties
private static final String RUNELITE_VERSION = "runelite.version"; private static final String RUNELITE_VERSION = "runelite.version";
private static final String RUNESCAPE_VERSION = "runescape.version"; private static final String RUNESCAPE_VERSION = "runescape.version";
private static final String DISCORD_APP_ID = "runelite.discord.appid"; 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(); private final Properties properties = new Properties();
@@ -76,4 +77,9 @@ public class RuneLiteProperties
{ {
return properties.getProperty(DISCORD_APP_ID); return properties.getProperty(DISCORD_APP_ID);
} }
public String getDiscordInvite()
{
return properties.getProperty(DISCORD_INVITE);
}
} }

View File

@@ -28,11 +28,15 @@ import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Font; import java.awt.Font;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout; import javax.swing.GroupLayout;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.LayoutStyle; import javax.swing.LayoutStyle;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
@@ -40,6 +44,9 @@ import net.runelite.api.events.SessionClose;
import net.runelite.api.events.SessionOpen; import net.runelite.api.events.SessionOpen;
import net.runelite.client.RuneLiteProperties; import net.runelite.client.RuneLiteProperties;
import net.runelite.client.account.SessionManager; 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.FontManager;
import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.RunnableExceptionLogger; import net.runelite.client.util.RunnableExceptionLogger;
@@ -53,6 +60,12 @@ public class InfoPanel extends PluginPanel
@Nullable @Nullable
private Client client; private Client client;
@Inject
private ClientUI clientUI;
@Inject
private RuneLiteConfig runeliteConfig;
@Inject @Inject
private RuneLiteProperties runeLiteProperties; private RuneLiteProperties runeLiteProperties;
@@ -67,6 +80,8 @@ public class InfoPanel extends PluginPanel
private final GroupLayout layout = new GroupLayout(this); private final GroupLayout layout = new GroupLayout(this);
private final JPanel toolbarPanelPlaceholder = new JPanel();
private final JLabel usernameHeader = new JLabel(); private final JLabel usernameHeader = new JLabel();
private final JRichTextPane username = new JRichTextPane(); private final JRichTextPane username = new JRichTextPane();
@@ -76,6 +91,8 @@ public class InfoPanel extends PluginPanel
final Font smallFont = FontManager.getRunescapeSmallFont(); final Font smallFont = FontManager.getRunescapeSmallFont();
toolbarPanelPlaceholder.setVisible(false);
final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); final JLabel runeliteVersionHeader = new JLabel("RuneLite version");
runeliteVersionHeader.setFont(smallFont); runeliteVersionHeader.setFont(smallFont);
final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion()); final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion());
@@ -112,7 +129,11 @@ public class InfoPanel extends PluginPanel
+ "</a>" + "</a>"
); );
setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6));
layout.setVerticalGroup(layout.createSequentialGroup() layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(toolbarPanelPlaceholder)
.addGap(3)
.addGroup(layout.createParallelGroup() .addGroup(layout.createParallelGroup()
.addComponent(runeliteVersionHeader) .addComponent(runeliteVersionHeader)
.addComponent(runescapeVersionHeader) .addComponent(runescapeVersionHeader)
@@ -130,6 +151,9 @@ public class InfoPanel extends PluginPanel
layout.setHorizontalGroup(layout.createParallelGroup() layout.setHorizontalGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(toolbarPanelPlaceholder)
).addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersionHeader) .addComponent(runeliteVersionHeader)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersionHeader) .addComponent(runescapeVersionHeader)
@@ -154,7 +178,29 @@ public class InfoPanel extends PluginPanel
} }
@Subscribe @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(); String name = sessionManager.getAccountSession().getUsername();
if (name != null) if (name != null)

View File

@@ -51,7 +51,6 @@ import java.util.regex.Pattern;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject; import javax.inject.Inject;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType; 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.Notifier;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.ClientUILoaded;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest; import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest;
@@ -85,7 +83,6 @@ import okhttp3.MediaType;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
@PluginDescriptor( @PluginDescriptor(
name = "Screenshot plugin" name = "Screenshot plugin"
@@ -133,7 +130,6 @@ public class ScreenshotPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
// prior to UI loading this does nothing
addButtonToTitleBar(); addButtonToTitleBar();
} }
@@ -143,12 +139,6 @@ public class ScreenshotPlugin extends Plugin
removeButtonFromTitlebar(); removeButtonFromTitlebar();
} }
@Subscribe
public void clientUiLoaded(ClientUILoaded e)
{
addButtonToTitleBar();
}
private void addButtonToTitleBar() private void addButtonToTitleBar()
{ {
try 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) catch (IOException ex)
@@ -183,14 +173,7 @@ public class ScreenshotPlugin extends Plugin
{ {
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
{ {
JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(clientUi); clientUi.getTitleToolbar().remove(titleBarButton);
if (titleBar != null)
{
titleBar.remove(titleBarButton);
clientUi.revalidate();
clientUi.repaint();
}
}); });
} }

View File

@@ -29,10 +29,8 @@ import java.awt.AWTException;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Canvas; import java.awt.Canvas;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Image;
import java.awt.SystemTray; import java.awt.SystemTray;
import java.awt.TrayIcon; import java.awt.TrayIcon;
import java.awt.event.ComponentAdapter; import java.awt.event.ComponentAdapter;
@@ -43,13 +41,9 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@@ -69,7 +63,6 @@ import net.runelite.api.GameState;
import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ConfigChanged;
import net.runelite.client.RuneLiteProperties; import net.runelite.client.RuneLiteProperties;
import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; 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.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; 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 int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH;
private static final BufferedImage ICON; private static final BufferedImage ICON;
private static final String DISCORD_INVITE = "https://discord.gg/R4BQ8tU";
@Getter @Getter
private TrayIcon trayIcon; private TrayIcon trayIcon;
@@ -89,6 +81,9 @@ public class ClientUI extends JFrame
private PluginToolbar pluginToolbar; private PluginToolbar pluginToolbar;
private PluginPanel pluginPanel; private PluginPanel pluginPanel;
@Getter
private TitleToolbar titleToolbar;
static static
{ {
BufferedImage icon = null; BufferedImage icon = null;
@@ -158,46 +153,29 @@ public class ClientUI extends JFrame
if (customChrome) if (customChrome)
{ {
getRootPane().setWindowDecorationStyle(JRootPane.FRAME); 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(); pack();
revalidateMinimumSize(); revalidateMinimumSize();
setLocationRelativeTo(getOwner()); 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); setVisible(true);
toFront(); toFront();
requestFocus(); 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 @Subscribe
public void onConfigChanged(ConfigChanged event) public void onConfigChanged(ConfigChanged event)
{ {
@@ -382,6 +322,8 @@ public class ClientUI extends JFrame
pluginToolbar = new PluginToolbar(this); pluginToolbar = new PluginToolbar(this);
container.add(pluginToolbar); container.add(pluginToolbar);
titleToolbar = new TitleToolbar(properties);
add(container); add(container);
} }

View File

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

View File

@@ -2,3 +2,4 @@ runelite.title=RuneLite
runelite.version=${project.version} runelite.version=${project.version}
runescape.version=${rs.version} runescape.version=${rs.version}
runelite.discord.appid=409416265891971072 runelite.discord.appid=409416265891971072
runelite.discord.invite=https://discord.gg/R4BQ8tU