DefaultJavaFXBinder.java

  1. /*
  2.  * *************************************************************************************************************************************************************
  3.  *
  4.  * SteelBlue: DCI User Interfaces
  5.  * http://tidalwave.it/projects/steelblue
  6.  *
  7.  * Copyright (C) 2015 - 2025 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.ui.javafx.impl;

  27. import jakarta.annotation.Nonnull;
  28. import java.util.concurrent.Executor;
  29. import java.util.function.Function;
  30. import javafx.beans.property.Property;
  31. import javafx.scene.control.TextField;
  32. import javafx.stage.Window;
  33. import javafx.application.Platform;
  34. import it.tidalwave.ui.core.BoundProperty;
  35. import it.tidalwave.ui.javafx.JavaFXBinder;
  36. import it.tidalwave.ui.javafx.impl.button.ButtonBindings;
  37. import it.tidalwave.ui.javafx.impl.combobox.ComboBoxBindings;
  38. import it.tidalwave.ui.javafx.impl.common.CellBinder;
  39. import it.tidalwave.ui.javafx.impl.common.DefaultCellBinder;
  40. import it.tidalwave.ui.javafx.impl.common.PropertyAdapter;
  41. import it.tidalwave.ui.javafx.impl.dialog.DialogBindings;
  42. import it.tidalwave.ui.javafx.impl.filechooser.FileChooserBindings;
  43. import it.tidalwave.ui.javafx.impl.list.ListViewBindings;
  44. import it.tidalwave.ui.javafx.impl.tableview.TableViewBindings;
  45. import it.tidalwave.ui.javafx.impl.tree.TreeViewBindings;
  46. import it.tidalwave.ui.javafx.impl.treetable.TreeTableViewBindings;
  47. import lombok.experimental.Delegate;
  48. import lombok.extern.slf4j.Slf4j;
  49. import static java.util.Objects.requireNonNull;

  50. /***************************************************************************************************************************************************************
  51.  *
  52.  * @author  Fabrizio Giudici
  53.  *
  54.  **************************************************************************************************************************************************************/
  55. @Slf4j
  56. public class DefaultJavaFXBinder implements JavaFXBinder
  57.   {
  58.     private final Executor executor;

  59.     private final String invalidTextFieldStyle = "-fx-background-color: pink";

  60.     @Delegate
  61.     private final ButtonBindings buttonBindings;

  62.     @Delegate
  63.     private final TreeViewBindings treeItemBindings;

  64.     @Delegate
  65.     private final TableViewBindings tableViewBindings;

  66.     @Delegate
  67.     private final TreeTableViewBindings treeTableViewBindings;

  68.     @Delegate
  69.     private final ListViewBindings listViewBindings;

  70.     @Delegate
  71.     private final ComboBoxBindings comboBoxBindings;

  72.     @Delegate
  73.     private final DialogBindings dialogBindings;

  74.     @Delegate
  75.     private final FileChooserBindings fileChooserBindings;

  76.     private final CellBinder cellBinder;

  77.     /***********************************************************************************************************************************************************
  78.      *
  79.      **********************************************************************************************************************************************************/
  80.     public DefaultJavaFXBinder (@Nonnull final Executor executor)
  81.       {
  82.         this.executor = executor;
  83.         cellBinder = new DefaultCellBinder(executor);
  84.         buttonBindings = new ButtonBindings(executor);
  85.         comboBoxBindings = new ComboBoxBindings(executor, cellBinder);
  86.         treeItemBindings = new TreeViewBindings(executor, cellBinder);
  87.         tableViewBindings = new TableViewBindings(executor, cellBinder);
  88.         treeTableViewBindings = new TreeTableViewBindings(executor, cellBinder);
  89.         listViewBindings = new ListViewBindings(executor, cellBinder);
  90.         dialogBindings = new DialogBindings(executor);
  91.         fileChooserBindings = new FileChooserBindings(executor);
  92.       }

  93.     /***********************************************************************************************************************************************************
  94.      * {@inheritDoc}
  95.      **********************************************************************************************************************************************************/
  96.     @Override
  97.     public void setMainWindow (@Nonnull final Window mainWindow)
  98.       {
  99.         treeItemBindings.setMainWindow(mainWindow);
  100.         tableViewBindings.setMainWindow(mainWindow);
  101.         dialogBindings.setMainWindow(mainWindow);
  102.         fileChooserBindings.setMainWindow(mainWindow);
  103.       }

  104.     /***********************************************************************************************************************************************************
  105.      * {@inheritDoc}
  106.      **********************************************************************************************************************************************************/
  107.     @Override
  108.     public <T, S> void bind (@Nonnull final BoundProperty<? super T> target, @Nonnull final Property<? extends S> source, @Nonnull final Function<S, T> adapter)
  109.       {
  110.         enforceFxApplicationThread();
  111.         source.addListener((_1, _2, newValue) -> executor.execute(() -> target.set(adapter.apply(newValue))));
  112.       }

  113.     /***********************************************************************************************************************************************************
  114.      * {@inheritDoc}
  115.      **********************************************************************************************************************************************************/
  116.     @Override @SuppressWarnings("unchecked")
  117.     public <T, S> void bindBidirectionally (@Nonnull final BoundProperty<? super T> property1,
  118.                                             @Nonnull final Property<S> property2,
  119.                                             @Nonnull final Function<? super S, T> adapter,
  120.                                             @Nonnull final Function<? super T, ? extends S> reverseAdapter)
  121.       {
  122.         enforceFxApplicationThread();
  123.         property2.addListener((_1, _2, newValue) -> executor.execute(() -> property1.set(adapter.apply(newValue))));
  124.         property1.addPropertyChangeListener(evt -> Platform.runLater(() -> property2.setValue(reverseAdapter.apply((T)evt.getNewValue()))));
  125.       }

  126.     /***********************************************************************************************************************************************************
  127.      * {@inheritDoc}
  128.      **********************************************************************************************************************************************************/
  129.     @Override
  130.     public void bindBidirectionally (@Nonnull final TextField textField,
  131.                                      @Nonnull final BoundProperty<String> textProperty,
  132.                                      @Nonnull final BoundProperty<Boolean> validProperty)
  133.       {
  134.         enforceFxApplicationThread();
  135.         requireNonNull(textField, "textField");
  136.         requireNonNull(textProperty, "textProperty");
  137.         requireNonNull(validProperty, "validProperty");

  138.         textField.textProperty().bindBidirectional(new PropertyAdapter<>(executor, textProperty));

  139.         // FIXME: weak listener
  140.         validProperty.addPropertyChangeListener(__ -> textField.setStyle(validProperty.get() ? "" : invalidTextFieldStyle));
  141.       }

  142.     /***********************************************************************************************************************************************************
  143.      *
  144.      **********************************************************************************************************************************************************/
  145.     public static void enforceFxApplicationThread()
  146.       {
  147.         if (!Platform.isFxApplicationThread())
  148.           {
  149.             throw new IllegalStateException("Must run in the JavaFX Application Thread");
  150.           }
  151.       }
  152.   }