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.tableview; 27 28 import jakarta.annotation.Nonnull; 29 import java.util.Optional; 30 import java.util.concurrent.Executor; 31 import javafx.beans.property.ReadOnlyObjectProperty; 32 import javafx.beans.value.ChangeListener; 33 import javafx.collections.ObservableList; 34 import javafx.scene.control.TableCell; 35 import javafx.scene.control.TableColumn; 36 import javafx.scene.control.TableView; 37 import javafx.util.Callback; 38 import it.tidalwave.ui.core.role.PresentationModel; 39 import it.tidalwave.ui.javafx.impl.common.CellBinder; 40 import it.tidalwave.ui.javafx.impl.common.ChangeListenerSelectableAdapter; 41 import it.tidalwave.ui.javafx.impl.common.DelegateSupport; 42 import it.tidalwave.ui.javafx.impl.common.JavaFXWorker; 43 import it.tidalwave.ui.javafx.impl.common.PresentationModelObservable; 44 import lombok.extern.slf4j.Slf4j; 45 import static it.tidalwave.ui.javafx.impl.DefaultJavaFXBinder.enforceFxApplicationThread; 46 import static it.tidalwave.ui.javafx.impl.common.JavaFXWorker.childrenPm; 47 48 /*************************************************************************************************************************************************************** 49 * 50 * This class takes care of bindings related to {@link TableView}. 51 * 52 * @author Fabrizio Giudici 53 * 54 **************************************************************************************************************************************************************/ 55 @Slf4j 56 public class TableViewBindings extends DelegateSupport 57 { 58 private final Callback<TableColumn<PresentationModel, PresentationModel>, TableCell<PresentationModel, PresentationModel>> cellFactory; 59 60 private final ChangeListener<PresentationModel> changeListener = new ChangeListenerSelectableAdapter(executor); 61 62 /*********************************************************************************************************************************************************** 63 * 64 **********************************************************************************************************************************************************/ 65 public TableViewBindings (@Nonnull final Executor executor, @Nonnull final CellBinder cellBinder) 66 { 67 super(executor); 68 cellFactory = ignored -> AsObjectTableCell.of(cellBinder); 69 } 70 71 /*********************************************************************************************************************************************************** 72 * Binds a {@link TableView}. 73 * @param tableView the {@code TableView} 74 * @param pm the {@link PresentationModel} 75 * @param callback an optional callback to invoke at the end of the binding 76 **********************************************************************************************************************************************************/ 77 public void bind (@Nonnull final TableView<PresentationModel> tableView, @Nonnull final PresentationModel pm, @Nonnull final Optional<Runnable> callback) 78 { 79 enforceFxApplicationThread(); 80 log.debug("bind({}, {}, {})", tableView, pm, callback); 81 final var selectedProperty = tableView.getSelectionModel().selectedItemProperty(); 82 selectedProperty.removeListener(changeListener); 83 JavaFXWorker.run(executor, 84 () -> childrenPm(pm), 85 items -> finalizeBinding(tableView, items, selectedProperty, callback)); 86 } 87 88 /*********************************************************************************************************************************************************** 89 * Finalizes binding in the JavaFX thread and eventually invokes a callback. 90 * @param tableView the {@link TableView} 91 * @param items the items of the table 92 * @param selectedProperty the 'selected' property 93 * @param callback an optional callback to invoke at the end 94 **********************************************************************************************************************************************************/ 95 @SuppressWarnings("unchecked") 96 private void finalizeBinding (@Nonnull final TableView<PresentationModel> tableView, 97 @Nonnull final ObservableList<PresentationModel> items, 98 @Nonnull final ReadOnlyObjectProperty<PresentationModel> selectedProperty, 99 @Nonnull final Optional<Runnable> callback) 100 { 101 tableView.setItems(items); 102 selectedProperty.addListener(changeListener); 103 tableView.getColumns() 104 .stream() 105 .map(c -> (TableColumn<PresentationModel, PresentationModel>)c) 106 .forEach(column -> 107 { 108 column.setCellValueFactory(PresentationModelObservable::of); 109 column.setCellFactory(cellFactory); 110 }); 111 112 callback.ifPresent(Runnable::run); 113 } 114 }