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,18 +21,18 @@
  * 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;
+package javafx.scene.control.skin;
 
-import java.lang.ref.WeakReference;
 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.Observable;
 import javafx.beans.WeakInvalidationListener;
 import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener;
 import javafx.collections.MapChangeListener;
 import javafx.collections.ObservableList;

@@ -41,139 +41,93 @@
 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.behavior.ListViewBehavior;
 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>, ListViewBehavior<T>, ListCell<T>> {
+public class ListViewSkin<T> extends VirtualContainerBase<ListView<T>, ListCell<T>> {
 
-    public static final String RECREATE = "listRecreateKey";
+    /***************************************************************************
+     *                                                                         *
+     * Static Fields                                                           *
+     *                                                                         *
+     **************************************************************************/
     
-    /**
-     * 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
+    // 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"));
+            AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("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();
+    /***************************************************************************
+     *                                                                         *
+     * Internal Fields                                                         *
+     *                                                                         *
+     **************************************************************************/
 
-        // 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);
+    private final VirtualFlow<ListCell<T>> 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);
-            }
+    /**
+     * 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;
 
-            // 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);
+    private ObservableList<T> listViewItems;
+    private final InvalidationListener itemsChangeListener = observable -> updateListViewItems();
         
-        updateRowCount();
+    private boolean needCellsRebuilt = true;
+    private boolean needCellsReconfigured = false;
 
-        listView.itemsProperty().addListener(new WeakInvalidationListener(itemsChangeListener));
+    private int itemCount = -1;
+    private ListViewBehavior<T> behavior;
 
-        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());
-        }
-    }
+    /***************************************************************************
+     *                                                                         *
+     * Listeners                                                               *
+     *                                                                         *
+     **************************************************************************/
 
     private MapChangeListener<Object, Object> propertiesMapListener = c -> {
         if (! c.wasAdded()) return;
-        if (RECREATE.equals(c.getKey())) {
+        if (Properties.RECREATE.equals(c.getKey())) {
             needCellsRebuilt = true;
             getSkinnable().requestLayout();
-            getSkinnable().getProperties().remove(RECREATE);
+            getSkinnable().getProperties().remove(Properties.RECREATE);
         }
     };
 
     private final ListChangeListener<T> listViewItemsListener = new ListChangeListener<T>() {
         @Override public void onChanged(Change<? extends T> c) {

@@ -209,36 +163,168 @@
     };
     
     private final WeakListChangeListener<T> weakListViewItemsListener =
             new WeakListChangeListener<T>(listViewItemsListener);
 
-    public void updateListViewItems() {
-        if (listViewItems != null) {
-            listViewItems.removeListener(weakListViewItemsListener);
+
+
+    /***************************************************************************
+     *                                                                         *
+     * 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.listViewItems = getSkinnable().getItems();
+            // 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);
 
-        if (listViewItems != null) {
-            listViewItems.addListener(weakListViewItemsListener);
+        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())
+        );
         }
 
-        rowCountDirty = true;
-        getSkinnable().requestLayout();
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    /** {@inheritDoc} */
+    @Override public void dispose() {
+        super.dispose();
+
+        if (behavior != null) {
+            behavior.dispose();
+        }
     }
     
-    private int itemCount = -1;
+    /** {@inheritDoc} */
+    @Override protected void layoutChildren(final double x, final double y,
+                                            final double w, final double h) {
+        super.layoutChildren(x, y, w, h);
 
-    @Override public int getItemCount() {
-//        return listViewItems == null ? 0 : listViewItems.size();
-        return itemCount;
+        if (needCellsRebuilt) {
+            flow.rebuildCells();
+        } else if (needCellsReconfigured) {
+            flow.reconfigureCells();
     }
     
-    private boolean needCellsRebuilt = true;
-    private boolean needCellsReconfigured = false;
+        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;
+    }
 
-    @Override protected void updateRowCount() {
+    /** {@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,11 +338,125 @@
         } else {
             needCellsReconfigured = true;
         }
     }
     
-    protected final void updatePlaceholderRegionVisibility() {
+    /** {@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,29 +473,16 @@
 
                 placeholderRegion.getChildren().setAll(placeholderNode);
             }
         }
 
-        flow.setVisible(! visible);
+        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);
                 

@@ -321,71 +508,28 @@
                 }
             }
         };
     }
 
-    @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());
+        flow.scrollTo(fm.getFocusedIndex());
     }
 
     private void onFocusNextCell() {
         FocusModel<T> fm = getSkinnable().getFocusModel();
         if (fm == null) return;
-        flow.show(fm.getFocusedIndex());
+        flow.scrollTo(fm.getFocusedIndex());
     }
 
     private void onSelectPreviousCell() {
         SelectionModel<T> sm = getSkinnable().getSelectionModel();
         if (sm == null) return;
 
         int pos = sm.getSelectedIndex();
-        flow.show(pos);
+        flow.scrollTo(pos);
 
         // Fix for RT-11299
         IndexedCell<T> cell = flow.getFirstVisibleCell();
         if (cell == null || pos < cell.getIndex()) {
             flow.setPosition(pos / (double) getItemCount());

@@ -395,31 +539,31 @@
     private void onSelectNextCell() {
         SelectionModel<T> sm = getSkinnable().getSelectionModel();
         if (sm == null) return;
 
         int pos = sm.getSelectedIndex();
-        flow.show(pos);
+        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.show(0);
+        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.show(endPos);
+        flow.scrollTo(endPos);
         flow.setPosition(1);
     }
 
     /**
      * Function used to scroll the container down by one 'page', although

@@ -449,11 +593,11 @@
                                || (! 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);
+                flow.scrollToTop(lastVisibleCell);
 
                 ListCell<T> newLastVisibleCell = flow.getLastVisibleCellWithinViewPort();
                 lastVisibleCell = newLastVisibleCell == null ? lastVisibleCell : newLastVisibleCell;
             }
         } else {

@@ -461,11 +605,11 @@
             // the selection down to that, without scrolling the contents, so
             // this is a no-op
         }
 
         int newSelectionIndex = lastVisibleCell.getIndex();
-        flow.show(lastVisibleCell);
+        flow.scrollTo(lastVisibleCell);
         return newSelectionIndex;
     }
 
     /**
      * Function used to scroll the container up by one 'page', although

@@ -494,11 +638,11 @@
                                || (! 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);
+                flow.scrollToBottom(firstVisibleCell);
 
                 ListCell<T> newFirstVisibleCell = flow.getFirstVisibleCellWithinViewPort();
                 firstVisibleCell = newFirstVisibleCell == null ? firstVisibleCell : newFirstVisibleCell;
             }
         } else {

@@ -506,86 +650,9 @@
             // the selection up to that, without scrolling the contents, so
             // this is a no-op
         }
 
         int newSelectionIndex = firstVisibleCell.getIndex();
-        flow.show(firstVisibleCell);
+        flow.scrollTo(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);
-        }
-    }
 }