AutoGrowPanel.java 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package org.leumasjaffe.recipe.view;
  2. import java.awt.Component;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.function.Consumer;
  6. import java.util.function.Function;
  7. import java.util.function.IntConsumer;
  8. import java.util.function.IntFunction;
  9. import java.util.function.Supplier;
  10. import javax.swing.JPanel;
  11. import javax.swing.event.DocumentEvent;
  12. import javax.swing.event.DocumentListener;
  13. import org.jdesktop.swingx.VerticalLayout;
  14. import org.leumasjaffe.event.AnyActionDocumentListener;
  15. import lombok.AllArgsConstructor;
  16. @SuppressWarnings("serial")
  17. public class AutoGrowPanel extends JPanel {
  18. public static interface DocumentListenable {
  19. void addDocumentListener(DocumentListener dl);
  20. void removeDocumentListener(DocumentListener dl);
  21. default void setListPosition(int zeroIndex) {}
  22. }
  23. @AllArgsConstructor
  24. private class DeleteOnEmpty implements AnyActionDocumentListener {
  25. DocumentListenable content;
  26. @Override public void update(DocumentEvent e) {
  27. if (e.getDocument().getLength() == 0) {
  28. content.removeDocumentListener(this);
  29. int index = members.indexOf(content);
  30. onDelete.accept(index);
  31. for (final int size = members.size(); index < size; ++index) {
  32. members.get(index).setListPosition(index);
  33. }
  34. remove((Component) content);
  35. validateNthParent(4);
  36. }
  37. }
  38. }
  39. @AllArgsConstructor
  40. private class ExtendAction<T, C extends Component & DocumentListenable> implements AnyActionDocumentListener {
  41. final Function<T, C> factory;
  42. final Consumer<? super T> previous;
  43. final Supplier<? extends T> next;
  44. T current = null;
  45. @Override
  46. public void update(DocumentEvent e) {
  47. previous.accept(current);
  48. final C object = factory.apply(current = next.get());
  49. final DocumentListenable back = getBack();
  50. back.removeDocumentListener(this);
  51. back.addDocumentListener(new DeleteOnEmpty(back));
  52. object.addDocumentListener(this);
  53. members.add(object);
  54. add(object);
  55. validateNthParent(4);
  56. }
  57. }
  58. IntFunction<DocumentListenable> prod;
  59. AnyActionDocumentListener grow;
  60. IntConsumer onDelete;
  61. List<DocumentListenable> members = new ArrayList<>();
  62. @SafeVarargs
  63. public <T, C extends Component & DocumentListenable> AutoGrowPanel(Function<T, C> function,
  64. Supplier<T> newItem, Consumer<? super T> onData, IntConsumer onDelete, T...ts) {
  65. setLayout(new VerticalLayout(5));
  66. T next = newItem.get();
  67. this.onDelete = onDelete;
  68. this.grow = new ExtendAction<T, C>(function, onData, newItem, next);
  69. for (T value : ts) {
  70. C listen = function.apply(value);
  71. members.add(listen);
  72. add(listen);
  73. listen.addDocumentListener(new DeleteOnEmpty(listen));
  74. }
  75. C empty = function.apply(next);
  76. members.add(empty);
  77. add(empty);
  78. empty.addDocumentListener(this.grow);
  79. }
  80. private DocumentListenable getBack() {
  81. return members.get(members.size() - 1);
  82. }
  83. private void validateNthParent(int i) {
  84. Component current = getParent();
  85. Component next = current;
  86. while (i --> 0 && next != null) {
  87. current = next;
  88. next = current.getParent();
  89. }
  90. current.validate();
  91. }
  92. }