biskuteri-cafe-JKomasto2/TwoToggleButton.java
Snowyfox 775faa0bcc Fixed window updater thread issue (seemingly).
Better support for multithreading.
Fixed notifications window refresh bug.
Fixed autoview window bug (temporary scrollpane doesn't reset scroll position)
2022-05-06 16:34:22 -04:00

415 lines
9.5 KiB
Java

import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.net.URL;
import javax.swing.JFrame;
class
TwoToggleButton extends AbstractButton
implements KeyListener, MouseListener, FocusListener {
private String
primaryName,
secondaryName;
// - -%- -
private boolean
primaryToggled = false,
secondaryToggled = false;
private Image
primaryToggledIcon,
secondaryToggledIcon,
primaryUntoggledIcon,
secondaryUntoggledIcon;
private int
nextEventID = ActionEvent.ACTION_FIRST;
// - -%- -
private static Image
button,
selectedOverlay,
disabledOverlay;
// ---%-@-%---
public void
togglePrimary()
{
setPrimaryToggled(!primaryToggled);
}
public void
toggleSecondary()
{
setSecondaryToggled(!secondaryToggled);
}
public void
setPrimaryToggled(boolean toggled)
{
primaryToggled = toggled;
repaint();
announce(primaryName, toggled);
}
public void
setSecondaryToggled(boolean toggled)
{
secondaryToggled = toggled;
repaint();
announce(secondaryName, toggled);
}
public boolean
getPrimaryToggled()
{
return primaryToggled;
}
public boolean
getSecondaryToggled()
{
return secondaryToggled;
}
// - -%- -
private void
announce(String name, boolean toggled)
{
ActionEvent eA = new ActionEvent(
this, nextEventID++,
name + (toggled ? "On" : "Off")
);
for (ActionListener listener: getActionListeners())
listener.actionPerformed(eA);
}
protected void
paintComponent(Graphics g)
{
g.drawImage(button, 0, 0, this);
if (!isEnabled())
g.drawImage(disabledOverlay, 0, 0, this);
if (isFocusOwner())
g.drawImage(selectedOverlay, 0, 0, this);
if (secondaryToggled)
g.drawImage(secondaryToggledIcon, 0, 0, this);
else
g.drawImage(secondaryUntoggledIcon, 0, 0, this);
if (primaryToggled)
g.drawImage(primaryToggledIcon, 0, 0, this);
else
g.drawImage(primaryUntoggledIcon, 0, 0, this);
}
public void
mouseClicked(MouseEvent eM)
{
switch (eM.getButton()) {
case MouseEvent.BUTTON1: togglePrimary(); break;
case MouseEvent.BUTTON3: toggleSecondary(); break;
}
requestFocusInWindow();
}
public void
keyPressed(KeyEvent eK)
{
switch (eK.getKeyCode()) {
case KeyEvent.VK_SPACE: togglePrimary(); break;
case KeyEvent.VK_ENTER: toggleSecondary(); break;
}
requestFocusInWindow();
}
public void
focusGained(FocusEvent eF) { repaint(); }
public void
focusLost(FocusEvent eF) { repaint(); }
public void
mousePressed(MouseEvent eM) { }
public void
mouseReleased(MouseEvent eM) { }
public void
mouseEntered(MouseEvent eM) { }
public void
mouseExited(MouseEvent eM) { }
public void
keyReleased(KeyEvent eK) { }
public void
keyTyped(KeyEvent eK) { }
// (悪) If we don't have the 'armed' behaviour of JButton,
// we really need to rectify that, it's important for this
// use case.
// ---%-@-%---
TwoToggleButton(String primaryName, String secondaryName)
{
if (button == null) loadCommonImages();
this.primaryName = primaryName;
this.secondaryName = secondaryName;
setModel(new DefaultButtonModel());
setFocusable(true);
setOpaque(false);
int w = button.getWidth(null);
int h = button.getHeight(null);
setPreferredSize(new Dimension(w, h));
loadSpecificImages();
this.addKeyListener(this);
this.addMouseListener(this);
this.addFocusListener(this);
}
private void
loadSpecificImages()
{
String p1 = "graphics/" + primaryName + "Toggled.png";
String p2 = "graphics/" + secondaryName + "Toggled.png";
String p3 = "graphics/" + primaryName + "Untoggled.png";
String p4 = "graphics/" + secondaryName + "Untoggled.png";
URL u1 = getClass().getResource(p1);
URL u2 = getClass().getResource(p2);
URL u3 = getClass().getResource(p3);
URL u4 = getClass().getResource(p4);
if (u1 == null) primaryToggledIcon = null;
else primaryToggledIcon = new ImageIcon(u1).getImage();
if (u2 == null) secondaryToggledIcon = null;
else secondaryToggledIcon = new ImageIcon(u2).getImage();
if (u3 == null) primaryUntoggledIcon = null;
else primaryUntoggledIcon = new ImageIcon(u3).getImage();
if (u4 == null) secondaryUntoggledIcon = null;
else secondaryUntoggledIcon = new ImageIcon(u4).getImage();
}
// - -%- -
private static void
loadCommonImages()
{
Class c = TwoToggleButton.class;
URL u1 = c.getResource("graphics/button.png");
URL u2 = c.getResource("graphics/disabledOverlay.png");
URL u3 = c.getResource("graphics/selectedOverlay.png");
assert u1 != null && u2 != null && u3 != null;
button = new ImageIcon(u1).getImage();
disabledOverlay = new ImageIcon(u2).getImage();
selectedOverlay = new ImageIcon(u3).getImage();
}
}
class
RoundButton extends AbstractButton
implements KeyListener, MouseListener, FocusListener {
private Image
image;
// - -%- -
private int
nextEventID = ActionEvent.ACTION_FIRST;
// - -%- -
private static Image
button,
selectedOverlay,
disabledOverlay;
// ---%-@-%---
public void
setImage(Image n) { image = n; }
// - -%- -
protected void
paintComponent(Graphics g)
{
g.drawImage(button, 0, 0, this);
if (!isEnabled())
g.drawImage(disabledOverlay, 0, 0, this);
if (isFocusOwner())
g.drawImage(selectedOverlay, 0, 0, this);
if (image == null) return;
int w1 = button.getWidth(this);
int h1 = button.getHeight(this);
int w2 = image.getWidth(this);
int h2 = image.getHeight(this);
if (w2 == -1) w2 = w1;
if (h2 == -1) h2 = h1;
if (h2 > w2) {
h2 = h2 * w1 / w2;
w2 = w1;
}
else {
w2 = w2 * h1 / h2;
h2 = h1;
}
Shape defaultClip, roundClip;
defaultClip = g.getClip();
roundClip = new Ellipse2D.Float(6, 6, w1 - 12, h1 - 12);
g.setClip(roundClip);
g.drawImage(image, 0, 0, w2, h2, getParent());
// I don't know why, but when we repaint ourselves, our
// parent doesn't repaint, so nothing seems to happen.
g.setClip(defaultClip);
}
private void
announce()
{
ActionEvent eA = new ActionEvent(this, nextEventID++, null);
for (ActionListener listener: getActionListeners())
listener.actionPerformed(eA);
}
public void
keyPressed(KeyEvent eK)
{
if (eK.getKeyCode() != KeyEvent.VK_SPACE) return;
doClick();
}
public void
mouseClicked(MouseEvent eM) { announce(); }
public void
focusGained(FocusEvent eF) { repaint(); }
public void
focusLost(FocusEvent eF) { repaint(); }
public void
mousePressed(MouseEvent eM) { }
public void
mouseReleased(MouseEvent eM) { }
public void
mouseEntered(MouseEvent eM) { }
public void
mouseExited(MouseEvent eM) { }
public void
keyReleased(KeyEvent eK) { }
public void
keyTyped(KeyEvent eK) { }
// (悪) Again, armed?
// ---%-@-%---
RoundButton()
{
if (button == null) loadCommonImages();
setModel(new DefaultButtonModel());
setFocusable(true);
setOpaque(false);
int w = button.getWidth(this);
int h = button.getHeight(this);
setPreferredSize(new Dimension(w, h));
this.addKeyListener(this);
this.addMouseListener(this);
this.addFocusListener(this);
}
// - -%- -
private static void
loadCommonImages()
{
Class c = TwoToggleButton.class;
URL u1 = c.getResource("graphics/button.png");
URL u2 = c.getResource("graphics/disabledOverlay.png");
URL u3 = c.getResource("graphics/selectedOverlay.png");
assert u1 != null && u2 != null && u3 != null;
button = new ImageIcon(u1).getImage();
disabledOverlay = new ImageIcon(u2).getImage();
selectedOverlay = new ImageIcon(u3).getImage();
}
}
class
TwoToggleButtonTest {
public static void
main(String... args)
{
if (args.length != 2)
{
String err = "Please give two toggle names.";
System.err.println(err);
System.exit(1);
}
String p = args[0], s = args[1];
TwoToggleButton t1 = new TwoToggleButton(p, s);
TwoToggleButton t2 = new TwoToggleButton(p, s);
TwoToggleButton t3 = new TwoToggleButton(p, s);
JFrame mainframe = new JFrame("Two-toggle button test");
mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainframe.setLocationByPlatform(true);
mainframe.add(t1, java.awt.BorderLayout.WEST);
mainframe.add(t2);
mainframe.add(t3, java.awt.BorderLayout.EAST);
mainframe.pack();
t2.setEnabled(false);
t3.requestFocusInWindow();
mainframe.setVisible(true);
}
}