package com.ts3client.ui; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JToggleButton; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.event.ActionListener; import java.util.function.Supplier; /** * A toolbar toggle button with a small arrow next to it that opens a menu: * clicking the button toggles, clicking the arrow offers the related actions. * *

The menu is built on every click by the supplier, so it can reflect the * current state (checked items, the presets that exist right now). */ public final class DropDownToggleButton extends JPanel { private final JToggleButton button = new JToggleButton(); private final JButton arrow = new JButton(new ArrowIcon()); public DropDownToggleButton(Icon icon, String tooltip, Supplier menu) { super(new BorderLayout()); setOpaque(false); button.setIcon(icon); button.setToolTipText(tooltip); arrow.setToolTipText(tooltip); arrow.setMargin(new Insets(0, 2, 0, 2)); arrow.setFocusable(false); arrow.addActionListener(e -> { JPopupMenu popup = menu.get(); if (popup != null) popup.show(this, 0, getHeight()); }); // Keep the whole control the size of a plain toolbar button: the arrow's // width comes out of the toggle half instead of being added to it. Dimension plain = button.getPreferredSize(); button.setPreferredSize(new Dimension( Math.max(icon.getIconWidth() + 8, plain.width - arrow.getPreferredSize().width), plain.height)); add(button, BorderLayout.CENTER); add(arrow, BorderLayout.EAST); } /** Listener for the toggle half only; the arrow half opens the menu instead. */ public void addActionListener(ActionListener l) { button.addActionListener(l); } public boolean isSelected() { return button.isSelected(); } public void setSelected(boolean selected) { button.setSelected(selected); } public void setIcon(Icon icon) { button.setIcon(icon); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); button.setEnabled(enabled); arrow.setEnabled(enabled); } /** Disables the toggle half on its own, leaving the menu reachable. */ public void setToggleEnabled(boolean enabled) { button.setEnabled(enabled); } @Override public Dimension getMaximumSize() { return getPreferredSize(); // keep the toolbar from stretching us } /** The downward triangle on the menu half of the button. */ private static final class ArrowIcon implements Icon { @Override public void paintIcon(Component c, Graphics g0, int x, int y) { Graphics2D g = (Graphics2D) g0.create(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(c.isEnabled() ? Color.DARK_GRAY : Color.GRAY); int[] xs = {x, x + getIconWidth(), x + getIconWidth() / 2}; int[] ys = {y, y, y + getIconHeight()}; g.fillPolygon(xs, ys, 3); g.dispose(); } @Override public int getIconWidth() { return 7; } @Override public int getIconHeight() { return 4; } } }