1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.metis.ui;
18
19 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceEvent;
20 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceResource;
21 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet;
22 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisBooleanPreference;
23 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisDatePreference;
24 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisEnumPreference;
25 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisIntegerPreference;
26 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisPreferenceItem;
27 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisStringPreference;
28 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceType;
29 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
30 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
31 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
32 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
33 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIAlignment;
34 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
35 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
36 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
37 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIGenericWrapper;
38 import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButton;
39 import io.github.tonywasher.joceanus.tethys.api.control.TethysUICheckBox;
40 import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
41 import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
42 import io.github.tonywasher.joceanus.tethys.api.dialog.TethysUIDirectorySelector;
43 import io.github.tonywasher.joceanus.tethys.api.dialog.TethysUIFileSelector;
44 import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
45 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIColorButtonField;
46 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIDateButtonField;
47 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIIntegerEditField;
48 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIScrollButtonField;
49 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIStringEditField;
50 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIFieldAttribute;
51 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIFieldType;
52 import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
53 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
54 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIFlowPaneManager;
55 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIGridPaneManager;
56 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
57
58 import java.io.File;
59 import java.util.ArrayList;
60 import java.util.List;
61 import java.util.function.Predicate;
62
63
64
65
66 public class MetisPreferenceSetView
67 implements OceanusEventProvider<MetisPreferenceEvent>, TethysUIComponent {
68
69
70
71 protected static final String STR_COLON = TethysUIConstant.STR_COLON;
72
73
74
75
76 private static final String NLS_PREFERENCES = MetisPreferenceResource.UI_TITLE_PREFERENCES.getValue();
77
78
79
80
81 private static final String NLS_OPTIONS = MetisPreferenceResource.UI_TITLE_OPTIONS.getValue();
82
83
84
85
86 private final OceanusEventManager<MetisPreferenceEvent> theEventManager;
87
88
89
90
91 private final MetisPreferenceSet thePreferences;
92
93
94
95
96 private final TethysUIBorderPaneManager thePane;
97
98
99
100
101 private final TethysUIGridPaneManager theGrid;
102
103
104
105
106 private final TethysUIFactory<?> theGuiFactory;
107
108
109
110
111 private final List<PreferenceElement> theElements;
112
113
114
115
116 private TethysUIFlowPaneManager theOptions;
117
118
119
120
121
122
123
124 protected MetisPreferenceSetView(final TethysUIFactory<?> pFactory,
125 final MetisPreferenceSet pPreferenceSet) {
126
127 theGuiFactory = pFactory;
128 thePreferences = pPreferenceSet;
129
130
131 theEventManager = new OceanusEventManager<>();
132
133
134 theElements = new ArrayList<>();
135
136
137 final TethysUIPaneFactory myPanes = theGuiFactory.paneFactory();
138 theGrid = myPanes.newGridPane();
139
140
141 for (MetisPreferenceItem myPref : thePreferences.getPreferences()) {
142
143 final PreferenceElement myElement = allocatePreferenceElement(myPref);
144 if (myElement != null) {
145 theElements.add(myElement);
146 }
147 }
148
149
150 thePane = myPanes.newBorderPane();
151 thePane.setBorderTitle(NLS_PREFERENCES);
152 thePane.setCentre(theGrid);
153
154
155 if (theOptions != null) {
156
157 thePane.setSouth(theOptions);
158 }
159
160
161 updateFields();
162 }
163
164
165
166
167
168
169 protected TethysUIFactory<?> getFactory() {
170 return theGuiFactory;
171 }
172
173
174
175
176
177
178 protected TethysUIGridPaneManager getGrid() {
179 return theGrid;
180 }
181
182 @Override
183 public String toString() {
184 return thePreferences.getName();
185 }
186
187 @Override
188 public TethysUIComponent getUnderlying() {
189 return thePane;
190 }
191
192 @Override
193 public OceanusEventRegistrar<MetisPreferenceEvent> getEventRegistrar() {
194 return theEventManager.getEventRegistrar();
195 }
196
197
198
199
200
201
202 boolean hasChanges() {
203 return thePreferences.hasChanges();
204 }
205
206
207
208
209 void resetChanges() {
210
211 thePreferences.resetChanges();
212
213
214 updateFields();
215 }
216
217
218
219
220
221
222 void storeChanges() throws OceanusException {
223
224 thePreferences.storeChanges();
225
226
227 updateFields();
228 }
229
230
231
232
233 private void updateFields() {
234
235 thePreferences.updateViewerEntry();
236
237
238 for (PreferenceElement myItem : theElements) {
239
240 myItem.updateField();
241 }
242 }
243
244
245
246
247 protected void notifyChanges() {
248
249 thePreferences.autoCorrectPreferences();
250
251
252 updateFields();
253
254
255 theEventManager.fireEvent(MetisPreferenceEvent.PREFCHANGED);
256 }
257
258
259
260
261
262
263
264 protected PreferenceElement allocatePreferenceElement(final MetisPreferenceItem pItem) {
265 return switch (pItem) {
266 case MetisEnumPreference<?> myEnum -> new EnumPreferenceElement<>(myEnum);
267 case MetisIntegerPreference myInt -> new IntegerPreferenceElement(myInt);
268 case MetisDatePreference myDate -> new DatePreferenceElement(myDate);
269 case MetisBooleanPreference myBool -> new BooleanPreferenceElement(myBool);
270 case MetisStringPreference myString -> switch (myString.getType()) {
271 case MetisPreferenceType.DIRECTORY -> new DirectoryPreferenceElement(myString);
272 case MetisPreferenceType.FILE -> new FilePreferenceElement(myString);
273 case MetisPreferenceType.COLOR -> new ColorPreferenceElement(myString);
274 default -> new StringPreferenceElement(myString);
275 };
276 default -> throw new IllegalArgumentException("Bad Preference Type: " + pItem.getType());
277 };
278 }
279
280
281
282
283 protected void determineFocus() {
284 thePreferences.setFocus();
285 }
286
287
288
289
290 @FunctionalInterface
291 protected interface PreferenceElement {
292
293
294
295 void updateField();
296 }
297
298
299
300
301 private final class StringPreferenceElement
302 implements PreferenceElement {
303
304
305
306 private final MetisStringPreference theItem;
307
308
309
310
311 private final TethysUIStringEditField theField;
312
313
314
315
316
317
318 StringPreferenceElement(final MetisStringPreference pItem) {
319
320 theItem = pItem;
321 theField = theGuiFactory.fieldFactory().newStringField();
322 theField.setEditable(true);
323
324
325 final TethysUILabel myLabel = theGuiFactory.controlFactory().newLabel(pItem.getDisplay() + STR_COLON);
326 myLabel.setAlignment(TethysUIAlignment.EAST);
327
328
329 theGrid.addCell(myLabel);
330 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
331 theGrid.addCell(theField);
332 theGrid.setCellColumnSpan(theField, 2);
333 theGrid.allowCellGrowth(theField);
334 theGrid.newRow();
335
336
337 theField.getEventRegistrar().addEventListener(e -> {
338 pItem.setValue(theField.getValue());
339 notifyChanges();
340 });
341 }
342
343 @Override
344 public void updateField() {
345
346 theField.setValue(theItem.getValue());
347
348
349 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
350 theField.adjustField();
351
352
353 theField.setEnabled(!theItem.isHidden());
354 }
355 }
356
357
358
359
360 private final class IntegerPreferenceElement
361 implements PreferenceElement {
362
363
364
365 private final MetisIntegerPreference theItem;
366
367
368
369
370 private final TethysUIIntegerEditField theField;
371
372
373
374
375 private final TethysUILabel theRangeLabel;
376
377
378
379
380
381
382 IntegerPreferenceElement(final MetisIntegerPreference pItem) {
383
384 theItem = pItem;
385 theField = theGuiFactory.fieldFactory().newIntegerField();
386 theField.setEditable(true);
387 theField.setPreferredWidth(TethysUIFieldType.INTEGER.getDefaultWidth());
388
389
390 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
391 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
392 myLabel.setAlignment(TethysUIAlignment.EAST);
393
394
395 theRangeLabel = myControls.newLabel();
396
397
398 theGrid.addCell(myLabel);
399 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
400 theGrid.addCell(theField);
401 theGrid.addCell(theRangeLabel);
402 theGrid.allowCellGrowth(theRangeLabel);
403 theGrid.newRow();
404
405
406 theField.getEventRegistrar().addEventListener(e -> {
407 pItem.setValue(theField.getValue());
408 notifyChanges();
409 });
410 }
411
412 @Override
413 public void updateField() {
414
415 theField.setValue(theItem.getValue());
416
417
418 handleRange();
419
420
421 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
422 theField.adjustField();
423
424
425 theField.setEnabled(!theItem.isHidden());
426 }
427
428
429
430
431 private void handleRange() {
432
433 final Integer myMin = theItem.getMinimum();
434 final Integer myMax = theItem.getMaximum();
435 final boolean hasMin = myMin != null;
436 final boolean hasMax = myMax != null;
437
438
439 theField.setValidator(p -> (hasMin && p < myMin) || (hasMax && p > myMax)
440 ? MetisPreferenceResource.UI_RANGE_ERROR.getValue()
441 : null);
442 theRangeLabel.setText(null);
443
444
445 if (hasMin || hasMax) {
446
447 final StringBuilder myBuilder = new StringBuilder();
448
449
450 if (hasMin) {
451 myBuilder.append(MetisPreferenceResource.UI_RANGE_MIN.getValue());
452 myBuilder.append(' ');
453 myBuilder.append(myMin);
454 if (hasMax) {
455 myBuilder.append(", ");
456 }
457 }
458
459
460 if (hasMax) {
461 myBuilder.append(MetisPreferenceResource.UI_RANGE_MAX.getValue());
462 myBuilder.append(' ');
463 myBuilder.append(myMax);
464 }
465
466
467 theRangeLabel.setText(myBuilder.toString());
468 }
469 }
470 }
471
472
473
474
475 private final class BooleanPreferenceElement
476 implements PreferenceElement {
477
478
479
480 private final MetisBooleanPreference theItem;
481
482
483
484
485 private final TethysUICheckBox theCheckBox;
486
487
488
489
490
491
492 BooleanPreferenceElement(final MetisBooleanPreference pItem) {
493
494 theItem = pItem;
495 theCheckBox = theGuiFactory.controlFactory().newCheckBox(pItem.getDisplay());
496
497
498 ensureOptionsPane();
499
500
501 theOptions.addNode(theCheckBox);
502
503
504 theCheckBox.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> {
505 pItem.setValue(theCheckBox.isSelected());
506 notifyChanges();
507 });
508 }
509
510 @Override
511 public void updateField() {
512
513 theCheckBox.setSelected(theItem.getValue());
514
515
516 theCheckBox.setChanged(theItem.isChanged());
517
518
519 theCheckBox.setEnabled(!theItem.isHidden());
520 }
521
522
523
524
525 private void ensureOptionsPane() {
526
527 if (theOptions == null) {
528
529 theOptions = theGuiFactory.paneFactory().newFlowPane();
530 theOptions.setBorderTitle(NLS_OPTIONS);
531 }
532 }
533 }
534
535
536
537
538 private final class DatePreferenceElement
539 implements PreferenceElement {
540
541
542
543 private final MetisDatePreference theItem;
544
545
546
547
548 private final TethysUIDateButtonField theField;
549
550
551
552
553
554
555 DatePreferenceElement(final MetisDatePreference pItem) {
556
557 theItem = pItem;
558 theField = theGuiFactory.fieldFactory().newDateField();
559 theField.setEditable(true);
560
561
562 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
563 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
564 myLabel.setAlignment(TethysUIAlignment.EAST);
565
566
567 final TethysUILabel myStub = myControls.newLabel();
568
569
570 theGrid.addCell(myLabel);
571 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
572 theGrid.addCell(theField);
573 theGrid.addCell(myStub);
574 theGrid.allowCellGrowth(myStub);
575 theGrid.newRow();
576
577
578 theField.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> {
579 pItem.setValue(theField.getValue());
580 notifyChanges();
581 });
582 }
583
584 @Override
585 public void updateField() {
586
587 theField.setValue(theItem.getValue());
588
589
590 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
591 theField.adjustField();
592
593
594 theField.setEnabled(!theItem.isHidden());
595 }
596 }
597
598
599
600
601
602
603 private final class EnumPreferenceElement<E extends Enum<E>>
604 implements PreferenceElement {
605
606
607
608 private final MetisEnumPreference<E> theItem;
609
610
611
612
613 private final TethysUIScrollButtonField<TethysUIGenericWrapper> theField;
614
615
616
617
618
619
620 @SuppressWarnings("unchecked")
621 EnumPreferenceElement(final MetisEnumPreference<E> pItem) {
622
623 theItem = pItem;
624 theField = theGuiFactory.fieldFactory().newScrollField(TethysUIGenericWrapper.class);
625 theField.setEditable(true);
626
627
628 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
629 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
630 myLabel.setAlignment(TethysUIAlignment.EAST);
631
632
633 final TethysUILabel myStub = myControls.newLabel();
634
635
636 theGrid.addCell(myLabel);
637 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
638 theGrid.addCell(theField);
639 theGrid.addCell(myStub);
640 theGrid.allowCellGrowth(myStub);
641 theGrid.newRow();
642
643
644 theField.setMenuConfigurator(this::buildMenu);
645 final OceanusEventRegistrar<TethysUIEvent> myRegistrar = theField.getEventRegistrar();
646 myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> {
647 pItem.setValue((E) theField.getValue().getData());
648 notifyChanges();
649 });
650 }
651
652
653
654
655
656
657 private void buildMenu(final TethysUIScrollMenu<TethysUIGenericWrapper> pMenu) {
658
659 pMenu.removeAllItems();
660
661
662 final Predicate<E> myFilter = theItem.getFilter();
663
664
665 for (E myEnum : theItem.getValues()) {
666
667 if (myFilter.test(myEnum)) {
668
669 pMenu.addItem(new TethysUIGenericWrapper(myEnum));
670 }
671 }
672 }
673
674 @Override
675 public void updateField() {
676
677 theField.setValue(new TethysUIGenericWrapper(theItem.getValue()));
678
679
680 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
681 theField.adjustField();
682
683
684 theField.setEnabled(!theItem.isHidden());
685 }
686 }
687
688
689
690
691 private final class ColorPreferenceElement
692 implements PreferenceElement {
693
694
695
696 private final MetisStringPreference theItem;
697
698
699
700
701 private final TethysUIColorButtonField theField;
702
703
704
705
706
707
708 ColorPreferenceElement(final MetisStringPreference pItem) {
709
710 theItem = pItem;
711 theField = theGuiFactory.fieldFactory().newColorField();
712 theField.setEditable(true);
713
714
715 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
716 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
717 myLabel.setAlignment(TethysUIAlignment.EAST);
718
719
720 final TethysUILabel myStub = myControls.newLabel();
721
722
723 theGrid.addCell(myLabel);
724 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
725 theGrid.addCell(theField);
726 theGrid.addCell(myStub);
727 theGrid.allowCellGrowth(myStub);
728 theGrid.newRow();
729
730
731 theField.getEventRegistrar().addEventListener(e -> {
732 pItem.setValue(theField.getValue());
733 notifyChanges();
734 });
735 }
736
737 @Override
738 public void updateField() {
739
740 theField.setValue(theItem.getValue());
741
742
743 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
744 theField.adjustField();
745
746
747 theField.setEnabled(!theItem.isHidden());
748 }
749 }
750
751
752
753
754 private final class FilePreferenceElement
755 implements PreferenceElement {
756
757
758
759 private final MetisStringPreference theItem;
760
761
762
763
764 private final TethysUIStringEditField theField;
765
766
767
768
769 private final TethysUIButton theButton;
770
771
772
773
774 private TethysUIFileSelector theSelector;
775
776
777
778
779
780
781 FilePreferenceElement(final MetisStringPreference pItem) {
782
783 theItem = pItem;
784 theField = theGuiFactory.fieldFactory().newStringField();
785 theField.setEditable(true);
786
787
788 theButton = theGuiFactory.buttonFactory().newButton();
789 theButton.setTextOnly();
790 theButton.setText(pItem.getDisplay());
791
792
793 theGrid.addCell(theButton);
794 theGrid.addCell(theField);
795 theGrid.setCellColumnSpan(theField, 2);
796 theGrid.allowCellGrowth(theField);
797 theGrid.newRow();
798
799
800 theButton.getEventRegistrar().addEventListener(e -> handleDialog());
801 theField.getEventRegistrar().addEventListener(e -> {
802 pItem.setValue(theField.getValue());
803 notifyChanges();
804 });
805 }
806
807 @Override
808 public void updateField() {
809
810 theField.setValue(theItem.getValue());
811
812
813 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
814 theField.adjustField();
815
816
817 final boolean isEnabled = !theItem.isHidden();
818 theField.setEnabled(isEnabled);
819 theButton.setEnabled(isEnabled);
820 }
821
822
823
824
825 private void handleDialog() {
826 ensureSelector();
827 theSelector.setInitialFile(new File(theItem.getValue()));
828 final File myFile = theSelector.selectFile();
829 if (myFile != null) {
830 theItem.setValue(myFile.getAbsolutePath());
831 notifyChanges();
832 }
833 }
834
835
836
837
838 private void ensureSelector() {
839 if (theSelector == null) {
840 theSelector = theGuiFactory.dialogFactory().newFileSelector();
841 theSelector.setTitle(theItem.getDisplay());
842 }
843 }
844 }
845
846
847
848
849 private final class DirectoryPreferenceElement
850 implements PreferenceElement {
851
852
853
854 private final MetisStringPreference theItem;
855
856
857
858
859 private final TethysUIStringEditField theField;
860
861
862
863
864 private final TethysUIButton theButton;
865
866
867
868
869 private TethysUIDirectorySelector theSelector;
870
871
872
873
874
875
876 DirectoryPreferenceElement(final MetisStringPreference pItem) {
877
878 theItem = pItem;
879 theField = theGuiFactory.fieldFactory().newStringField();
880 theField.setEditable(true);
881
882
883 theButton = theGuiFactory.buttonFactory().newButton();
884 theButton.setTextOnly();
885 theButton.setText(pItem.getDisplay());
886
887
888 theGrid.addCell(theButton);
889 theGrid.addCell(theField);
890 theGrid.setCellColumnSpan(theField, 2);
891 theGrid.allowCellGrowth(theField);
892 theGrid.newRow();
893
894
895 theButton.getEventRegistrar().addEventListener(e -> handleDialog());
896 theField.getEventRegistrar().addEventListener(e -> {
897 pItem.setValue(theField.getValue());
898 notifyChanges();
899 });
900 }
901
902 @Override
903 public void updateField() {
904
905 theField.setValue(theItem.getValue());
906
907
908 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
909 theField.adjustField();
910
911
912 final boolean isEnabled = !theItem.isHidden();
913 theField.setEnabled(isEnabled);
914 theButton.setEnabled(isEnabled);
915 }
916
917
918
919
920 private void handleDialog() {
921 ensureSelector();
922 theSelector.setInitialDirectory(new File(theItem.getValue()));
923 final File myDir = theSelector.selectDirectory();
924 if (myDir != null) {
925 theItem.setValue(myDir.getAbsolutePath());
926 notifyChanges();
927 }
928 }
929
930
931
932
933 private void ensureSelector() {
934 if (theSelector == null) {
935 theSelector = theGuiFactory.dialogFactory().newDirectorySelector();
936 theSelector.setTitle(theItem.getDisplay());
937 }
938 }
939 }
940 }