UIForScrollPanels.java
package swingtree;
import org.jspecify.annotations.Nullable;
import sprouts.Change;
import sprouts.Vals;
import sprouts.Var;
import swingtree.api.mvvm.EntryViewModel;
import swingtree.api.mvvm.ViewSupplier;
import swingtree.components.JScrollPanels;
import javax.swing.*;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/**
* A builder node for {@link JScrollPanels}, a custom SwingTree component,
* which is similar to a {@link JList} but with the ability to interact with
* the individual components in the list.
*
* @param <P> The type of the component which this builder node wraps.
* @author Daniel Nepp
*/
public class UIForScrollPanels<P extends JScrollPanels> extends UIForAnyScrollPane<UIForScrollPanels<P>, P>
{
private final BuilderState<P> _state;
/**
* Extensions of the {@link UIForAnySwing} always wrap
* a single component for which they are responsible.
*
* @param state The {@link BuilderState} modelling how the underlying component is build.
*/
protected UIForScrollPanels( BuilderState<P> state ) {
Objects.requireNonNull(state);
_state = state;
}
@Override
protected BuilderState<P> _state() {
return _state;
}
@Override
protected UIForScrollPanels<P> _newBuilderWithState(BuilderState<P> newState ) {
return new UIForScrollPanels<>(newState);
}
@Override
protected void _addComponentTo(P thisComponent, JComponent addedComponent, @Nullable Object constraints) {
Objects.requireNonNull(addedComponent);
EntryViewModel entry = _entryModel();
if ( constraints == null )
thisComponent.addEntry( entry, m -> UI.of(addedComponent) );
else
thisComponent.addEntry( constraints.toString(), entry, m -> UI.of(addedComponent) );
}
private EntryViewModel _entryModel() {
Var<Boolean> selected = Var.of(false);
Var<Integer> position = Var.of(0);
return new EntryViewModel() {
@Override public Var<Boolean> isSelected() { return selected; }
@Override public Var<Integer> position() { return position; }
};
}
@Override
protected <M> void _addViewableProps(
Vals<M> models, @Nullable String attr, ViewSupplier<M> viewSupplier, P thisComponent
) {
BiFunction<Integer, Vals<M>, @Nullable M> modelFetcher = (i, vals) -> {
M v = vals.at(i).get();
if ( v instanceof EntryViewModel ) ((EntryViewModel) v).position().set(i);
return v;
};
BiFunction<Integer, Vals<M>, M> entryFetcher = (i, vals) -> {
M v = modelFetcher.apply(i, vals);
return ( v != null ? (M) v : (M)_entryModel() );
};
Consumer<Vals<M>> addAll = vals -> {
boolean allAreEntries = vals.stream().allMatch( v -> v instanceof EntryViewModel );
if ( allAreEntries ) {
List<EntryViewModel> entries = (List) vals.toList();
thisComponent.addAllEntries(attr, entries, (ViewSupplier<EntryViewModel>) viewSupplier);
}
else
for ( int i = 0; i< vals.size(); i++ ) {
int finalI = i;
thisComponent.addEntry(
_entryModel(),
m -> viewSupplier.createViewFor(entryFetcher.apply(finalI,vals))
);
}
};
_onShow( models, thisComponent, (c, delegate) -> {
Vals<M> vals = delegate.vals();
int delegateIndex = delegate.index();
Change changeType = delegate.changeType();
// we simply redo all the components.
switch ( changeType ) {
case SET:
case ADD:
case REMOVE:
if ( delegateIndex >= 0 ) {
if ( changeType == Change.ADD ) {
M m = entryFetcher.apply(delegateIndex, vals);
if ( m instanceof EntryViewModel )
c.addEntryAt(delegateIndex, null, (EntryViewModel)m, (ViewSupplier<EntryViewModel>) viewSupplier);
else
c.addEntryAt(delegateIndex, null, _entryModel(), em -> viewSupplier.createViewFor(m));
} else if ( changeType == Change.REMOVE )
c.removeEntryAt( delegateIndex );
else if ( changeType == Change.SET ) {
M m = entryFetcher.apply(delegateIndex, vals);
if ( m instanceof EntryViewModel )
c.setEntryAt(delegateIndex, null, (EntryViewModel)m, (ViewSupplier<EntryViewModel>) viewSupplier);
else
c.setEntryAt(delegateIndex, null, _entryModel(), em -> viewSupplier.createViewFor(m));
}
// Now we need to update the positions of all the entries
for ( int i = delegateIndex; i < vals.size(); i++ ) {
M m = entryFetcher.apply(i, vals);
if ( m instanceof EntryViewModel )
((EntryViewModel)m).position().set(i);
}
} else {
c.removeAllEntries();
addAll.accept(vals);
}
break;
case CLEAR: c.removeAllEntries(); break;
case NONE: break;
default: throw new IllegalStateException("Unknown type: "+delegate.changeType());
}
});
addAll.accept(models);
}
}