mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
413 lines
16 KiB
Java
413 lines
16 KiB
Java
package forge.gui.toolbox;
|
|
|
|
import java.awt.AlphaComposite;
|
|
import java.awt.Color;
|
|
import java.awt.Dimension;
|
|
import java.awt.Font;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.Image;
|
|
import java.awt.Point;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.ComponentAdapter;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
|
|
import javax.swing.Icon;
|
|
import javax.swing.ImageIcon;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.SwingConstants;
|
|
import javax.swing.Timer;
|
|
|
|
import forge.Command;
|
|
import forge.gui.framework.ILocalRepaint;
|
|
|
|
/**
|
|
* Uses the Builder pattern to facilitate/encourage inline styling.
|
|
* Credit to Effective Java 2 (Joshua Bloch).
|
|
* Methods in builder can be chained. To declare:
|
|
* <code>new FLabel.Builder().method1(foo).method2(bar).method3(baz)...</code>
|
|
* <br>and then call build() to make the label (don't forget that part).
|
|
* <br><br>
|
|
* Adjustable features of FLabel:<br>
|
|
* - Automatic font scaling (60% size by default, can toggle on/off)<br>
|
|
* - Automatic icon scaling (80% size by default, can toggle on/off)<br>
|
|
* - Scale font according to height or width<br>
|
|
* - Hoverable<br>
|
|
* - Selectable<br>
|
|
* - Can execute command when clicked
|
|
*/
|
|
@SuppressWarnings("serial")
|
|
public class FLabel extends JLabel implements ILocalRepaint {
|
|
/**
|
|
* Uses the Builder pattern to facilitate/encourage inline styling.
|
|
* Credit to Effective Java 2 (Joshua Bloch).
|
|
* Methods in builder can be chained. To declare:
|
|
* <code>new FLabel.Builder().method1(foo).method2(bar).method3(baz)...</code>
|
|
* <br>and then call build() to make the label (don't forget that part).
|
|
*/
|
|
public static class Builder extends FLabel {
|
|
//========== Default values for FLabel are set here.
|
|
private double bldIconScaleFactor = 0.8;
|
|
private int bldFontStyle = Font.PLAIN;
|
|
private int bldFontSize = 14;
|
|
private float bldIconAlpha = 1.0f;
|
|
private int bldIconAlignX = SwingConstants.LEFT;
|
|
private Point bldIconInsets = new Point(0, 0);
|
|
|
|
private boolean bldSelectable = false;
|
|
private boolean bldHoverable = false;
|
|
private boolean bldOpaque = false;
|
|
private boolean bldIconInBackground = false;
|
|
private boolean bldIconScaleAuto = true;
|
|
|
|
private String bldText, bldToolTip;
|
|
private ImageIcon bldIcon;
|
|
private int bldFontAlign;
|
|
private Command bldCmd;
|
|
|
|
// Build!
|
|
/** @return {@link forge.gui.toolbox.FLabel} */
|
|
public FLabel build() { return new FLabel(this); }
|
|
|
|
// Begin builder methods.
|
|
/**@param s0   {@link java.lang.String}
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder text(final String s0) { this.bldText = s0; return this; }
|
|
|
|
/**@param s0   {@link java.lang.String}
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder tooltip(final String s0) { this.bldToolTip = s0; return this; }
|
|
|
|
/**@param i0   {@link javax.swing.ImageIcon}
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder icon(final ImageIcon i0) { this.bldIcon = i0; return this; }
|
|
|
|
/**@param i0   SwingConstants.CENTER, .LEFT, or .RIGHT
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder fontAlign(final int i0) { this.bldFontAlign = i0; return this; }
|
|
|
|
/**@param b0   boolean
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder opaque(final boolean b0) { this.bldOpaque = b0; return this; }
|
|
|
|
/**@param b0   boolean
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder hoverable(final boolean b0) { this.bldHoverable = b0; return this; }
|
|
|
|
/**@param b0   boolean
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder selectable(final boolean b0) { this.bldSelectable = b0; return this; }
|
|
|
|
/**@param c0   {@link forge.Command} to execute if clicked
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder cmdClick(final Command c0) { this.bldCmd = c0; return this; }
|
|
|
|
/**@param i0   int
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder fontSize(final int i0) { this.bldFontSize = i0; return this; }
|
|
|
|
/**@param i0   Font.PLAIN, Font.BOLD, or Font.ITALIC
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder fontStyle(final int i0) { this.bldFontStyle = i0; return this; }
|
|
|
|
/**@param b0   boolean
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconScaleAuto(final boolean b0) { this.bldIconScaleAuto = b0; return this; }
|
|
|
|
/**@param d0   double between 0 and 1, 0.8 by default
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconScaleFactor(final double d0) { this.bldIconScaleFactor = d0; return this; }
|
|
|
|
/**@param b0   boolean, icon will be drawn independent of text
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconInBackground(final boolean b0) { this.bldIconInBackground = b0; return this; }
|
|
|
|
/**@param f0   0.0f - 1.0f. If icon is in background, this alpha is applied.
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconAlpha(final float f0) { this.bldIconAlpha = f0; return this; }
|
|
|
|
/**@param i0   Int. Only available for background icon.
|
|
* SwingConstants.HORIZONTAL .VERTICAL or .CENTER
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconAlignX(final int i0) { this.bldIconAlignX = i0; return this; }
|
|
|
|
/**@param i0   Point. Only available for background icon.
|
|
* Additional padding to top left corner of icon, after alignX.
|
|
* @return {@link forge.gui.toolbox.Builder} */
|
|
public Builder iconInsets(final Point i0) { this.bldIconInsets = i0; return this; }
|
|
}
|
|
|
|
//========== Constructors
|
|
/** Must have protected constructor to allow Builder to subclass. */
|
|
protected FLabel() { }
|
|
|
|
// Call this using FLabel.Builder()...
|
|
private FLabel(final Builder b0) {
|
|
super(b0.bldText);
|
|
|
|
// Init fields from builder
|
|
this.iconScaleFactor = b0.bldIconScaleFactor;
|
|
|
|
this.opaque = b0.bldOpaque;
|
|
this.iconInBackground = b0.bldIconInBackground;
|
|
this.iconScaleAuto = b0.bldIconScaleAuto;
|
|
this.selectable = b0.bldSelectable;
|
|
this.iconAlignX = b0.bldIconAlignX;
|
|
this.iconInsets = b0.bldIconInsets;
|
|
|
|
this.setFontStyle(b0.bldFontStyle);
|
|
this.setFontSize(b0.bldFontSize);
|
|
this.setIconAlpha(b0.bldIconAlpha);
|
|
this.setCommand(b0.bldCmd);
|
|
this.setFontAlign(b0.bldFontAlign);
|
|
this.setText(b0.bldText);
|
|
this.setToolTipText(b0.bldToolTip);
|
|
this.setHoverable(b0.bldHoverable);
|
|
|
|
// Call this last; to allow the properties which affect icons to already be in place.
|
|
this.setIcon(b0.bldIcon);
|
|
|
|
// Non-custom display properties
|
|
this.setForeground(clrText);
|
|
this.setBackground(clrInactive);
|
|
this.setVerticalTextPosition(SwingConstants.CENTER);
|
|
this.setVerticalAlignment(SwingConstants.CENTER);
|
|
|
|
// Resize adapter
|
|
this.removeComponentListener(cadResize);
|
|
this.addComponentListener(cadResize);
|
|
}
|
|
|
|
//========== Variable initialization
|
|
// Final inits
|
|
private final Color clrText = FSkin.getColor(FSkin.Colors.CLR_TEXT);
|
|
private final Color clrBorders = FSkin.getColor(FSkin.Colors.CLR_BORDERS);
|
|
private final Color clrHover = FSkin.getColor(FSkin.Colors.CLR_HOVER);
|
|
private final Color clrInactive = FSkin.getColor(FSkin.Colors.CLR_INACTIVE);
|
|
private final Color clrActive = FSkin.getColor(FSkin.Colors.CLR_ACTIVE);
|
|
|
|
// Custom properties, assigned either at realization (using builder)
|
|
// or dynamically (using methods below).
|
|
private double iconScaleFactor;
|
|
private int fontStyle, iconAlignX;
|
|
private boolean selectable, selected, hoverable, hovered, opaque,
|
|
iconInBackground, iconScaleAuto;
|
|
private Point iconInsets;
|
|
|
|
// Various variables used in image rendering.
|
|
private Image img;
|
|
private Graphics2D g2d;
|
|
private Command cmdClick;
|
|
private int x, y, w, h, iw, ih, sw, sh;
|
|
private double iar;
|
|
|
|
private AlphaComposite alphaDim, alphaStrong;
|
|
|
|
private final ActionListener fireResize = new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(final ActionEvent evt) { resize(); resizeTimer.stop(); }
|
|
};
|
|
|
|
private final Timer resizeTimer = new Timer(10, fireResize);
|
|
|
|
// Resize adapter; on a timer to prevent resizing while "sliding" between sizes
|
|
private final ComponentAdapter cadResize = new ComponentAdapter() {
|
|
@Override
|
|
public void componentResized(final ComponentEvent e) { resizeTimer.restart(); }
|
|
};
|
|
|
|
// Mouse event handler
|
|
private final MouseAdapter madEvents = new MouseAdapter() {
|
|
@Override
|
|
public void mouseEntered(final MouseEvent e) {
|
|
hovered = true; repaintSelf();
|
|
}
|
|
|
|
@Override
|
|
public void mouseExited(final MouseEvent e) {
|
|
hovered = false; repaintSelf();
|
|
}
|
|
@Override
|
|
public void mouseClicked(final MouseEvent e) {
|
|
if (cmdClick != null && FLabel.this.isEnabled()) { cmdClick.execute(); }
|
|
if (!selectable) { return; }
|
|
if (selected) { setSelected(false); }
|
|
else { setSelected(true); }
|
|
}
|
|
};
|
|
|
|
//========== Methods
|
|
/** @param b0   boolean */
|
|
// Must be public.
|
|
public void setHoverable(final boolean b0) {
|
|
this.hoverable = b0;
|
|
if (!b0) { this.removeMouseListener(madEvents); }
|
|
else { this.addMouseListener(madEvents); }
|
|
}
|
|
|
|
/** @param b0   boolean */
|
|
// Must be public.
|
|
public void setSelected(final boolean b0) {
|
|
this.selected = b0;
|
|
repaintSelf();
|
|
}
|
|
|
|
/** Sets alpha if icon is in background.
|
|
* @param f0   float */
|
|
// NOT public; must be set when label is built.
|
|
private void setIconAlpha(final float f0) {
|
|
this.alphaDim = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, f0);
|
|
this.alphaStrong = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);
|
|
}
|
|
|
|
private void setFontSize(final int i0) {
|
|
switch(this.fontStyle) {
|
|
case Font.BOLD: this.setFont(FSkin.getBoldFont(i0)); break;
|
|
case Font.ITALIC: this.setFont(FSkin.getItalicFont(i0)); break;
|
|
default: this.setFont(FSkin.getFont(i0));
|
|
}
|
|
}
|
|
|
|
/** @param i0   Font.PLAIN, .BOLD, or .ITALIC */
|
|
// NOT public; must be set when label is built.
|
|
private void setFontStyle(final int i0) {
|
|
if (i0 != Font.PLAIN && i0 != Font.BOLD && i0 != Font.ITALIC) {
|
|
throw new IllegalArgumentException("FLabel$setFontStyle "
|
|
+ "must be passed either Font.PLAIN, Font.BOLD, or Font.ITALIC.");
|
|
}
|
|
this.fontStyle = i0;
|
|
}
|
|
|
|
/** @param i0   SwingConstants.CENTER, .LEFT or .RIGHT */
|
|
// NOT public; must be set when label is built.
|
|
private void setFontAlign(final int i0) {
|
|
if (i0 != SwingConstants.CENTER && i0 != SwingConstants.LEFT && i0 != SwingConstants.RIGHT) {
|
|
throw new IllegalArgumentException("FLabel$setFontStyle "
|
|
+ "must be passed either SwingConstants.CENTER, "
|
|
+ "SwingConstants.LEFT, or SwingConstants.RIGHT");
|
|
}
|
|
this.setHorizontalAlignment(i0);
|
|
}
|
|
|
|
/** Resizing in MigLayout "slides" between the original and destination sizes.
|
|
* To prevent this label from recalculating on each increment, a timer
|
|
* is run to check that the the "sliding" is finished. To resize this label
|
|
* explicitly, retrieve this timer and start it. It will stop automatically.
|
|
*
|
|
* @return {@link javax.swing.Timer}
|
|
*/
|
|
public Timer getResizeTimer() {
|
|
return this.resizeTimer;
|
|
}
|
|
|
|
/** @return {@link forge.Command} */
|
|
public Command getCommand() {
|
|
return this.cmdClick;
|
|
}
|
|
|
|
/** @return boolean */
|
|
public boolean isSelected() {
|
|
return selected;
|
|
}
|
|
|
|
@Override
|
|
// Must be public.
|
|
public void setIcon(final Icon i0) {
|
|
if (i0 == null) { this.img = null; return; }
|
|
// Will need image (not icon) for scaled and non-scaled.
|
|
if (iconInBackground) { this.img = ((ImageIcon) i0).getImage(); }
|
|
// Will need image if not in background, but scaled.
|
|
else if (iconScaleAuto) { this.img = ((ImageIcon) i0).getImage(); }
|
|
// If not in background, not scaled, can use original icon.
|
|
else { super.setIcon(i0); }
|
|
|
|
if (img != null) {
|
|
iw = img.getWidth(null);
|
|
ih = img.getHeight(null);
|
|
iar = ((double) iw) / ((double) ih);
|
|
}
|
|
}
|
|
|
|
/** @param c0   {@link forge.Command} on click */
|
|
public void setCommand(final Command c0) {
|
|
this.cmdClick = c0;
|
|
}
|
|
|
|
@Override
|
|
public void setOpaque(final boolean b0) {
|
|
// Must be overridden to allow drawing order of background, icon, string
|
|
this.opaque = b0;
|
|
super.setOpaque(false);
|
|
}
|
|
|
|
/** Major performance kicker - won't repaint whole screen! */
|
|
@Override
|
|
public void repaintSelf() {
|
|
final Dimension d = FLabel.this.getSize();
|
|
repaint(0, 0, d.width, d.height);
|
|
}
|
|
|
|
@Override
|
|
public void paintComponent(final Graphics g) {
|
|
g2d = (Graphics2D) g.create();
|
|
w = getWidth();
|
|
h = getHeight();
|
|
|
|
// Opacity, select
|
|
if (this.opaque && !selected) {
|
|
g2d.setColor(getBackground());
|
|
g2d.fillRect(0, 0, w, h);
|
|
}
|
|
else if (selectable && selected) {
|
|
g2d.setColor(clrActive);
|
|
g2d.fillRect(0, 0, w, h);
|
|
}
|
|
|
|
// Hover
|
|
if (hoverable && hovered && !selected && isEnabled()) {
|
|
g2d.setColor(clrHover);
|
|
g2d.fillRect(0, 0, w, h);
|
|
g2d.setColor(clrBorders);
|
|
g2d.drawRect(0, 0, w - 1, h - 1);
|
|
}
|
|
|
|
// Icon in background
|
|
if (iconInBackground) {
|
|
sh = (int) (h * iconScaleFactor);
|
|
sw = (int) (sh * iar);
|
|
|
|
if (iconAlignX == SwingConstants.CENTER) {
|
|
x = (int) ((w - sw) / 2 + iconInsets.getX());
|
|
}
|
|
else {
|
|
x = (int) iconInsets.getX();
|
|
}
|
|
y = (int) (((h - sh) / 2) + iconInsets.getY());
|
|
|
|
if (hoverable && hovered && !selected) {
|
|
g2d.setComposite(alphaStrong);
|
|
}
|
|
else {
|
|
g2d.setComposite(alphaDim);
|
|
}
|
|
g2d.drawImage(img, x, y, sw + x, sh + y, 0, 0, iw, ih, null);
|
|
}
|
|
|
|
super.paintComponent(g);
|
|
}
|
|
|
|
private void resize() {
|
|
// Non-background icon
|
|
if (img != null && iconScaleAuto && !iconInBackground) {
|
|
h = (int) (getHeight() * iconScaleFactor);
|
|
w = (int) (h * iar);
|
|
if (w == 0 || h == 0) { return; }
|
|
|
|
FLabel.super.setIcon(new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH)));
|
|
}
|
|
}
|
|
}
|