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

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

*** 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 com.sun.javafx.scene.control.skin; - import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import javafx.beans.InvalidationListener; - import javafx.beans.Observable; import javafx.beans.WeakInvalidationListener; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener; import javafx.collections.ObservableList; --- 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 java.util.ArrayList; import java.util.List; + import com.sun.javafx.scene.control.Properties; + import com.sun.javafx.scene.control.behavior.BehaviorBase; import javafx.beans.InvalidationListener; import javafx.beans.WeakInvalidationListener; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener; import javafx.collections.ObservableList;
*** 41,179 **** import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.scene.AccessibleAction; import javafx.scene.AccessibleAttribute; import javafx.scene.Node; import javafx.scene.control.FocusModel; import javafx.scene.control.IndexedCell; import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.MultipleSelectionModel; import javafx.scene.control.SelectionModel; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import java.security.AccessController; import java.security.PrivilegedAction; - import com.sun.javafx.scene.control.behavior.ListViewBehavior; import com.sun.javafx.scene.control.skin.resources.ControlResources; /** * */ ! public class ListViewSkin<T> extends VirtualContainerBase<ListView<T>, ListViewBehavior<T>, ListCell<T>> { ! public static final String RECREATE = "listRecreateKey"; - /** - * Region placed over the top of the flow (and possibly the header row) if - * there is no data. - */ - // FIXME this should not be a StackPane - private StackPane placeholderRegion; - private Node placeholderNode; - // private Label placeholderLabel; private static final String EMPTY_LIST_TEXT = ControlResources.getString("ListView.noContent"); // RT-34744 : IS_PANNABLE will be false unless ! // com.sun.javafx.scene.control.skin.ListViewSkin.pannable // is set to true. This is done in order to make ListView functional // on embedded systems with touch screens which do not generate scroll // events for touch drag gestures. private static final boolean IS_PANNABLE = ! AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("com.sun.javafx.scene.control.skin.ListViewSkin.pannable")); - private ObservableList<T> listViewItems; - private final InvalidationListener itemsChangeListener = observable -> updateListViewItems(); - public ListViewSkin(final ListView<T> listView) { - super(listView, new ListViewBehavior<T>(listView)); ! updateListViewItems(); ! // init the VirtualFlow ! flow.setId("virtual-flow"); ! flow.setPannable(IS_PANNABLE); ! flow.setVertical(getSkinnable().getOrientation() == Orientation.VERTICAL); ! flow.setCreateCell(flow1 -> ListViewSkin.this.createCell()); ! flow.setFixedCellSize(listView.getFixedCellSize()); ! getChildren().add(flow); ! 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 (listView.getEditingIndex() > -1) { ! listView.edit(-1); ! } ! // This ensures that the list 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 (listView.isFocusTraversable()) { ! listView.requestFocus(); ! } ! }; ! flow.getVbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); ! flow.getHbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml); ! updateRowCount(); ! listView.itemsProperty().addListener(new WeakInvalidationListener(itemsChangeListener)); - final ObservableMap<Object, Object> properties = listView.getProperties(); - properties.remove(RECREATE); - properties.addListener(propertiesMapListener); - // init the behavior 'closures' - getBehavior().setOnFocusPreviousRow(() -> { onFocusPreviousCell(); }); - getBehavior().setOnFocusNextRow(() -> { onFocusNextCell(); }); - getBehavior().setOnMoveToFirstCell(() -> { onMoveToFirstCell(); }); - getBehavior().setOnMoveToLastCell(() -> { onMoveToLastCell(); }); - getBehavior().setOnScrollPageDown(isFocusDriven -> onScrollPageDown(isFocusDriven)); - getBehavior().setOnScrollPageUp(isFocusDriven -> onScrollPageUp(isFocusDriven)); - getBehavior().setOnSelectPreviousRow(() -> { onSelectPreviousCell(); }); - getBehavior().setOnSelectNextRow(() -> { onSelectNextCell(); }); ! // Register listeners ! registerChangeListener(listView.itemsProperty(), "ITEMS"); ! registerChangeListener(listView.orientationProperty(), "ORIENTATION"); ! registerChangeListener(listView.cellFactoryProperty(), "CELL_FACTORY"); ! registerChangeListener(listView.parentProperty(), "PARENT"); ! registerChangeListener(listView.placeholderProperty(), "PLACEHOLDER"); ! registerChangeListener(listView.fixedCellSizeProperty(), "FIXED_CELL_SIZE"); ! } ! ! @Override protected void handleControlPropertyChanged(String p) { ! super.handleControlPropertyChanged(p); ! if ("ITEMS".equals(p)) { ! updateListViewItems(); ! } else if ("ORIENTATION".equals(p)) { ! flow.setVertical(getSkinnable().getOrientation() == Orientation.VERTICAL); ! } else if ("CELL_FACTORY".equals(p)) { ! flow.recreateCells(); ! } else if ("PARENT".equals(p)) { ! if (getSkinnable().getParent() != null && getSkinnable().isVisible()) { ! getSkinnable().requestLayout(); ! } ! } else if ("PLACEHOLDER".equals(p)) { ! updatePlaceholderRegionVisibility(); ! } else if ("FIXED_CELL_SIZE".equals(p)) { ! flow.setFixedCellSize(getSkinnable().getFixedCellSize()); ! } ! } private MapChangeListener<Object, Object> propertiesMapListener = c -> { if (! c.wasAdded()) return; ! if (RECREATE.equals(c.getKey())) { needCellsRebuilt = true; getSkinnable().requestLayout(); ! getSkinnable().getProperties().remove(RECREATE); } }; private final ListChangeListener<T> listViewItemsListener = new ListChangeListener<T>() { @Override public void onChanged(Change<? extends T> c) { --- 41,133 ---- import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.scene.AccessibleAction; import javafx.scene.AccessibleAttribute; import javafx.scene.Node; + import javafx.scene.control.Button; + import javafx.scene.control.Control; import javafx.scene.control.FocusModel; import javafx.scene.control.IndexedCell; import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.MultipleSelectionModel; import javafx.scene.control.SelectionModel; + import com.sun.javafx.scene.control.behavior.ListViewBehavior; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import java.security.AccessController; import java.security.PrivilegedAction; import com.sun.javafx.scene.control.skin.resources.ControlResources; /** + * Default skin implementation for the {@link ListView} control. * + * @see ListView + * @since 9 */ ! public class ListViewSkin<T> extends VirtualContainerBase<ListView<T>, ListCell<T>> { ! /*************************************************************************** ! * * ! * Static Fields * ! * * ! **************************************************************************/ private static final String EMPTY_LIST_TEXT = ControlResources.getString("ListView.noContent"); // RT-34744 : IS_PANNABLE will be false unless ! // javafx.scene.control.skin.ListViewSkin.pannable // is set to true. This is done in order to make ListView functional // on embedded systems with touch screens which do not generate scroll // events for touch drag gestures. private static final boolean IS_PANNABLE = ! AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.scene.control.skin.ListViewSkin.pannable")); ! /*************************************************************************** ! * * ! * Internal Fields * ! * * ! **************************************************************************/ ! private final VirtualFlow<ListCell<T>> flow; ! /** ! * Region placed over the top of the flow (and possibly the header row) if ! * there is no data. ! */ ! // FIXME this should not be a StackPane ! private StackPane placeholderRegion; ! private Node placeholderNode; ! private ObservableList<T> listViewItems; ! private final InvalidationListener itemsChangeListener = observable -> updateListViewItems(); ! private boolean needCellsRebuilt = true; ! private boolean needCellsReconfigured = false; ! private int itemCount = -1; ! private ListViewBehavior<T> behavior; ! /*************************************************************************** ! * * ! * Listeners * ! * * ! **************************************************************************/ private MapChangeListener<Object, Object> propertiesMapListener = c -> { if (! c.wasAdded()) return; ! if (Properties.RECREATE.equals(c.getKey())) { needCellsRebuilt = true; getSkinnable().requestLayout(); ! getSkinnable().getProperties().remove(Properties.RECREATE); } }; private final ListChangeListener<T> listViewItemsListener = new ListChangeListener<T>() { @Override public void onChanged(Change<? extends T> c) {
*** 209,244 **** }; private final WeakListChangeListener<T> weakListViewItemsListener = new WeakListChangeListener<T>(listViewItemsListener); ! public void updateListViewItems() { ! if (listViewItems != null) { ! listViewItems.removeListener(weakListViewItemsListener); } ! this.listViewItems = getSkinnable().getItems(); ! if (listViewItems != null) { ! listViewItems.addListener(weakListViewItemsListener); } ! rowCountDirty = true; ! getSkinnable().requestLayout(); } ! private int itemCount = -1; ! @Override public int getItemCount() { ! // return listViewItems == null ? 0 : listViewItems.size(); ! return itemCount; } ! private boolean needCellsRebuilt = true; ! private boolean needCellsReconfigured = false; ! @Override protected void updateRowCount() { if (flow == null) return; int oldCount = itemCount; int newCount = listViewItems == null ? 0 : listViewItems.size(); --- 163,330 ---- }; private final WeakListChangeListener<T> weakListViewItemsListener = new WeakListChangeListener<T>(listViewItemsListener); ! ! ! /*************************************************************************** ! * * ! * Constructors * ! * * ! **************************************************************************/ ! ! /** ! * Creates a new ListViewSkin 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 ListViewSkin(final ListView<T> control) { ! super(control); ! ! // install default input map for the ListView control ! behavior = new ListViewBehavior<>(control); ! // control.setInputMap(behavior.getInputMap()); ! ! // init the behavior 'closures' ! behavior.setOnFocusPreviousRow(() -> onFocusPreviousCell()); ! behavior.setOnFocusNextRow(() -> onFocusNextCell()); ! behavior.setOnMoveToFirstCell(() -> onMoveToFirstCell()); ! behavior.setOnMoveToLastCell(() -> onMoveToLastCell()); ! behavior.setOnSelectPreviousRow(() -> onSelectPreviousCell()); ! behavior.setOnSelectNextRow(() -> onSelectNextCell()); ! behavior.setOnScrollPageDown(this::onScrollPageDown); ! behavior.setOnScrollPageUp(this::onScrollPageUp); ! ! updateListViewItems(); ! ! // init the VirtualFlow ! flow = getVirtualFlow(); ! flow.setId("virtual-flow"); ! flow.setPannable(IS_PANNABLE); ! flow.setVertical(control.getOrientation() == Orientation.VERTICAL); ! flow.setCellFactory(flow -> createCell()); ! flow.setFixedCellSize(control.getFixedCellSize()); ! getChildren().add(flow); ! ! 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.getEditingIndex() > -1) { ! control.edit(-1); } ! // This ensures that the list 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); ! updateRowCount(); ! ! control.itemsProperty().addListener(new WeakInvalidationListener(itemsChangeListener)); ! ! final ObservableMap<Object, Object> properties = control.getProperties(); ! properties.remove(Properties.RECREATE); ! properties.addListener(propertiesMapListener); ! ! // Register listeners ! registerChangeListener(control.itemsProperty(), o -> updateListViewItems()); ! registerChangeListener(control.orientationProperty(), o -> ! flow.setVertical(control.getOrientation() == Orientation.VERTICAL) ! ); ! registerChangeListener(control.cellFactoryProperty(), o -> flow.recreateCells()); ! registerChangeListener(control.parentProperty(), o -> { ! if (control.getParent() != null && control.isVisible()) { ! control.requestLayout(); ! } ! }); ! registerChangeListener(control.placeholderProperty(), o -> updatePlaceholderRegionVisibility()); ! registerChangeListener(control.fixedCellSizeProperty(), o -> ! flow.setFixedCellSize(control.getFixedCellSize()) ! ); } ! ! ! /*************************************************************************** ! * * ! * Public API * ! * * ! **************************************************************************/ ! ! /** {@inheritDoc} */ ! @Override public void dispose() { ! super.dispose(); ! ! if (behavior != null) { ! behavior.dispose(); ! } } ! /** {@inheritDoc} */ ! @Override protected void layoutChildren(final double x, final double y, ! final double w, final double h) { ! super.layoutChildren(x, y, w, h); ! if (needCellsRebuilt) { ! flow.rebuildCells(); ! } else if (needCellsReconfigured) { ! flow.reconfigureCells(); } ! needCellsRebuilt = false; ! needCellsReconfigured = false; ! ! if (getItemCount() == 0) { ! // show message overlay instead of empty listview ! if (placeholderRegion != null) { ! placeholderRegion.setVisible(w > 0 && h > 0); ! placeholderRegion.resizeRelocate(x, y, w, h); ! } ! } else { ! flow.resizeRelocate(x, y, w, h); ! } ! } ! ! /** {@inheritDoc} */ ! @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { ! checkState(); ! ! if (getItemCount() == 0) { ! if (placeholderRegion == null) { ! updatePlaceholderRegionVisibility(); ! } ! if (placeholderRegion != null) { ! return placeholderRegion.prefWidth(height) + leftInset + rightInset; ! } ! } ! ! return computePrefHeight(-1, topInset, rightInset, bottomInset, leftInset) * 0.618033987; ! } ! /** {@inheritDoc} */ ! @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { ! return 400; ! } ! ! /** {@inheritDoc} */ ! @Override int getItemCount() { ! return itemCount; ! } ! ! /** {@inheritDoc} */ ! @Override void updateRowCount() { if (flow == null) return; int oldCount = itemCount; int newCount = listViewItems == null ? 0 : listViewItems.size();
*** 252,262 **** } else { needCellsReconfigured = true; } } ! protected final void updatePlaceholderRegionVisibility() { boolean visible = getItemCount() == 0; if (visible) { placeholderNode = getSkinnable().getPlaceholder(); if (placeholderNode == null && (EMPTY_LIST_TEXT != null && ! EMPTY_LIST_TEXT.isEmpty())) { --- 338,462 ---- } else { needCellsReconfigured = true; } } ! /** {@inheritDoc} */ ! @Override protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { ! switch (attribute) { ! case FOCUS_ITEM: { ! FocusModel<?> fm = getSkinnable().getFocusModel(); ! int focusedIndex = fm.getFocusedIndex(); ! if (focusedIndex == -1) { ! if (placeholderRegion != null && placeholderRegion.isVisible()) { ! return placeholderRegion.getChildren().get(0); ! } ! if (getItemCount() > 0) { ! focusedIndex = 0; ! } else { ! return null; ! } ! } ! return flow.getPrivateCell(focusedIndex); ! } ! case ITEM_COUNT: return getItemCount(); ! case ITEM_AT_INDEX: { ! Integer rowIndex = (Integer)parameters[0]; ! if (rowIndex == null) return null; ! if (0 <= rowIndex && rowIndex < getItemCount()) { ! return flow.getPrivateCell(rowIndex); ! } ! return null; ! } ! case SELECTED_ITEMS: { ! MultipleSelectionModel<T> sm = getSkinnable().getSelectionModel(); ! ObservableList<Integer> indices = sm.getSelectedIndices(); ! List<Node> selection = new ArrayList<>(indices.size()); ! for (int i : indices) { ! ListCell<T> row = flow.getPrivateCell(i); ! if (row != null) selection.add(row); ! } ! return FXCollections.observableArrayList(selection); ! } ! case VERTICAL_SCROLLBAR: return flow.getVbar(); ! case HORIZONTAL_SCROLLBAR: return flow.getHbar(); ! default: return super.queryAccessibleAttribute(attribute, parameters); ! } ! } ! ! /** {@inheritDoc} */ ! @Override protected void executeAccessibleAction(AccessibleAction action, Object... parameters) { ! switch (action) { ! case SHOW_ITEM: { ! Node item = (Node)parameters[0]; ! if (item instanceof ListCell) { ! @SuppressWarnings("unchecked") ! ListCell<T> cell = (ListCell<T>)item; ! flow.scrollTo(cell.getIndex()); ! } ! break; ! } ! case SET_SELECTED_ITEMS: { ! @SuppressWarnings("unchecked") ! ObservableList<Node> items = (ObservableList<Node>)parameters[0]; ! if (items != null) { ! MultipleSelectionModel<T> sm = getSkinnable().getSelectionModel(); ! if (sm != null) { ! sm.clearSelection(); ! for (Node item : items) { ! if (item instanceof ListCell) { ! @SuppressWarnings("unchecked") ! ListCell<T> cell = (ListCell<T>)item; ! sm.select(cell.getIndex()); ! } ! } ! } ! } ! break; ! } ! default: super.executeAccessibleAction(action, parameters); ! } ! } ! ! ! ! /*************************************************************************** ! * * ! * Private implementation * ! * * ! **************************************************************************/ ! ! /** {@inheritDoc} */ ! private ListCell<T> createCell() { ! ListCell<T> cell; ! if (getSkinnable().getCellFactory() != null) { ! cell = getSkinnable().getCellFactory().call(getSkinnable()); ! } else { ! cell = createDefaultCellImpl(); ! } ! ! cell.updateListView(getSkinnable()); ! ! return cell; ! } ! ! private void updateListViewItems() { ! if (listViewItems != null) { ! listViewItems.removeListener(weakListViewItemsListener); ! } ! ! this.listViewItems = getSkinnable().getItems(); ! ! if (listViewItems != null) { ! listViewItems.addListener(weakListViewItemsListener); ! } ! ! rowCountDirty = true; ! getSkinnable().requestLayout(); ! } ! ! private final void updatePlaceholderRegionVisibility() { boolean visible = getItemCount() == 0; if (visible) { placeholderNode = getSkinnable().getPlaceholder(); if (placeholderNode == null && (EMPTY_LIST_TEXT != null && ! EMPTY_LIST_TEXT.isEmpty())) {
*** 273,301 **** placeholderRegion.getChildren().setAll(placeholderNode); } } ! flow.setVisible(! visible); if (placeholderRegion != null) { placeholderRegion.setVisible(visible); } } - @Override public ListCell<T> createCell() { - ListCell<T> cell; - if (getSkinnable().getCellFactory() != null) { - cell = getSkinnable().getCellFactory().call(getSkinnable()); - } else { - cell = createDefaultCellImpl(); - } - - cell.updateListView(getSkinnable()); - - return cell; - } - private static <T> ListCell<T> createDefaultCellImpl() { return new ListCell<T>() { @Override public void updateItem(T item, boolean empty) { super.updateItem(item, empty); --- 473,488 ---- placeholderRegion.getChildren().setAll(placeholderNode); } } ! flow.setVisible(!visible); if (placeholderRegion != null) { placeholderRegion.setVisible(visible); } } private static <T> ListCell<T> createDefaultCellImpl() { return new ListCell<T>() { @Override public void updateItem(T item, boolean empty) { super.updateItem(item, empty);
*** 321,391 **** } } }; } - @Override protected void layoutChildren(final double x, final double y, - final double w, final double h) { - super.layoutChildren(x, y, w, h); - - if (needCellsRebuilt) { - flow.rebuildCells(); - } else if (needCellsReconfigured) { - flow.reconfigureCells(); - } - - needCellsRebuilt = false; - needCellsReconfigured = false; - - if (getItemCount() == 0) { - // show message overlay instead of empty listview - if (placeholderRegion != null) { - placeholderRegion.setVisible(w > 0 && h > 0); - placeholderRegion.resizeRelocate(x, y, w, h); - } - } else { - flow.resizeRelocate(x, y, w, h); - } - } - - @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { - checkState(); - - if (getItemCount() == 0) { - if (placeholderRegion == null) { - updatePlaceholderRegionVisibility(); - } - if (placeholderRegion != null) { - return placeholderRegion.prefWidth(height) + leftInset + rightInset; - } - } - - return computePrefHeight(-1, topInset, rightInset, bottomInset, leftInset) * 0.618033987; - } - - @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { - return 400; - } - private void onFocusPreviousCell() { FocusModel<T> fm = getSkinnable().getFocusModel(); if (fm == null) return; ! flow.show(fm.getFocusedIndex()); } private void onFocusNextCell() { FocusModel<T> fm = getSkinnable().getFocusModel(); if (fm == null) return; ! flow.show(fm.getFocusedIndex()); } private void onSelectPreviousCell() { SelectionModel<T> sm = getSkinnable().getSelectionModel(); if (sm == null) return; int pos = sm.getSelectedIndex(); ! flow.show(pos); // Fix for RT-11299 IndexedCell<T> cell = flow.getFirstVisibleCell(); if (cell == null || pos < cell.getIndex()) { flow.setPosition(pos / (double) getItemCount()); --- 508,535 ---- } } }; } private void onFocusPreviousCell() { FocusModel<T> fm = getSkinnable().getFocusModel(); if (fm == null) return; ! flow.scrollTo(fm.getFocusedIndex()); } private void onFocusNextCell() { FocusModel<T> fm = getSkinnable().getFocusModel(); if (fm == null) return; ! flow.scrollTo(fm.getFocusedIndex()); } private void onSelectPreviousCell() { SelectionModel<T> sm = getSkinnable().getSelectionModel(); if (sm == null) return; int pos = sm.getSelectedIndex(); ! flow.scrollTo(pos); // Fix for RT-11299 IndexedCell<T> cell = flow.getFirstVisibleCell(); if (cell == null || pos < cell.getIndex()) { flow.setPosition(pos / (double) getItemCount());
*** 395,425 **** private void onSelectNextCell() { SelectionModel<T> sm = getSkinnable().getSelectionModel(); if (sm == null) return; int pos = sm.getSelectedIndex(); ! flow.show(pos); // Fix for RT-11299 ListCell<T> cell = flow.getLastVisibleCell(); if (cell == null || cell.getIndex() < pos) { flow.setPosition(pos / (double) getItemCount()); } } private void onMoveToFirstCell() { ! flow.show(0); flow.setPosition(0); } private void onMoveToLastCell() { // SelectionModel sm = getSkinnable().getSelectionModel(); // if (sm == null) return; // int endPos = getItemCount() - 1; // sm.select(endPos); ! flow.show(endPos); flow.setPosition(1); } /** * Function used to scroll the container down by one 'page', although --- 539,569 ---- private void onSelectNextCell() { SelectionModel<T> sm = getSkinnable().getSelectionModel(); if (sm == null) return; int pos = sm.getSelectedIndex(); ! flow.scrollTo(pos); // Fix for RT-11299 ListCell<T> cell = flow.getLastVisibleCell(); if (cell == null || cell.getIndex() < pos) { flow.setPosition(pos / (double) getItemCount()); } } private void onMoveToFirstCell() { ! flow.scrollTo(0); flow.setPosition(0); } private void onMoveToLastCell() { // SelectionModel sm = getSkinnable().getSelectionModel(); // if (sm == null) return; // int endPos = getItemCount() - 1; // sm.select(endPos); ! flow.scrollTo(endPos); flow.setPosition(1); } /** * Function used to scroll the container down by one 'page', although
*** 449,459 **** || (! isFocusDriven && sm.getSelectedIndex() == lastVisibleCellIndex); if (isLeadIndex) { // if the last visible cell is selected, we want to shift that cell up // to be the top-most cell, or at least as far to the top as we can go. ! flow.showAsFirst(lastVisibleCell); ListCell<T> newLastVisibleCell = flow.getLastVisibleCellWithinViewPort(); lastVisibleCell = newLastVisibleCell == null ? lastVisibleCell : newLastVisibleCell; } } else { --- 593,603 ---- || (! isFocusDriven && sm.getSelectedIndex() == lastVisibleCellIndex); if (isLeadIndex) { // if the last visible cell is selected, we want to shift that cell up // to be the top-most cell, or at least as far to the top as we can go. ! flow.scrollToTop(lastVisibleCell); ListCell<T> newLastVisibleCell = flow.getLastVisibleCellWithinViewPort(); lastVisibleCell = newLastVisibleCell == null ? lastVisibleCell : newLastVisibleCell; } } else {
*** 461,471 **** // the selection down to that, without scrolling the contents, so // this is a no-op } int newSelectionIndex = lastVisibleCell.getIndex(); ! flow.show(lastVisibleCell); return newSelectionIndex; } /** * Function used to scroll the container up by one 'page', although --- 605,615 ---- // the selection down to that, without scrolling the contents, so // this is a no-op } int newSelectionIndex = lastVisibleCell.getIndex(); ! flow.scrollTo(lastVisibleCell); return newSelectionIndex; } /** * Function used to scroll the container up by one 'page', although
*** 494,504 **** || (! isFocusDriven && sm.getSelectedIndex() == firstVisibleCellIndex); if (isLeadIndex) { // if the first visible cell is selected, we want to shift that cell down // to be the bottom-most cell, or at least as far to the bottom as we can go. ! flow.showAsLast(firstVisibleCell); ListCell<T> newFirstVisibleCell = flow.getFirstVisibleCellWithinViewPort(); firstVisibleCell = newFirstVisibleCell == null ? firstVisibleCell : newFirstVisibleCell; } } else { --- 638,648 ---- || (! isFocusDriven && sm.getSelectedIndex() == firstVisibleCellIndex); if (isLeadIndex) { // if the first visible cell is selected, we want to shift that cell down // to be the bottom-most cell, or at least as far to the bottom as we can go. ! flow.scrollToBottom(firstVisibleCell); ListCell<T> newFirstVisibleCell = flow.getFirstVisibleCellWithinViewPort(); firstVisibleCell = newFirstVisibleCell == null ? firstVisibleCell : newFirstVisibleCell; } } else {
*** 506,591 **** // the selection up to that, without scrolling the contents, so // this is a no-op } int newSelectionIndex = firstVisibleCell.getIndex(); ! flow.show(firstVisibleCell); return newSelectionIndex; } - - @Override - protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { - switch (attribute) { - case FOCUS_ITEM: { - FocusModel<?> fm = getSkinnable().getFocusModel(); - int focusedIndex = fm.getFocusedIndex(); - if (focusedIndex == -1) { - if (placeholderRegion != null && placeholderRegion.isVisible()) { - return placeholderRegion.getChildren().get(0); - } - if (getItemCount() > 0) { - focusedIndex = 0; - } else { - return null; - } - } - return flow.getPrivateCell(focusedIndex); - } - case ITEM_COUNT: return getItemCount(); - case ITEM_AT_INDEX: { - Integer rowIndex = (Integer)parameters[0]; - if (rowIndex == null) return null; - if (0 <= rowIndex && rowIndex < getItemCount()) { - return flow.getPrivateCell(rowIndex); - } - return null; - } - case SELECTED_ITEMS: { - MultipleSelectionModel<T> sm = getSkinnable().getSelectionModel(); - ObservableList<Integer> indices = sm.getSelectedIndices(); - List<Node> selection = new ArrayList<>(indices.size()); - for (int i : indices) { - ListCell<T> row = flow.getPrivateCell(i); - if (row != null) selection.add(row); - } - return FXCollections.observableArrayList(selection); - } - case VERTICAL_SCROLLBAR: return flow.getVbar(); - case HORIZONTAL_SCROLLBAR: return flow.getHbar(); - 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 ListCell) { - @SuppressWarnings("unchecked") - ListCell<T> cell = (ListCell<T>)item; - flow.show(cell.getIndex()); - } - break; - } - case SET_SELECTED_ITEMS: { - @SuppressWarnings("unchecked") - ObservableList<Node> items = (ObservableList<Node>)parameters[0]; - if (items != null) { - MultipleSelectionModel<T> sm = getSkinnable().getSelectionModel(); - if (sm != null) { - sm.clearSelection(); - for (Node item : items) { - if (item instanceof ListCell) { - @SuppressWarnings("unchecked") - ListCell<T> cell = (ListCell<T>)item; - sm.select(cell.getIndex()); - } - } - } - } - break; - } - default: super.executeAccessibleAction(action, parameters); - } - } } --- 650,658 ---- // the selection up to that, without scrolling the contents, so // this is a no-op } int newSelectionIndex = firstVisibleCell.getIndex(); ! flow.scrollTo(firstVisibleCell); return newSelectionIndex; } }