| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package org.leumasjaffe.recipe.view;
- import java.awt.Component;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.function.Consumer;
- import java.util.function.Function;
- import java.util.function.Supplier;
- import javax.swing.JPanel;
- import javax.swing.event.DocumentEvent;
- import javax.swing.event.DocumentListener;
- import org.jdesktop.swingx.VerticalLayout;
- import lombok.AccessLevel;
- import lombok.AllArgsConstructor;
- import lombok.NonNull;
- import lombok.RequiredArgsConstructor;
- import lombok.experimental.Delegate;
- import lombok.experimental.FieldDefaults;
- import lombok.experimental.NonFinal;
- @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
- public class BetterAutoGrowPanel<C extends Component & BetterAutoGrowPanel.ChildComponent, T> extends JPanel {
- private static interface SetGap { void setGap(int gap); }
- public static interface ChildComponent {
- void addGrowShrinkListener(DocumentListener dl);
- void removeGrowShrinkListener(DocumentListener dl);
- default void setListPosition(int zeroIndex) {}
- }
-
- @RequiredArgsConstructor
- @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
- private class GrowOnData implements DocumentListener {
- Supplier<T> makeEmptyModel;
- Function<T, C> makeComponent;
- @NonFinal T model = null;
-
- @Override public void changedUpdate(DocumentEvent e) {}
- @Override public void removeUpdate(DocumentEvent e) {}
- @Override
- public void insertUpdate(DocumentEvent e) {
- if (model != null) {
- models.add(model);
- last().removeGrowShrinkListener(this);
- last().addGrowShrinkListener(new ShrinkOnEmpty(last()));
- }
-
- model = makeEmptyModel.get();
- final C comp = makeComponent.apply(model);
- comp.addGrowShrinkListener(this);
- members.add(comp);
- add(comp);
- callback.accept(true);
- }
- }
-
- @AllArgsConstructor
- private class ShrinkOnEmpty implements DocumentListener {
- ChildComponent component;
-
- @Override public void insertUpdate(DocumentEvent e) {}
- @Override public void changedUpdate(DocumentEvent e) {}
- @Override
- public void removeUpdate(DocumentEvent e) {
- if (e.getDocument().getLength() > 0) {
- return;
- }
-
- component.removeGrowShrinkListener(this);
- remove(members.indexOf(component));
- callback.accept(false);
- }
- }
-
- @Delegate(types={SetGap.class})
- VerticalLayout layout = new VerticalLayout();
- List<ChildComponent> members = new ArrayList<>();
- GrowOnData grow;
-
- @NonFinal List<T> models = null;
- @NonFinal Consumer<Boolean> callback = (b) -> {};
-
- /**
- *
- * @param makeEmptyModel A function to produce a blank model object for display.
- * If the model is updated in such a way as to be non-empty, it will be inserted
- * into the list of models.
- * @param makeComponent A function to generate a UI object given a model. The
- * object must meet the ChildComponent interface to install the AutoGrowPanel's
- * growing/shrinking listener objects.
- */
- public BetterAutoGrowPanel(final @NonNull Supplier<T> makeEmptyModel,
- final @NonNull Function<T, C> makeComponent) {
- setLayout(layout);
- this.grow = new GrowOnData(makeEmptyModel, makeComponent);
- this.grow.insertUpdate(null);
- }
-
- /**
- * Activate this component against the target list of children
- * @param models A mutable list object containing the "child models" to be
- * rendered in this component
- */
- public void setModel(final @NonNull List<T> models) {
- setModel(models, (b) -> {});
- }
-
- /**
- *
- * @param models A mutable list object containing the "child models" to be
- * rendered in this component
- * @param callback A callback that will be invoked each time a child is
- * added or removed. This allows us to provide some custom interactions
- * with the parent's context in case other actions need to occur.
- */
- public void setModel(final @NonNull List<T> models,
- final Consumer<Boolean> callback) {
- this.models = models;
- this.callback = callback;
-
- this.members.subList(0, lastIndex()).clear();
- models.forEach(model -> {
- final C comp = this.grow.makeComponent.apply(model);
- comp.addGrowShrinkListener(new ShrinkOnEmpty(comp));
- add(comp, lastIndex());
- this.members.add(lastIndex(), comp);
- });
- }
-
- @Override
- public void remove(final int index) {
- super.remove(index);
- members.remove(index);
- for (int size = lastIndex(); size >= index; --size) {
- members.get(index).setListPosition(index);
- }
- models.remove(index);
- }
-
- private int lastIndex() { return members.size() - 1; }
-
- private ChildComponent last() {
- return members.get(lastIndex());
- }
- }
|