modules/controls/src/main/java/javafx/scene/control/skin/TreeTableViewSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

*** 1,7 **** /* ! * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 21,35 **** * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ ! package com.sun.javafx.scene.control.skin; import com.sun.javafx.collections.NonIterableChange; import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList; import javafx.event.WeakEventHandler; import javafx.scene.control.*; import com.sun.javafx.scene.control.behavior.TreeTableViewBehavior; --- 21,38 ---- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ ! package javafx.scene.control.skin; import com.sun.javafx.collections.NonIterableChange; + import com.sun.javafx.scene.control.Properties; import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList; + import com.sun.javafx.scene.control.behavior.BehaviorBase; + import com.sun.javafx.scene.control.skin.Utils; import javafx.event.WeakEventHandler; import javafx.scene.control.*; import com.sun.javafx.scene.control.behavior.TreeTableViewBehavior;
*** 51,320 **** import javafx.scene.input.MouseEvent; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.util.Callback; ! public class TreeTableViewSkin<S> extends TableViewSkinBase<S, TreeItem<S>, TreeTableView<S>, TreeTableViewBehavior<S>, TreeTableRow<S>, TreeTableColumn<S,?>> { ! public TreeTableViewSkin(final TreeTableView<S> treeTableView) { ! super(treeTableView, new TreeTableViewBehavior<S>(treeTableView)); ! this.treeTableView = treeTableView; ! this.tableBackingList = new TreeTableViewBackingList<S>(treeTableView); ! this.tableBackingListProperty = new SimpleObjectProperty<ObservableList<TreeItem<S>>>(tableBackingList); ! flow.setFixedCellSize(treeTableView.getFixedCellSize()); ! super.init(treeTableView); setRoot(getSkinnable().getRoot()); EventHandler<MouseEvent> ml = event -> { // RT-15127: cancel editing on scroll. This is a bit extreme // (we are cancelling editing on touching the scrollbars). // This can be improved at a later date. ! if (treeTableView.getEditingCell() != null) { ! treeTableView.edit(-1, null); } // This ensures that the table maintains the focus, even when the vbar // and hbar controls inside the flow are clicked. Without this, the // focus border will not be shown when the user interacts with the // scrollbars, and more importantly, keyboard navigation won't be // available to the user. ! if (treeTableView.isFocusTraversable()) { ! treeTableView.requestFocus(); } }; flow.getVbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); flow.getHbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); // init the behavior 'closures' ! TreeTableViewBehavior<S> behavior = getBehavior(); ! behavior.setOnFocusPreviousRow(() -> { onFocusPreviousCell(); }); ! behavior.setOnFocusNextRow(() -> { onFocusNextCell(); }); ! behavior.setOnMoveToFirstCell(() -> { onMoveToFirstCell(); }); ! behavior.setOnMoveToLastCell(() -> { onMoveToLastCell(); }); behavior.setOnScrollPageDown(isFocusDriven -> onScrollPageDown(isFocusDriven)); behavior.setOnScrollPageUp(isFocusDriven -> onScrollPageUp(isFocusDriven)); ! behavior.setOnSelectPreviousRow(() -> { onSelectPreviousCell(); }); ! behavior.setOnSelectNextRow(() -> { onSelectNextCell(); }); ! behavior.setOnSelectLeftCell(() -> { onSelectLeftCell(); }); ! behavior.setOnSelectRightCell(() -> { onSelectRightCell(); }); ! ! registerChangeListener(treeTableView.rootProperty(), "ROOT"); ! registerChangeListener(treeTableView.showRootProperty(), "SHOW_ROOT"); ! registerChangeListener(treeTableView.rowFactoryProperty(), "ROW_FACTORY"); ! registerChangeListener(treeTableView.expandedItemCountProperty(), "TREE_ITEM_COUNT"); ! registerChangeListener(treeTableView.fixedCellSizeProperty(), "FIXED_CELL_SIZE"); ! } ! @Override protected void handleControlPropertyChanged(String p) { ! super.handleControlPropertyChanged(p); ! ! if ("ROOT".equals(p)) { // fix for RT-37853 getSkinnable().edit(-1, null); setRoot(getSkinnable().getRoot()); ! } else if ("SHOW_ROOT".equals(p)) { // if we turn off showing the root, then we must ensure the root // is expanded - otherwise we end up with no visible items in // the tree. if (! getSkinnable().isShowRoot() && getRoot() != null) { getRoot().setExpanded(true); } // update the item count in the flow and behavior instances updateRowCount(); ! } else if ("ROW_FACTORY".equals(p)) { ! flow.recreateCells(); ! } else if ("TREE_ITEM_COUNT".equals(p)) { ! rowCountDirty = true; ! } else if ("FIXED_CELL_SIZE".equals(p)) { ! flow.setFixedCellSize(getSkinnable().getFixedCellSize()); ! } } /*************************************************************************** * * ! * Listeners * * * **************************************************************************/ /*************************************************************************** * * ! * Internal Fields * * * **************************************************************************/ ! private TreeTableViewBackingList<S> tableBackingList; ! private ObjectProperty<ObservableList<TreeItem<S>>> tableBackingListProperty; ! private TreeTableView<S> treeTableView; ! private WeakReference<TreeItem<S>> weakRootRef; ! private EventHandler<TreeItem.TreeModificationEvent<S>> rootListener = e -> { ! if (e.wasAdded() && e.wasRemoved() && e.getAddedSize() == e.getRemovedSize()) { ! // Fix for RT-14842, where the children of a TreeItem were changing, ! // but because the overall item count was staying the same, there was ! // no event being fired to the skin to be informed that the items ! // had changed. So, here we just watch for the case where the number ! // of items being added is equal to the number of items being removed. ! rowCountDirty = true; ! getSkinnable().requestLayout(); ! } else if (e.getEventType().equals(TreeItem.valueChangedEvent())) { ! // Fix for RT-14971 and RT-15338. ! needCellsRebuilt = true; ! getSkinnable().requestLayout(); } else { ! // Fix for RT-20090. We are checking to see if the event coming ! // from the TreeItem root is an event where the count has changed. ! EventType<?> eventType = e.getEventType(); ! while (eventType != null) { ! if (eventType.equals(TreeItem.<S>expandedItemCountChangeEvent())) { ! rowCountDirty = true; ! getSkinnable().requestLayout(); ! break; ! } ! eventType = eventType.getSuperType(); ! } } ! // fix for RT-37853 ! getSkinnable().edit(-1, null); ! }; ! private WeakEventHandler<TreeModificationEvent<S>> weakRootListener; ! // private WeakReference<TreeItem> weakRoot; ! private TreeItem<S> getRoot() { return weakRootRef == null ? null : weakRootRef.get(); } ! private void setRoot(TreeItem<S> newRoot) { if (getRoot() != null && weakRootListener != null) { ! getRoot().removeEventHandler(TreeItem.<S>treeNotificationEvent(), weakRootListener); } weakRootRef = new WeakReference<>(newRoot); if (getRoot() != null) { weakRootListener = new WeakEventHandler<>(rootListener); ! getRoot().addEventHandler(TreeItem.<S>treeNotificationEvent(), weakRootListener); } updateRowCount(); } - - /*************************************************************************** - * * - * Public API * - * * - **************************************************************************/ - /** {@inheritDoc} */ ! @Override protected ObservableList<TreeTableColumn<S, ?>> getVisibleLeafColumns() { ! return treeTableView.getVisibleLeafColumns(); } ! @Override protected int getVisibleLeafIndex(TreeTableColumn<S,?> tc) { ! return treeTableView.getVisibleLeafIndex(tc); } ! @Override protected TreeTableColumn<S,?> getVisibleLeafColumn(int col) { ! return treeTableView.getVisibleLeafColumn(col); } /** {@inheritDoc} */ ! @Override protected TreeTableView.TreeTableViewFocusModel<S> getFocusModel() { ! return treeTableView.getFocusModel(); } /** {@inheritDoc} */ ! @Override protected TreeTablePosition<S, ?> getFocusedCell() { ! return treeTableView.getFocusModel().getFocusedCell(); } /** {@inheritDoc} */ ! @Override protected TableSelectionModel<TreeItem<S>> getSelectionModel() { ! return treeTableView.getSelectionModel(); } /** {@inheritDoc} */ ! @Override protected ObjectProperty<Callback<TreeTableView<S>, TreeTableRow<S>>> rowFactoryProperty() { ! return treeTableView.rowFactoryProperty(); } /** {@inheritDoc} */ ! @Override protected ObjectProperty<Node> placeholderProperty() { ! return treeTableView.placeholderProperty(); } /** {@inheritDoc} */ ! @Override protected ObjectProperty<ObservableList<TreeItem<S>>> itemsProperty() { return tableBackingListProperty; } /** {@inheritDoc} */ ! @Override protected ObservableList<TreeTableColumn<S,?>> getColumns() { ! return treeTableView.getColumns(); } /** {@inheritDoc} */ ! @Override protected BooleanProperty tableMenuButtonVisibleProperty() { ! return treeTableView.tableMenuButtonVisibleProperty(); } /** {@inheritDoc} */ ! @Override protected ObjectProperty<Callback<ResizeFeaturesBase, Boolean>> columnResizePolicyProperty() { ! // TODO Ugly! ! return (ObjectProperty<Callback<ResizeFeaturesBase, Boolean>>) (Object) treeTableView.columnResizePolicyProperty(); } /** {@inheritDoc} */ ! @Override protected ObservableList<TreeTableColumn<S,?>> getSortOrder() { ! return treeTableView.getSortOrder(); ! } ! ! @Override protected boolean resizeColumn(TreeTableColumn<S,?> tc, double delta) { ! return treeTableView.resizeColumn(tc, delta); } ! @Override protected void edit(int index, TreeTableColumn<S, ?> column) { ! treeTableView.edit(index, column); } /* * FIXME: Naive implementation ahead * Attempts to resize column based on the pref width of all items contained * in this column. This can be potentially very expensive if the number of * rows is large. */ ! @Override protected void resizeColumnToFitContent(TreeTableColumn<S,?> tc, int maxRows) { final TreeTableColumn col = tc; List<?> items = itemsProperty().get(); if (items == null || items.isEmpty()) return; Callback cellFactory = col.getCellFactory(); if (cellFactory == null) return; ! TreeTableCell<S,?> cell = (TreeTableCell) cellFactory.call(col); if (cell == null) return; // set this property to tell the TableCell we want to know its actual // preferred width, not the width of the associated TableColumnBase ! cell.getProperties().put(TableCellSkin.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE); // determine cell padding double padding = 10; Node n = cell.getSkin() == null ? null : cell.getSkin().getNode(); if (n instanceof Region) { Region r = (Region) n; padding = r.snappedLeftInset() + r.snappedRightInset(); } ! TreeTableRow<S> treeTableRow = new TreeTableRow<>(); treeTableRow.updateTreeTableView(treeTableView); int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows); double maxWidth = 0; for (int row = 0; row < rows; row++) { --- 54,435 ---- import javafx.scene.input.MouseEvent; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.util.Callback; ! /** ! * Default skin implementation for the {@link TreeTableView} control. ! * ! * @see TreeTableView ! * @since 9 ! */ ! public class TreeTableViewSkin<T> extends TableViewSkinBase<T, TreeItem<T>, TreeTableView<T>, TreeTableRow<T>, TreeTableColumn<T,?>> { ! ! /*************************************************************************** ! * * ! * Private Fields * ! * * ! **************************************************************************/ ! ! private TreeTableViewBackingList<T> tableBackingList; ! private ObjectProperty<ObservableList<TreeItem<T>>> tableBackingListProperty; ! private WeakReference<TreeItem<T>> weakRootRef; ! private final TreeTableViewBehavior<T> behavior; ! ! ! ! /*************************************************************************** ! * * ! * Listeners * ! * * ! **************************************************************************/ ! ! private EventHandler<TreeItem.TreeModificationEvent<T>> rootListener = e -> { ! if (e.wasAdded() && e.wasRemoved() && e.getAddedSize() == e.getRemovedSize()) { ! // Fix for RT-14842, where the children of a TreeItem were changing, ! // but because the overall item count was staying the same, there was ! // no event being fired to the skin to be informed that the items ! // had changed. So, here we just watch for the case where the number ! // of items being added is equal to the number of items being removed. ! rowCountDirty = true; ! getSkinnable().requestLayout(); ! } else if (e.getEventType().equals(TreeItem.valueChangedEvent())) { ! // Fix for RT-14971 and RT-15338. ! needCellsRebuilt = true; ! getSkinnable().requestLayout(); ! } else { ! // Fix for RT-20090. We are checking to see if the event coming ! // from the TreeItem root is an event where the count has changed. ! EventType<?> eventType = e.getEventType(); ! while (eventType != null) { ! if (eventType.equals(TreeItem.<T>expandedItemCountChangeEvent())) { ! rowCountDirty = true; ! getSkinnable().requestLayout(); ! break; ! } ! eventType = eventType.getSuperType(); ! } ! } ! // fix for RT-37853 ! getSkinnable().edit(-1, null); ! }; ! ! private WeakEventHandler<TreeModificationEvent<T>> weakRootListener; ! ! ! ! /*************************************************************************** ! * * ! * Constructors * ! * * ! **************************************************************************/ ! /** ! * Creates a new TreeTableViewSkin instance, installing the necessary child ! * nodes into the Control {@link Control#getChildren() children} list, as ! * well as the necessary input mappings for handling key, mouse, etc events. ! * ! * @param control The control that this skin should be installed onto. ! */ ! public TreeTableViewSkin(final TreeTableView<T> control) { ! super(control); ! // install default input map for the TreeTableView control ! behavior = new TreeTableViewBehavior<>(control); ! // control.setInputMap(behavior.getInputMap()); ! flow.setFixedCellSize(control.getFixedCellSize()); ! flow.setCellFactory(flow -> createCell()); setRoot(getSkinnable().getRoot()); EventHandler<MouseEvent> ml = event -> { // RT-15127: cancel editing on scroll. This is a bit extreme // (we are cancelling editing on touching the scrollbars). // This can be improved at a later date. ! if (control.getEditingCell() != null) { ! control.edit(-1, null); } // This ensures that the table maintains the focus, even when the vbar // and hbar controls inside the flow are clicked. Without this, the // focus border will not be shown when the user interacts with the // scrollbars, and more importantly, keyboard navigation won't be // available to the user. ! if (control.isFocusTraversable()) { ! control.requestFocus(); } }; flow.getVbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); flow.getHbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); // init the behavior 'closures' ! behavior.setOnFocusPreviousRow(() -> onFocusPreviousCell()); ! behavior.setOnFocusNextRow(() -> onFocusNextCell()); ! behavior.setOnMoveToFirstCell(() -> onMoveToFirstCell()); ! behavior.setOnMoveToLastCell(() -> onMoveToLastCell()); behavior.setOnScrollPageDown(isFocusDriven -> onScrollPageDown(isFocusDriven)); behavior.setOnScrollPageUp(isFocusDriven -> onScrollPageUp(isFocusDriven)); ! behavior.setOnSelectPreviousRow(() -> onSelectPreviousCell()); ! behavior.setOnSelectNextRow(() -> onSelectNextCell()); ! behavior.setOnSelectLeftCell(() -> onSelectLeftCell()); ! behavior.setOnSelectRightCell(() -> onSelectRightCell()); ! registerChangeListener(control.rootProperty(), e -> { // fix for RT-37853 getSkinnable().edit(-1, null); setRoot(getSkinnable().getRoot()); ! }); ! registerChangeListener(control.showRootProperty(), e -> { // if we turn off showing the root, then we must ensure the root // is expanded - otherwise we end up with no visible items in // the tree. if (! getSkinnable().isShowRoot() && getRoot() != null) { getRoot().setExpanded(true); } // update the item count in the flow and behavior instances updateRowCount(); ! }); ! registerChangeListener(control.rowFactoryProperty(), e -> flow.recreateCells()); ! registerChangeListener(control.expandedItemCountProperty(), e -> rowCountDirty = true); ! registerChangeListener(control.fixedCellSizeProperty(), e -> flow.setFixedCellSize(getSkinnable().getFixedCellSize())); } + + /*************************************************************************** * * ! * Public API * * * **************************************************************************/ + /** {@inheritDoc} */ + @Override public void dispose() { + super.dispose(); + + if (behavior != null) { + behavior.dispose(); + } + } + + /** {@inheritDoc} */ + @Override protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { + switch (attribute) { + case ROW_AT_INDEX: { + final int rowIndex = (Integer)parameters[0]; + return rowIndex < 0 ? null : flow.getPrivateCell(rowIndex); + } + case SELECTED_ITEMS: { + List<Node> selection = new ArrayList<>(); + TreeTableView.TreeTableViewSelectionModel<T> sm = getSkinnable().getSelectionModel(); + for (TreeTablePosition<T,?> pos : sm.getSelectedCells()) { + TreeTableRow<T> row = flow.getPrivateCell(pos.getRow()); + if (row != null) selection.add(row); + } + return FXCollections.observableArrayList(selection); + } + case FOCUS_ITEM: // TableViewSkinBase + case CELL_AT_ROW_COLUMN: // TableViewSkinBase + case COLUMN_AT_INDEX: // TableViewSkinBase + case HEADER: // TableViewSkinBase + case VERTICAL_SCROLLBAR: // TableViewSkinBase + case HORIZONTAL_SCROLLBAR: // TableViewSkinBase + default: return super.queryAccessibleAttribute(attribute, parameters); + } + } + + @Override + protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { + switch (action) { + case SHOW_ITEM: { + Node item = (Node)parameters[0]; + if (item instanceof TreeTableCell) { + @SuppressWarnings("unchecked") + TreeTableCell<T, ?> cell = (TreeTableCell<T, ?>)item; + flow.scrollTo(cell.getIndex()); + } + break; + } + case SET_SELECTED_ITEMS: { + @SuppressWarnings("unchecked") + ObservableList<Node> items = (ObservableList<Node>)parameters[0]; + if (items != null) { + TreeTableView.TreeTableViewSelectionModel<T> sm = getSkinnable().getSelectionModel(); + if (sm != null) { + sm.clearSelection(); + for (Node item : items) { + if (item instanceof TreeTableCell) { + @SuppressWarnings("unchecked") + TreeTableCell<T, ?> cell = (TreeTableCell<T, ?>)item; + sm.select(cell.getIndex(), cell.getTableColumn()); + } + } + } + } + break; + } + default: super.executeAccessibleAction(action, parameters); + } + } + /*************************************************************************** * * ! * Private methods * * * **************************************************************************/ ! /** {@inheritDoc} */ ! private TreeTableRow<T> createCell() { ! TreeTableRow<T> cell; ! TreeTableView<T> treeTableView = getSkinnable(); ! if (treeTableView.getRowFactory() != null) { ! cell = treeTableView.getRowFactory().call(treeTableView); } else { ! cell = new TreeTableRow<T>(); } ! // If there is no disclosure node, then add one of my own ! if (cell.getDisclosureNode() == null) { ! final StackPane disclosureNode = new StackPane(); ! disclosureNode.getStyleClass().setAll("tree-disclosure-node"); ! disclosureNode.setMouseTransparent(true); ! final StackPane disclosureNodeArrow = new StackPane(); ! disclosureNodeArrow.getStyleClass().setAll("arrow"); ! disclosureNode.getChildren().add(disclosureNodeArrow); + cell.setDisclosureNode(disclosureNode); + } ! cell.updateTreeTableView(treeTableView); ! return cell; ! } ! ! private TreeItem<T> getRoot() { return weakRootRef == null ? null : weakRootRef.get(); } ! private void setRoot(TreeItem<T> newRoot) { if (getRoot() != null && weakRootListener != null) { ! getRoot().removeEventHandler(TreeItem.<T>treeNotificationEvent(), weakRootListener); } weakRootRef = new WeakReference<>(newRoot); if (getRoot() != null) { weakRootListener = new WeakEventHandler<>(rootListener); ! getRoot().addEventHandler(TreeItem.<T>treeNotificationEvent(), weakRootListener); } updateRowCount(); } /** {@inheritDoc} */ ! @Override ObservableList<TreeTableColumn<T, ?>> getVisibleLeafColumns() { ! return getSkinnable().getVisibleLeafColumns(); } ! @Override int getVisibleLeafIndex(TreeTableColumn<T,?> tc) { ! return getSkinnable().getVisibleLeafIndex(tc); } ! @Override TreeTableColumn<T,?> getVisibleLeafColumn(int col) { ! return getSkinnable().getVisibleLeafColumn(col); } /** {@inheritDoc} */ ! @Override TreeTableView.TreeTableViewFocusModel<T> getFocusModel() { ! return getSkinnable().getFocusModel(); } /** {@inheritDoc} */ ! @Override TreeTablePosition<T, ?> getFocusedCell() { ! return getSkinnable().getFocusModel().getFocusedCell(); } /** {@inheritDoc} */ ! @Override TableSelectionModel<TreeItem<T>> getSelectionModel() { ! return getSkinnable().getSelectionModel(); } /** {@inheritDoc} */ ! @Override ObjectProperty<Callback<TreeTableView<T>, TreeTableRow<T>>> rowFactoryProperty() { ! return getSkinnable().rowFactoryProperty(); } /** {@inheritDoc} */ ! @Override ObjectProperty<Node> placeholderProperty() { ! return getSkinnable().placeholderProperty(); } /** {@inheritDoc} */ ! @Override ObjectProperty<ObservableList<TreeItem<T>>> itemsProperty() { ! if (tableBackingListProperty == null) { ! this.tableBackingList = new TreeTableViewBackingList<>(getSkinnable()); ! this.tableBackingListProperty = new SimpleObjectProperty<>(tableBackingList); ! } return tableBackingListProperty; } /** {@inheritDoc} */ ! @Override ObservableList<TreeTableColumn<T,?>> getColumns() { ! return getSkinnable().getColumns(); } /** {@inheritDoc} */ ! @Override BooleanProperty tableMenuButtonVisibleProperty() { ! return getSkinnable().tableMenuButtonVisibleProperty(); } /** {@inheritDoc} */ ! @Override ObjectProperty<Callback<ResizeFeaturesBase, Boolean>> columnResizePolicyProperty() { ! return (ObjectProperty<Callback<ResizeFeaturesBase, Boolean>>) (Object) getSkinnable().columnResizePolicyProperty(); } /** {@inheritDoc} */ ! @Override ObservableList<TreeTableColumn<T,?>> getSortOrder() { ! return getSkinnable().getSortOrder(); } ! @Override boolean resizeColumn(TreeTableColumn<T,?> tc, double delta) { ! return getSkinnable().resizeColumn(tc, delta); } /* * FIXME: Naive implementation ahead * Attempts to resize column based on the pref width of all items contained * in this column. This can be potentially very expensive if the number of * rows is large. */ ! @Override void resizeColumnToFitContent(TreeTableColumn<T,?> tc, int maxRows) { final TreeTableColumn col = tc; List<?> items = itemsProperty().get(); if (items == null || items.isEmpty()) return; Callback cellFactory = col.getCellFactory(); if (cellFactory == null) return; ! TreeTableCell<T,?> cell = (TreeTableCell) cellFactory.call(col); if (cell == null) return; // set this property to tell the TableCell we want to know its actual // preferred width, not the width of the associated TableColumnBase ! cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE); // determine cell padding double padding = 10; Node n = cell.getSkin() == null ? null : cell.getSkin().getNode(); if (n instanceof Region) { Region r = (Region) n; padding = r.snappedLeftInset() + r.snappedRightInset(); } ! final TreeTableView<T> treeTableView = getSkinnable(); ! ! TreeTableRow<T> treeTableRow = new TreeTableRow<>(); treeTableRow.updateTreeTableView(treeTableView); int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows); double maxWidth = 0; for (int row = 0; row < rows; row++) {
*** 357,480 **** col.impl_setWidth(maxWidth); } /** {@inheritDoc} */ ! @Override public int getItemCount() { ! return treeTableView.getExpandedItemCount(); } /** {@inheritDoc} */ ! @Override public TreeTableRow<S> createCell() { ! TreeTableRow<S> cell; ! ! if (treeTableView.getRowFactory() != null) { ! cell = treeTableView.getRowFactory().call(treeTableView); ! } else { ! cell = new TreeTableRow<S>(); ! } ! ! // If there is no disclosure node, then add one of my own ! if (cell.getDisclosureNode() == null) { ! final StackPane disclosureNode = new StackPane(); ! disclosureNode.getStyleClass().setAll("tree-disclosure-node"); ! disclosureNode.setMouseTransparent(true); ! ! final StackPane disclosureNodeArrow = new StackPane(); ! disclosureNodeArrow.getStyleClass().setAll("arrow"); ! disclosureNode.getChildren().add(disclosureNodeArrow); ! ! cell.setDisclosureNode(disclosureNode); ! } ! ! cell.updateTreeTableView(treeTableView); ! return cell; ! } ! ! @Override protected void horizontalScroll() { super.horizontalScroll(); if (getSkinnable().getFixedCellSize() > 0) { flow.requestCellLayout(); } } ! @Override ! protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { ! switch (attribute) { ! case ROW_AT_INDEX: { ! final int rowIndex = (Integer)parameters[0]; ! return rowIndex < 0 ? null : flow.getPrivateCell(rowIndex); ! } ! case SELECTED_ITEMS: { ! List<Node> selection = new ArrayList<>(); ! TreeTableView.TreeTableViewSelectionModel<S> sm = getSkinnable().getSelectionModel(); ! for (TreeTablePosition<S,?> pos : sm.getSelectedCells()) { ! TreeTableRow<S> row = flow.getPrivateCell(pos.getRow()); ! if (row != null) selection.add(row); ! } ! return FXCollections.observableArrayList(selection); ! } ! case FOCUS_ITEM: // TableViewSkinBase ! case CELL_AT_ROW_COLUMN: // TableViewSkinBase ! case COLUMN_AT_INDEX: // TableViewSkinBase ! case HEADER: // TableViewSkinBase ! case VERTICAL_SCROLLBAR: // TableViewSkinBase ! case HORIZONTAL_SCROLLBAR: // TableViewSkinBase ! default: return super.queryAccessibleAttribute(attribute, parameters); ! } ! } ! ! @Override ! protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { ! switch (action) { ! case SHOW_ITEM: { ! Node item = (Node)parameters[0]; ! if (item instanceof TreeTableCell) { ! @SuppressWarnings("unchecked") ! TreeTableCell<S, ?> cell = (TreeTableCell<S, ?>)item; ! flow.show(cell.getIndex()); ! } ! break; ! } ! case SET_SELECTED_ITEMS: { ! @SuppressWarnings("unchecked") ! ObservableList<Node> items = (ObservableList<Node>)parameters[0]; ! if (items != null) { ! TreeTableView.TreeTableViewSelectionModel<S> sm = getSkinnable().getSelectionModel(); ! if (sm != null) { ! sm.clearSelection(); ! for (Node item : items) { ! if (item instanceof TreeTableCell) { ! @SuppressWarnings("unchecked") ! TreeTableCell<S, ?> cell = (TreeTableCell<S, ?>)item; ! sm.select(cell.getIndex(), cell.getTableColumn()); ! } ! } ! } ! } ! break; ! } ! default: super.executeAccessibleAction(action, parameters); ! } ! } ! ! /*************************************************************************** ! * * ! * Layout * ! * * ! **************************************************************************/ ! ! ! ! ! /*************************************************************************** ! * * ! * Private methods * ! * * ! **************************************************************************/ ! ! @Override protected void updateRowCount() { updatePlaceholderRegionVisibility(); tableBackingList.resetSize(); int oldCount = flow.getCellCount(); --- 472,495 ---- col.impl_setWidth(maxWidth); } /** {@inheritDoc} */ ! @Override int getItemCount() { ! return getSkinnable().getExpandedItemCount(); } /** {@inheritDoc} */ ! @Override void horizontalScroll() { super.horizontalScroll(); if (getSkinnable().getFixedCellSize() > 0) { flow.requestCellLayout(); } } ! /** {@inheritDoc} */ ! @Override void updateRowCount() { updatePlaceholderRegionVisibility(); tableBackingList.resetSize(); int oldCount = flow.getCellCount();
*** 493,525 **** } else { needCellsReconfigured = true; } } /** * A simple read only list structure that maps into the TreeTableView tree * structure. */ ! private static class TreeTableViewBackingList<S> extends ReadOnlyUnbackedObservableList<TreeItem<S>> { ! private final TreeTableView<S> treeTable; private int size = -1; ! TreeTableViewBackingList(TreeTableView<S> treeTable) { this.treeTable = treeTable; } void resetSize() { int oldSize = size; size = -1; // TODO we can certainly make this better....but it may not really matter ! callObservers(new NonIterableChange.GenericAddRemoveChange<TreeItem<S>>( ! 0, oldSize, FXCollections.<TreeItem<S>>emptyObservableList(), this)); } ! @Override public TreeItem<S> get(int i) { return treeTable.getTreeItem(i); } @Override public int size() { if (size == -1) { --- 508,548 ---- } else { needCellsReconfigured = true; } } + + + /*************************************************************************** + * * + * Support classes * + * * + **************************************************************************/ + /** * A simple read only list structure that maps into the TreeTableView tree * structure. */ ! private static class TreeTableViewBackingList<T> extends ReadOnlyUnbackedObservableList<TreeItem<T>> { ! private final TreeTableView<T> treeTable; private int size = -1; ! TreeTableViewBackingList(TreeTableView<T> treeTable) { this.treeTable = treeTable; } void resetSize() { int oldSize = size; size = -1; // TODO we can certainly make this better....but it may not really matter ! callObservers(new NonIterableChange.GenericAddRemoveChange<TreeItem<T>>( ! 0, oldSize, FXCollections.<TreeItem<T>>emptyObservableList(), this)); } ! @Override public TreeItem<T> get(int i) { return treeTable.getTreeItem(i); } @Override public int size() { if (size == -1) {