Skip to contentMethod: lambda$onButtonPickFilePressed$12(BoundProperty)
1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * SteelBlue: DCI User Interfaces
5: * http://tidalwave.it/projects/steelblue
6: *
7: * Copyright (C) 2015 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/steelblue-src
22: * git clone https://github.com/tidalwave-it/steelblue-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.role.ui.example.presentation.impl;
27:
28: import javax.annotation.Nonnull;
29: import javax.annotation.PostConstruct;
30: import java.util.List;
31: import java.nio.file.Path;
32: import java.nio.file.Paths;
33: import org.springframework.stereotype.Component;
34: import it.tidalwave.message.PowerOnEvent;
35: import it.tidalwave.util.annotation.VisibleForTesting;
36: import it.tidalwave.role.Aggregate;
37: import it.tidalwave.role.ui.BoundProperty;
38: import it.tidalwave.role.ui.Displayable;
39: import it.tidalwave.role.ui.MenuBarModel.MenuPlacement;
40: import it.tidalwave.role.ui.PresentationModel;
41: import it.tidalwave.role.ui.PresentationModelAggregate;
42: import it.tidalwave.role.ui.Selectable;
43: import it.tidalwave.role.ui.UserAction;
44: import it.tidalwave.role.ui.UserActionProvider;
45: import it.tidalwave.role.ui.Visible;
46: import it.tidalwave.role.ui.example.model.Dao;
47: import it.tidalwave.role.ui.example.model.SimpleDciEntity;
48: import it.tidalwave.role.ui.example.model.SimpleEntity;
49: import it.tidalwave.role.ui.example.presentation.MainPanelPresentation;
50: import it.tidalwave.role.ui.example.presentation.MainPanelPresentation.Bindings;
51: import it.tidalwave.role.ui.example.presentation.MainPanelPresentationControl;
52: import it.tidalwave.messagebus.annotation.ListensTo;
53: import lombok.Getter;
54: import lombok.RequiredArgsConstructor;
55: import static it.tidalwave.util.Parameters.r;
56: import static it.tidalwave.util.ui.UserNotificationWithFeedback.*;
57: import static it.tidalwave.role.ui.Presentable._Presentable_;
58: import static it.tidalwave.role.ui.spi.PresentationModelCollectors.toCompositePresentationModel;
59:
60: /***************************************************************************************************************************************************************
61: *
62: * @stereotype Control
63: *
64: * @author Fabrizio Giudici
65: *
66: **************************************************************************************************************************************************************/
67: @Component @RequiredArgsConstructor
68: // @SimpleMessageSubscriber
69: public class DefaultMainPanelPresentationControl implements MainPanelPresentationControl
70: {
71: private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
72:
73: // START SNIPPET: injections
74: @Nonnull
75: private final Dao dao;
76:
77: @Nonnull
78: private final MainPanelPresentation presentation;
79: // END SNIPPET: injections
80:
81: // For each button on the presentation that can do something, a UserAction is provided.
82: // START SNIPPET: userActions
83: @Getter
84: private final UserAction actionButton = UserAction.of(this::onButtonPressed,
85: Displayable.of("Press me"));
86:
87: @Getter
88: private final UserAction actionDialogOk = UserAction.of(this::onButtonDialogOkPressed,
89: Displayable.of("Dialog with ok"));
90:
91: @Getter
92: private final UserAction actionDialogCancelOk = UserAction.of(this::onButtonDialogOkCancelPressed, List.of(
93: Displayable.of("Dialog with ok/cancel"),
94: MenuPlacement.under("Tools")));
95:
96: @Getter
97: private final UserAction actionPickFile = UserAction.of(this::onButtonPickFilePressed, List.of(
98: Displayable.of("Open file..."),
99: MenuPlacement.under("File")));
100:
101: @Getter
102: private final UserAction actionPickDirectory = UserAction.of(this::onButtonPickDirectoryPressed, List.of(
103: Displayable.of("Open directory..."),
104: MenuPlacement.under("File")));
105: // END SNIPPET: userActions
106: // START SNIPPET: bindings
107: private final Bindings bindings = Bindings.builder()
108: .actionButton(actionButton)
109: .actionDialogOk(actionDialogOk)
110: .actionDialogCancelOk(actionDialogCancelOk)
111: .actionPickFile(actionPickFile)
112: .actionPickDirectory(actionPickDirectory)
113: .build();
114: // END SNIPPET: bindings
115:
116: // Then there can be a set of variables that represent the internal state of the control.
117: @VisibleForTesting int status = 1;
118:
119: /***********************************************************************************************************************************************************
120: * At {@link PostConstruct} time the control just performs the binding to the presentation.
121: **********************************************************************************************************************************************************/
122: // START SNIPPET: initialization
123: @PostConstruct
124: @VisibleForTesting void initialize()
125: {
126: presentation.bind(bindings);
127: }
128: // END SNIPPET: initialization
129:
130: /***********************************************************************************************************************************************************
131: * {@inheritDoc}
132: *
133: * This method demonstrates the typical idiom for populating model:
134: *
135: * 1. A dao is called to provide raw model - let's say in form of collections;
136: * 2. Objects in the collection are transformed into PresentationModels.
137: * 3. The PresentationModels are then passed to the presentation.
138: **********************************************************************************************************************************************************/
139: // START SNIPPET: populate
140: @Override
141: public void populate ()
142: {
143: final var entities1 = dao.getSimpleEntities();
144: final var entities2 = dao.getDciEntities();
145: final var files = dao.getFiles();
146: final var pmEnt1 = entities1.stream().map(this::pmFor).collect(toCompositePresentationModel());
147: final var pmEnt2 = entities2.stream().map(this::pmFor).collect(toCompositePresentationModel());
148: final var pmFiles = files.stream()
149: .map(item -> item.as(_Presentable_).createPresentationModel())
150: .collect(toCompositePresentationModel(r(Visible.INVISIBLE)));
151: presentation.populate(pmEnt1, pmEnt2, pmFiles);
152: }
153: // END SNIPPET: populate
154:
155: /***********************************************************************************************************************************************************
156: * Alternatively to expose methods to a public interface, a pubsub facility can be used. This method is called back at application initialisation.
157: * @param event the 'power on' event
158: **********************************************************************************************************************************************************/
159: // START SNIPPET: onPowerOn
160: @VisibleForTesting void onPowerOn (@ListensTo final PowerOnEvent event)
161: {
162: presentation.bind(bindings);
163: populate();
164: }
165: // END SNIPPET: onPowerOn
166:
167: /***********************************************************************************************************************************************************
168: * Factory method for the PresentationModel of SimpleEntity instances.
169: *
170: * It aggregates a few extra roles into the PresentationModel that are used by the control, such as callbacks
171: * for action associated to the context menu. Also a Displayable role is usually injected to control the rendering
172: * of entities.
173: **********************************************************************************************************************************************************/
174: // START SNIPPET: pmSimpleEntity
175: @Nonnull
176: private PresentationModel pmFor (@Nonnull final SimpleEntity entity)
177: {
178: final Selectable selectable = () -> onSelected(entity);
179: final var action1 = UserAction.of(() -> action1(entity), Displayable.of("Action 1"));
180: final var action2 = UserAction.of(() -> action2(entity), Displayable.of("Action 2"));
181: final var action3 = UserAction.of(() -> action3(entity), Displayable.of("Action 3"));
182: return PresentationModel.of(entity, r(Displayable.of("Item #" + entity.getName()),
183: selectable,
184: UserActionProvider.of(action1, action2, action3)));
185: }
186: // END SNIPPET: pmSimpleEntity
187:
188: /***********************************************************************************************************************************************************
189: * Factory method for the PresentationModel of SimpleDciEntity instances.
190: **********************************************************************************************************************************************************/
191: // START SNIPPET: pmSimpleDciEntity
192: @Nonnull
193: private PresentationModel pmFor (@Nonnull final SimpleDciEntity entity)
194: {
195: // FIXME: column names
196: final Aggregate<PresentationModel> aggregate = PresentationModelAggregate.newInstance()
197: .withPmOf("C1", r(Displayable.of(entity.getName())))
198: .withPmOf("C2", r(Displayable.of("" + entity.getAttribute1())))
199: .withPmOf("C3", r(Displayable.of("" + entity.getAttribute2())));
200: final Selectable selectable = () -> onSelected(entity);
201: final var action1 = UserAction.of(() -> action1(entity), Displayable.of("Action 1"));
202: final var action2 = UserAction.of(() -> action2(entity), Displayable.of("Action 2"));
203: final var action3 = UserAction.of(() -> action3(entity), Displayable.of("Action 3"));
204: // No explicit Displayable here, as the one inside SimpleDciEntity is used.
205: return PresentationModel.of(entity, r(aggregate, selectable, UserActionProvider.of(action1, action2, action3)));
206: }
207: // END SNIPPET: pmSimpleDciEntity
208:
209: // Below simple business methods, as per usual business.
210:
211: /***********************************************************************************************************************************************************
212: *
213: **********************************************************************************************************************************************************/
214: // START SNIPPET: onButtonPressed
215: private void onButtonPressed()
216: {
217: presentation.notify("Button pressed");
218: status++;
219: bindings.textProperty.set(Integer.toString(status));
220: }
221: // END SNIPPET: onButtonPressed
222:
223: /***********************************************************************************************************************************************************
224: *
225: **********************************************************************************************************************************************************/
226: // START SNIPPET: onButtonDialogOkPressed
227: private void onButtonDialogOkPressed()
228: {
229: presentation.notify(notificationWithFeedback()
230: .withCaption("Notification")
231: .withText("Now press the button")
232: .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Pressed ok"))));
233: }
234: // END SNIPPET: onButtonDialogOkPressed
235:
236: /***********************************************************************************************************************************************************
237: *
238: **********************************************************************************************************************************************************/
239: // START SNIPPET: onButtonDialogOkCancelPressed
240: private void onButtonDialogOkCancelPressed()
241: {
242: presentation.notify(notificationWithFeedback()
243: .withCaption("Notification")
244: .withText("Now press the button")
245: .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Pressed ok"))
246: .withOnCancel(() -> presentation.notify("Pressed cancel"))));
247: }
248: // END SNIPPET: onButtonDialogOkCancelPressed
249:
250: /***********************************************************************************************************************************************************
251: * This method demonstrates how to pick a file name by using the proper UI dialog.
252: **********************************************************************************************************************************************************/
253: // START SNIPPET: onButtonPickFilePressed
254: private void onButtonPickFilePressed()
255: {
256: final var selectedFile = new BoundProperty<>(USER_HOME);
257: presentation.pickFile(selectedFile,
258: notificationWithFeedback()
259: .withCaption("Pick a file")
260: .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Selected file: " + selectedFile.get()))
261: .withOnCancel(() -> presentation.notify("Selection cancelled"))));
262: }
263: // END SNIPPET: onButtonPickFilePressed
264:
265: /***********************************************************************************************************************************************************
266: * This method demonstrates how to pick a directory name by using the proper UI dialog.
267: **********************************************************************************************************************************************************/
268: // START SNIPPET: onButtonPickDirectoryPressed
269: private void onButtonPickDirectoryPressed()
270: {
271: final var selectedFolder = new BoundProperty<>(USER_HOME);
272: presentation.pickDirectory(selectedFolder,
273: notificationWithFeedback()
274: .withCaption("Pick a directory")
275: .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Selected directory: " + selectedFolder.get()))
276: .withOnCancel(() -> presentation.notify("Selection cancelled"))));
277: }
278: // END SNIPPET: onButtonPickDirectoryPressed
279:
280: /***********************************************************************************************************************************************************
281: *
282: **********************************************************************************************************************************************************/
283: // START SNIPPET: onSelected
284: private void onSelected (@Nonnull final Object object)
285: {
286: presentation.notify("Selected " + object);
287: }
288: // END SNIPPET: onSelected
289:
290: /***********************************************************************************************************************************************************
291: *
292: **********************************************************************************************************************************************************/
293: // START SNIPPET: action1
294: private void action1 (@Nonnull final Object object)
295: {
296: presentation.notify("Action 1 on " + object);
297: }
298: // END SNIPPET: action1
299:
300: /***********************************************************************************************************************************************************
301: *
302: **********************************************************************************************************************************************************/
303: private void action2 (@Nonnull final Object object)
304: {
305: presentation.notify("Action 2 on " + object);
306: }
307:
308: /***********************************************************************************************************************************************************
309: *
310: **********************************************************************************************************************************************************/
311: private void action3 (@Nonnull final Object object)
312: {
313: presentation.notify("Action 3 on " + object);
314: }
315: }