CellDelegate.java
package swingtree;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JComponent;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* This class models the state of an individual table/list/drop down cell alongside
* various properties that a cell should have, like for example
* the value of the cell, its position within the table
* as well as a renderer in the form of a AWT {@link Component}
* which may or not be replaced or modified.
* <br>
* When configuring your cell, you may use methods like
* {@link CellDelegate#withRenderer(Component)} or {@link CellDelegate#withRenderer(Consumer)}
* to define how the cell should be rendered.
*
* @param <V> The value type of the entry of this {@link CellDelegate}.
*/
public class CellDelegate<C extends JComponent, V>
{
private static final Logger log = LoggerFactory.getLogger(CellDelegate.class);
private final C owner;
private final @Nullable V value;
private final boolean isSelected;
private final boolean hasFocus;
private final int row;
private final int column;
private final @Nullable Component componentRef;
private final List<String> toolTips;
private final @Nullable Object presentationValue;
private final Supplier<Component> defaultRenderSource;
public CellDelegate(
C owner,
@Nullable V value,
boolean isSelected,
boolean hasFocus,
int row,
int column,
@Nullable Component renderer,
List<String> toolTips,
@Nullable Object presentationValue,
Supplier<Component> defaultRenderSource
) {
this.owner = Objects.requireNonNull(owner);
this.value = value;
this.isSelected = isSelected;
this.hasFocus = hasFocus;
this.row = row;
this.column = column;
this.componentRef = renderer;
this.toolTips = Objects.requireNonNull(toolTips);
this.presentationValue = presentationValue;
this.defaultRenderSource = Objects.requireNonNull(defaultRenderSource);
}
public C getComponent() {
return owner;
}
public Optional<V> value() {
return Optional.ofNullable(value);
}
public Optional<String> valueAsString() {
return value().map(Object::toString);
}
public boolean isSelected() {
return isSelected;
}
public boolean hasFocus() {
return hasFocus;
}
public int getRow() {
return row;
}
public int getColumn() {
return column;
}
public Optional<Component> renderer() {
return Optional.ofNullable(componentRef);
}
public CellDelegate<C, V> withRenderer(Component component) {
return new CellDelegate<>(
owner,
value,
isSelected,
hasFocus,
row,
column,
component,
toolTips,
presentationValue,
defaultRenderSource
);
}
public CellDelegate<C, V> withRenderer( Consumer<Graphics2D> painter ) {
return withRenderer(new Component() {
@Override
public void paint(Graphics g) {
super.paint(g);
painter.accept((Graphics2D) g);
}
});
}
public CellDelegate<C, V> withToolTip( String toolTip ) {
ArrayList<String> newToolTips = new ArrayList<>(toolTips);
newToolTips.add(toolTip);
return new CellDelegate<>(
owner,
value,
isSelected,
hasFocus,
row,
column,
componentRef,
newToolTips,
presentationValue,
defaultRenderSource
);
}
/**
* The presentation value is the first choice of the
* cell renderer to be used for rendering and presentation
* to the user. If it does not exist then the regular
* cell value is used for rendering.
*
* @return An optional of the presentation value.
* It may be an empty optional if no presentation value was specified.
*/
public Optional<Object> presentationValue() {
return Optional.ofNullable(presentationValue);
}
/**
* Represents the value how it should be displayed
* to the user by the cell renderer. By default, this
* value is null, in which case the regular cell value is
* presented to the user.
*
* @param toBeShown The object which should be used by the renderer
* to present to the user, typically a String.
* @return An updated cell delegate object with the new presentation value.
*/
public CellDelegate<C, V> withPresentationValue( @Nullable Object toBeShown ) {
return new CellDelegate<>(
owner,
value,
isSelected,
hasFocus,
row,
column,
componentRef,
toolTips,
toBeShown,
defaultRenderSource
);
}
public CellDelegate<C, V> withDefaultRenderer() {
try {
return this.withRenderer(this.defaultRenderSource.get());
} catch (Exception e) {
log.error("Failed to create default renderer!", e);
}
return this;
}
}