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