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;
}
}