Summary
Add new protected VirtualFlow and TableView related methods for subclassing.
Problem
Anyone trying to extend the TableView and tweak it will be prevented to do it properly because some crucial methods are declared private or package-protected. These methods are located in TableViewSkinBase, TableHeaderRow, TableColumnHeader and VirtualFlow.
Solution
The solution is to open some methods the community has identified as crucial to anyone willing to extend the TableView.
Specification
The following API is added:
javafx/scene/control/skin/TableColumnHeader.java
:
/**
* Returns the {@link TableHeaderRow} associated with this {@code TableColumnHeader}.
*
* @return the {@code TableHeaderRow} associated with this {@code TableColumnHeader}
* @since 12
*/
protected TableHeaderRow getTableHeaderRow() {
/**
* Returns the {@code TableViewSkinBase} in which this {@code TableColumnHeader} is inserted. This will return
* {@code null} until the {@code TableHeaderRow} has been set.
*
* @return the {@code TableViewSkinBase} in which this {@code TableColumnHeader} is inserted, or {@code null}
* @since 12
*/
protected TableViewSkinBase<?, ?, ?, ?, ?> getTableSkin() {
javafx/scene/control/skin/TableHeaderRow.java
:
/**
* Indicates if a reordering operation of a column is in progress. The value is {@code true} during a column
* reordering operation, and {@code false} otherwise. When a column is reordered (for example, by dragging its
* header), this property is updated automatically. Setting the value manually should be done when a subclass
* overrides the default reordering behavior. Calling {@link #setReorderingRegion(TableColumnHeader)} before setting
* this property is required as well.
*
* @since 12
*/
private BooleanProperty reordering = new SimpleBooleanProperty(this, "reordering", false) {
public final void setReordering(boolean value) {
public final boolean isReordering() {
public final BooleanProperty reorderingProperty() {
/**
* Returns the root header for all columns. The root header is a {@link NestedTableColumnHeader} that contains the
* {@code NestedTableColumnHeader}s that represent each column. It spans the entire width of the {@code TableView}.
* This allows any developer overriding a {@code TableColumnHeader} to easily access the root header and all others
* {@code TableColumnHeader}s.
*
* @return the root header
* @implNote This design enforces that column reordering occurs only within a single {@code NestedTableColumnHeader}
* and only at that level.
* @since 12
*/
public final NestedTableColumnHeader getRootHeader() {
/**
* Called whenever the value of the horizontal scrollbar changes in order to request layout changes, shifting the
* {@code TableColumnHeader}s.
* <p>
* For example, if custom components are added around a {@code TableColumnHeader} (such as icons above), they will
* also need to be shifted. When overriding, calling {@code super()} is required to shift the {@code
* TableColumnHeader}s, and it's up to the developer to notify its own custom components of this change.
*
* @since 12
*/
protected void updateScrollX() {
/**
* Updates the table width when a resize operation occurs. This method is called continuously when the control width
* is resizing in order to properly clip this {@code TableHeaderRow}. Overriding this method allows a subclass to
* customize the resizing behavior.
* <p>
* Normally, the {@code TableHeaderRow} is using the full space ({@code TableView} width), but in some cases that
* space may be reduced. For example, if a vertical header that will display the row number is introduced, the
* {@code TableHeaderRow} would need to be clipped a bit shorter in order not to overlap that vertical header.
* Calling {@code super()} first when overriding this method allows {@link #getClip()} to compute the right width in
* order apply a transformation.
*
* @since 12
*/
protected void updateTableWidth() {
/**
* Returns the current {@link TableColumnHeader} being moved during reordering.
*
* @return the current {@code TableColumnHeader} being moved
* @since 12
*/
protected TableColumnHeader getReorderingRegion() {
/**
* Sets the {@code TableColumnHeader} that is being moved during a reordering operation. This is automatically set
* by the {@code TableColumnHeader} when reordering starts. This method should only be called manually if the
* default reordering behavior is overridden. Calling {@link #setReordering(boolean)} after the call is required.
*
* @param reorderingRegion the {@code TableColumnHeader} being reordered
* @since 12
*/
protected void setReorderingRegion(TableColumnHeader reorderingRegion) {
javafx/scene/control/skin/TableViewSkinBase.java
:
/**
* Returns the {@code TableHeaderRow} created using {@link #createTableHeaderRow()}.
*
* @return the {@code TableHeaderRow} for this {@code TableViewSkinBase}
* @since 12
*/
protected TableHeaderRow getTableHeaderRow() {
/**
* Called when the focus is set on the cell above the current focused cell in order to scroll to it to make it
* visible.
*
* @since 12
*/
protected void onFocusAboveCell() {
/**
* Called when the focus is set on the cell below the current focused cell in order to scroll to it to make it
* visible.
*
* @since 12
*/
protected void onFocusBelowCell() {
/**
* Called when the selection is set on the the cell above the current focused cell in order to scroll to it to make
* it visible.
*
* @since 12
*/
protected void onSelectAboveCell() {
/**
* Called when the selection is set on the cell below the current focused cell in order to scroll to it to make it
* visible.
*
* @since 12
*/
protected void onSelectBelowCell() {
/**
* Called when the selection is set on the left cell of the current selected one in order to horizontally scroll to
* it to make it visible.
*
* @since 12
*/
protected void onSelectLeftCell() {
/**
* Called when the selection is set on the right cell of the current selected one in order to horizontally scroll to
* it to make it visible.
*
* @since 12
*/
protected void onSelectRightCell() {
/**
* Called when the focus is set on the left cell of the current selected one in order to horizontally scroll to it
* to make it visible.
*
* @since 12
*/
protected void onFocusLeftCell() {
/**
* Called when the focus is set on the right cell of the current selected one in order to horizontally scroll to it
* to make it visible.
*
* @since 12
*/
protected void onFocusRightCell() {
/**
* Called when the selection is set on the first cell of the table (first row and first column) in order to scroll
* to it to make it visible.
*
* @since 12
*/
protected void onMoveToFirstCell() {
/**
* Called when the selection is set on the last cell of the table (last row and last column) in order to scroll to
* it to make it visible.
*
* @since 12
*/
protected void onMoveToLastCell() {
/**
* Returns the index of the selected (or focused, if {@code isFocusDriven} is {@code true}) cell after a page scroll
* operation. If the selected/focused cell is not the last fully visible cell, then the last fully visible cell is
* selected/focused. Otherwise, the content is scrolled such that the cell is made visible at the top of the
* viewport (and the new last fully visible cell is selected/focused instead).
*
* @param isFocusDriven {@code true} if focused cell should be considered over selection
* @return the new index to select, or to focus if {@code isFocusDriven} is {@code true}
* @since 12
*/
protected int onScrollPageDown(boolean isFocusDriven) {
/**
* Returns the index of the selected (or focused, if {@code isFocusDriven} is {@code true}) cell after a page scroll
* operation. If the selected/focused cell is not the first fully visible cell, then the first fully visible cell is
* selected/focused. Otherwise, the content is scrolled such that the cell is made visible at the bottom of the
* viewport (and the new first fully visible cell is selected/focused instead).
*
* @param isFocusDriven {@code true} if focused cell should be considered over selection
* @return the new index to select, or to focus if {@code isFocusDriven} is {@code true}
* @since 12
*/
protected int onScrollPageUp(boolean isFocusDriven) {
/**
* Scrolls to the column containing the current focused cell.
* <p>
* Handles the horizontal scrolling when the selection mode is cell-based and the newly selected cell belongs to a
* column which is not completely visible.
*
* @since 12
*/
public void scrollHorizontally() {
/**
* Programmatically scrolls to the given column. This call will ensure that the column is aligned on the left edge
* of the {@code TableView} and also that the columns don't become detached from the right edge of the table.
*
* @param col the column to scroll to
* @since 12
*/
protected void scrollHorizontally(TC col) {
javafx/scene/control/skin/VirtualFlow.java
:
/**
* Returns the scroll bar used for scrolling horizontally. A developer who needs to be notified when a scroll is
* happening could attach a listener to the {@link ScrollBar#valueProperty()}.
*
* @return the scroll bar used for scrolling horizontally
* @since 12
*/
protected final ScrollBar getHbar() {
/**
* Returns the scroll bar used for scrolling vertically. A developer who needs to be notified when a scroll is
* happening could attach a listener to the {@link ScrollBar#valueProperty()}. The {@link ScrollBar#getWidth()} is
* also useful when adding a component over the {@code TableView} in order to clip it so that it doesn't overlap the
* {@code ScrollBar}.
*
* @return the scroll bar used for scrolling vertically
* @since 12
*/
protected final ScrollBar getVbar() {
/**
* Resizes the given cell. If {@link #isVertical()} is set to {@code true}, the cell width will be the maximum
* between the viewport width and the sum of all the cells' {@code prefWidth}. The cell height will be computed by
* the cell itself unless {@code fixedCellSizeEnabled} is set to {@code true}, then {@link #getFixedCellSize()} is
* used. If {@link #isVertical()} is set to {@code false}, the width and height calculations are reversed.
*
* @param cell the cell to resize
* @since 12
*/
protected void resizeCell(T cell) {
/**
* Returns the list of cells displayed in the current viewport.
* <p>
* The cells are ordered such that the first cell in this list is the first in the view, and the last cell is the
* last in the view. When pixel scrolling, the list is simply shifted and items drop off the beginning or the end,
* depending on the order of scrolling.
*
* @return the cells displayed in the current viewport
* @since 12
*/
protected List<T> getCells() {
/**
* Returns the last visible cell whose bounds are entirely within the viewport. When manually inserting rows, one
* may need to know which cell indices are visible in the viewport.
*
* @return last visible cell whose bounds are entirely within the viewport
* @since 12
*/
protected T getLastVisibleCellWithinViewport() {
/**
* Returns the first visible cell whose bounds are entirely within the viewport. When manually inserting rows, one
* may need to know which cell indices are visible in the viewport.
*
* @return first visible cell whose bounds are entirely within the viewport
* @since 12
*/
protected T getFirstVisibleCellWithinViewport() {
/**
* Informs the {@code VirtualFlow} that a layout pass should be done, and the cell contents have not changed. For
* example, this might be called from a {@code TableView} or {@code ListView} when a layout is needed and no cells
* have been added or removed.
*
* @since 12
*/
protected void reconfigureCells() {
/**
* Informs the {@code VirtualFlow} that a layout pass should be done, and that the cell factory has changed. All
* cells in the viewport are recreated with the new cell factory.
*
* @since 12
*/
protected void recreateCells() {
/**
* Informs the {@code VirtualFlow} that a layout pass should be done, and cell contents have changed. All cells are
* removed and then added properly in the viewport.
*
* @since 12
*/
protected void rebuildCells() {
/**
* Informs the {@code VirtualFlow} that a layout pass should be done and only the cell layout will be requested.
*
* @since 12
*/
protected void requestCellLayout() {
/**
* Creates and returns a new cell for the given index.
* <p>
* If the requested index is not already an existing visible cell, it will create a cell for the given index and
* insert it into the {@code VirtualFlow} container. If the index exists, simply returns the visible cell. From that
* point on, it will be unmanaged, and is up to the caller of this method to manage it.
* <p>
* This is useful if a row that should not be visible must be accessed (a row that always stick to the top for
* example). It can then be easily created, correctly initialized and inserted in the {@code VirtualFlow}
* container.
*
* @param index the cell index
* @return a cell for the given index inserted in the VirtualFlow container
* @since 12
*/
protected T getPrivateCell(int index) {
- csr of
-
JDK-8207942 Add new protected VirtualFlow methods for subclassing
- Resolved