Skip to content

Content of file DefaultJavaFXBinder.java.html

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultJavaFXBinder.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">SteelBlue - Examples - Large</a> &gt; <a href="../index.html" class="el_bundle">it-tidalwave-role-ui-javafx</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.role.ui.javafx.impl</a> &gt; <span class="el_source">DefaultJavaFXBinder.java</span></div><h1>DefaultJavaFXBinder.java</h1><pre class="source lang-java linenums">/*
 * *********************************************************************************************************************
 *
 * SteelBlue: DCI User Interfaces
 * http://tidalwave.it/projects/steelblue
 *
 * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
 *
 * *********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * *********************************************************************************************************************
 *
 * git clone https://bitbucket.org/tidalwave/steelblue-src
 * git clone https://github.com/tidalwave-it/steelblue-src
 *
 * *********************************************************************************************************************
 */
package it.tidalwave.role.ui.javafx.impl;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import it.tidalwave.role.ui.Displayable;
import it.tidalwave.role.ui.Styleable;
import it.tidalwave.role.ui.UserActionProvider;
import it.tidalwave.role.ui.javafx.impl.common.CellBinder;
import it.tidalwave.role.ui.javafx.impl.common.ChangeListenerSelectableAdapter;
import it.tidalwave.role.ui.javafx.impl.common.DefaultCellBinder;
import javafx.beans.property.Property;
import javafx.beans.binding.BooleanExpression;
import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.stage.Window;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.application.Platform;
import it.tidalwave.role.SimpleComposite;
import it.tidalwave.role.ui.PresentationModel;
import it.tidalwave.role.ui.BoundProperty;
import it.tidalwave.role.ui.UserAction;
import it.tidalwave.role.ui.javafx.JavaFXBinder;
import it.tidalwave.role.ui.javafx.impl.dialog.DialogBindings;
import it.tidalwave.role.ui.javafx.impl.combobox.ComboBoxBindings;
import it.tidalwave.role.ui.javafx.impl.filechooser.FileChooserBindings;
import it.tidalwave.role.ui.javafx.impl.list.ListViewBindings;
import it.tidalwave.role.ui.javafx.impl.tableview.TableViewBindings;
import it.tidalwave.role.ui.javafx.impl.tree.TreeViewBindings;
import it.tidalwave.role.ui.javafx.impl.treetable.TreeTableViewBindings;
import it.tidalwave.role.ui.javafx.impl.common.PropertyAdapter;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import static java.util.Collections.*;
import static java.util.Objects.*;
import static java.util.stream.Collectors.toList;
import static it.tidalwave.role.SimpleComposite._SimpleComposite_;
import static it.tidalwave.role.ui.Displayable._Displayable_;
import static it.tidalwave.role.ui.Styleable._Styleable_;
import static it.tidalwave.role.ui.UserActionProvider._UserActionProvider_;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="nc bnc" id="L86" title="All 2 branches missed.">@Slf4j</span>
public class DefaultJavaFXBinder implements JavaFXBinder
  {
    private final Executor executor;

<span class="nc" id="L91">    private final String invalidTextFieldStyle = &quot;-fx-background-color: pink&quot;;</span>

    interface Exclusions
      {
        public void setMainWindow (Window window);
        // duplicated in TableViewBindings and TreeTableViewBindings due to common super class
        public ChangeListenerSelectableAdapter getSelectionListener();
      }

<span class="nc" id="L100">    @Delegate(excludes = Exclusions.class)</span>
    private final TreeViewBindings treeItemBindings;

<span class="nc" id="L103">    @Delegate(excludes = Exclusions.class)</span>
    private final TableViewBindings tableViewBindings;

<span class="nc" id="L106">    @Delegate(excludes = Exclusions.class)</span>
    private final TreeTableViewBindings treeTableViewBindings;

<span class="nc" id="L109">    @Delegate(excludes = Exclusions.class)</span>
    private final ListViewBindings listViewBindings;

<span class="nc" id="L112">    @Delegate(excludes = Exclusions.class)</span>
    private final ComboBoxBindings comboBoxBindings;

<span class="nc" id="L115">    @Delegate(excludes = Exclusions.class)</span>
    private final DialogBindings dialogBindings;

<span class="nc" id="L118">    @Delegate(excludes = Exclusions.class)</span>
    private final FileChooserBindings fileChooserBindings;

    private final CellBinder cellBinder;

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public DefaultJavaFXBinder (@Nonnull final Executor executor)
<span class="nc" id="L129">      {</span>
<span class="nc" id="L130">        this.executor = executor;</span>
<span class="nc" id="L131">        cellBinder = new DefaultCellBinder(executor);</span>
<span class="nc" id="L132">        comboBoxBindings = new ComboBoxBindings(executor, cellBinder);</span>
<span class="nc" id="L133">        treeItemBindings = new TreeViewBindings(executor, cellBinder);</span>
<span class="nc" id="L134">        tableViewBindings = new TableViewBindings(executor, cellBinder);</span>
<span class="nc" id="L135">        treeTableViewBindings = new TreeTableViewBindings(executor, cellBinder);</span>
<span class="nc" id="L136">        listViewBindings = new ListViewBindings(executor, cellBinder);</span>
<span class="nc" id="L137">        dialogBindings = new DialogBindings(executor);</span>
<span class="nc" id="L138">        fileChooserBindings = new FileChooserBindings(executor);</span>
<span class="nc" id="L139">      }</span>

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void setMainWindow (@Nonnull final Window mainWindow)
      {
<span class="nc" id="L149">        treeItemBindings.setMainWindow(mainWindow);</span>
<span class="nc" id="L150">        tableViewBindings.setMainWindow(mainWindow);</span>
<span class="nc" id="L151">        dialogBindings.setMainWindow(mainWindow);</span>
<span class="nc" id="L152">        fileChooserBindings.setMainWindow(mainWindow);</span>
<span class="nc" id="L153">      }</span>

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void bind (@Nonnull final ButtonBase button, @Nonnull final UserAction action)
      {
<span class="nc" id="L163">        assertIsFxApplicationThread();</span>
<span class="nc" id="L164">        button.setText(action.maybeAs(_Displayable_).map(Displayable::getDisplayName).orElse(&quot;&quot;));</span>
<span class="nc" id="L165">        button.disableProperty().bind(adaptBoolean(action.enabled()).not());</span>
<span class="nc" id="L166">        button.setOnAction(__ -&gt; executor.execute(action::actionPerformed));</span>
<span class="nc" id="L167">      }</span>

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void bind (@Nonnull final MenuItem menuItem, @Nonnull final UserAction action)
      {
<span class="nc" id="L177">        assertIsFxApplicationThread();</span>
<span class="nc" id="L178">        menuItem.setText(action.maybeAs(_Displayable_).map(Displayable::getDisplayName).orElse(&quot;&quot;));</span>
<span class="nc" id="L179">        menuItem.disableProperty().bind(adaptBoolean(action.enabled()).not());</span>
<span class="nc" id="L180">        menuItem.setOnAction(__ -&gt; executor.execute(action::actionPerformed));</span>
<span class="nc" id="L181">      }</span>

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public &lt;T&gt; void bindBidirectionally (@Nonnull final Property&lt;T&gt; property1,
                                         @Nonnull final BoundProperty&lt;T&gt; property2)
      {
<span class="nc" id="L192">        assertIsFxApplicationThread();</span>
<span class="nc" id="L193">        property1.bindBidirectional(new PropertyAdapter&lt;&gt;(executor, property2));</span>
<span class="nc" id="L194">      }</span>

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public &lt;T&gt; void bindBidirectionally (@Nonnull final TextField textField,
                                         @Nonnull final BoundProperty&lt;String&gt; textProperty,
                                         @Nonnull final BoundProperty&lt;Boolean&gt; validProperty)
      {
<span class="nc" id="L206">        assertIsFxApplicationThread();</span>
<span class="nc" id="L207">        requireNonNull(textField, &quot;textField&quot;);</span>
<span class="nc" id="L208">        requireNonNull(textProperty, &quot;textProperty&quot;);</span>
<span class="nc" id="L209">        requireNonNull(validProperty, &quot;validProperty&quot;);</span>

<span class="nc" id="L211">        textField.textProperty().bindBidirectional(new PropertyAdapter&lt;&gt;(executor, textProperty));</span>

        // FIXME: weak listener
weak listener
<span class="nc" id="L214"> validProperty.addPropertyChangeListener(</span> <span class="nc bnc" id="L215" title="All 2 branches missed."> __ -&gt; textField.setStyle(validProperty.get() ? &quot;&quot; : invalidTextFieldStyle));</span> <span class="nc" id="L216"> }</span> /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override public void bindToggleButtons (@Nonnull final Pane pane, @Nonnull final PresentationModel pm) { <span class="nc bnc" id="L226" title="All 4 branches missed."> assert Platform.isFxApplicationThread();</span> <span class="nc" id="L228"> final ToggleGroup group = new ToggleGroup();</span> <span class="nc" id="L229"> final ObservableList&lt;Node&gt; children = pane.getChildren();</span> <span class="nc" id="L230"> final ObservableList&lt;String&gt; prototypeStyleClass = children.get(0).getStyleClass();</span> <span class="nc" id="L231"> final SimpleComposite&lt;PresentationModel&gt; pmc = pm.as(_SimpleComposite_);</span> <span class="nc" id="L232"> children.setAll(pmc.findChildren().results().stream()</span> <span class="nc" id="L233"> .map(cpm -&gt; createToggleButton(cpm, prototypeStyleClass, group))</span> <span class="nc" id="L234"> .collect(toList()));</span> <span class="nc" id="L235"> }</span> /******************************************************************************************************************* * * ******************************************************************************************************************/ @Override public void bindButtonsInPane (@Nonnull final GridPane gridPane, @Nonnull final Collection&lt;UserAction&gt; actions) { <span class="nc bnc" id="L245" title="All 4 branches missed."> assert Platform.isFxApplicationThread();</span> <span class="nc" id="L247"> final ObservableList&lt;ColumnConstraints&gt; columnConstraints = gridPane.getColumnConstraints();</span> <span class="nc" id="L248"> final ObservableList&lt;Node&gt; children = gridPane.getChildren();</span> <span class="nc" id="L250"> columnConstraints.clear();</span> <span class="nc" id="L251"> children.clear();</span> <span class="nc" id="L252"> final AtomicInteger columnIndex = new AtomicInteger(0);</span> <span class="nc" id="L254"> actions.forEach(menuAction -&gt;</span> { <span class="nc" id="L256"> final ColumnConstraints column = new ColumnConstraints();</span> <span class="nc" id="L257"> column.setPercentWidth(100.0 / actions.size());</span> <span class="nc" id="L258"> columnConstraints.add(column);</span> <span class="nc" id="L259"> final Button button = createButton();</span> <span class="nc" id="L260"> GridPane.setConstraints(button, columnIndex.getAndIncrement(), 0);</span> <span class="nc" id="L261"> bind(button, menuAction);</span> <span class="nc" id="L262"> children.add(button);</span> <span class="nc" id="L263"> });</span> <span class="nc" id="L264"> }</span> /******************************************************************************************************************* * * Create a {@code Button} for the menu bar. * * @param text the label of the button * @return the button * ******************************************************************************************************************/ @Nonnull private Button createButton() { <span class="nc" id="L277"> final Button button = new Button();</span> <span class="nc" id="L278"> GridPane.setHgrow(button, Priority.ALWAYS);</span> <span class="nc" id="L279"> GridPane.setVgrow(button, Priority.ALWAYS);</span> <span class="nc" id="L280"> GridPane.setHalignment(button, HPos.CENTER);</span> <span class="nc" id="L281"> GridPane.setValignment(button, VPos.CENTER);</span> <span class="nc" id="L282"> button.setPrefSize(999, 999); // fill</span> <span class="nc" id="L283"> button.getStyleClass().add(&quot;mainMenuButton&quot;);</span> <span class="nc" id="L285"> return button;</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ @Nonnull private ToggleButton createToggleButton (@Nonnull final PresentationModel pm, @Nonnull final List&lt;String&gt; baseStyleClass, @Nonnull final ToggleGroup group) { <span class="nc" id="L298"> final ToggleButton button = new ToggleButton();</span> <span class="nc" id="L299"> button.setToggleGroup(group);</span> <span class="nc" id="L300"> button.setText(pm.maybeAs(_Displayable_).map(Displayable::getDisplayName).orElse(&quot;&quot;));</span> <span class="nc" id="L301"> button.getStyleClass().addAll(baseStyleClass);</span> <span class="nc" id="L302"> button.getStyleClass().addAll(pm.maybeAs(_Styleable_).map(Styleable::getStyles).orElse(emptyList()));</span> <span class="nc" id="L303"> pm.maybeAs(_UserActionProvider_).flatMap(UserActionProvider::getOptionalDefaultAction)</span> <span class="nc" id="L304"> .ifPresent(action -&gt; bind(button, action));</span> // try // { // bind(button, pm.as(_UserActionProvider_).getDefaultAction()); // } // catch (NotFoundException e) // { // // ok, no UserActionProvider // } <span class="nc bnc" id="L315" title="All 2 branches missed."> if (group.getSelectedToggle() == null)</span> { <span class="nc" id="L317"> group.selectToggle(button);</span> } <span class="nc" id="L320"> return button;</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ private void assertIsFxApplicationThread() { <span class="nc bnc" id="L330" title="All 2 branches missed."> if (!Platform.isFxApplicationThread())</span> { <span class="nc" id="L332"> throw new AssertionError(&quot;Must run in the JavaFX Application Thread&quot;);</span> } <span class="nc" id="L334"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ @Nonnull private BooleanExpression adaptBoolean (@Nonnull final BoundProperty&lt;Boolean&gt; property) { <span class="nc" id="L344"> return BooleanExpression.booleanExpression(new PropertyAdapter&lt;&gt;(executor, property));</span> } } </pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html>