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.IntFunction; 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 org.leumasjaffe.event.AnyActionDocumentListener; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; @SuppressWarnings("serial") public class AutoGrowPanel extends JPanel { public static interface DocumentListenable { void addDocumentListener(DocumentListener dl); void removeDocumentListener(DocumentListener dl); default void setListPosition(int zeroIndex) {} } @AllArgsConstructor private class DeleteOnEmpty implements AnyActionDocumentListener { DocumentListenable content; @Override public void update(DocumentEvent e) { if (e.getDocument().getLength() == 0) { content.removeDocumentListener(this); int index = members.indexOf(content); members.remove(index); for (final int size = members.size(); index < size; ++index) { members.get(index).setListPosition(index); } remove((Component) content); getParent().getParent().validate(); } } } private class GrowAction implements AnyActionDocumentListener { @Override public void update(DocumentEvent e) { final DocumentListenable back = getBack(); back.removeDocumentListener(this); back.addDocumentListener(new DeleteOnEmpty(back)); extend(); getBack().addDocumentListener(this); getParent().getParent().validate(); } } @RequiredArgsConstructor private class ExtendAction implements AnyActionDocumentListener { final Function factory; final Consumer previous; final Supplier next; T current = null; @Override public void update(DocumentEvent e) { if (current != null) { previous.accept(current); } final C object = factory.apply(current = next.get()); final DocumentListenable back = getBack(); back.removeDocumentListener(this); back.addDocumentListener(new DeleteOnEmpty(back)); object.addDocumentListener(this); members.add(object); add(object); getParent().getParent().validate(); } } IntFunction prod; AnyActionDocumentListener grow; List members = new ArrayList<>(); /** * @wbp.parser.constructor */ public AutoGrowPanel(Supplier prod) { this(i -> prod.get(), 1); } public AutoGrowPanel(IntFunction prod, int create) { this.prod = prod; this.grow = new GrowAction(); setLayout(new VerticalLayout(5)); if (create == 0) return; // TODO while (create-- > 1) { extend(); getBack().addDocumentListener(new DeleteOnEmpty(getBack())); } extend(); getBack().addDocumentListener(grow); } public AutoGrowPanel(Function function, List list) { this(i -> list.size() > i ? function.apply(list.get(i)) : null, list.size()); } @SafeVarargs public AutoGrowPanel(Function function, Supplier newItem, Consumer onData, Consumer onDelete, T...ts) { setLayout(new VerticalLayout(5)); this.grow = new ExtendAction(function, onData, newItem); for (T value : ts) { C listen = function.apply(value); members.add(listen); add(listen); listen.addDocumentListener(new DeleteOnEmpty(listen)); } T next = newItem.get(); C empty = function.apply(next); members.add(empty); add(empty); empty.addDocumentListener(this.grow); } private void extend() { DocumentListenable value = prod.apply(members.size()); if (value != null) { members.add(value); add((Component) getBack()); } } private DocumentListenable getBack() { return members.get(members.size() - 1); } }