1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package it.tidalwave.ui.javafx.impl;
27
28 import jakarta.annotation.Nonnull;
29 import java.util.concurrent.Executor;
30 import java.util.function.Function;
31 import java.util.function.Supplier;
32 import javafx.beans.property.Property;
33 import javafx.scene.control.TextField;
34 import javafx.stage.Window;
35 import javafx.application.Platform;
36 import it.tidalwave.ui.core.BoundProperty;
37 import it.tidalwave.ui.javafx.JavaFXBinder;
38 import it.tidalwave.ui.javafx.impl.button.ButtonBindings;
39 import it.tidalwave.ui.javafx.impl.combobox.ComboBoxBindings;
40 import it.tidalwave.ui.javafx.impl.common.DefaultCellBinder;
41 import it.tidalwave.ui.javafx.impl.common.PropertyAdapter;
42 import it.tidalwave.ui.javafx.impl.dialog.DialogBindings;
43 import it.tidalwave.ui.javafx.impl.filechooser.FileChooserBindings;
44 import it.tidalwave.ui.javafx.impl.list.ListViewBindings;
45 import it.tidalwave.ui.javafx.impl.tableview.TableViewBindings;
46 import it.tidalwave.ui.javafx.impl.tree.TreeViewBindings;
47 import it.tidalwave.ui.javafx.impl.treetable.TreeTableViewBindings;
48 import lombok.experimental.Delegate;
49 import lombok.extern.slf4j.Slf4j;
50 import static java.util.Objects.requireNonNull;
51
52
53
54
55
56
57 @Slf4j
58 public class DefaultJavaFXBinder implements JavaFXBinder
59 {
60 private final Executor executor;
61
62 private static final String FX_BACKGROUND_COLOR_PINK = "-fx-background-color: pink";
63
64 @Delegate
65 private final ButtonBindings buttonBindings;
66
67 @Delegate
68 private final TreeViewBindings treeItemBindings;
69
70 @Delegate
71 private final TableViewBindings tableViewBindings;
72
73 @Delegate
74 private final TreeTableViewBindings treeTableViewBindings;
75
76 @Delegate
77 private final ListViewBindings listViewBindings;
78
79 @Delegate
80 private final ComboBoxBindings comboBoxBindings;
81
82 @Delegate
83 private final DialogBindings dialogBindings;
84
85 @Delegate
86 private final FileChooserBindings fileChooserBindings;
87
88
89
90
91 public DefaultJavaFXBinder (@Nonnull final Executor executor, @Nonnull final Supplier<Window> mainWindowSupplier)
92 {
93 this.executor = executor;
94 final var cellBinder = new DefaultCellBinder(executor);
95 buttonBindings = new ButtonBindings(executor, mainWindowSupplier);
96 comboBoxBindings = new ComboBoxBindings(executor, cellBinder, mainWindowSupplier);
97 treeItemBindings = new TreeViewBindings(executor, cellBinder, mainWindowSupplier);
98 tableViewBindings = new TableViewBindings(executor, cellBinder, mainWindowSupplier);
99 treeTableViewBindings = new TreeTableViewBindings(executor, cellBinder, mainWindowSupplier);
100 listViewBindings = new ListViewBindings(executor, cellBinder, mainWindowSupplier);
101 dialogBindings = new DialogBindings(executor, mainWindowSupplier);
102 fileChooserBindings = new FileChooserBindings(executor, mainWindowSupplier);
103 }
104
105
106
107
108 @Override
109 public <T, S> void bind (@Nonnull final BoundProperty<? super T> target, @Nonnull final Property<? extends S> source, @Nonnull final Function<S, T> adapter)
110 {
111 enforceFxApplicationThread();
112 source.addListener((_1, _2, newValue) -> executor.execute(() -> target.set(adapter.apply(newValue))));
113 }
114
115
116
117
118 @Override @SuppressWarnings("unchecked")
119 public <T, S> void bindBidirectionally (@Nonnull final BoundProperty<? super T> property1,
120 @Nonnull final Property<S> property2,
121 @Nonnull final Function<? super S, T> adapter,
122 @Nonnull final Function<? super T, ? extends S> reverseAdapter)
123 {
124 enforceFxApplicationThread();
125 property2.addListener((_1, _2, newValue) -> executor.execute(() -> property1.set(adapter.apply(newValue))));
126 property1.addPropertyChangeListener(evt -> Platform.runLater(() -> property2.setValue(reverseAdapter.apply((T)evt.getNewValue()))));
127 }
128
129
130
131
132 @Override
133 public void bindBidirectionally (@Nonnull final TextField textField,
134 @Nonnull final BoundProperty<String> textProperty,
135 @Nonnull final BoundProperty<Boolean> validProperty)
136 {
137 enforceFxApplicationThread();
138 requireNonNull(textField, "textField");
139 requireNonNull(textProperty, "textProperty");
140 requireNonNull(validProperty, "validProperty");
141
142 textField.textProperty().bindBidirectional(new PropertyAdapter<>(executor, textProperty));
143
144
145 validProperty.addPropertyChangeListener(ignored -> textField.setStyle(validProperty.get() ? "" : FX_BACKGROUND_COLOR_PINK));
146 }
147
148
149
150
151 public static void enforceFxApplicationThread()
152 {
153 if (!Platform.isFxApplicationThread())
154 {
155 throw new IllegalStateException("Must run in the JavaFX Application Thread");
156 }
157 }
158 }