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

View File

@@ -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
+ "</a>"
);
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)

View File

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

View File

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

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}
runescape.version=${rs.version}
runelite.discord.appid=409416265891971072
runelite.discord.invite=https://discord.gg/R4BQ8tU