mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2024-11-20 04:54:50 +01:00
509 lines
12 KiB
Java
509 lines
12 KiB
Java
/* copyright
|
|
|
|
This file is part of JKomasto2.
|
|
Written in 2022 by Usawashi <usawashi16@yahoo.co.jp>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
copyright */
|
|
|
|
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.Rectangle;
|
|
import java.awt.Component;
|
|
import java.awt.image.BufferedImage;
|
|
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
|
|
mousePressed(MouseEvent eM)
|
|
{
|
|
boolean shift = eM.isShiftDown();
|
|
boolean prim = eM.getButton() == MouseEvent.BUTTON1;
|
|
boolean secon = eM.getButton() == MouseEvent.BUTTON3;
|
|
secon |= shift && prim;
|
|
|
|
if (secon) toggleSecondary();
|
|
else if (prim) togglePrimary();
|
|
|
|
requestFocusInWindow();
|
|
}
|
|
|
|
public void
|
|
keyPressed(KeyEvent eK)
|
|
{
|
|
boolean shift = eK.isShiftDown();
|
|
boolean prim = eK.getKeyCode() == KeyEvent.VK_SPACE;
|
|
boolean secon = eK.getKeyCode() == KeyEvent.VK_ENTER;
|
|
secon |= shift && prim;
|
|
|
|
if (secon) toggleSecondary();
|
|
else if (prim) togglePrimary();
|
|
|
|
requestFocusInWindow();
|
|
}
|
|
|
|
public void
|
|
focusGained(FocusEvent eF) { repaint(); }
|
|
|
|
public void
|
|
focusLost(FocusEvent eF) { repaint(); }
|
|
|
|
|
|
public void
|
|
mouseClicked(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 Image
|
|
copy,
|
|
scaled;
|
|
|
|
private int
|
|
nextEventID = ActionEvent.ACTION_FIRST;
|
|
|
|
// - -%- -
|
|
|
|
private static Image
|
|
button,
|
|
selectedOverlay,
|
|
disabledOverlay;
|
|
|
|
private static Shape
|
|
roundClip;
|
|
|
|
// ---%-@-%---
|
|
|
|
public void
|
|
setImage(Image n)
|
|
{
|
|
image = n;
|
|
copy = null;
|
|
scaled = null;
|
|
|
|
if (image != null)
|
|
{
|
|
image.flush();
|
|
prepareImage(image, this);
|
|
}
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
public boolean
|
|
imageUpdate(Image img, int f, int x, int y, int w, int h)
|
|
{
|
|
// AbstractButton overrode this to refuse updates for
|
|
// images that aren't the button's icon. We don't use
|
|
// the icon, so we're overriding it back. Also, we have
|
|
// some async work to do regarding the images.
|
|
|
|
if ((f & (ABORT|ERROR)) != 0) return false;
|
|
|
|
boolean all = (f & ALLBITS) != 0;
|
|
boolean frame = (f & FRAMEBITS) != 0;
|
|
boolean some = (f & SOMEBITS) != 0;
|
|
|
|
if (frame && img != this.image) return false;
|
|
|
|
if (img == this.image && (all || frame))
|
|
{
|
|
int ow = img.getWidth(null);
|
|
int oh = img.getHeight(null);
|
|
|
|
if (copy == null)
|
|
{
|
|
int type = BufferedImage.TYPE_INT_ARGB;
|
|
copy = new BufferedImage(ow, oh, type);
|
|
}
|
|
|
|
Graphics g = copy.getGraphics();
|
|
g.drawImage(img, 0, 0, null);
|
|
g.dispose();
|
|
|
|
int algo = Image.SCALE_SMOOTH;
|
|
Rectangle b = roundClip.getBounds();
|
|
int sw = ow > oh ? -1 : b.width;
|
|
int sh = oh > ow ? -1 : b.height;
|
|
scaled = copy.getScaledInstance(sw, sh, algo);
|
|
/*
|
|
* We create a scaled instance from a BufferedImage copy
|
|
* rather than this.image directly, to avoid a ClassCast
|
|
* Exception bug in the JDK, where ColorModel was
|
|
* incorrectly casting an int array of input data into
|
|
* a byte array. I'm not sure why that bug exists nor
|
|
* why they haven't noticed it, but.
|
|
*/
|
|
repaint();
|
|
}
|
|
if (img == scaled && (some || all))
|
|
{
|
|
repaint();
|
|
}
|
|
return all ? false : true;
|
|
}
|
|
|
|
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 (scaled == null) return;
|
|
|
|
Rectangle b = roundClip.getBounds();
|
|
Shape defaultClip = g.getClip();
|
|
g.setClip(roundClip);
|
|
g.drawImage(scaled, b.x, b.y, this);
|
|
getParent().repaint();
|
|
// 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(null);
|
|
int h = button.getHeight(null);
|
|
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();
|
|
|
|
int radius = 6;
|
|
roundClip = new Ellipse2D.Float(
|
|
radius,
|
|
radius,
|
|
button.getWidth(null) - (2 * radius),
|
|
button.getHeight(null) - (2 * radius)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
}
|