modules/controls/src/main/java/javafx/scene/control/skin/TableRowSkinBase.java
Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
*** 21,31 ****
* 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.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
--- 21,31 ----
* 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.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
*** 45,61 ****
import javafx.scene.control.Control;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.TableColumnBase;
import javafx.util.Duration;
- import com.sun.javafx.scene.control.behavior.CellBehaviorBase;
import com.sun.javafx.tk.Toolkit;
public abstract class TableRowSkinBase<T,
C extends IndexedCell/*<T>*/,
! B extends CellBehaviorBase<C>,
! R extends IndexedCell> extends CellSkinBase<C,B> {
/***************************************************************************
* *
* Static Fields *
* *
--- 45,76 ----
import javafx.scene.control.Control;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.TableColumnBase;
import javafx.util.Duration;
import com.sun.javafx.tk.Toolkit;
+ /**
+ * TableRowSkinBase is the base skin class used by controls such as
+ * {@link javafx.scene.control.TableRow} and {@link javafx.scene.control.TreeTableRow}
+ * (the concrete classes are {@link TableRowSkin} and {@link TreeTableRowSkin},
+ * respectively).
+ *
+ * @param <T> The type of the cell (i.e. the generic type of the {@link IndexedCell} subclass).
+ * @param <C> The cell type (e.g. TableRow or TreeTableRow)
+ * @param <R> The type of cell that is contained within each row (e.g.
+ * {@link javafx.scene.control.TableCell or {@link javafx.scene.control.TreeTableCell}}).
+ *
+ * @since 9
+ * @see javafx.scene.control.TableRow
+ * @see javafx.scene.control.TreeTableRow
+ * @see TableRowSkin
+ * @see TreeTableRowSkin
+ */
public abstract class TableRowSkinBase<T,
C extends IndexedCell/*<T>*/,
! R extends IndexedCell> extends CellSkinBase<C> {
/***************************************************************************
* *
* Static Fields *
* *
*** 107,125 ****
* Note that this means that it is possible for this map to therefore be
* a memory leak if an application uses TableView and is creating and removing
* a large number of tableColumns. This is mitigated in the recreateCells()
* function below - refer to that to learn more.
*/
! protected WeakHashMap<TableColumnBase, Reference<R>> cellsMap;
// This observableArrayList contains the currently visible table cells for this row.
! protected final List<R> cells = new ArrayList<R>();
private int fullRefreshCounter = DEFAULT_FULL_REFRESH_COUNTER;
! protected boolean isDirty = false;
! protected boolean updateCells = false;
private double fixedCellSize;
private boolean fixedCellSizeEnabled;
--- 122,140 ----
* Note that this means that it is possible for this map to therefore be
* a memory leak if an application uses TableView and is creating and removing
* a large number of tableColumns. This is mitigated in the recreateCells()
* function below - refer to that to learn more.
*/
! WeakHashMap<TableColumnBase, Reference<R>> cellsMap;
// This observableArrayList contains the currently visible table cells for this row.
! final List<R> cells = new ArrayList<>();
private int fullRefreshCounter = DEFAULT_FULL_REFRESH_COUNTER;
! boolean isDirty = false;
! boolean updateCells = false;
private double fixedCellSize;
private boolean fixedCellSizeEnabled;
*** 128,148 ****
* *
* Constructors *
* *
**************************************************************************/
! public TableRowSkinBase(C control, B behavior) {
! super(control, behavior);
!
! // init(control) should not be called here - it should be called by the
! // subclass after initialising itself. This is to prevent NPEs (for
! // example, getVisibleLeafColumns() throws a NPE as the control itself
! // is not yet set in subclasses).
! }
!
! // init isn't a constructor, but it is part of the initialisation routine
! protected void init(C control) {
getSkinnable().setPickOnBounds(false);
recreateCells();
updateCells(true);
--- 143,161 ----
* *
* Constructors *
* *
**************************************************************************/
! /**
! * Creates a new instance of TableRowSkinBase, although note that this
! * instance does not handle any behavior / input mappings - this needs to be
! * handled appropriately by subclasses.
! *
! * @param control The control that this skin should be installed onto.
! */
! public TableRowSkinBase(C control) {
! super(control);
getSkinnable().setPickOnBounds(false);
recreateCells();
updateCells(true);
*** 155,168 ****
// use invalidation listener here to update even when item equality is true
// (e.g. see RT-22463)
control.itemProperty().addListener(o -> requestCellUpdate());
! registerChangeListener(control.indexProperty(), "INDEX");
if (fixedCellSizeProperty() != null) {
! registerChangeListener(fixedCellSizeProperty(), "FIXED_CELL_SIZE");
fixedCellSize = fixedCellSizeProperty().get();
fixedCellSizeEnabled = fixedCellSize > 0;
}
}
--- 168,192 ----
// use invalidation listener here to update even when item equality is true
// (e.g. see RT-22463)
control.itemProperty().addListener(o -> requestCellUpdate());
! registerChangeListener(control.indexProperty(), e -> {
! // Fix for RT-36661, where empty table cells were showing content, as they
! // had incorrect table cell indices (but the table row index was correct).
! // Note that we only do the update on empty cells to avoid the issue
! // noted below in requestCellUpdate().
! if (getSkinnable().isEmpty()) {
! requestCellUpdate();
! }
! });
if (fixedCellSizeProperty() != null) {
! registerChangeListener(fixedCellSizeProperty(), e -> {
! fixedCellSize = fixedCellSizeProperty().get();
! fixedCellSizeEnabled = fixedCellSize > 0;
! });
fixedCellSize = fixedCellSizeProperty().get();
fixedCellSizeEnabled = fixedCellSize > 0;
}
}
*** 194,246 ****
* Returns the graphic to draw on the inside of the disclosure node. Null
* is acceptable when no graphic should be shown. Commonly this is the
* graphic associated with a TreeItem (i.e. treeItem.getGraphic()), rather
* than a graphic associated with a cell.
*/
! protected abstract ObjectProperty<Node> graphicProperty();
// return TableView / TreeTableView / etc
! protected abstract Control getVirtualFlowOwner();
! protected abstract ObservableList<? extends TableColumnBase/*<T,?>*/> getVisibleLeafColumns();
// cell.updateTableRow(skinnable); (i.e cell.updateTableRow(row))
! protected abstract void updateCell(R cell, C row);
! protected abstract DoubleProperty fixedCellSizeProperty();
! protected abstract boolean isColumnPartiallyOrFullyVisible(TableColumnBase tc);
! protected abstract R getCell(TableColumnBase tc);
! protected abstract TableColumnBase<T,?> getTableColumnBase(R cell);
/***************************************************************************
* *
* Public Methods *
* *
**************************************************************************/
! @Override protected void handleControlPropertyChanged(String p) {
! super.handleControlPropertyChanged(p);
!
! if ("INDEX".equals(p)) {
! // Fix for RT-36661, where empty table cells were showing content, as they
! // had incorrect table cell indices (but the table row index was correct).
! // Note that we only do the update on empty cells to avoid the issue
! // noted below in requestCellUpdate().
! if (getSkinnable().isEmpty()) {
! requestCellUpdate();
! }
! } else if ("FIXED_CELL_SIZE".equals(p)) {
! fixedCellSize = fixedCellSizeProperty().get();
! fixedCellSizeEnabled = fixedCellSize > 0;
! }
! }
!
@Override protected void layoutChildren(double x, final double y, final double w, final double h) {
checkState();
if (cellsMap.isEmpty()) return;
ObservableList<? extends TableColumnBase> visibleLeafColumns = getVisibleLeafColumns();
--- 218,254 ----
* Returns the graphic to draw on the inside of the disclosure node. Null
* is acceptable when no graphic should be shown. Commonly this is the
* graphic associated with a TreeItem (i.e. treeItem.getGraphic()), rather
* than a graphic associated with a cell.
*/
! abstract ObjectProperty<Node> graphicProperty();
// return TableView / TreeTableView / etc
! abstract Control getVirtualFlowOwner();
! abstract ObservableList<? extends TableColumnBase/*<T,?>*/> getVisibleLeafColumns();
// cell.updateTableRow(skinnable); (i.e cell.updateTableRow(row))
! abstract void updateCell(R cell, C row);
! abstract DoubleProperty fixedCellSizeProperty();
! abstract boolean isColumnPartiallyOrFullyVisible(TableColumnBase tc);
! abstract R getCell(TableColumnBase tc);
! abstract TableColumnBase<T,?> getTableColumnBase(R cell);
/***************************************************************************
* *
* Public Methods *
* *
**************************************************************************/
! /** {@inheritDoc} */
@Override protected void layoutChildren(double x, final double y, final double w, final double h) {
checkState();
if (cellsMap.isEmpty()) return;
ObservableList<? extends TableColumnBase> visibleLeafColumns = getVisibleLeafColumns();
*** 440,497 ****
x += width;
}
}
! protected int getIndentationLevel(C control) {
return 0;
}
! protected double getIndentationPerLevel() {
return 0;
}
/**
* Used to represent whether the current virtual flow owner is wanting
* indentation to be used in this table row.
*/
! protected boolean isIndentationRequired() {
return false;
}
/**
* Returns the table column that should show the disclosure nodes and / or
* a graphic. By default this is the left-most column.
*/
! protected TableColumnBase getTreeColumn() {
return null;
}
! protected Node getDisclosureNode() {
return null;
}
/**
* Used to represent whether a disclosure node is visible for _this_
* table row. Not to be confused with isIndentationRequired(), which is the
* more general API.
*/
! protected boolean isDisclosureNodeVisible() {
return false;
}
! protected boolean isShowRoot() {
return true;
}
! protected TableColumnBase<T,?> getVisibleLeafColumn(int column) {
final List<? extends TableColumnBase/*<T,?>*/> visibleLeafColumns = getVisibleLeafColumns();
if (column < 0 || column >= visibleLeafColumns.size()) return null;
return visibleLeafColumns.get(column);
}
! protected void updateCells(boolean resetChildren) {
// To avoid a potential memory leak (when the TableColumns in the
// TableView are created/inserted/removed/deleted, we have a 'refresh
// counter' that when we reach 0 will delete all cells in this row
// and recreate all of them.
if (resetChildren) {
--- 448,505 ----
x += width;
}
}
! int getIndentationLevel(C control) {
return 0;
}
! double getIndentationPerLevel() {
return 0;
}
/**
* Used to represent whether the current virtual flow owner is wanting
* indentation to be used in this table row.
*/
! boolean isIndentationRequired() {
return false;
}
/**
* Returns the table column that should show the disclosure nodes and / or
* a graphic. By default this is the left-most column.
*/
! TableColumnBase getTreeColumn() {
return null;
}
! Node getDisclosureNode() {
return null;
}
/**
* Used to represent whether a disclosure node is visible for _this_
* table row. Not to be confused with isIndentationRequired(), which is the
* more general API.
*/
! boolean isDisclosureNodeVisible() {
return false;
}
! boolean isShowRoot() {
return true;
}
! TableColumnBase<T,?> getVisibleLeafColumn(int column) {
final List<? extends TableColumnBase/*<T,?>*/> visibleLeafColumns = getVisibleLeafColumns();
if (column < 0 || column >= visibleLeafColumns.size()) return null;
return visibleLeafColumns.get(column);
}
! void updateCells(boolean resetChildren) {
// To avoid a potential memory leak (when the TableColumns in the
// TableView are created/inserted/removed/deleted, we have a 'refresh
// counter' that when we reach 0 will delete all cells in this row
// and recreate all of them.
if (resetChildren) {
*** 572,582 ****
// Support for RT-18467: making it easier to specify a height for
// cells via CSS, where the desired height is less than the height
// of the TableCells. Essentially, -fx-cell-size is given higher
// precedence now
! if (getCellSize() < CellSkinBase.DEFAULT_CELL_SIZE) {
return getCellSize();
}
// FIXME according to profiling, this method is slow and should
// be optimised
--- 580,590 ----
// Support for RT-18467: making it easier to specify a height for
// cells via CSS, where the desired height is less than the height
// of the TableCells. Essentially, -fx-cell-size is given higher
// precedence now
! if (getCellSize() < DEFAULT_CELL_SIZE) {
return getCellSize();
}
// FIXME according to profiling, this method is slow and should
// be optimised
*** 601,611 ****
// Support for RT-18467: making it easier to specify a height for
// cells via CSS, where the desired height is less than the height
// of the TableCells. Essentially, -fx-cell-size is given higher
// precedence now
! if (getCellSize() < CellSkinBase.DEFAULT_CELL_SIZE) {
return getCellSize();
}
// FIXME according to profiling, this method is slow and should
// be optimised
--- 609,619 ----
// Support for RT-18467: making it easier to specify a height for
// cells via CSS, where the desired height is less than the height
// of the TableCells. Essentially, -fx-cell-size is given higher
// precedence now
! if (getCellSize() < DEFAULT_CELL_SIZE) {
return getCellSize();
}
// FIXME according to profiling, this method is slow and should
// be optimised
*** 623,634 ****
return fixedCellSize;
}
return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);
}
! // protected to allow subclasses to ensure a consistent state during layout
! protected final void checkState() {
if (isDirty) {
updateCells(true);
isDirty = false;
} else if (updateCells) {
updateCells(false);
--- 631,641 ----
return fixedCellSize;
}
return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);
}
! final void checkState() {
if (isDirty) {
updateCells(true);
isDirty = false;
} else if (updateCells) {
updateCells(false);