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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,15 +26,15 @@ package net.runelite.client.plugins.info;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.awt.Font;
import com.google.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.awt.Font;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.LayoutStyle;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
@@ -46,23 +46,25 @@ import net.runelite.client.RuneLiteProperties;
import net.runelite.client.account.SessionManager;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.events.ClientUILoaded;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.events.TitleToolbarButtonAdded;
import net.runelite.client.events.TitleToolbarButtonRemoved;
import net.runelite.client.ui.ClientTitleToolbar;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.RunnableExceptionLogger;
import net.runelite.client.util.SwingUtil;
@Slf4j
@Singleton
public class InfoPanel extends PluginPanel
{
private final static String RUNELITE_LOGIN = "https://runelite_login/";
private static final int TITLEBAR_SIZE = 23;
private static final String RUNELITE_LOGIN = "https://runelite_login/";
@Inject
@Nullable
private Client client;
@Inject
private ClientUI clientUI;
@Inject
private RuneLiteConfig runeliteConfig;
@@ -79,9 +81,7 @@ public class InfoPanel extends PluginPanel
private ScheduledExecutorService executor;
private final GroupLayout layout = new GroupLayout(this);
private final JPanel toolbarPanelPlaceholder = new JPanel();
private final ClientTitleToolbar titleBar = new ClientTitleToolbar();
private final JLabel usernameHeader = new JLabel();
private final JRichTextPane username = new JRichTextPane();
@@ -90,8 +90,7 @@ public class InfoPanel extends PluginPanel
setLayout(layout);
final Font smallFont = FontManager.getRunescapeSmallFont();
toolbarPanelPlaceholder.setVisible(false);
updateTitleBar();
final JLabel runeliteVersionHeader = new JLabel("RuneLite version");
runeliteVersionHeader.setFont(smallFont);
@@ -119,7 +118,8 @@ public class InfoPanel extends PluginPanel
}
}
});
setNotLoggedIn();
updateLoggedIn();
final JRichTextPane issueLink = new JRichTextPane("text/html",
"RuneLite is open source!<br>"
@@ -132,7 +132,7 @@ public class InfoPanel extends PluginPanel
setBorder(BorderFactory.createEmptyBorder(2, 6, 6, 6));
layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(toolbarPanelPlaceholder)
.addComponent(titleBar)
.addGap(3)
.addGroup(layout.createParallelGroup()
.addComponent(runeliteVersionHeader)
@@ -152,16 +152,15 @@ public class InfoPanel extends PluginPanel
layout.setHorizontalGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(toolbarPanelPlaceholder)
).addGroup(layout.createSequentialGroup()
.addComponent(titleBar))
.addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersionHeader)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersionHeader)
).addGroup(layout.createSequentialGroup()
.addComponent(runescapeVersionHeader))
.addGroup(layout.createSequentialGroup()
.addComponent(runeliteVersion)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
.addComponent(runescapeVersion)
)
.addComponent(runescapeVersion))
.addComponent(usernameHeader)
.addComponent(username)
.addComponent(issueLink)
@@ -170,50 +169,81 @@ public class InfoPanel extends PluginPanel
eventBus.register(this);
}
private void setNotLoggedIn()
private void updateLoggedIn()
{
username.setContentType("text/html");
username.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud.");
usernameHeader.setText("Not logged in");
}
final String name = sessionManager.getAccountSession() != null
? sessionManager.getAccountSession().getUsername()
: 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)
{
username.setContentType("text/plain");
username.setText(name);
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
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 javax.imageio.ImageIO;
import javax.inject.Inject;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.client.ui.TitleToolbar;
@PluginDescriptor(
name = "Info Panel",
@@ -39,7 +41,13 @@ import net.runelite.client.ui.NavigationButton;
public class InfoPlugin extends Plugin
{
@Inject
private ClientUI ui;
private PluginToolbar pluginToolbar;
@Inject
private TitleToolbar titleToolbar;
@Inject
private RuneLiteConfig runeLiteConfig;
private NavigationButton navButton;
@@ -55,18 +63,23 @@ public class InfoPlugin extends Plugin
icon = ImageIO.read(getClass().getResourceAsStream("info_icon.png"));
}
navButton = new NavigationButton(
"Info",
icon,
() -> panel
);
navButton = NavigationButton.builder()
.name("Info")
.icon(icon)
.panel(panel)
.build();
ui.getPluginToolbar().addNavigation(navButton);
pluginToolbar.addNavigation(navButton);
if (!runeLiteConfig.enableCustomChrome())
{
titleToolbar.refresh();
}
}
@Override
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.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.client.ui.overlay.Overlay;
@PluginDescriptor(
@@ -57,7 +57,7 @@ public class KourendLibraryPlugin extends Plugin
final static boolean debug = false;
@Inject
private ClientUI ui;
private PluginToolbar pluginToolbar;
@Inject
private Client client;
@@ -86,19 +86,19 @@ public class KourendLibraryPlugin extends Plugin
icon = ImageIO.read(Book.class.getResourceAsStream("panel_icon.png"));
}
navButton = new NavigationButton(
"Kourend Library",
icon,
() -> panel
);
navButton = NavigationButton.builder()
.name("Kourend Library")
.icon(icon)
.panel(panel)
.build();
ui.getPluginToolbar().addNavigation(navButton);
pluginToolbar.addNavigation(navButton);
}
@Override
protected void shutDown()
{
ui.getPluginToolbar().removeNavigation(navButton);
pluginToolbar.removeNavigation(navButton);
}
@Override

View File

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

View File

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

View File

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

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;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.applet.Applet;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Enumeration;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.FontUIResource;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
@@ -65,31 +57,29 @@ import net.runelite.api.GameState;
import net.runelite.api.events.ConfigChanged;
import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.events.ClientUILoaded;
import net.runelite.client.events.PluginToolbarButtonAdded;
import net.runelite.client.events.PluginToolbarButtonRemoved;
import net.runelite.client.events.TitleToolbarButtonAdded;
import net.runelite.client.events.TitleToolbarButtonRemoved;
import net.runelite.client.util.OSType;
import net.runelite.client.util.OSXUtil;
import net.runelite.client.util.SwingUtil;
import org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities;
/**
* Client UI.
*/
@Slf4j
public class ClientUI extends JFrame
@Singleton
public class ClientUI
{
private static final int PANEL_EXPANDED_WIDTH = PluginPanel.PANEL_WIDTH + PluginPanel.SCROLLBAR_WIDTH;
public static final BufferedImage ICON;
@Getter
private TrayIcon trayIcon;
private final RuneLite runelite;
private final Applet client;
private final RuneLiteProperties properties;
private JPanel navContainer;
private PluginToolbar pluginToolbar;
private PluginPanel pluginPanel;
@Getter
private TitleToolbar titleToolbar;
static
{
BufferedImage icon = null;
@@ -109,129 +99,39 @@ public class ClientUI extends JFrame
ICON = icon;
}
public static ClientUI create(RuneLite runelite, RuneLiteProperties properties, Applet client)
{
// Force heavy-weight popups/tooltips.
// Prevents them from being obscured by the game applet.
ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
@Getter
private TrayIcon trayIcon;
// Do not render shadows under popups/tooltips.
// Fixes black boxes under popups that are above the game applet.
System.setProperty("jgoodies.popupDropShadowEnabled", "false");
private final RuneLite runelite;
private final RuneLiteProperties properties;
private final RuneLiteConfig config;
private final EventBus eventBus;
private Applet client;
private JFrame frame;
private JPanel navContainer;
private PluginPanel pluginPanel;
private ClientPluginToolbar pluginToolbar;
private ClientTitleToolbar titleToolbar;
private JButton currentButton;
// Do not fill in background on repaint. Reduces flickering when
// the applet is resized.
System.setProperty("sun.awt.noerasebackground", "true");
// Use substance look and feel
try
{
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)
@Inject
private ClientUI(
RuneLite runelite,
RuneLiteProperties properties,
RuneLiteConfig config,
EventBus eventBus)
{
this.runelite = runelite;
this.properties = properties;
this.client = client;
this.trayIcon = setupTrayIcon();
init();
setTitle(properties.getTitle());
setIconImage(ICON);
// Prevent substance from using a resize cursor for pointing
getLayeredPane().setCursor(Cursor.getDefaultCursor());
setLocationRelativeTo(getOwner());
setResizable(true);
}
public void showWithChrome(boolean customChrome)
{
setUndecorated(customChrome);
if (customChrome)
{
getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
JComponent titleBar = SubstanceCoreUtilities.getTitlePaneComponent(this);
titleToolbar.putClientProperty(SubstanceTitlePaneUtilities.EXTRA_COMPONENT_KIND, SubstanceTitlePaneUtilities.ExtraComponentKind.TRAILING);
titleBar.add(titleToolbar);
// Substance's default layout manager for the title bar only lays out substance's components
// This wraps the default manager and lays out the TitleToolbar as well.
LayoutManager delegate = titleBar.getLayout();
titleBar.setLayout(new LayoutManager()
{
@Override
public void addLayoutComponent(String name, Component comp)
{
delegate.addLayoutComponent(name, comp);
}
@Override
public void removeLayoutComponent(Component comp)
{
delegate.removeLayoutComponent(comp);
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
return delegate.preferredLayoutSize(parent);
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
return delegate.minimumLayoutSize(parent);
}
@Override
public void layoutContainer(Container parent)
{
delegate.layoutContainer(parent);
final int width = titleToolbar.getPreferredSize().width;
titleToolbar.setBounds(titleBar.getWidth() - 75 - width, 0, width, titleBar.getHeight());
}
});
}
pack();
revalidateMinimumSize();
setLocationRelativeTo(getOwner());
setVisible(true);
toFront();
requestFocus();
giveClientFocus();
}
private void giveClientFocus()
{
if (client instanceof Client)
{
final Canvas c = ((Client) client).getCanvas();
c.requestFocusInWindow();
}
else if (client != null)
{
client.requestFocusInWindow();
}
this.config = config;
this.eventBus = eventBus;
}
/**
* On config changed.
*
* @param event the event
*/
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
@@ -240,147 +140,303 @@ public class ClientUI extends JFrame
return;
}
if (event.getKey().equals("gameAlwaysOnTop"))
{
if (this.isAlwaysOnTopSupported())
{
this.setAlwaysOnTop(Boolean.valueOf(event.getNewValue()));
}
}
if (event.getKey().equals("lockWindowSize"))
{
SwingUtilities.invokeLater(() -> setResizable(!Boolean.valueOf(event.getNewValue())));
}
if (!event.getKey().equals("gameSize"))
{
return;
}
if (client == null)
{
return;
}
String[] splitStr = event.getNewValue().split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
// The upper bounds are defined by the applet's max size
// The lower bounds are taken care of by ClientPanel's setMinimumSize
if (width > 7680)
{
width = 7680;
}
if (height > 2160)
{
height = 2160;
}
Dimension size = new Dimension(width, height);
SwingUtilities.invokeLater(() ->
{
if (event.getKey().equals("gameAlwaysOnTop"))
{
if (frame.isAlwaysOnTopSupported())
{
frame.setAlwaysOnTop(config.gameAlwaysOnTop());
}
}
if (event.getKey().equals("lockWindowSize"))
{
SwingUtilities.invokeLater(() -> frame.setResizable(!config.lockWindowSize()));
}
if (!event.getKey().equals("gameSize"))
{
return;
}
if (client == null)
{
return;
}
int width = config.gameSize().width;
int height = config.gameSize().height;
// The upper bounds are defined by the applet's max size
// The lower bounds are taken care of by ClientPanel's setMinimumSize
if (width > 7680)
{
width = 7680;
}
if (height > 2160)
{
height = 2160;
}
final Dimension size = new Dimension(width, height);
client.setSize(size);
client.setPreferredSize(size);
client.getParent().setPreferredSize(size);
client.getParent().setSize(size);
if (isVisible())
if (frame.isVisible())
{
pack();
frame.pack();
}
});
}
private static void setUIFont(FontUIResource f)
@Subscribe
public void onPluginToolbarButtonAdded(final PluginToolbarButtonAdded event)
{
final Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements())
SwingUtilities.invokeLater(() ->
{
final Object key = keys.nextElement();
final Object value = UIManager.get(key);
if (value instanceof FontUIResource)
final JButton button = SwingUtil.createSwingButton(event.getButton(), 0, (jButton) ->
{
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();
TrayIcon trayIcon = new TrayIcon(ICON, properties.getTitle());
trayIcon.setImageAutoSize(true);
SwingUtilities.invokeLater(() ->
{
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);
}
catch (AWTException ex)
{
log.debug("Unable to add system tray icon", ex);
return trayIcon;
return;
}
// bring to front when tray icon is clicked
trayIcon.addMouseListener(new MouseAdapter()
SwingUtilities.invokeLater(() -> titleToolbar.removeComponent(event.getButton()));
}
/**
* 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
public void mouseClicked(MouseEvent e)
// Set some sensible swing defaults
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);
setState(Frame.NORMAL); // unminimize
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
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();
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);
frame.paint(graphics);
}
@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()
{
if (OSType.getOSType() == OSType.MacOS)
@@ -388,17 +444,11 @@ public class ClientUI extends JFrame
OSXUtil.requestFocus();
}
super.requestFocus();
frame.requestFocus();
giveClientFocus();
}
private void revalidateMinimumSize()
{
// The JFrame only respects minimumSize if it was set by setMinimumSize, for some reason. (atleast on windows/native)
this.setMinimumSize(this.getLayout().minimumLayoutSize(this));
}
void expand(PluginPanel panel)
private void expand(PluginPanel panel)
{
if (pluginPanel != null)
{
@@ -406,9 +456,11 @@ public class ClientUI extends JFrame
}
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();
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();
navContainer.remove(0);
navContainer.setMinimumSize(new Dimension(0, 0));
navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
navContainer.setMaximumSize(new Dimension(0, 0));
navContainer.revalidate();
giveClientFocus();
revalidateMinimumSize();
SwingUtil.revalidateMinimumSize(frame);
if (wasMinimumWidth)
{
this.setSize((int) this.getMinimumSize().getWidth(), getHeight());
frame.setSize(frame.getMinimumSize().width, frame.getHeight());
}
else if (getWidth() < Toolkit.getDefaultToolkit().getScreenSize().getWidth())
else if (frame.getWidth() < Toolkit.getDefaultToolkit().getScreenSize().getWidth())
{
this.setSize(getWidth() - PANEL_EXPANDED_WIDTH, getHeight());
frame.setSize(frame.getWidth() - PANEL_EXPANDED_WIDTH, frame.getHeight());
}
pluginPanel = null;
}
private boolean isInScreenBounds(int x, int y)
private void giveClientFocus()
{
Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
return x >= 0 && x <= size.getWidth() && y >= 0 && y <= size.getHeight();
}
private void checkExit()
{
int result = JOptionPane.OK_OPTION;
// only ask if not logged out
if (client != null && client instanceof Client && ((Client) client).getGameState() != GameState.LOGIN_SCREEN)
if (client instanceof Client)
{
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();
}
if (result == JOptionPane.OK_OPTION)
else if (client != null)
{
runelite.shutdown();
System.exit(0);
client.requestFocusInWindow();
}
}
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.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,30 +25,57 @@
*/
package net.runelite.client.ui;
import java.awt.Image;
import java.util.function.Supplier;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
import java.util.Map;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
@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)
{
this(name, icon, null);
}
/**
* Icon of button.
*/
private final BufferedImage icon;
public NavigationButton(String name, Image icon, Supplier<PluginPanel> panelSupplier)
{
super();
setName(name);
setToolTipText(name);
setIcon(new ImageIcon(icon));
this.panelSupplier = panelSupplier;
}
/**
* Tooltip to show when hovered.
*/
private String tooltip;
/**
* Button selection state
*/
private boolean selected;
/**
* On select action of the button.
*/
private Runnable onSelect;
/**
* On click action of the button.
*/
private Runnable onClick;
/**
* 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;
import java.awt.Component;
import java.awt.Dimension;
import com.google.common.eventbus.EventBus;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.function.Supplier;
import javax.swing.JToolBar;
import lombok.extern.slf4j.Slf4j;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.client.events.PluginToolbarButtonAdded;
import net.runelite.client.events.PluginToolbarButtonRemoved;
@Slf4j
public class PluginToolbar extends JToolBar
/**
* Plugin toolbar buttons holder.
*/
@Singleton
public class PluginToolbar
{
public static final int TOOLBAR_WIDTH = 36, TOOLBAR_HEIGHT = 503;
private final EventBus eventBus;
private final TreeSet<NavigationButton> buttons = new TreeSet<>(Comparator.comparing(NavigationButton::getName));
private final ClientUI ui;
private final TreeSet<NavigationButton> buttons = new TreeSet<>(Comparator.comparing(Component::getName));
private NavigationButton current;
public PluginToolbar(ClientUI ui)
@Inject
private PluginToolbar(final EventBus eventBus)
{
super(JToolBar.VERTICAL);
this.ui = ui;
super.setFloatable(false);
super.setSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT));
super.setMinimumSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT));
super.setPreferredSize(new Dimension(TOOLBAR_WIDTH, TOOLBAR_HEIGHT));
super.setMaximumSize(new Dimension(TOOLBAR_WIDTH, Integer.MAX_VALUE));
this.eventBus = eventBus;
}
public void addNavigation(NavigationButton button)
/**
* Add navigation.
*
* @param button the button
*/
public void addNavigation(final NavigationButton button)
{
button.addActionListener((ae) -> onClick(button));
button.setToolTipText(button.getName());
if (buttons.contains(button))
{
log.warn("Button already in container '{}'", button.getName());
return;
}
buttons.add(button);
if (buttons.add(button))
{
int index = buttons.headSet(button).size();
eventBus.post(new PluginToolbarButtonAdded(button, index));
}
}
/**
* Remove navigation.
*
* @param button the button
*/
public void removeNavigation(final NavigationButton button)
{
int index = buttons.headSet(button).size();
add(button, index);
revalidate();
repaint();
}
public void removeNavigation(NavigationButton button)
{
buttons.remove(button);
remove(button);
revalidate();
repaint();
}
private void onClick(NavigationButton button)
{
Supplier<PluginPanel> panelSupplier = button.getPanelSupplier();
if (panelSupplier == null)
if (buttons.remove(button))
{
return;
}
if (current != null)
{
current.setSelected(false);
}
if (current == button)
{
ui.contract();
current = null;
}
else
{
current = button;
current.setSelected(true);
PluginPanel pluginPanel = panelSupplier.get();
ui.expand(pluginPanel);
eventBus.post(new PluginToolbarButtonRemoved(button, index));
}
}
}

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.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,174 +25,78 @@
*/
package net.runelite.client.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.LayoutManager2;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLiteProperties;
import org.pushingpixels.substance.internal.SubstanceSynapse;
import com.google.common.eventbus.EventBus;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.client.events.TitleToolbarButtonAdded;
import net.runelite.client.events.TitleToolbarButtonRemoved;
@Slf4j
public class TitleToolbar extends JPanel
/**
* Title toolbar buttons holder.
*/
@Singleton
public class TitleToolbar
{
private static final int TITLEBAR_SIZE = 23;
private static final int ITEM_PADDING = 4;
private final EventBus eventBus;
private final TreeSet<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
// was the GroupLayout manager, which doesn't work with dynamic layouts like this one.
// Primarily, it would not remove components unless it was immediately repainted.
setLayout(new LayoutManager2()
this.eventBus = eventBus;
}
/**
* Add navigation.
*
* @param button the button
*/
public void addNavigation(final NavigationButton button)
{
if (buttons.contains(button))
{
@Override
public void addLayoutComponent(String name, Component comp)
{
}
@Override
public void addLayoutComponent(Component comp, Object constraints)
{
}
@Override
public void removeLayoutComponent(Component comp)
{
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
int width = parent.getComponentCount() * (TITLEBAR_SIZE + ITEM_PADDING);
return new Dimension(width, TITLEBAR_SIZE);
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
return preferredLayoutSize(parent);
}
@Override
public Dimension maximumLayoutSize(Container parent)
{
return preferredLayoutSize(parent);
}
@Override
public float getLayoutAlignmentX(Container target)
{
return 0;
}
@Override
public float getLayoutAlignmentY(Container target)
{
return 0;
}
@Override
public void invalidateLayout(Container target)
{
}
@Override
public void layoutContainer(Container parent)
{
int x = 0;
for (Component c : parent.getComponents())
{
x += ITEM_PADDING;
int height = c.getPreferredSize().height;
if (height > TITLEBAR_SIZE)
{
height = TITLEBAR_SIZE;
}
c.setBounds(x, (TITLEBAR_SIZE - height) / 2, TITLEBAR_SIZE, height);
x += TITLEBAR_SIZE;
}
}
});
try
{
BufferedImage discordIcon;
BufferedImage invertedIcon;
synchronized (ImageIO.class)
{
discordIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord.png"));
invertedIcon = ImageIO.read(ClientUI.class.getResourceAsStream("discord_inverted.png"));
}
JButton discordButton = new JButton();
discordButton.setToolTipText("Join Discord");
discordButton.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
try
{
Desktop.getDesktop().browse(new URL(properties.getDiscordInvite()).toURI());
}
catch (IOException | URISyntaxException ex)
{
log.warn("error opening browser", ex);
}
}
});
addButton(discordButton, discordIcon, invertedIcon);
return;
}
catch (IOException ex)
if (buttons.add(button))
{
log.warn("unable to load discord button", ex);
int index = buttons.headSet(button).size();
eventBus.post(new TitleToolbarButtonAdded(button, index));
}
}
public void addButton(JButton button, Image iconImage, Image invertedIconImage)
/**
* Remove navigation.
*
* @param button the button
*/
public void removeNavigation(final NavigationButton button)
{
final int iconSize = TITLEBAR_SIZE - 6;
ImageIcon icon = new ImageIcon(iconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH));
ImageIcon invertedIcon;
if (invertedIconImage == null)
{
invertedIcon = icon;
}
else
{
invertedIcon = new ImageIcon(invertedIconImage.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH));
}
int index = buttons.headSet(button).size();
button.setIcon(icon);
button.setRolloverIcon(invertedIcon);
button.putClientProperty(SubstanceSynapse.FLAT_LOOK, Boolean.TRUE);
button.setFocusable(false);
add(button);
revalidate();
repaint();
if (buttons.remove(button))
{
eventBus.post(new TitleToolbarButtonRemoved(button, index));
}
}
@Override
public void remove(Component c)
/**
* Refresh all buttons
*/
public void refresh()
{
super.remove(c);
revalidate();
repaint();
final Iterator<NavigationButton> iterator = buttons.iterator();
int index = 0;
while (iterator.hasNext())
{
final NavigationButton button = iterator.next();
eventBus.post(new TitleToolbarButtonRemoved(button, index));
eventBus.post(new TitleToolbarButtonAdded(button, index));
index++;
}
}
}

View File

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

View File

@@ -26,8 +26,8 @@ package net.runelite.client.util;
import com.apple.eawt.Application;
import com.apple.eawt.FullScreenUtilities;
import javax.swing.JFrame;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.ui.ClientUI;
/**
* A class with OSX-specific functions to improve integration.
@@ -40,7 +40,7 @@ public class OSXUtil
*
* @param gui The gui to enable the fullscreen on.
*/
public static void tryEnableFullscreen(ClientUI gui)
public static void tryEnableFullscreen(JFrame gui)
{
if (OSType.getOSType() == OSType.MacOS)
{

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