JIcon.java
package swingtree.components;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sprouts.From;
import sprouts.Val;
import sprouts.Viewable;
import swingtree.UI;
import swingtree.api.IconDeclaration;
import swingtree.layout.Size;
import swingtree.style.ComponentExtension;
import swingtree.style.StylableComponent;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.plaf.ComponentUI;
import java.awt.Graphics;
/**
* A {@link JLabel} subclass specifically designed to display icons only.
* Although a {@link JLabel} already provides the ability to display icons,
* this class is useful for styling purposes, as it is possible to specifically
* target icons inside of {@link swingtree.style.StyleSheet}s.
*/
public class JIcon extends JLabel implements StylableComponent
{
private static final Logger log = LoggerFactory.getLogger(JIcon.class);
@SuppressWarnings("UnusedVariable")
private final @Nullable Val<IconDeclaration> dynamicIcon;
/* ^
We need to keep a strong reference to the dynamic icon, otherwise
it will be garbage collected and the change listener will not update
the icon when it changes.
*/
public JIcon(String path) {
super(_getFromCacheOrLoadFrom(IconDeclaration.of(path)));
updateUI();
dynamicIcon = null;
}
public JIcon(IconDeclaration declaration) {
super(_getFromCacheOrLoadFrom(declaration));
dynamicIcon = null;
}
public JIcon(Icon icon) {
super(icon);
updateUI();
dynamicIcon = null;
}
public JIcon( Size size, Icon icon ) {
super(UI.scaleIconTo(size, icon));
updateUI();
dynamicIcon = null;
}
public JIcon(Icon icon, String text, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
updateUI();
dynamicIcon = null;
}
public JIcon(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
updateUI();
dynamicIcon = null;
}
public JIcon(String path, String text) {
super(text, _getFromCacheOrLoadFrom(IconDeclaration.of(path)), CENTER);
updateUI();
dynamicIcon = null;
}
public JIcon( Val<IconDeclaration> declaration ) {
Viewable.cast(declaration).onChange(From.ALL, it -> {
UI.runNow(()->{
setIcon(_getFromCacheOrLoadFrom(it.currentValue().orElseThrowUnchecked()));
});
});
declaration.ifPresent( it -> setIcon(_getFromCacheOrLoadFrom(it)) );
updateUI();
dynamicIcon = declaration;
}
public JIcon() {
super();
updateUI();
dynamicIcon = null;
}
/** {@inheritDoc} */
@Override public void paintComponent(Graphics g){
paintBackground(g, super::paintComponent);
}
/** {@inheritDoc} */
@Override public void paintChildren(Graphics g) {
paintForeground(g, super::paintChildren);
}
@Override public void setUISilently( ComponentUI ui ) {
this.ui = ui;
}
@Override
public void updateUI() {
ComponentExtension.from(this).installCustomUIIfPossible();
/*
The JIcon is a SwingTree native component type, so it also
enjoys the perks of having a SwingTree based look and feel!
*/
}
@SuppressWarnings("NullAway")
private static @Nullable ImageIcon _getFromCacheOrLoadFrom( IconDeclaration declaration ) {
if ( !UI.thisIsUIThread() ) {
log.warn(
"Loading an icon off the UI thread. " +
"This may lead to unexpected behavior and should be avoided.",
new Throwable() // Log the stack trace for debugging purposes.
);
return UI.runAndGet(()->_getFromCacheOrLoadFrom(declaration));
}
return UI.findIcon(declaration).orElse(null);
}
}