TreeViewBindings.java

  1. /*
  2.  * #%L
  3.  * *********************************************************************************************************************
  4.  *
  5.  * SteelBlue
  6.  * http://steelblue.tidalwave.it - git clone git@bitbucket.org:tidalwave/steelblue-src.git
  7.  * %%
  8.  * Copyright (C) 2015 - 2021 Tidalwave s.a.s. (http://tidalwave.it)
  9.  * %%
  10.  *
  11.  * *********************************************************************************************************************
  12.  *
  13.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  14.  * the License. You may obtain a copy of the License at
  15.  *
  16.  *     http://www.apache.org/licenses/LICENSE-2.0
  17.  *
  18.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  19.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  20.  * specific language governing permissions and limitations under the License.
  21.  *
  22.  * *********************************************************************************************************************
  23.  *
  24.  *
  25.  *
  26.  * *********************************************************************************************************************
  27.  * #L%
  28.  */
  29. package it.tidalwave.role.ui.javafx.impl.tree;

  30. import javax.annotation.Nonnull;
  31. import java.util.List;
  32. import java.util.Optional;
  33. import java.util.concurrent.Executor;
  34. import java.beans.PropertyChangeListener;
  35. import javafx.util.Callback;
  36. import javafx.beans.property.ObjectProperty;
  37. import javafx.beans.property.ReadOnlyObjectProperty;
  38. import javafx.beans.value.ChangeListener;
  39. import javafx.scene.control.TreeCell;
  40. import javafx.scene.control.TreeItem;
  41. import javafx.scene.control.TreeView;
  42. import javafx.application.Platform;
  43. import it.tidalwave.util.AsException;
  44. import it.tidalwave.util.VisibleForTesting;
  45. import it.tidalwave.role.SimpleComposite;
  46. import it.tidalwave.role.ui.PresentationModel;
  47. import it.tidalwave.role.ui.javafx.impl.CellBinder;
  48. import it.tidalwave.role.ui.javafx.impl.common.DelegateSupport;
  49. import lombok.extern.slf4j.Slf4j;
  50. import static it.tidalwave.role.SimpleComposite.*;
  51. import static it.tidalwave.role.ui.Selectable.Selectable;
  52. import static it.tidalwave.role.ui.javafx.impl.Logging.*;

  53. /***********************************************************************************************************************
  54.  *
  55.  * @author  Fabrizio Giudici
  56.  *
  57.  **********************************************************************************************************************/
  58. @Slf4j
  59. public class TreeViewBindings extends DelegateSupport
  60.   {
  61. //    @Nonnull
  62. //    private final CellBinder cellBinder;
  63. //
  64.     @VisibleForTesting final Callback<TreeView<PresentationModel>, TreeCell<PresentationModel>> treeCellFactory;

  65.     private final ObsoletePresentationModelDisposer presentationModelDisposer = new ObsoletePresentationModelDisposer();

  66.     /*******************************************************************************************************************
  67.      *
  68.      *
  69.      *
  70.      ******************************************************************************************************************/
  71.     public TreeViewBindings (final @Nonnull Executor executor, final @Nonnull CellBinder cellBinder)
  72.       {
  73.         super(executor);
  74. //        this.cellBinder = cellBinder;
  75.         treeCellFactory = treeView -> new AsObjectTreeCell<>(cellBinder);
  76.       }

  77.     /*******************************************************************************************************************
  78.      *
  79.      *
  80.      *
  81.      ******************************************************************************************************************/
  82.     @VisibleForTesting final ChangeListener<TreeItem<PresentationModel>> treeItemChangeListener = (ov, oldItem, item) ->
  83.       {
  84.         executor.execute(() ->
  85.           {
  86.             try
  87.               {
  88.                 item.getValue().as(Selectable).select();
  89.               }
  90.             catch (AsException e)
  91.               {
  92.                 TreeViewBindings.log.debug("No Selectable role for {}", item); // ok, do nothing
  93.               }
  94.           });
  95.       };

  96.     /*******************************************************************************************************************
  97.      *
  98.      * {@inheritDoc}
  99.      *
  100.      ******************************************************************************************************************/
  101.     public void bind (final @Nonnull TreeView<PresentationModel> treeView,
  102.                       final @Nonnull PresentationModel pm,
  103.                       final @Nonnull Optional<Runnable> callback)
  104.       {
  105.         assertIsFxApplicationThread();
  106.         log.debug("bind({}, {}, {})", treeView, pm, callback);

  107.         final ObjectProperty<TreeItem<PresentationModel>> rootProperty = treeView.rootProperty();
  108.         rootProperty.removeListener(presentationModelDisposer);
  109.         rootProperty.addListener(presentationModelDisposer);
  110.         rootProperty.set(createTreeItem(pm, 0));
  111.         callback.ifPresent(Runnable::run);

  112.         treeView.setCellFactory(treeCellFactory);

  113.         final ReadOnlyObjectProperty<TreeItem<PresentationModel>> pmProperty =
  114.                 treeView.getSelectionModel().selectedItemProperty();
  115.         pmProperty.removeListener(treeItemChangeListener);
  116.         pmProperty.addListener(treeItemChangeListener);
  117.      }

  118.     /*******************************************************************************************************************
  119.      *
  120.      *
  121.      *
  122.      ******************************************************************************************************************/
  123.     @Nonnull
  124.     private TreeItem<PresentationModel> createTreeItem (final @Nonnull PresentationModel pm, final int recursion)
  125.       {
  126.         assertIsFxApplicationThread();
  127.         final TreeItem<PresentationModel> item = new TreeItem<>(pm);

  128.         final PropertyChangeListener recreateChildrenOnUpdateListener = event ->
  129.           {
  130.             Platform.runLater(() ->
  131.               {
  132.                 log.debug("On recreateChildrenOnUpdateListener");
  133.                 item.getChildren().clear(); // FIXME: should update it incrementally
  134.                 createChildren(item, pm, recursion + 1);
  135.                 item.setExpanded(true);
  136.               });
  137.           };

  138.         pm.addPropertyChangeListener(PresentationModel.PROPERTY_CHILDREN, recreateChildrenOnUpdateListener);
  139.         // FIXME: only if already expanded, otherwise defer the call when expanded
  140.         createChildren(item, pm, recursion + 1);

  141.         return item;
  142.       }

  143.     /*******************************************************************************************************************
  144.      *
  145.      *
  146.      *
  147.      ******************************************************************************************************************/
  148.     // FIXME: add on demand, upon node expansion
  149.     private void createChildren (final @Nonnull TreeItem<PresentationModel> parentItem,
  150.                                  final @Nonnull PresentationModel pm,
  151.                                  final int recursion)
  152.       {
  153.         assertIsFxApplicationThread();
  154.         final String prefix = INDENT.substring(0, recursion * 8);
  155.         final SimpleComposite<PresentationModel> composite = pm.as(SimpleComposite);
  156.         logObject(prefix, composite);

  157.         // FIXME: results() in bg thread
  158.         final List<? extends PresentationModel> childPMs = composite.findChildren().results();
  159.         logObjects(prefix, childPMs);

  160.         for (final PresentationModel childPm : childPMs)
  161.           {
  162.             parentItem.getChildren().add(createTreeItem(childPm, recursion));
  163.           }
  164.       }
  165.   }