DefaultMainPanelPresentationControl.java

  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. import javax.annotation.Nonnull;
  28. import javax.annotation.PostConstruct;
  29. import java.nio.file.Path;
  30. import java.nio.file.Paths;
  31. import org.springframework.stereotype.Component;
  32. import it.tidalwave.util.annotation.VisibleForTesting;
  33. import it.tidalwave.role.Aggregate;
  34. import it.tidalwave.role.ui.BoundProperty;
  35. import it.tidalwave.role.ui.Displayable;
  36. import it.tidalwave.role.ui.PresentationModel;
  37. import it.tidalwave.role.ui.PresentationModelAggregate;
  38. import it.tidalwave.role.ui.Selectable;
  39. import it.tidalwave.role.ui.UserAction;
  40. import it.tidalwave.role.ui.UserActionProvider;
  41. import it.tidalwave.role.ui.Visible;
  42. import it.tidalwave.role.ui.example.model.Dao;
  43. import it.tidalwave.role.ui.example.model.SimpleDciEntity;
  44. import it.tidalwave.role.ui.example.model.SimpleEntity;
  45. import it.tidalwave.role.ui.example.presentation.MainPanelPresentation;
  46. import it.tidalwave.role.ui.example.presentation.MainPanelPresentation.Bindings;
  47. import it.tidalwave.role.ui.example.presentation.MainPanelPresentationControl;
  48. import lombok.Getter;
  49. import lombok.RequiredArgsConstructor;
  50. import static it.tidalwave.util.Parameters.r;
  51. import static it.tidalwave.util.ui.UserNotificationWithFeedback.*;
  52. import static it.tidalwave.role.ui.Presentable._Presentable_;
  53. import static it.tidalwave.role.ui.spi.PresentationModelCollectors.toCompositePresentationModel;

  54. /***************************************************************************************************************************************************************
  55.  *
  56.  * @stereotype  Control
  57.  *
  58.  * @author  Fabrizio Giudici
  59.  *
  60.  **************************************************************************************************************************************************************/
  61. @Component @RequiredArgsConstructor
  62. public class DefaultMainPanelPresentationControl implements MainPanelPresentationControl
  63.   {
  64.     private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));

  65.     // START SNIPPET: injections
  66.     @Nonnull
  67.     private final Dao dao;

  68.     @Nonnull
  69.     private final MainPanelPresentation presentation;
  70.     // END SNIPPET: injections

  71.     // For each button on the presentation that can do something, a UserAction is provided.
  72.     // START SNIPPET: userActions
  73.     @Getter
  74.     private final UserAction actionButton = UserAction.of(this::onButtonPressed,
  75.                                                           Displayable.of("Press me"));

  76.     @Getter
  77.     private final UserAction actionDialogOk = UserAction.of(this::onButtonDialogOkPressed,
  78.                                                             Displayable.of("Dialog with ok"));

  79.     @Getter
  80.     private final UserAction actionDialogCancelOk = UserAction.of(this::onButtonDialogOkCancelPressed,
  81.                                                                   Displayable.of("Dialog with ok/cancel"));

  82.     @Getter
  83.     private final UserAction actionPickFile = UserAction.of(this::onButtonPickFilePressed,
  84.                                                             Displayable.of("Pick file"));

  85.     @Getter
  86.     private final UserAction actionPickDirectory = UserAction.of(this::onButtonPickDirectoryPressed,
  87.                                                                  Displayable.of("Pick directory"));
  88.     // END SNIPPET: userActions
  89.     // START SNIPPET: bindings
  90.     private final Bindings bindings = Bindings.builder()
  91.                                               .actionButton(actionButton)
  92.                                               .actionDialogOk(actionDialogOk)
  93.                                               .actionDialogCancelOk(actionDialogCancelOk)
  94.                                               .actionPickFile(actionPickFile)
  95.                                               .actionPickDirectory(actionPickDirectory)
  96.                                               .build();
  97.     // END SNIPPET: bindings

  98.     // Then there can be a set of variables that represent the internal state of the control.
  99.     @VisibleForTesting int status = 1;

  100.     /***********************************************************************************************************************************************************
  101.      * At {@link PostConstruct} time the control just performs the binding to the presentation.
  102.      **********************************************************************************************************************************************************/
  103.     // START SNIPPET: initialization
  104.     @PostConstruct
  105.     @VisibleForTesting void initialize()
  106.       {
  107.         presentation.bind(bindings);
  108.       }
  109.     // END SNIPPET: initialization

  110.     /***********************************************************************************************************************************************************
  111.      * {@inheritDoc}
  112.      *
  113.      * This method demonstrates the typical idiom for populating model:
  114.      *
  115.      * 1. A dao is called to provide raw model - let's say in form of collections;
  116.      * 2. Objects in the collection are transformed into PresentationModels.
  117.      * 3. The PresentationModels are then passed to the presentation.
  118.      **********************************************************************************************************************************************************/
  119.     // START SNIPPET: populate
  120.     @Override
  121.     public void populate ()
  122.       {
  123.         final var entities1 = dao.getSimpleEntities();
  124.         final var entities2 = dao.getDciEntities();
  125.         final var files = dao.getFiles();
  126.         final var pm1 = entities1.stream().map(this::pmFor).collect(toCompositePresentationModel());
  127.         final var pm2 = entities2.stream().map(this::pmFor).collect(toCompositePresentationModel());
  128.         final var pm3 = files.stream()
  129.                              .map(item -> item.as(_Presentable_).createPresentationModel())
  130.                              .collect(toCompositePresentationModel(r(Visible.INVISIBLE)));
  131.         presentation.populate(pm1, pm2, pm3);
  132.       }
  133.     // END SNIPPET: populate

  134.     /***********************************************************************************************************************************************************
  135.      * Factory method for the PresentationModel of SimpleEntity instances.
  136.      *
  137.      * It aggregates a few extra roles into the PresentationModel that are used by the control, such as callbacks
  138.      * for action associated to the context menu. Also a Displayable role is usually injected to control the rendering
  139.      * of entities.
  140.      **********************************************************************************************************************************************************/
  141.     // START SNIPPET: pmSimpleEntity
  142.     @Nonnull
  143.     private PresentationModel pmFor (@Nonnull final SimpleEntity entity)
  144.       {
  145.         final Selectable selectable = () -> onSelected(entity);
  146.         final var action1 = UserAction.of(() -> action1(entity), Displayable.of("Action 1"));
  147.         final var action2 = UserAction.of(() -> action2(entity), Displayable.of("Action 2"));
  148.         final var action3 = UserAction.of(() -> action3(entity), Displayable.of("Action 3"));
  149.         return PresentationModel.of(entity, r(Displayable.of("Item #" + entity.getName()),
  150.                                               selectable,
  151.                                               UserActionProvider.of(action1, action2, action3)));
  152.       }
  153.     // END SNIPPET: pmSimpleEntity

  154.     /***********************************************************************************************************************************************************
  155.      * Factory method for the PresentationModel of SimpleDciEntity instances.
  156.      **********************************************************************************************************************************************************/
  157.     // START SNIPPET: pmSimpleDciEntity
  158.     @Nonnull
  159.     private PresentationModel pmFor (@Nonnull final SimpleDciEntity entity)
  160.       {
  161.         // FIXME: column names
  162.         final Aggregate<PresentationModel> aggregate = PresentationModelAggregate.newInstance()
  163.              .withPmOf("C1", r(Displayable.of(entity.getName())))
  164.              .withPmOf("C2", r(Displayable.of("" + entity.getAttribute1())))
  165.              .withPmOf("C3", r(Displayable.of("" + entity.getAttribute2())));
  166.         final Selectable selectable = () -> onSelected(entity);
  167.         final var action1 = UserAction.of(() -> action1(entity), Displayable.of("Action 1"));
  168.         final var action2 = UserAction.of(() -> action2(entity), Displayable.of("Action 2"));
  169.         final var action3 = UserAction.of(() -> action3(entity), Displayable.of("Action 3"));
  170.         // No explicit Displayable here, as the one inside SimpleDciEntity is used.
  171.         return PresentationModel.of(entity, r(aggregate, selectable, UserActionProvider.of(action1, action2, action3)));
  172.       }
  173.     // END SNIPPET: pmSimpleDciEntity

  174.     // Below simple business methods, as per usual business.

  175.     /***********************************************************************************************************************************************************
  176.      *
  177.      **********************************************************************************************************************************************************/
  178.     // START SNIPPET: onButtonPressed
  179.     private void onButtonPressed()
  180.       {
  181.         presentation.notify("Button pressed");
  182.         status++;
  183.         bindings.textProperty.set(Integer.toString(status));
  184.       }
  185.     // END SNIPPET: onButtonPressed

  186.     /***********************************************************************************************************************************************************
  187.      *
  188.      **********************************************************************************************************************************************************/
  189.     // START SNIPPET: onButtonDialogOkPressed
  190.     private void onButtonDialogOkPressed()
  191.       {
  192.         presentation.notify(notificationWithFeedback()
  193.                 .withCaption("Notification")
  194.                 .withText("Now press the button")
  195.                 .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Pressed ok"))));
  196.       }
  197.     // END SNIPPET: onButtonDialogOkPressed

  198.     /***********************************************************************************************************************************************************
  199.      *
  200.      **********************************************************************************************************************************************************/
  201.     // START SNIPPET: onButtonDialogOkCancelPressed
  202.     private void onButtonDialogOkCancelPressed()
  203.       {
  204.         presentation.notify(notificationWithFeedback()
  205.                 .withCaption("Notification")
  206.                 .withText("Now press the button")
  207.                 .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Pressed ok"))
  208.                                         .withOnCancel(() -> presentation.notify("Pressed cancel"))));
  209.       }
  210.     // END SNIPPET: onButtonDialogOkCancelPressed

  211.     /***********************************************************************************************************************************************************
  212.      * This method demonstrates how to pick a file name by using the proper UI dialog.
  213.      **********************************************************************************************************************************************************/
  214.     // START SNIPPET: onButtonPickFilePressed
  215.     private void onButtonPickFilePressed()
  216.       {
  217.         final var selectedFile = new BoundProperty<>(USER_HOME);
  218.         presentation.pickFile(selectedFile,
  219.             notificationWithFeedback()
  220.                 .withCaption("Pick a file")
  221.                 .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Selected file: " + selectedFile.get()))
  222.                                         .withOnCancel(() -> presentation.notify("Selection cancelled"))));
  223.       }
  224.     // END SNIPPET: onButtonPickFilePressed

  225.     /***********************************************************************************************************************************************************
  226.      * This method demonstrates how to pick a directory name by using the proper UI dialog.
  227.      **********************************************************************************************************************************************************/
  228.     // START SNIPPET: onButtonPickDirectoryPressed
  229.     private void onButtonPickDirectoryPressed()
  230.       {
  231.         final var selectedFolder = new BoundProperty<>(USER_HOME);
  232.         presentation.pickDirectory(selectedFolder,
  233.             notificationWithFeedback()
  234.                 .withCaption("Pick a directory")
  235.                 .withFeedback(feedback().withOnConfirm(() -> presentation.notify("Selected directory: " + selectedFolder.get()))
  236.                                         .withOnCancel(() -> presentation.notify("Selection cancelled"))));
  237.       }
  238.     // END SNIPPET: onButtonPickDirectoryPressed

  239.     /***********************************************************************************************************************************************************
  240.      *
  241.      **********************************************************************************************************************************************************/
  242.     // START SNIPPET: onSelected
  243.     private void onSelected (@Nonnull final Object object)
  244.       {
  245.         presentation.notify("Selected " + object);
  246.       }
  247.     // END SNIPPET: onSelected

  248.     /***********************************************************************************************************************************************************
  249.      *
  250.      **********************************************************************************************************************************************************/
  251.     // START SNIPPET: action1
  252.     private void action1 (@Nonnull final Object object)
  253.       {
  254.         presentation.notify("Action 1 on " + object);
  255.       }
  256.     // END SNIPPET: action1

  257.     /***********************************************************************************************************************************************************
  258.      *
  259.      **********************************************************************************************************************************************************/
  260.     private void action2 (@Nonnull final Object object)
  261.       {
  262.         presentation.notify("Action 2 on " + object);
  263.       }

  264.     /***********************************************************************************************************************************************************
  265.      *
  266.      **********************************************************************************************************************************************************/
  267.     private void action3 (@Nonnull final Object object)
  268.       {
  269.         presentation.notify("Action 3 on " + object);
  270.       }
  271.   }