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