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.button;
27
28 import jakarta.annotation.Nonnull;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.concurrent.Executor;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import java.util.function.Supplier;
34 import javafx.beans.property.BooleanProperty;
35 import javafx.geometry.HPos;
36 import javafx.geometry.VPos;
37 import javafx.scene.control.Button;
38 import javafx.scene.control.ButtonBase;
39 import javafx.scene.control.MenuItem;
40 import javafx.scene.control.ToggleButton;
41 import javafx.scene.control.ToggleGroup;
42 import javafx.scene.layout.ColumnConstraints;
43 import javafx.scene.layout.GridPane;
44 import javafx.scene.layout.Pane;
45 import javafx.scene.layout.Priority;
46 import javafx.stage.Window;
47 import javafx.application.Platform;
48 import it.tidalwave.ui.core.BoundProperty;
49 import it.tidalwave.ui.core.role.Displayable;
50 import it.tidalwave.ui.core.role.PresentationModel;
51 import it.tidalwave.ui.core.role.Styleable;
52 import it.tidalwave.ui.core.role.UserAction;
53 import it.tidalwave.ui.core.role.UserActionProvider;
54 import it.tidalwave.ui.javafx.impl.common.DelegateSupport;
55 import it.tidalwave.util.As;
56 import it.tidalwave.role.SimpleComposite;
57 import static it.tidalwave.ui.core.role.Displayable._Displayable_;
58 import static it.tidalwave.ui.core.role.Styleable._Styleable_;
59 import static it.tidalwave.ui.core.role.UserActionProvider._UserActionProvider_;
60 import static it.tidalwave.ui.javafx.impl.DefaultJavaFXBinder.enforceFxApplicationThread;
61 import static java.util.Collections.emptyList;
62 import static java.util.stream.Collectors.*;
63
64
65
66
67
68
69 public class ButtonBindings extends DelegateSupport
70 {
71 private static final As.Type<SimpleComposite<PresentationModel>> _SimpleCompositePresentationModel_ = new As.Type<>(SimpleComposite.class);
72
73
74
75
76 public ButtonBindings (@Nonnull final Executor executor, @Nonnull final Supplier<Window> mainWindow)
77 {
78 super(executor, mainWindow);
79 }
80
81
82
83
84 public void bind (@Nonnull final ButtonBase button, @Nonnull final UserAction action)
85 {
86 enforceFxApplicationThread();
87 action.maybeAs(_Displayable_).ifPresent(d -> button.setText(d.getDisplayName()));
88 button.setOnAction(ignored -> executor.execute(action::actionPerformed));
89 bindEnableProperty(button.disableProperty(), action.enabled());
90 }
91
92
93
94
95 public void bind (@Nonnull final MenuItem menuItem, @Nonnull final UserAction action)
96 {
97 enforceFxApplicationThread();
98 menuItem.setText(action.maybeAs(_Displayable_).map(Displayable::getDisplayName).orElse(""));
99 menuItem.setOnAction(ignored -> executor.execute(action::actionPerformed));
100 bindEnableProperty(menuItem.disableProperty(), action.enabled());
101 }
102
103
104
105
106 public void bindToggleButtons (@Nonnull final Pane pane, @Nonnull final PresentationModel pm)
107 {
108 enforceFxApplicationThread();
109 final var group = new ToggleGroup();
110 final var children = pane.getChildren();
111 final var prototypeStyleClass = children.get(0).getStyleClass();
112 final var pmc = pm.as(_SimpleCompositePresentationModel_);
113 children.setAll(pmc.findChildren().stream().map(cpm -> createToggleButton(cpm, prototypeStyleClass, group)).collect(toList()));
114 }
115
116
117
118
119 public void bindButtonsInPane (@Nonnull final GridPane gridPane, @Nonnull final Collection<UserAction> actions)
120 {
121 enforceFxApplicationThread();
122 final var columnConstraints = gridPane.getColumnConstraints();
123 final var children = gridPane.getChildren();
124
125 columnConstraints.clear();
126 children.clear();
127 final var columnIndex = new AtomicInteger(0);
128
129 actions.forEach(menuAction ->
130 {
131 final var column = new ColumnConstraints();
132 column.setPercentWidth(100.0 / actions.size());
133 columnConstraints.add(column);
134 final var button = createButton();
135 GridPane.setConstraints(button, columnIndex.getAndIncrement(), 0);
136 bind(button, menuAction);
137 children.add(button);
138 });
139 }
140
141
142
143
144 @Nonnull
145 private Button createButton()
146 {
147 final var button = new Button();
148 GridPane.setHgrow(button, Priority.ALWAYS);
149 GridPane.setVgrow(button, Priority.ALWAYS);
150 GridPane.setHalignment(button, HPos.CENTER);
151 GridPane.setValignment(button, VPos.CENTER);
152 button.setPrefSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
153 button.getStyleClass().add("mainMenuButton");
154
155 return button;
156 }
157
158
159
160
161 @Nonnull
162 private ToggleButton createToggleButton (@Nonnull final PresentationModel pm, @Nonnull final List<String> baseStyleClass, @Nonnull final ToggleGroup group)
163 {
164 final var button = new ToggleButton();
165 button.setToggleGroup(group);
166 button.setText(pm.maybeAs(_Displayable_).map(Displayable::getDisplayName).orElse(""));
167 button.getStyleClass().addAll(baseStyleClass);
168 button.getStyleClass().addAll(pm.maybeAs(_Styleable_).map(Styleable::getStyles).orElse(emptyList()));
169 pm.maybeAs(_UserActionProvider_).flatMap(UserActionProvider::getOptionalDefaultAction)
170 .ifPresent(action -> bind(button, action));
171
172 if (group.getSelectedToggle() == null)
173 {
174 group.selectToggle(button);
175 }
176
177 return button;
178 }
179
180
181
182
183 private void bindEnableProperty (@Nonnull final BooleanProperty property1, @Nonnull final BoundProperty<Boolean> property2)
184 {
185 property1.setValue(!property2.get());
186 property1.addListener((_1, _2, newValue) -> executor.execute(() -> property2.set(!newValue)));
187 property2.addPropertyChangeListener(evt -> Platform.runLater(() -> property1.setValue(!(boolean)evt.getNewValue())));
188 }
189 }