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 javafx.beans.property.Property;
32 import javafx.scene.control.TextField;
33 import javafx.stage.Window;
34 import javafx.application.Platform;
35 import it.tidalwave.ui.core.BoundProperty;
36 import it.tidalwave.ui.javafx.JavaFXBinder;
37 import it.tidalwave.ui.javafx.impl.button.ButtonBindings;
38 import it.tidalwave.ui.javafx.impl.combobox.ComboBoxBindings;
39 import it.tidalwave.ui.javafx.impl.common.CellBinder;
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 final String invalidTextFieldStyle = "-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 private final CellBinder cellBinder;
89
90
91
92
93 public DefaultJavaFXBinder (@Nonnull final Executor executor)
94 {
95 this.executor = executor;
96 cellBinder = new DefaultCellBinder(executor);
97 buttonBindings = new ButtonBindings(executor);
98 comboBoxBindings = new ComboBoxBindings(executor, cellBinder);
99 treeItemBindings = new TreeViewBindings(executor, cellBinder);
100 tableViewBindings = new TableViewBindings(executor, cellBinder);
101 treeTableViewBindings = new TreeTableViewBindings(executor, cellBinder);
102 listViewBindings = new ListViewBindings(executor, cellBinder);
103 dialogBindings = new DialogBindings(executor);
104 fileChooserBindings = new FileChooserBindings(executor);
105 }
106
107
108
109
110 @Override
111 public void setMainWindow (@Nonnull final Window mainWindow)
112 {
113 treeItemBindings.setMainWindow(mainWindow);
114 tableViewBindings.setMainWindow(mainWindow);
115 dialogBindings.setMainWindow(mainWindow);
116 fileChooserBindings.setMainWindow(mainWindow);
117 }
118
119
120
121
122 @Override
123 public <T, S> void bind (@Nonnull final BoundProperty<? super T> target, @Nonnull final Property<? extends S> source, @Nonnull final Function<S, T> adapter)
124 {
125 enforceFxApplicationThread();
126 source.addListener((_1, _2, newValue) -> executor.execute(() -> target.set(adapter.apply(newValue))));
127 }
128
129
130
131
132 @Override @SuppressWarnings("unchecked")
133 public <T, S> void bindBidirectionally (@Nonnull final BoundProperty<? super T> property1,
134 @Nonnull final Property<S> property2,
135 @Nonnull final Function<? super S, T> adapter,
136 @Nonnull final Function<? super T, ? extends S> reverseAdapter)
137 {
138 enforceFxApplicationThread();
139 property2.addListener((_1, _2, newValue) -> executor.execute(() -> property1.set(adapter.apply(newValue))));
140 property1.addPropertyChangeListener(evt -> Platform.runLater(() -> property2.setValue(reverseAdapter.apply((T)evt.getNewValue()))));
141 }
142
143
144
145
146 @Override
147 public void bindBidirectionally (@Nonnull final TextField textField,
148 @Nonnull final BoundProperty<String> textProperty,
149 @Nonnull final BoundProperty<Boolean> validProperty)
150 {
151 enforceFxApplicationThread();
152 requireNonNull(textField, "textField");
153 requireNonNull(textProperty, "textProperty");
154 requireNonNull(validProperty, "validProperty");
155
156 textField.textProperty().bindBidirectional(new PropertyAdapter<>(executor, textProperty));
157
158
159 validProperty.addPropertyChangeListener(__ -> textField.setStyle(validProperty.get() ? "" : invalidTextFieldStyle));
160 }
161
162
163
164
165 public static void enforceFxApplicationThread()
166 {
167 if (!Platform.isFxApplicationThread())
168 {
169 throw new IllegalStateException("Must run in the JavaFX Application Thread");
170 }
171 }
172 }