Content of file DefaultCellBinder.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>DefaultCellBinder.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.common</a> &gt; <span class="el_source">DefaultCellBinder.java</span></div><h1>DefaultCellBinder.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.common;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import javafx.collections.ObservableList;
import javafx.scene.control.Cell;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import it.tidalwave.util.As;
import it.tidalwave.util.annotation.VisibleForTesting;
import it.tidalwave.role.ui.Displayable;
import it.tidalwave.role.ui.UserAction;
import it.tidalwave.role.ui.UserActionProvider;
import it.tidalwave.ui.role.javafx.CustomGraphicProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import static java.util.stream.Collectors.toList;
import static it.tidalwave.role.ui.Displayable._Displayable_;
import static it.tidalwave.role.ui.Styleable._Styleable_;
import static it.tidalwave.role.ui.UserActionProvider._UserActionProvider_;
import static it.tidalwave.ui.role.javafx.CustomGraphicProvider._CustomGraphicProvider_;

/***********************************************************************************************************************
 *
 * An implementation of {@link CellBinder} that extracts information from a {@link UserActionProvider}.
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="pc bpc" id="L61" title="1 of 2 branches missed.">@RequiredArgsConstructor @Slf4j</span>
public class DefaultCellBinder implements CellBinder
  {
    /** Roles to preload, so they are computed in the background thread. */
<span class="fc" id="L65">    private static final List&lt;Class&lt;?&gt;&gt; PRELOADING_ROLE_TYPES = List.of(</span>
            _Displayable_, _UserActionProvider_, _Styleable_, _CustomGraphicProvider_);

    private static final String ROLE_STYLE_PREFIX = &quot;-rs-&quot;;

    @Nonnull
    private final Executor executor;

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void bind (@Nonnull final Cell&lt;?&gt; cell, @Nullable final As item, final boolean empty)
      {
<span class="nc" id="L81">        log.trace(&quot;bind({}, {}, {})&quot;, cell, item, empty);</span>
<span class="nc" id="L82">        clearBindings(cell);</span>

<span class="nc bnc" id="L84" title="All 4 branches missed.">        if (!empty &amp;&amp; (item != null))</span>
          {
<span class="nc" id="L86">            JavaFXWorker.run(executor,</span>
<span class="nc" id="L87">                             () -&gt; new RoleBag(item, PRELOADING_ROLE_TYPES),</span>
<span class="nc" id="L88">                             roles -&gt; bindAll(cell, roles));</span>
          }
<span class="nc" id="L90">      }</span>

    /*******************************************************************************************************************
     *
     * Binds everything provided by the given {@link RoleBag} to the given {@link Cell}.
     *
     * @param     cell            the {@code Cell}
     * @param     roles           the role bag
     *
     ******************************************************************************************************************/
    private void bindAll (@Nonnull final Cell&lt;?&gt; cell, @Nonnull final RoleBag roles)
      {
<span class="nc" id="L102">        bindTextAndGraphic(cell, roles);</span>
<span class="nc" id="L103">        bindDefaultAction(cell, roles);</span>
<span class="nc" id="L104">        bindContextMenu(cell, roles);</span>
<span class="nc" id="L105">        bindStyles(cell.getStyleClass(), roles);</span>
<span class="nc" id="L106">      }</span>

    /*******************************************************************************************************************
     *
     * Binds the text and eventual custom {@link javafx.scene.Node} provided by the given {@link RoleBag} to the given
     * {@link Cell}.
     *
     * @param     cell            the {@code Cell}
     * @param     roles           the role bag
     *
     ******************************************************************************************************************/
    private void bindTextAndGraphic (@Nonnull final Cell&lt;?&gt; cell, @Nonnull final RoleBag roles)
      {
<span class="nc" id="L119">        final Optional&lt;CustomGraphicProvider&gt; cgp = roles.get(_CustomGraphicProvider_);</span>
<span class="nc" id="L120">        cell.setGraphic(cgp.map(CustomGraphicProvider::getGraphic).orElse(null));</span>
<span class="nc" id="L121">        cell.setText(cgp.map(c -&gt; &quot;&quot;).orElse(roles.get(_Displayable_).map(Displayable::getDisplayName).orElse(&quot;&quot;)));</span>
<span class="nc" id="L122">      }</span>

    /*******************************************************************************************************************
     *
     * Binds the default {@link UserAction}s provided by the given {@link RoleBag} as the default action of the given
     * {@link Cell} (activated by double click or key pressure).
     *
     * @param     cell            the {@code Cell}
     * @param     roles           the role bag
     *
     ******************************************************************************************************************/
    private void bindDefaultAction (@Nonnull final Cell&lt;?&gt; cell, @Nonnull final RoleBag roles)
      {
<span class="nc" id="L135">        roles.getDefaultUserAction().ifPresent(defaultAction -&gt;</span>
          {
            // FIXME: doesn't work - keyevents are probably handled by ListView
doesn't work - keyevents are probably handled by ListView
<span class="nc" id="L138"> cell.setOnKeyPressed(event -&gt;</span> { <span class="nc" id="L140"> log.debug(&quot;onKeyPressed: {}&quot;, event);</span> <span class="nc bnc" id="L142" title="All 2 branches missed."> if (event.getCode().equals(KeyCode.SPACE))</span> { <span class="nc" id="L144"> executor.execute(defaultAction::actionPerformed);</span> } <span class="nc" id="L146"> });</span> // FIXME: depends on mouse click, won't handle keyboard <span class="nc" id="L149"> cell.setOnMouseClicked(event -&gt;</span> { <span class="nc bnc" id="L151" title="All 2 branches missed."> if (event.getClickCount() == 2)</span> { <span class="nc" id="L153"> executor.execute(defaultAction::actionPerformed);</span> } <span class="nc" id="L155"> });</span> <span class="nc" id="L156"> });</span> <span class="nc" id="L157"> }</span> /******************************************************************************************************************* * * Binds the {@link UserAction}s provided by the given {@link RoleBag} as items of the contextual menu of a * {@link Cell}. * * @param cell the {@code Cell} * @param roles the role bag * ******************************************************************************************************************/ private void bindContextMenu (@Nonnull final Cell&lt;?&gt; cell, @Nonnull final RoleBag roles) { <span class="nc" id="L170"> final List&lt;MenuItem&gt; menuItems = createMenuItems(roles);</span> <span class="nc bnc" id="L171" title="All 2 branches missed."> cell.setContextMenu(menuItems.isEmpty() ? null : new ContextMenu(menuItems.toArray(new MenuItem[0])));</span> <span class="nc" id="L172"> }</span> /******************************************************************************************************************* * * Adds all the styles provided by the given {@link RoleBag} to a {@link ObservableList} of styles. * * @param styleClasses the destination where to add styles * @param roles the role bag * ******************************************************************************************************************/ @Nonnull private void bindStyles (@Nonnull final ObservableList&lt;String&gt; styleClasses, @Nonnull final RoleBag roles) { <span class="nc" id="L185"> final List&lt;String&gt; styles = styleClasses.stream()</span> <span class="nc bnc" id="L186" title="All 2 branches missed."> .filter(s -&gt; !s.startsWith(ROLE_STYLE_PREFIX))</span> <span class="nc" id="L187"> .collect(toList());</span> // FIXME: shouldn't reset them? In case of cell reuse, they get accumulated <span class="nc" id="L189"> styles.addAll(roles.getMany(_Styleable_)</span> <span class="nc" id="L190"> .stream()</span> <span class="nc" id="L191"> .flatMap(styleable -&gt; styleable.getStyles().stream())</span> <span class="nc" id="L192"> .map(s -&gt; ROLE_STYLE_PREFIX + s)</span> <span class="nc" id="L193"> .collect(toList()));</span> <span class="nc" id="L194"> styleClasses.setAll(styles);</span> <span class="nc" id="L195"> }</span> /******************************************************************************************************************* * * Create a list of {@link MenuItem}s for each action provided by the given {@link RoleBag}. * Don't directly return a ContextMenu otherwise it will be untestable. * * @param roles the role bag * @return the list of {@MenuItem}s * ******************************************************************************************************************/ @Nonnull @VisibleForTesting public List&lt;MenuItem&gt; createMenuItems (@Nonnull final RoleBag roles) { <span class="fc" id="L209"> return roles.getMany(_UserActionProvider_).stream()</span> <span class="fc" id="L210"> .flatMap(uap -&gt; uap.getActions().stream())</span> <span class="fc" id="L211"> .map(this::createMenuItem)</span> <span class="fc" id="L212"> .collect(Collectors.toList());</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ private void clearBindings (@Nonnull final Cell&lt;?&gt; cell) { <span class="nc" id="L222"> cell.setText(&quot;&quot;);</span> <span class="nc" id="L223"> cell.setGraphic(null);</span> <span class="nc" id="L224"> cell.setContextMenu(null);</span> <span class="nc" id="L225"> cell.setOnKeyPressed(null);</span> <span class="nc" id="L226"> cell.setOnMouseClicked(null);</span> <span class="nc" id="L227"> }</span> /******************************************************************************************************************* * * Creates a {@link MenuItem} bound to the given action. * * @param action the action * @return the bound {@code MenuItem} * ******************************************************************************************************************/ @Nonnull private MenuItem createMenuItem (@Nonnull final UserAction action) { <span class="fc" id="L240"> final MenuItem menuItem = new MenuItem(action.as(_Displayable_).getDisplayName());</span> <span class="fc" id="L241"> menuItem.setOnAction(new EventHandlerUserActionAdapter(executor, action));</span> <span class="fc" id="L242"> return menuItem;</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>