AutoGrowPanel.java 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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.IntFunction;
  8. import java.util.function.Supplier;
  9. import javax.swing.JPanel;
  10. import javax.swing.event.DocumentEvent;
  11. import javax.swing.event.DocumentListener;
  12. import org.jdesktop.swingx.VerticalLayout;
  13. import org.leumasjaffe.event.AnyActionDocumentListener;
  14. import lombok.AllArgsConstructor;
  15. import lombok.RequiredArgsConstructor;
  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. members.remove(index);
  31. for (final int size = members.size(); index < size; ++index) {
  32. members.get(index).setListPosition(index);
  33. }
  34. remove((Component) content);
  35. getParent().getParent().validate();
  36. }
  37. }
  38. }
  39. private class GrowAction implements AnyActionDocumentListener {
  40. @Override
  41. public void update(DocumentEvent e) {
  42. final DocumentListenable back = getBack();
  43. back.removeDocumentListener(this);
  44. back.addDocumentListener(new DeleteOnEmpty(back));
  45. extend();
  46. getBack().addDocumentListener(this);
  47. getParent().getParent().validate();
  48. }
  49. }
  50. @RequiredArgsConstructor
  51. private class ExtendAction<T, C extends Component & DocumentListenable> implements AnyActionDocumentListener {
  52. final Function<T, C> factory;
  53. final Consumer<? super T> previous;
  54. final Supplier<? extends T> next;
  55. T current = null;
  56. @Override
  57. public void update(DocumentEvent e) {
  58. if (current != null) {
  59. previous.accept(current);
  60. }
  61. final C object = factory.apply(current = next.get());
  62. final DocumentListenable back = getBack();
  63. back.removeDocumentListener(this);
  64. back.addDocumentListener(new DeleteOnEmpty(back));
  65. object.addDocumentListener(this);
  66. members.add(object);
  67. add(object);
  68. getParent().getParent().validate();
  69. }
  70. }
  71. IntFunction<DocumentListenable> prod;
  72. AnyActionDocumentListener grow;
  73. List<DocumentListenable> members = new ArrayList<>();
  74. /**
  75. * @wbp.parser.constructor
  76. */
  77. public AutoGrowPanel(Supplier<DocumentListenable> prod) {
  78. this(i -> prod.get(), 1);
  79. }
  80. public AutoGrowPanel(IntFunction<DocumentListenable> prod, int create) {
  81. this.prod = prod;
  82. this.grow = new GrowAction();
  83. setLayout(new VerticalLayout(5));
  84. if (create == 0) return; // TODO
  85. while (create-- > 1) {
  86. extend();
  87. getBack().addDocumentListener(new DeleteOnEmpty(getBack()));
  88. }
  89. extend();
  90. getBack().addDocumentListener(grow);
  91. }
  92. public <T> AutoGrowPanel(Function<T, DocumentListenable> function, List<T> list) {
  93. this(i -> list.size() > i ? function.apply(list.get(i)) : null, list.size());
  94. }
  95. @SafeVarargs
  96. public <T, C extends Component & DocumentListenable> AutoGrowPanel(Function<T, C> function,
  97. Supplier<T> newItem, Consumer<? super T> onData, Consumer<Integer> onDelete, T...ts) {
  98. setLayout(new VerticalLayout(5));
  99. this.grow = new ExtendAction<T, C>(function, onData, newItem);
  100. for (T value : ts) {
  101. C listen = function.apply(value);
  102. members.add(listen);
  103. add(listen);
  104. listen.addDocumentListener(new DeleteOnEmpty(listen));
  105. }
  106. T next = newItem.get();
  107. C empty = function.apply(next);
  108. members.add(empty);
  109. add(empty);
  110. empty.addDocumentListener(this.grow);
  111. }
  112. private void extend() {
  113. DocumentListenable value = prod.apply(members.size());
  114. if (value != null) {
  115. members.add(value);
  116. add((Component) getBack());
  117. }
  118. }
  119. private DocumentListenable getBack() {
  120. return members.get(members.size() - 1);
  121. }
  122. }