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.Optional;
  32. import java.util.concurrent.Executor;
  33. import java.beans.PropertyChangeListener;
  34. import javafx.application.Platform;
  35. import it.tidalwave.role.ui.Visible;
  36. import it.tidalwave.role.ui.javafx.impl.common.ChangeListenerSelectableAdapter;
  37. import it.tidalwave.role.ui.javafx.impl.common.PresentationModelTreeItem;
  38. import javafx.util.Callback;
  39. import javafx.beans.property.ObjectProperty;
  40. import javafx.beans.property.ReadOnlyObjectProperty;
  41. import javafx.scene.control.TreeCell;
  42. import javafx.scene.control.TreeItem;
  43. import javafx.scene.control.TreeView;
  44. import it.tidalwave.util.annotation.VisibleForTesting;
  45. import it.tidalwave.role.ui.PresentationModel;
  46. import it.tidalwave.role.ui.javafx.impl.common.CellBinder;
  47. import it.tidalwave.role.ui.javafx.impl.common.DelegateSupport;
  48. import it.tidalwave.role.ui.javafx.impl.common.JavaFXWorker;
  49. import lombok.extern.slf4j.Slf4j;
  50. import static it.tidalwave.role.ui.Visible._Visible_;

  51. /***********************************************************************************************************************
  52.  *
  53.  * @author  Fabrizio Giudici
  54.  *
  55.  **********************************************************************************************************************/
  56. @Slf4j
  57. public class TreeViewBindings extends DelegateSupport
  58.   {
  59.     @VisibleForTesting final Callback<TreeView<PresentationModel>, TreeCell<PresentationModel>> treeCellFactory;

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

  61.     @VisibleForTesting final ChangeListenerSelectableAdapter changeListener = new ChangeListenerSelectableAdapter(executor);

  62.     /*******************************************************************************************************************
  63.      *
  64.      *
  65.      *
  66.      ******************************************************************************************************************/
  67.     public TreeViewBindings (@Nonnull final Executor executor, @Nonnull final CellBinder cellBinder)
  68.       {
  69.         super(executor);
  70.         treeCellFactory = treeView -> new AsObjectTreeCell<>(cellBinder);
  71.       }

  72.     /*******************************************************************************************************************
  73.      *
  74.      * {@inheritDoc}
  75.      *
  76.      ******************************************************************************************************************/
  77.     public void bind (@Nonnull final TreeView<PresentationModel> treeView,
  78.                       @Nonnull final PresentationModel pm,
  79.                       @Nonnull final Optional<Runnable> callback)
  80.       {
  81.         assertIsFxApplicationThread();
  82.         log.debug("bind({}, {}, {})", treeView, pm, callback);

  83.         final ObjectProperty<TreeItem<PresentationModel>> rootProperty = treeView.rootProperty();
  84.         rootProperty.removeListener(presentationModelDisposer);
  85.         rootProperty.addListener(presentationModelDisposer);
  86.         rootProperty.set(createTreeItem(pm, 0));
  87.         callback.ifPresent(Runnable::run);

  88.         treeView.setCellFactory(treeCellFactory);
  89.         treeView.setShowRoot(pm.maybeAs(_Visible_).map(Visible::isVisible).orElse(true));

  90.         final ReadOnlyObjectProperty<TreeItem<PresentationModel>> selectionProperty =
  91.                 treeView.getSelectionModel().selectedItemProperty();
  92.         selectionProperty.removeListener(changeListener.asTreeItemChangeListener());
  93.         selectionProperty.addListener(changeListener.asTreeItemChangeListener());
  94.      }

  95.     /*******************************************************************************************************************
  96.      *
  97.      *
  98.      *
  99.      ******************************************************************************************************************/
  100.     @Nonnull
  101.     private TreeItem<PresentationModel> createTreeItem (@Nonnull final PresentationModel pm, final int depth)
  102.       {
  103.         assertIsFxApplicationThread();
  104.         final TreeItem<PresentationModel> item = new PresentationModelTreeItem(pm);

  105.         final PropertyChangeListener recreateChildrenOnUpdateListener = __ ->
  106.           Platform.runLater(() ->
  107.             {
  108.               log.debug("On recreateChildrenOnUpdateListener");
  109.               item.getChildren().clear(); // FIXME: should update it incrementally
  110.               createChildren(item, pm, depth + 1);
  111.               item.setExpanded(true);
  112.             });

  113.         pm.addPropertyChangeListener(PresentationModel.PROPERTY_CHILDREN, recreateChildrenOnUpdateListener);

  114.         item.expandedProperty().addListener(((observable, oldValue, newValue) ->
  115.           {
  116.             if (newValue)
  117.               {
  118.                 createChildren(item, pm, depth + 1);
  119.               }
  120.           }));

  121.         return item;
  122.       }

  123.     /*******************************************************************************************************************
  124.      *
  125.      *
  126.      *
  127.      ******************************************************************************************************************/
  128.     // FIXME: add on demand, upon node expansion
  129.     private void createChildren (@Nonnull final TreeItem<PresentationModel> parentItem,
  130.                                  @Nonnull final PresentationModel pm,
  131.                                  final int recursion)
  132.       {
  133.         assertIsFxApplicationThread();
  134.         JavaFXWorker.run(executor,
  135.                          () -> JavaFXWorker.childrenPm(pm, recursion),
  136.                          items -> items.forEach(item -> parentItem.getChildren().add(createTreeItem(item, recursion))));
  137.       }
  138.   }