Remove need to extend JToolBar in PluginToolbar

- Create PluginToolbar through Guice
- Create separate component class for toolbar layout
- Create custom navigation button component to not depend on swing
- Create new events that are fired on new toolbar button addition and
removal

Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
This commit is contained in:
Tomas Slusny
2018-03-07 14:59:37 +01:00
parent 9bcc646d88
commit 445c5baf99
6 changed files with 286 additions and 100 deletions

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,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

@@ -39,11 +39,14 @@ import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
@@ -58,6 +61,8 @@ 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.util.OSType;
import net.runelite.client.util.OSXUtil;
import net.runelite.client.util.SwingUtil;
@@ -97,9 +102,6 @@ public class ClientUI
@Getter
private TrayIcon trayIcon;
@Getter
private PluginToolbar pluginToolbar;
@Getter
private TitleToolbar titleToolbar;
@@ -111,6 +113,8 @@ public class ClientUI
private JFrame frame;
private JPanel navContainer;
private PluginPanel pluginPanel;
private ClientPluginToolbar pluginToolbar;
private JButton currentButton;
@Inject
private ClientUI(
@@ -193,6 +197,56 @@ public class ClientUI
});
}
@Subscribe
public void onPluginToolbarButtonAdded(final PluginToolbarButtonAdded event)
{
final JButton button = new JButton();
button.setName(event.getButton().getName());
button.setToolTipText(event.getButton().getTooltip());
button.setToolTipText(event.getButton().getTooltip());
button.setIcon(new ImageIcon(event.getButton().getIcon()));
button.addActionListener(e ->
{
final Supplier<PluginPanel> panelSupplier = event.getButton().getPanel();
if (panelSupplier == null)
{
return;
}
if (currentButton != null)
{
currentButton.setSelected(false);
}
if (currentButton == button)
{
contract();
currentButton = null;
}
else
{
currentButton = button;
currentButton.setSelected(true);
expand(panelSupplier.get());
}
if (event.getButton().getOnClick() != null)
{
event.getButton().getOnClick().run();
}
});
event.getButton().setOnSelect(() -> button.setSelected(event.getButton().isSelected()));
SwingUtilities.invokeLater(() -> pluginToolbar.addComponent(event.getIndex(), event.getButton(), button));
}
@Subscribe
public void onPluginToolbarButtonRemoved(final PluginToolbarButtonRemoved event)
{
SwingUtilities.invokeLater(() -> pluginToolbar.removeComponent(event.getButton()));
}
/**
* Initialize UI.
*
@@ -243,7 +297,7 @@ public class ClientUI
navContainer.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
container.add(navContainer);
pluginToolbar = new PluginToolbar(this);
pluginToolbar = new ClientPluginToolbar();
container.add(pluginToolbar);
titleToolbar = new TitleToolbar(properties);
@@ -378,12 +432,7 @@ public class ClientUI
giveClientFocus();
}
/**
* Expand panel.
*
* @param panel the panel
*/
public void expand(PluginPanel panel)
private void expand(PluginPanel panel)
{
if (pluginPanel != null)
{
@@ -415,10 +464,7 @@ public class ClientUI
SwingUtil.revalidateMinimumSize(frame);
}
/**
* Contract. panel.
*/
public void contract()
private void contract()
{
boolean wasMinimumWidth = frame.getWidth() == frame.getMinimumSize().width;
pluginPanel.onDeactivate();

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,50 @@
*/
package net.runelite.client.ui;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.function.Supplier;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import lombok.Builder;
import lombok.Data;
@Slf4j
public class NavigationButton extends JButton
/**
* UI navigation button.
*/
@Data
@Builder
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;
/**
* Supplier for plugin panel, used when expanding and contracting sidebar.
*/
private Supplier<PluginPanel> panel;
}

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