Merge pull request #880 from deathbeam/cleanup-clientui

Cleanup ClientUI
This commit is contained in:
Adam
2018-03-15 11:16:51 -04:00
committed by GitHub
36 changed files with 1460 additions and 864 deletions

View File

@@ -35,7 +35,6 @@ import java.applet.Applet;
import java.io.File; import java.io.File;
import java.util.Optional; import java.util.Optional;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import joptsimple.OptionParser; import joptsimple.OptionParser;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -43,12 +42,11 @@ import net.runelite.api.Client;
import net.runelite.client.account.SessionManager; import net.runelite.client.account.SessionManager;
import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.discord.DiscordService; import net.runelite.client.discord.DiscordService;
import net.runelite.client.events.ClientUILoaded;
import net.runelite.client.menus.MenuManager; import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.PluginManager; import net.runelite.client.plugins.PluginManager;
import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.TitleToolbar;
import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.OverlayRenderer;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.MDC; import org.slf4j.MDC;
@@ -66,9 +64,6 @@ public class RuneLite
private static Injector injector; private static Injector injector;
private static OptionSet options; private static OptionSet options;
@Inject
private RuneLiteProperties properties;
@Inject @Inject
private PluginManager pluginManager; private PluginManager pluginManager;
@@ -90,17 +85,19 @@ public class RuneLite
@Inject @Inject
private SessionManager sessionManager; private SessionManager sessionManager;
@Inject
private RuneLiteConfig runeliteConfig;
@Inject @Inject
private DiscordService discordService; private DiscordService discordService;
@Inject @Inject
private ClientSessionManager clientSessionManager; private ClientSessionManager clientSessionManager;
@Inject
private ClientUI clientUI;
@Inject
private TitleToolbar titleToolbar;
Client client; Client client;
ClientUI gui;
public static void main(String[] args) throws Exception public static void main(String[] args) throws Exception
{ {
@@ -147,8 +144,8 @@ public class RuneLite
this.client = (Client) client; this.client = (Client) client;
} }
// Load swing UI // Initialize UI
SwingUtilities.invokeAndWait(() -> setGui(ClientUI.create(this, properties, client))); clientUI.init(client);
// Initialize Discord service // Initialize Discord service
discordService.init(); discordService.init();
@@ -157,10 +154,10 @@ public class RuneLite
configManager.load(); configManager.load();
// Register event listeners // Register event listeners
eventBus.register(clientUI);
eventBus.register(overlayRenderer); eventBus.register(overlayRenderer);
eventBus.register(menuManager); eventBus.register(menuManager);
eventBus.register(chatMessageManager); eventBus.register(chatMessageManager);
eventBus.register(gui);
eventBus.register(pluginManager); eventBus.register(pluginManager);
// Tell the plugin manager if client is outdated or not // Tell the plugin manager if client is outdated or not
@@ -183,28 +180,11 @@ public class RuneLite
// Load the session, including saved configuration // Load the session, including saved configuration
sessionManager.loadSession(); sessionManager.loadSession();
SwingUtilities.invokeAndWait(() -> // Refresh title toolbar
{ titleToolbar.refresh();
if (client != null)
{
client.setSize(runeliteConfig.gameSize());
client.setPreferredSize(runeliteConfig.gameSize());
client.getParent().setPreferredSize(runeliteConfig.gameSize()); // Show UI after all plugins are loaded
client.getParent().setSize(runeliteConfig.gameSize()); clientUI.show();
}
gui.showWithChrome(runeliteConfig.enableCustomChrome());
if (gui.isAlwaysOnTopSupported())
{
gui.setAlwaysOnTop(runeliteConfig.gameAlwaysOnTop());
}
gui.setResizable(!runeliteConfig.lockWindowSize());
});
eventBus.post(new ClientUILoaded());
} }
public void shutdown() public void shutdown()
@@ -213,11 +193,6 @@ public class RuneLite
discordService.close(); discordService.close();
} }
public void setGui(ClientUI gui)
{
this.gui = gui;
}
@VisibleForTesting @VisibleForTesting
public void setClient(Client client) public void setClient(Client client)
{ {

View File

@@ -41,7 +41,6 @@ import net.runelite.client.game.ItemManager;
import net.runelite.client.menus.MenuManager; import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.PluginManager; import net.runelite.client.plugins.PluginManager;
import net.runelite.client.task.Scheduler; import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.util.QueryRunner; import net.runelite.client.util.QueryRunner;
@Slf4j @Slf4j
@@ -67,12 +66,6 @@ public class RuneLiteModule extends AbstractModule
return runeLite.client; return runeLite.client;
} }
@Provides
ClientUI provideClientUi(RuneLite runelite)
{
return runelite.gui;
}
@Provides @Provides
@Singleton @Singleton
RuneLiteConfig provideConfig(ConfigManager configManager) RuneLiteConfig provideConfig(ConfigManager configManager)

View File

@@ -60,16 +60,14 @@ public class SessionManager
private final EventBus eventBus; private final EventBus eventBus;
private ConfigManager configManager; private ConfigManager configManager;
private ScheduledExecutorService executor; private ScheduledExecutorService executor;
private final LinkBrowser browser;
private final AccountClient loginClient = new AccountClient(); private final AccountClient loginClient = new AccountClient();
@Inject @Inject
public SessionManager(ConfigManager configManager, EventBus eventBus, ScheduledExecutorService executor, LinkBrowser browser) public SessionManager(ConfigManager configManager, EventBus eventBus, ScheduledExecutorService executor)
{ {
this.configManager = configManager; this.configManager = configManager;
this.eventBus = eventBus; this.eventBus = eventBus;
this.executor = executor; this.executor = executor;
this.browser = browser;
eventBus.register(this); eventBus.register(this);
} }
@@ -213,7 +211,7 @@ public class SessionManager
openSession(new AccountSession(login.getUid(), Instant.now())); openSession(new AccountSession(login.getUid(), Instant.now()));
// Navigate to login link // Navigate to login link
browser.browse(login.getOauthUrl()); LinkBrowser.browse(login.getOauthUrl());
} }
@Subscribe @Subscribe

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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;
}

View File

@@ -25,22 +25,20 @@
package net.runelite.client.plugins.account; package net.runelite.client.plugins.account;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject; import javax.inject.Inject;
import javax.swing.JButton;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.events.SessionClose; import net.runelite.api.events.SessionClose;
import net.runelite.api.events.SessionOpen;
import net.runelite.client.account.AccountSession; import net.runelite.client.account.AccountSession;
import net.runelite.client.account.SessionManager; import net.runelite.client.account.SessionManager;
import net.runelite.api.events.SessionOpen;
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.ui.ClientUI; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.TitleToolbar; import net.runelite.client.ui.TitleToolbar;
import net.runelite.client.util.RunnableExceptionLogger; import net.runelite.client.util.RunnableExceptionLogger;
@@ -55,13 +53,13 @@ public class AccountPlugin extends Plugin
private SessionManager sessionManager; private SessionManager sessionManager;
@Inject @Inject
private ClientUI ui; private TitleToolbar titleToolbar;
@Inject @Inject
private ScheduledExecutorService executor; private ScheduledExecutorService executor;
private JButton loginButton; private NavigationButton loginButton;
private JButton logoutButton; private NavigationButton logoutButton;
private static final BufferedImage LOGIN_IMAGE, LOGOUT_IMAGE; private static final BufferedImage LOGIN_IMAGE, LOGOUT_IMAGE;
@@ -84,47 +82,45 @@ public class AccountPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
loginButton = new JButton(); loginButton = NavigationButton.builder()
loginButton.setToolTipText("Login"); .icon(LOGIN_IMAGE)
loginButton.addActionListener(this::loginClick); .tooltip("Login")
.onClick(this::loginClick)
.build();
logoutButton = new JButton(); logoutButton = NavigationButton.builder()
logoutButton.setToolTipText("Logout"); .icon(LOGOUT_IMAGE)
logoutButton.addActionListener(this::logoutClick); .tooltip("Logout")
.onClick(this::logoutClick)
.build();
addAndRemoveButtons(); addAndRemoveButtons();
} }
private void addAndRemoveButtons() private void addAndRemoveButtons()
{ {
TitleToolbar tb = ui.getTitleToolbar(); titleToolbar.removeNavigation(loginButton);
tb.remove(loginButton); titleToolbar.removeNavigation(logoutButton);
tb.remove(logoutButton); titleToolbar.addNavigation(sessionManager.getAccountSession() == null
if (sessionManager.getAccountSession() == null) ? loginButton
{ : logoutButton);
tb.addButton(loginButton, LOGIN_IMAGE, LOGIN_IMAGE);
}
else
{
tb.addButton(logoutButton, LOGOUT_IMAGE, LOGOUT_IMAGE);
}
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getTitleToolbar().remove(loginButton); titleToolbar.removeNavigation(loginButton);
ui.getTitleToolbar().remove(logoutButton); titleToolbar.removeNavigation(logoutButton);
} }
private void loginClick(ActionEvent ae) private void loginClick()
{ {
executor.execute(RunnableExceptionLogger.wrap(sessionManager::login)); 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", "Are you sure you want to logout?", "Logout Confirmation",
JOptionPane.YES_NO_OPTION)) JOptionPane.YES_NO_OPTION))
{ {

View File

@@ -36,8 +36,8 @@ import net.runelite.client.events.PluginChanged;
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.PluginManager; import net.runelite.client.plugins.PluginManager;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
@PluginDescriptor( @PluginDescriptor(
name = "Configuration", name = "Configuration",
@@ -47,7 +47,7 @@ import net.runelite.client.ui.NavigationButton;
public class ConfigPlugin extends Plugin public class ConfigPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private ConfigManager configManager; private ConfigManager configManager;
@@ -75,18 +75,19 @@ public class ConfigPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("config_icon.png")); icon = ImageIO.read(getClass().getResourceAsStream("config_icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Configuration", .name("Configuration")
icon, .icon(icon)
() -> configPanel); .panel(configPanel)
.build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
@Subscribe @Subscribe

View File

@@ -35,9 +35,9 @@ import net.runelite.api.widgets.Widget;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
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.ui.ClientUI;
import net.runelite.client.ui.FontManager; import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.Overlay;
@PluginDescriptor( @PluginDescriptor(
@@ -47,7 +47,7 @@ import net.runelite.client.ui.overlay.Overlay;
public class DevToolsPlugin extends Plugin public class DevToolsPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private DevToolsOverlay overlay; private DevToolsOverlay overlay;
@@ -89,12 +89,13 @@ public class DevToolsPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("devtools_icon.png")); icon = ImageIO.read(getClass().getResourceAsStream("devtools_icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Developer Tools", .name("Developer Tools")
icon, .icon(icon)
() -> panel); .panel(panel)
.build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
font = FontManager.getRunescapeFont() font = FontManager.getRunescapeFont()
.deriveFont(Font.BOLD, 16); .deriveFont(Font.BOLD, 16);
@@ -103,7 +104,7 @@ public class DevToolsPlugin extends Plugin
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
@Override @Override

View File

@@ -27,19 +27,25 @@ package net.runelite.client.plugins.discord;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.imageio.ImageIO;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.Skill; import net.runelite.api.Skill;
import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService; import net.runelite.client.discord.DiscordService;
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.task.Schedule; 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( @PluginDescriptor(
name = "Discord" name = "Discord"
@@ -55,9 +61,16 @@ public class DiscordPlugin extends Plugin
@Inject @Inject
private DiscordService discordService; private DiscordService discordService;
@Inject
private TitleToolbar titleToolbar;
@Inject
private RuneLiteProperties properties;
private final DiscordState discordState = new DiscordState(); private final DiscordState discordState = new DiscordState();
private Map<Skill, Integer> skillExp = new HashMap<>(); private Map<Skill, Integer> skillExp = new HashMap<>();
private boolean loggedIn = false; private boolean loggedIn = false;
private NavigationButton discordButton;
@Provides @Provides
private DiscordConfig provideConfig(ConfigManager configManager) private DiscordConfig provideConfig(ConfigManager configManager)
@@ -68,12 +81,26 @@ public class DiscordPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception 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); updateGameStatus(client.getGameState(), true);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
titleToolbar.removeNavigation(discordButton);
discordService.clearPresence(); discordService.clearPresence();
discordState.reset(); discordState.reset();
} }

View File

@@ -120,13 +120,11 @@ class FeedPanel extends PluginPanel
private final FeedConfig config; private final FeedConfig config;
private final Supplier<FeedResult> feedSupplier; private final Supplier<FeedResult> feedSupplier;
private final LinkBrowser linkBrowser;
FeedPanel(FeedConfig config, Supplier<FeedResult> feedSupplier, LinkBrowser linkBrowser) FeedPanel(FeedConfig config, Supplier<FeedResult> feedSupplier)
{ {
this.config = config; this.config = config;
this.feedSupplier = feedSupplier; this.feedSupplier = feedSupplier;
this.linkBrowser = linkBrowser;
} }
void rebuildFeed() void rebuildFeed()
@@ -294,7 +292,7 @@ class FeedPanel extends PluginPanel
public void mouseReleased(MouseEvent e) public void mouseReleased(MouseEvent e)
{ {
avatarAndRight.setBackground(hoverColor); avatarAndRight.setBackground(hoverColor);
linkBrowser.browse(item.getUrl()); LinkBrowser.browse(item.getUrl());
} }
}); });

View File

@@ -41,9 +41,8 @@ import net.runelite.client.config.ConfigManager;
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.task.Schedule; import net.runelite.client.task.Schedule;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.LinkBrowser; import net.runelite.client.ui.PluginToolbar;
import net.runelite.http.api.feed.FeedClient; import net.runelite.http.api.feed.FeedClient;
import net.runelite.http.api.feed.FeedResult; import net.runelite.http.api.feed.FeedResult;
@@ -55,7 +54,7 @@ import net.runelite.http.api.feed.FeedResult;
public class FeedPlugin extends Plugin public class FeedPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private FeedConfig config; private FeedConfig config;
@@ -63,9 +62,6 @@ public class FeedPlugin extends Plugin
@Inject @Inject
private ScheduledExecutorService executorService; private ScheduledExecutorService executorService;
@Inject
private LinkBrowser linkBrowser;
private FeedPanel feedPanel; private FeedPanel feedPanel;
private NavigationButton navButton; private NavigationButton navButton;
@@ -86,7 +82,7 @@ public class FeedPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
feedPanel = new FeedPanel(config, feedSupplier, linkBrowser); feedPanel = new FeedPanel(config, feedSupplier);
BufferedImage icon; BufferedImage icon;
synchronized (ImageIO.class) synchronized (ImageIO.class)
@@ -94,20 +90,20 @@ public class FeedPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("icon.png")); icon = ImageIO.read(getClass().getResourceAsStream("icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"News Feed", .name("News Feed")
icon, .icon(icon)
() -> feedPanel); .panel(feedPanel)
.build();
ui.getPluginToolbar().addNavigation(navButton);
pluginToolbar.addNavigation(navButton);
executorService.submit(this::updateFeed); executorService.submit(this::updateFeed);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
private void updateFeed() private void updateFeed()

View File

@@ -45,10 +45,9 @@ import net.runelite.client.util.LinkBrowser;
class GrandExchangeItemPanel extends JPanel class GrandExchangeItemPanel extends JPanel
{ {
private static final NumberFormat NUMBER_FORMATTER = NumberFormat.getInstance(); private static final NumberFormat NUMBER_FORMATTER = NumberFormat.getInstance();
private static final Dimension ICON_SIZE = new Dimension(32, 32); 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) haPrice)
{ {
BorderLayout layout = new BorderLayout(); BorderLayout layout = new BorderLayout();
@@ -75,7 +74,7 @@ class GrandExchangeItemPanel extends JPanel
@Override @Override
public void mouseReleased(MouseEvent e) public void mouseReleased(MouseEvent e)
{ {
geLink(linkBrowser, name, itemID); geLink(name, itemID);
} }
}); });
@@ -125,13 +124,13 @@ class GrandExchangeItemPanel extends JPanel
add(rightPanel, BorderLayout.CENTER); 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/" final String url = "http://services.runescape.com/m=itemdb_oldschool/"
+ name.replaceAll(" ", "_") + name.replaceAll(" ", "_")
+ "/viewitem?obj=" + "/viewitem?obj="
+ itemID; + itemID;
linkBrowser.browse(url); LinkBrowser.browse(url);
} }
} }

View File

@@ -37,7 +37,6 @@ import net.runelite.api.Client;
import net.runelite.api.GrandExchangeOffer; import net.runelite.api.GrandExchangeOffer;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.LinkBrowser;
@Slf4j @Slf4j
class GrandExchangePanel extends PluginPanel class GrandExchangePanel extends PluginPanel
@@ -54,7 +53,7 @@ class GrandExchangePanel extends PluginPanel
private JTabbedPane tabbedPane = new JTabbedPane(); private JTabbedPane tabbedPane = new JTabbedPane();
@Inject @Inject
GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor, LinkBrowser linkBrowser) GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
{ {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.NORTH); add(tabbedPane, BorderLayout.NORTH);
@@ -68,7 +67,7 @@ class GrandExchangePanel extends PluginPanel
} }
// Search Panel // Search Panel
searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor, linkBrowser); searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor);
tabbedPane.addTab("Offers", offerPanel); tabbedPane.addTab("Offers", offerPanel);
tabbedPane.addTab("Search", searchPanel); tabbedPane.addTab("Search", searchPanel);

View File

@@ -48,8 +48,8 @@ import net.runelite.client.input.MouseListener;
import net.runelite.client.input.MouseManager; import net.runelite.client.input.MouseManager;
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.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
@PluginDescriptor( @PluginDescriptor(
name = "Grand Exchange" name = "Grand Exchange"
@@ -69,7 +69,7 @@ public class GrandExchangePlugin extends Plugin
private Client client; private Client client;
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private GrandExchangeConfig config; private GrandExchangeConfig config;
@@ -84,13 +84,20 @@ public class GrandExchangePlugin extends Plugin
protected void startUp() throws IOException protected void startUp() throws IOException
{ {
panel = injector.getInstance(GrandExchangePanel.class); panel = injector.getInstance(GrandExchangePanel.class);
BufferedImage icon; BufferedImage icon;
synchronized (ImageIO.class) synchronized (ImageIO.class)
{ {
icon = ImageIO.read(getClass().getResourceAsStream("ge_icon.png")); 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() itemClick = new MouseListener()
{ {
@@ -119,7 +126,8 @@ public class GrandExchangePlugin extends Plugin
if (!button.isSelected()) if (!button.isSelected())
{ {
button.doClick(); button.setSelected(true);
button.getOnSelect().run();
} }
panel.getSearchPanel().priceLookup(itemComp.getName()); panel.getSearchPanel().priceLookup(itemComp.getName());
@@ -145,8 +153,7 @@ public class GrandExchangePlugin extends Plugin
@Override @Override
protected void shutDown() protected void shutDown()
{ {
ui.getPluginToolbar().removeNavigation(button); pluginToolbar.removeNavigation(button);
mouseManager.unregisterMouseListener(itemClick); mouseManager.unregisterMouseListener(itemClick);
} }
@@ -172,10 +179,6 @@ public class GrandExchangePlugin extends Plugin
@Subscribe @Subscribe
public void onGrandExchangeOfferChanged(GrandExchangeOfferChanged offerEvent) public void onGrandExchangeOfferChanged(GrandExchangeOfferChanged offerEvent)
{ {
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() -> panel.updateOffer(offerEvent.getOffer(), offerEvent.getSlot()));
{
panel.updateOffer(offerEvent.getOffer(), offerEvent.getSlot());
});
} }
} }

View File

@@ -46,7 +46,6 @@ import net.runelite.api.Client;
import net.runelite.api.ItemComposition; import net.runelite.api.ItemComposition;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.hiscore.IconTextField; 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.Item;
import net.runelite.http.api.item.ItemClient; import net.runelite.http.api.item.ItemClient;
import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.item.ItemPrice;
@@ -60,7 +59,6 @@ class GrandExchangeSearchPanel extends JPanel
private final Client client; private final Client client;
private final ItemManager itemManager; private final ItemManager itemManager;
private final ScheduledExecutorService executor; private final ScheduledExecutorService executor;
private final LinkBrowser linkBrowser;
private ItemClient itemClient; private ItemClient itemClient;
@@ -71,12 +69,11 @@ class GrandExchangeSearchPanel extends JPanel
private JPanel searchItemsPanel = new JPanel(); private JPanel searchItemsPanel = new JPanel();
private JLabel searchingLabel = new JLabel(); 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.client = client;
this.itemManager = itemManager; this.itemManager = itemManager;
this.executor = executor; this.executor = executor;
this.linkBrowser = linkBrowser;
init(); init();
} }
@@ -203,7 +200,7 @@ class GrandExchangeSearchPanel extends JPanel
{ {
for (GrandExchangeItems item : ITEMS_LIST) 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()); item.getItemId(), item.getGePrice(), item.getHaPrice());
searchItemsPanel.add(panel); searchItemsPanel.add(panel);

View File

@@ -38,8 +38,8 @@ import net.runelite.client.config.ConfigManager;
import net.runelite.client.menus.MenuManager; import net.runelite.client.menus.MenuManager;
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.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
@PluginDescriptor( @PluginDescriptor(
name = "HiScore", name = "HiScore",
@@ -50,7 +50,7 @@ public class HiscorePlugin extends Plugin
private static final String LOOKUP = "Lookup"; private static final String LOOKUP = "Lookup";
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private MenuManager menuManager; private MenuManager menuManager;
@@ -81,12 +81,13 @@ public class HiscorePlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("hiscore.gif")); icon = ImageIO.read(getClass().getResourceAsStream("hiscore.gif"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Hiscore", .name("Hiscore")
icon, .icon(icon)
() -> hiscorePanel); .panel(hiscorePanel)
.build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
if (config.playerOption()) if (config.playerOption())
{ {
@@ -97,8 +98,7 @@ public class HiscorePlugin extends Plugin
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
menuManager.removePlayerMenuItem(LOOKUP); menuManager.removePlayerMenuItem(LOOKUP);
} }
@@ -129,7 +129,8 @@ public class HiscorePlugin extends Plugin
{ {
if (!navButton.isSelected()) if (!navButton.isSelected())
{ {
navButton.doClick(); navButton.setSelected(true);
navButton.getOnSelect().run();
} }
}); });
} }

View File

@@ -26,15 +26,15 @@ package net.runelite.client.plugins.info;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Font;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.lang.reflect.InvocationTargetException; import java.awt.Font;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.GroupLayout; import javax.swing.GroupLayout;
import javax.swing.JButton;
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.SwingUtilities;
import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent;
@@ -46,23 +46,25 @@ 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.config.RuneLiteConfig;
import net.runelite.client.events.ClientUILoaded; 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.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;
import net.runelite.client.util.SwingUtil;
@Slf4j @Slf4j
@Singleton
public class InfoPanel extends PluginPanel 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 @Inject
@Nullable @Nullable
private Client client; private Client client;
@Inject
private ClientUI clientUI;
@Inject @Inject
private RuneLiteConfig runeliteConfig; private RuneLiteConfig runeliteConfig;
@@ -79,9 +81,7 @@ public class InfoPanel extends PluginPanel
private ScheduledExecutorService executor; private ScheduledExecutorService executor;
private final GroupLayout layout = new GroupLayout(this); private final GroupLayout layout = new GroupLayout(this);
private final ClientTitleToolbar titleBar = new ClientTitleToolbar();
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();
@@ -90,8 +90,7 @@ public class InfoPanel extends PluginPanel
setLayout(layout); setLayout(layout);
final Font smallFont = FontManager.getRunescapeSmallFont(); final Font smallFont = FontManager.getRunescapeSmallFont();
updateTitleBar();
toolbarPanelPlaceholder.setVisible(false);
final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); final JLabel runeliteVersionHeader = new JLabel("RuneLite version");
runeliteVersionHeader.setFont(smallFont); runeliteVersionHeader.setFont(smallFont);
@@ -119,7 +118,8 @@ public class InfoPanel extends PluginPanel
} }
} }
}); });
setNotLoggedIn();
updateLoggedIn();
final JRichTextPane issueLink = new JRichTextPane("text/html", final JRichTextPane issueLink = new JRichTextPane("text/html",
"RuneLite is open source!<br>" "RuneLite is open source!<br>"
@@ -132,7 +132,7 @@ public class InfoPanel extends PluginPanel
setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6)); setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6));
layout.setVerticalGroup(layout.createSequentialGroup() layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(toolbarPanelPlaceholder) .addComponent(titleBar)
.addGap(3) .addGap(3)
.addGroup(layout.createParallelGroup() .addGroup(layout.createParallelGroup()
.addComponent(runeliteVersionHeader) .addComponent(runeliteVersionHeader)
@@ -152,16 +152,15 @@ 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) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(toolbarPanelPlaceholder) .addComponent(titleBar))
).addGroup(layout.createSequentialGroup() .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))
).addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersion) .addComponent(runeliteVersion)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersion) .addComponent(runescapeVersion))
)
.addComponent(usernameHeader) .addComponent(usernameHeader)
.addComponent(username) .addComponent(username)
.addComponent(issueLink) .addComponent(issueLink)
@@ -170,50 +169,81 @@ public class InfoPanel extends PluginPanel
eventBus.register(this); eventBus.register(this);
} }
private void setNotLoggedIn() private void updateLoggedIn()
{ {
username.setContentType("text/html"); final String name = sessionManager.getAccountSession() != null
username.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud."); ? sessionManager.getAccountSession().getUsername()
usernameHeader.setText("Not logged in"); : null;
}
@Subscribe
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) if (name != null)
{ {
username.setContentType("text/plain"); username.setContentType("text/plain");
username.setText(name); username.setText(name);
usernameHeader.setText("Logged in as"); usernameHeader.setText("Logged in as");
} }
else
{
username.setContentType("text/html");
username.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud.");
usernameHeader.setText("Not logged in");
}
}
private void updateTitleBar()
{
titleBar.setVisible(!runeliteConfig.enableCustomChrome());
} }
@Subscribe @Subscribe
private void onSessionClose(SessionClose e) public void onClientUILoaded(ClientUILoaded e)
{ {
setNotLoggedIn(); // 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)
{
updateLoggedIn();
}
@Subscribe
public void onTitleToolbarButtonAdded(TitleToolbarButtonAdded event)
{
if (runeliteConfig.enableCustomChrome())
{
return;
}
SwingUtilities.invokeLater(() ->
{
final int iconSize = TITLEBAR_SIZE - 6;
final JButton button = SwingUtil.createSwingButton(event.getButton(), iconSize, null);
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();
});
} }
} }

View File

@@ -27,10 +27,12 @@ package net.runelite.client.plugins.info;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.client.config.RuneLiteConfig;
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.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.client.ui.TitleToolbar;
@PluginDescriptor( @PluginDescriptor(
name = "Info Panel", name = "Info Panel",
@@ -39,7 +41,13 @@ import net.runelite.client.ui.NavigationButton;
public class InfoPlugin extends Plugin public class InfoPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject
private TitleToolbar titleToolbar;
@Inject
private RuneLiteConfig runeLiteConfig;
private NavigationButton navButton; private NavigationButton navButton;
@@ -55,18 +63,23 @@ public class InfoPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("info_icon.png")); icon = ImageIO.read(getClass().getResourceAsStream("info_icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Info", .name("Info")
icon, .icon(icon)
() -> panel .panel(panel)
); .build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
if (!runeLiteConfig.enableCustomChrome())
{
titleToolbar.refresh();
}
} }
@Override @Override
protected void shutDown() protected void shutDown()
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
} }

View File

@@ -44,8 +44,8 @@ import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
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.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.Overlay;
@PluginDescriptor( @PluginDescriptor(
@@ -57,7 +57,7 @@ public class KourendLibraryPlugin extends Plugin
final static boolean debug = false; final static boolean debug = false;
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private Client client; private Client client;
@@ -86,19 +86,19 @@ public class KourendLibraryPlugin extends Plugin
icon = ImageIO.read(Book.class.getResourceAsStream("panel_icon.png")); icon = ImageIO.read(Book.class.getResourceAsStream("panel_icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Kourend Library", .name("Kourend Library")
icon, .icon(icon)
() -> panel .panel(panel)
); .build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
} }
@Override @Override
protected void shutDown() protected void shutDown()
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
@Override @Override

View File

@@ -24,19 +24,18 @@
*/ */
package net.runelite.client.plugins.notes; package net.runelite.client.plugins.notes;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
import net.runelite.api.events.SessionOpen; import net.runelite.api.events.SessionOpen;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
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.ui.ClientUI;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import lombok.extern.slf4j.Slf4j; import net.runelite.client.ui.PluginToolbar;
@PluginDescriptor( @PluginDescriptor(
name = "Notes", name = "Notes",
@@ -46,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
public class NotesPlugin extends Plugin public class NotesPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private NotesConfig config; private NotesConfig config;
@@ -72,19 +71,19 @@ public class NotesPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("notes_icon.png")); icon = ImageIO.read(getClass().getResourceAsStream("notes_icon.png"));
} }
navButton = new NavigationButton( navButton = NavigationButton.builder()
"Notes", .name("Notes")
icon, .icon(icon)
() -> panel .panel(panel)
); .build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
} }
@Override @Override
protected void shutDown() protected void shutDown()
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
@Subscribe @Subscribe

View File

@@ -25,19 +25,17 @@
package net.runelite.client.plugins.screenshot; package net.runelite.client.plugins.screenshot;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.Color; import java.awt.Color;
import java.awt.Desktop; import java.awt.Desktop;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.TrayIcon; import java.awt.TrayIcon;
import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -52,10 +50,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; 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.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
@@ -80,6 +74,8 @@ import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest;
import net.runelite.client.plugins.screenshot.imgur.ImageUploadResponse; import net.runelite.client.plugins.screenshot.imgur.ImageUploadResponse;
import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.FontManager; 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.ui.overlay.OverlayRenderer;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
@@ -125,13 +121,16 @@ public class ScreenshotPlugin extends Plugin
@Inject @Inject
private ClientUI clientUi; private ClientUI clientUi;
@Inject
private TitleToolbar titleToolbar;
@Inject @Inject
private OverlayRenderer overlayRenderer; private OverlayRenderer overlayRenderer;
@Inject @Inject
private ScheduledExecutorService executor; private ScheduledExecutorService executor;
private JButton titleBarButton; private NavigationButton titleBarButton;
@Provides @Provides
ScreenshotConfig getConfig(ConfigManager configManager) ScreenshotConfig getConfig(ConfigManager configManager)
@@ -141,66 +140,39 @@ public class ScreenshotPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{
addButtonToTitleBar();
}
@Override
protected void shutDown() throws Exception
{
removeButtonFromTitlebar();
}
private void addButtonToTitleBar()
{ {
try try
{ {
BufferedImage iconImage; BufferedImage iconImage;
BufferedImage invertedIconImage;
synchronized (ImageIO.class) synchronized (ImageIO.class)
{ {
iconImage = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("screenshot.png")); iconImage = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("screenshot.png"));
invertedIconImage = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("screenshot_inverted.png"));
} }
SwingUtilities.invokeLater(() -> titleBarButton = NavigationButton.builder()
{ .tooltip("Take screenshot")
titleBarButton = new JButton(); .icon(iconImage)
titleBarButton.setToolTipText("Take screenshot"); .onClick(() -> takeScreenshot(
titleBarButton.addMouseListener(new MouseAdapter() TIME_FORMAT.format(new Date()),
{ client.getLocalPlayer() != null))
@Override .popup(ImmutableMap
public void mouseClicked(MouseEvent e) .<String, Runnable>builder()
.put("Open screenshot folder...", () ->
{ {
super.mouseClicked(e); try
if (SwingUtilities.isLeftMouseButton(e))
{ {
takeScreenshot(TIME_FORMAT.format(new Date()), client.getLocalPlayer() != null); Desktop.getDesktop().open(RuneLite.SCREENSHOT_DIR);
} }
} catch (IOException ex)
}); {
log.warn("Error opening screenshot dir", ex);
JPopupMenu popupMenu = new JPopupMenu(); }
})
.build())
.build();
JMenuItem folderItem = new JMenuItem("Open screenshot folder..."); titleToolbar.addNavigation(titleBarButton);
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);
});
} }
catch (IOException ex) catch (IOException ex)
{ {
@@ -208,12 +180,10 @@ public class ScreenshotPlugin extends Plugin
} }
} }
private void removeButtonFromTitlebar() @Override
protected void shutDown() throws Exception
{ {
SwingUtilities.invokeLater(() -> titleToolbar.removeNavigation(titleBarButton);
{
clientUi.getTitleToolbar().remove(titleBarButton);
});
} }
@Subscribe @Subscribe
@@ -410,9 +380,8 @@ public class ScreenshotPlugin extends Plugin
clientUi.paint(graphics); clientUi.paint(graphics);
// Evaluate the position of the game inside the frame // Evaluate the position of the game inside the frame
Point gamePoint = SwingUtilities.convertPoint(client.getCanvas(), 0, 0, clientUi); gameOffsetX = 6;
gameOffsetX = gamePoint.x; gameOffsetY = 0;
gameOffsetY = gamePoint.y;
} }
// Draw the game onto the screenshot // Draw the game onto the screenshot

View File

@@ -47,8 +47,8 @@ import net.runelite.client.game.SkillIconManager;
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 static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL; 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.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.http.api.worlds.World; import net.runelite.http.api.worlds.World;
import net.runelite.http.api.worlds.WorldClient; import net.runelite.http.api.worlds.WorldClient;
import net.runelite.http.api.worlds.WorldResult; import net.runelite.http.api.worlds.WorldResult;
@@ -62,7 +62,7 @@ import net.runelite.http.api.xp.XpClient;
public class XpTrackerPlugin extends Plugin public class XpTrackerPlugin extends Plugin
{ {
@Inject @Inject
private ClientUI ui; private PluginToolbar pluginToolbar;
@Inject @Inject
private Client client; private Client client;
@@ -104,25 +104,27 @@ public class XpTrackerPlugin extends Plugin
log.warn("Error looking up worlds list", e); log.warn("Error looking up worlds list", e);
} }
xpPanel = new XpPanel(this, client, skillIconManager);
BufferedImage icon; BufferedImage icon;
synchronized (ImageIO.class) synchronized (ImageIO.class)
{ {
icon = ImageIO.read(getClass().getResourceAsStream("xp.png")); icon = ImageIO.read(getClass().getResourceAsStream("xp.png"));
} }
xpPanel = new XpPanel(this, client, skillIconManager); navButton = NavigationButton.builder()
navButton = new NavigationButton( .name("XP Tracker")
"XP Tracker", .icon(icon)
icon, .panel(xpPanel)
() -> xpPanel); .build();
ui.getPluginToolbar().addNavigation(navButton); pluginToolbar.addNavigation(navButton);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
ui.getPluginToolbar().removeNavigation(navButton); pluginToolbar.removeNavigation(navButton);
} }
@Subscribe @Subscribe

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2017-2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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<NavigationButton, Component> 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();
}
}
}

View File

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

View File

@@ -24,40 +24,32 @@
*/ */
package net.runelite.client.ui; package net.runelite.client.ui;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.applet.Applet; import java.applet.Applet;
import java.awt.AWTException;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Canvas; import java.awt.Canvas;
import java.awt.Component; import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Graphics;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.SystemTray;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.TrayIcon; 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.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
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.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities; 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.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
@@ -65,31 +57,29 @@ import net.runelite.api.GameState;
import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ConfigChanged;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties; 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.events.TitleToolbarButtonAdded;
import net.runelite.client.events.TitleToolbarButtonRemoved;
import net.runelite.client.util.OSType; import net.runelite.client.util.OSType;
import net.runelite.client.util.OSXUtil; import net.runelite.client.util.OSXUtil;
import net.runelite.client.util.SwingUtil;
import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel; import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel;
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;
/**
* Client UI.
*/
@Slf4j @Slf4j
public class ClientUI extends JFrame @Singleton
public class ClientUI
{ {
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;
public static final BufferedImage ICON; 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 static
{ {
BufferedImage icon = null; BufferedImage icon = null;
@@ -109,129 +99,39 @@ public class ClientUI extends JFrame
ICON = icon; ICON = icon;
} }
public static ClientUI create(RuneLite runelite, RuneLiteProperties properties, Applet client) @Getter
{ private TrayIcon trayIcon;
// 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. private final RuneLite runelite;
// Fixes black boxes under popups that are above the game applet. private final RuneLiteProperties properties;
System.setProperty("jgoodies.popupDropShadowEnabled", "false"); private final RuneLiteConfig config;
private final EventBus eventBus;
private Applet client;
private JFrame frame;
private JPanel navContainer;
private PluginPanel pluginPanel;
private ClientPluginToolbar pluginToolbar;
private ClientTitleToolbar titleToolbar;
private JButton currentButton;
// Do not fill in background on repaint. Reduces flickering when @Inject
// the applet is resized. private ClientUI(
System.setProperty("sun.awt.noerasebackground", "true"); RuneLite runelite,
RuneLiteProperties properties,
// Use substance look and feel RuneLiteConfig config,
try EventBus eventBus)
{
UIManager.setLookAndFeel(new SubstanceGraphiteLookAndFeel());
}
catch (UnsupportedLookAndFeelException ex)
{
log.warn("unable to set look and feel", ex);
}
// Use custom UI font
setUIFont(new FontUIResource(FontManager.getRunescapeFont()));
ClientUI gui = new ClientUI(runelite, properties, client);
OSXUtil.tryEnableFullscreen(gui);
return gui;
}
private ClientUI(RuneLite runelite, RuneLiteProperties properties, Applet client)
{ {
this.runelite = runelite; this.runelite = runelite;
this.properties = properties; this.properties = properties;
this.client = client; this.config = config;
this.trayIcon = setupTrayIcon(); this.eventBus = eventBus;
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();
}
} }
/**
* On config changed.
*
* @param event the event
*/
@Subscribe @Subscribe
public void onConfigChanged(ConfigChanged event) public void onConfigChanged(ConfigChanged event)
{ {
@@ -240,147 +140,303 @@ public class ClientUI extends JFrame
return; 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(() -> 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.setSize(size);
client.setPreferredSize(size); client.setPreferredSize(size);
client.getParent().setPreferredSize(size); client.getParent().setPreferredSize(size);
client.getParent().setSize(size); client.getParent().setSize(size);
if (isVisible()) if (frame.isVisible())
{ {
pack(); frame.pack();
} }
}); });
} }
private static void setUIFont(FontUIResource f) @Subscribe
public void onPluginToolbarButtonAdded(final PluginToolbarButtonAdded event)
{ {
final Enumeration keys = UIManager.getDefaults().keys(); SwingUtilities.invokeLater(() ->
while (keys.hasMoreElements())
{ {
final Object key = keys.nextElement(); final JButton button = SwingUtil.createSwingButton(event.getButton(), 0, (jButton) ->
final Object value = UIManager.get(key);
if (value instanceof FontUIResource)
{ {
UIManager.put(key, f); final PluginPanel panel = event.getButton().getPanel();
}
} if (panel == null)
{
return;
}
if (currentButton != null)
{
currentButton.setSelected(false);
}
if (currentButton == jButton)
{
contract();
currentButton = null;
}
else
{
currentButton = jButton;
currentButton.setSelected(true);
expand(panel);
}
});
pluginToolbar.addComponent(event.getIndex(), event.getButton(), button);
});
} }
private TrayIcon setupTrayIcon() @Subscribe
public void onPluginToolbarButtonRemoved(final PluginToolbarButtonRemoved event)
{ {
if (!SystemTray.isSupported()) SwingUtilities.invokeLater(() -> pluginToolbar.removeComponent(event.getButton()));
}
@Subscribe
public void onTitleToolbarButtonAdded(final TitleToolbarButtonAdded event)
{
if (!config.enableCustomChrome() && !SwingUtil.isCustomTitlePanePresent(frame))
{ {
return null; return;
} }
SystemTray systemTray = SystemTray.getSystemTray(); SwingUtilities.invokeLater(() ->
TrayIcon trayIcon = new TrayIcon(ICON, properties.getTitle()); {
trayIcon.setImageAutoSize(true); final int iconSize = ClientTitleToolbar.TITLEBAR_SIZE - 6;
final JButton button = SwingUtil.createSwingButton(event.getButton(), iconSize, null);
titleToolbar.addComponent(event.getButton(), button);
});
}
try @Subscribe
public void onTitleToolbarButtonRemoved(final TitleToolbarButtonRemoved event)
{
if (!config.enableCustomChrome() && !SwingUtil.isCustomTitlePanePresent(frame))
{ {
systemTray.add(trayIcon); return;
}
catch (AWTException ex)
{
log.debug("Unable to add system tray icon", ex);
return trayIcon;
} }
// bring to front when tray icon is clicked SwingUtilities.invokeLater(() -> titleToolbar.removeComponent(event.getButton()));
trayIcon.addMouseListener(new MouseAdapter() }
/**
* 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
{
this.client = client;
SwingUtilities.invokeAndWait(() ->
{ {
@Override // Set some sensible swing defaults
public void mouseClicked(MouseEvent e) SwingUtil.setupDefaults();
// Use substance look and feel
SwingUtil.setTheme(new SubstanceGraphiteLookAndFeel());
// Use custom UI font
SwingUtil.setFont(FontManager.getRunescapeFont());
// Create main window
frame = new JFrame();
// Try to enable fullscreen on OSX
OSXUtil.tryEnableFullscreen(frame);
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 ClientPluginToolbar();
container.add(pluginToolbar);
titleToolbar = new ClientTitleToolbar();
frame.add(container);
});
}
/**
* 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)
{ {
setVisible(true); frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
setState(Frame.NORMAL); // unminimize
final 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();
}); });
return trayIcon; eventBus.post(new ClientUILoaded());
} }
private void init() /**
* Paint this component to target graphics
*
* @param graphics the graphics
*/
public void paint(final Graphics graphics)
{ {
assert SwingUtilities.isEventDispatchThread(); frame.paint(graphics);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
checkExit();
}
});
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);
add(container);
} }
@Override /**
* 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() public void requestFocus()
{ {
if (OSType.getOSType() == OSType.MacOS) if (OSType.getOSType() == OSType.MacOS)
@@ -388,17 +444,11 @@ public class ClientUI extends JFrame
OSXUtil.requestFocus(); OSXUtil.requestFocus();
} }
super.requestFocus(); frame.requestFocus();
giveClientFocus(); giveClientFocus();
} }
private void revalidateMinimumSize() private void expand(PluginPanel panel)
{
// 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)
{ {
if (pluginPanel != null) if (pluginPanel != null)
{ {
@@ -406,9 +456,11 @@ public class ClientUI extends JFrame
} }
else else
{ {
if (isInScreenBounds((int) getLocationOnScreen().getX() + getWidth() + PANEL_EXPANDED_WIDTH, (int) getLocationOnScreen().getY())) if (SwingUtil.isInScreenBounds(
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());
} }
} }
@@ -425,57 +477,42 @@ public class ClientUI extends JFrame
panel.onActivate(); panel.onActivate();
wrappedPanel.repaint(); wrappedPanel.repaint();
revalidateMinimumSize(); SwingUtil.revalidateMinimumSize(frame);
} }
void contract() private void contract()
{ {
boolean wasMinimumWidth = this.getWidth() == (int) this.getMinimumSize().getWidth(); boolean wasMinimumWidth = frame.getWidth() == frame.getMinimumSize().width;
pluginPanel.onDeactivate(); pluginPanel.onDeactivate();
navContainer.remove(0); navContainer.remove(0);
navContainer.setMinimumSize(new Dimension(0, 0)); navContainer.setMinimumSize(new Dimension(0, 0));
navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE)); navContainer.setMaximumSize(new Dimension(0, 0));
navContainer.revalidate(); navContainer.revalidate();
giveClientFocus(); giveClientFocus();
revalidateMinimumSize(); SwingUtil.revalidateMinimumSize(frame);
if (wasMinimumWidth) 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; pluginPanel = null;
} }
private boolean isInScreenBounds(int x, int y) private void giveClientFocus()
{ {
Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); if (client instanceof Client)
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); final Canvas c = ((Client) client).getCanvas();
c.requestFocusInWindow();
} }
else if (client != null)
if (result == JOptionPane.OK_OPTION)
{ {
runelite.shutdown(); client.requestFocusInWindow();
System.exit(0);
} }
} }
public PluginToolbar getPluginToolbar()
{
return pluginToolbar;
}
} }

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2017, Adam <Adam@sigterm.info> * Copyright (c) 2017-2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.con>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -24,30 +25,57 @@
*/ */
package net.runelite.client.ui; package net.runelite.client.ui;
import java.awt.Image; import java.awt.image.BufferedImage;
import java.util.function.Supplier; import java.util.Map;
import javax.swing.ImageIcon; import lombok.Builder;
import javax.swing.JButton; import lombok.Data;
import lombok.Getter; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
@Slf4j /**
public class NavigationButton extends JButton * UI navigation button.
*/
@Data
@Builder
@EqualsAndHashCode(of = {"name", "tooltip"})
public class NavigationButton
{ {
@Getter /**
private final Supplier<PluginPanel> panelSupplier; * Button name.
*/
private final String name;
public NavigationButton(String name, Image icon) /**
{ * Icon of button.
this(name, icon, null); */
} private final BufferedImage icon;
public NavigationButton(String name, Image icon, Supplier<PluginPanel> panelSupplier) /**
{ * Tooltip to show when hovered.
super(); */
setName(name); private String tooltip;
setToolTipText(name);
setIcon(new ImageIcon(icon)); /**
this.panelSupplier = panelSupplier; * Button selection state
} */
private boolean selected;
/**
* On select action of the button.
*/
private Runnable onSelect;
/**
* On click action of the button.
*/
private Runnable onClick;
/**
* Plugin panel, used when expanding and contracting sidebar.
*/
private PluginPanel panel;
/**
* Map of key-value pairs for setting the popup menu
*/
private Map<String, Runnable> popup;
} }

View File

@@ -24,87 +24,60 @@
*/ */
package net.runelite.client.ui; package net.runelite.client.ui;
import java.awt.Component; import com.google.common.eventbus.EventBus;
import java.awt.Dimension;
import java.util.Comparator; import java.util.Comparator;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.function.Supplier; import javax.inject.Inject;
import javax.swing.JToolBar; import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j; 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<NavigationButton> buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getName));
private final ClientUI ui; @Inject
private final TreeSet<NavigationButton> buttons = new TreeSet<>(Comparator.comparing(Component::getName)); private PluginToolbar(final EventBus eventBus)
private NavigationButton current;
public PluginToolbar(ClientUI ui)
{ {
super(JToolBar.VERTICAL); this.eventBus = eventBus;
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));
} }
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)) if (buttons.contains(button))
{ {
log.warn("Button already in container '{}'", button.getName());
return; 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(); int index = buttons.headSet(button).size();
add(button, index);
revalidate();
repaint();
}
public void removeNavigation(NavigationButton button) if (buttons.remove(button))
{
buttons.remove(button);
remove(button);
revalidate();
repaint();
}
private void onClick(NavigationButton button)
{
Supplier<PluginPanel> panelSupplier = button.getPanelSupplier();
if (panelSupplier == null)
{ {
return; eventBus.post(new PluginToolbarButtonRemoved(button, index));
}
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);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2018 Abex * Copyright (c) 2017-2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -24,174 +25,78 @@
*/ */
package net.runelite.client.ui; package net.runelite.client.ui;
import java.awt.Component; import com.google.common.eventbus.EventBus;
import java.awt.Container; import java.util.Comparator;
import java.awt.Desktop; import java.util.Iterator;
import java.awt.Dimension; import java.util.TreeSet;
import java.awt.Image; import javax.inject.Inject;
import java.awt.LayoutManager2; import javax.inject.Singleton;
import java.awt.event.MouseAdapter; import net.runelite.client.events.TitleToolbarButtonAdded;
import java.awt.event.MouseEvent; import net.runelite.client.events.TitleToolbarButtonRemoved;
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;
@Slf4j /**
public class TitleToolbar extends JPanel * Title toolbar buttons holder.
*/
@Singleton
public class TitleToolbar
{ {
private static final int TITLEBAR_SIZE = 23; private final EventBus eventBus;
private static final int ITEM_PADDING = 4; private final TreeSet<NavigationButton> 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 this.eventBus = eventBus;
// 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() /**
* Add navigation.
*
* @param button the button
*/
public void addNavigation(final NavigationButton button)
{
if (buttons.contains(button))
{ {
@Override return;
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);
} }
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; int index = buttons.headSet(button).size();
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));
}
button.setIcon(icon); if (buttons.remove(button))
button.setRolloverIcon(invertedIcon); {
button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE); eventBus.post(new TitleToolbarButtonRemoved(button, index));
button.setFocusable(false); }
add(button);
revalidate();
repaint();
} }
@Override /**
public void remove(Component c) * Refresh all buttons
*/
public void refresh()
{ {
super.remove(c); final Iterator<NavigationButton> iterator = buttons.iterator();
revalidate(); int index = 0;
repaint();
while (iterator.hasNext())
{
final NavigationButton button = iterator.next();
eventBus.post(new TitleToolbarButtonRemoved(button, index));
eventBus.post(new TitleToolbarButtonAdded(button, index));
index++;
}
} }
} }

View File

@@ -30,13 +30,10 @@ import java.awt.datatransfer.StringSelection;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ClientUI;
/** /**
* Utility class used for browser navigation * Utility class used for browser navigation
@@ -45,21 +42,13 @@ import net.runelite.client.ui.ClientUI;
@Slf4j @Slf4j
public class LinkBrowser public class LinkBrowser
{ {
private final Provider<ClientUI> clientUIProvider;
@Inject
private LinkBrowser(final Provider<ClientUI> clientUIProvider)
{
this.clientUIProvider = clientUIProvider;
}
/** /**
* Tries to navigate to specified URL in browser. In case operation fails, displays message box with message * 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. * and copies link to clipboard to navigate to.
* @param url url to open * @param url url to open
* @return true if operation was successful * @return true if operation was successful
*/ */
public boolean browse(final String url) public static boolean browse(final String url)
{ {
if (!Desktop.isDesktopSupported()) if (!Desktop.isDesktopSupported())
{ {
@@ -93,18 +82,11 @@ public class LinkBrowser
* Open swing message box with specified message and copy data to clipboard * Open swing message box with specified message and copy data to clipboard
* @param message message to show * @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(() -> SwingUtilities.invokeLater(() ->
{ {
final int result = JOptionPane.showConfirmDialog(clientUI, message, "Message", final int result = JOptionPane.showConfirmDialog(null, message, "Message",
JOptionPane.OK_CANCEL_OPTION); JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) if (result == JOptionPane.OK_OPTION)

View File

@@ -26,8 +26,8 @@ package net.runelite.client.util;
import com.apple.eawt.Application; import com.apple.eawt.Application;
import com.apple.eawt.FullScreenUtilities; import com.apple.eawt.FullScreenUtilities;
import javax.swing.JFrame;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ClientUI;
/** /**
* A class with OSX-specific functions to improve integration. * 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. * @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) if (OSType.getOSType() == OSType.MacOS)
{ {

View File

@@ -0,0 +1,318 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.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.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;
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;
import net.runelite.client.ui.NavigationButton;
import org.pushingpixels.substance.internal.SubstanceSynapse;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
/**
* 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<Boolean> 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));
}
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);
final Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
/**
* 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<JButton> specialCallback)
{
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;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -45,7 +45,6 @@ import joptsimple.OptionSet;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteModule; import net.runelite.client.RuneLiteModule;
import net.runelite.client.ui.ClientUI;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@@ -67,9 +66,6 @@ public class PluginManagerTest
private RuneLite runelite; private RuneLite runelite;
private Set<Class> pluginClasses; private Set<Class> pluginClasses;
@Mock
ClientUI clientUi;
@Mock @Mock
Client client; Client client;
@@ -83,7 +79,6 @@ public class PluginManagerTest
RuneLite.setInjector(injector); RuneLite.setInjector(injector);
runelite = injector.getInstance(RuneLite.class); runelite = injector.getInstance(RuneLite.class);
runelite.setGui(clientUi);
// Find plugins we expect to have // Find plugins we expect to have
pluginClasses = new HashSet<>(); pluginClasses = new HashSet<>();