modules/controls/src/main/java/javafx/scene/control/skin/NestedTableColumnHeader.java
Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -21,12 +21,13 @@
* 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 com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
import javafx.collections.WeakListChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -49,10 +50,14 @@
* that every TableView header is nested - even if it isn't. This allows for us
* to use the same code for building a single row of TableColumns as we would
* with a heavily nested sequences of TableColumns. Because of this, the
* TableHeaderRow class consists of just one instance of a NestedTableColumnHeader.
*
+ * @since 9
+ * @see TableColumnHeader
+ * @see TableHeaderRow
+ * @see TableColumnBase
*/
public class NestedTableColumnHeader extends TableColumnHeader {
/***************************************************************************
* *
@@ -80,10 +85,11 @@
private ObservableList<? extends TableColumnBase> columns;
private TableColumnHeader label;
private ObservableList<TableColumnHeader> columnHeaders;
+ private ObservableList<TableColumnHeader> unmodifiableColumnHeaders;
// used for column resizing
private double lastX = 0.0F;
private double dragAnchorX = 0.0;
@@ -98,10 +104,17 @@
* *
* Constructor *
* *
**************************************************************************/
+ /**
+ * Creates a new NestedTableColumnHeader instance to visually represent the given
+ * {@link TableColumnBase} instance.
+ *
+ * @param skin The skin used by the UI control.
+ * @param tc The table column to be visually represented by this instance.
+ */
public NestedTableColumnHeader(final TableViewSkinBase skin, final TableColumnBase tc) {
super(skin, tc);
getStyleClass().setAll("nested-column-header");
setFocusTraversable(false);
@@ -111,14 +124,15 @@
label.setTableHeaderRow(getTableHeaderRow());
label.setParentHeader(getParentHeader());
label.setNestedColumnHeader(this);
if (getTableColumn() != null) {
- changeListenerHandler.registerChangeListener(getTableColumn().textProperty(), "TABLE_COLUMN_TEXT");
+ changeListenerHandler.registerChangeListener(getTableColumn().textProperty(), e ->
+ label.setVisible(getTableColumn().getText() != null && ! getTableColumn().getText().isEmpty()));
}
- changeListenerHandler.registerChangeListener(skin.columnResizePolicyProperty(), "TABLE_VIEW_COLUMN_RESIZE_POLICY");
+ changeListenerHandler.registerChangeListener(skin.columnResizePolicyProperty(), e -> updateContent());
}
/***************************************************************************
@@ -132,12 +146,11 @@
};
private final WeakListChangeListener weakColumnsListener =
new WeakListChangeListener(columnsListener);
- private static final EventHandler<MouseEvent> rectMousePressed = new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
+ private static final EventHandler<MouseEvent> rectMousePressed = me -> {
Rectangle rect = (Rectangle) me.getSource();
TableColumnBase column = (TableColumnBase) rect.getProperties().get(TABLE_COLUMN_KEY);
NestedTableColumnHeader header = (NestedTableColumnHeader) rect.getProperties().get(TABLE_COLUMN_HEADER_KEY);
if (! header.isColumnResizingEnabled()) return;
@@ -153,82 +166,195 @@
double startX = header.getTableHeaderRow().sceneToLocal(innerRect.localToScene(innerRect.getBoundsInLocal())).getMinX() + 2;
header.dragAnchorX = me.getSceneX();
header.columnResizingStarted(startX);
}
me.consume();
- }
};
- private static final EventHandler<MouseEvent> rectMouseDragged = new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
+ private static final EventHandler<MouseEvent> rectMouseDragged = me -> {
Rectangle rect = (Rectangle) me.getSource();
TableColumnBase column = (TableColumnBase) rect.getProperties().get(TABLE_COLUMN_KEY);
NestedTableColumnHeader header = (NestedTableColumnHeader) rect.getProperties().get(TABLE_COLUMN_HEADER_KEY);
if (! header.isColumnResizingEnabled()) return;
header.columnResizing(column, me);
me.consume();
- }
};
- private static final EventHandler<MouseEvent> rectMouseReleased = new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
+ private static final EventHandler<MouseEvent> rectMouseReleased = me -> {
Rectangle rect = (Rectangle) me.getSource();
TableColumnBase column = (TableColumnBase) rect.getProperties().get(TABLE_COLUMN_KEY);
NestedTableColumnHeader header = (NestedTableColumnHeader) rect.getProperties().get(TABLE_COLUMN_HEADER_KEY);
if (! header.isColumnResizingEnabled()) return;
header.columnResizingComplete(column, me);
me.consume();
- }
};
- private static final EventHandler<MouseEvent> rectCursorChangeListener = new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
+ private static final EventHandler<MouseEvent> rectCursorChangeListener = me -> {
Rectangle rect = (Rectangle) me.getSource();
TableColumnBase column = (TableColumnBase) rect.getProperties().get(TABLE_COLUMN_KEY);
NestedTableColumnHeader header = (NestedTableColumnHeader) rect.getProperties().get(TABLE_COLUMN_HEADER_KEY);
if (header.getCursor() == null) { // If there's a cursor for the whole header, don't override it
rect.setCursor(header.isColumnResizingEnabled() && rect.isHover() &&
column.isResizable() ? Cursor.H_RESIZE : null);
}
- }
};
/***************************************************************************
* *
* Public Methods *
* *
**************************************************************************/
- @Override protected void handlePropertyChanged(String p) {
- super.handlePropertyChanged(p);
+ /** {@inheritDoc} */
+ @Override void dispose() {
+ super.dispose();
- if ("TABLE_VIEW_COLUMN_RESIZE_POLICY".equals(p)) {
- updateContent();
- } else if ("TABLE_COLUMN_TEXT".equals(p)) {
- label.setVisible(getTableColumn().getText() != null && ! getTableColumn().getText().isEmpty());
+ if (label != null) {
+ label.dispose();
+ }
+
+ if (getColumns() != null) {
+ getColumns().removeListener(weakColumnsListener);
+ }
+
+ for (int i = 0; i < getColumnHeaders().size(); i++) {
+ TableColumnHeader header = getColumnHeaders().get(i);
+ header.dispose();
+ }
+
+ for (Rectangle rect : dragRects.values()) {
+ if (rect != null) {
+ rect.visibleProperty().unbind();
+ }
+ }
+ dragRects.clear();
+ getChildren().clear();
+
+ changeListenerHandler.dispose();
+ }
+
+ /**
+ * Returns an unmodifiable list of the {@link TableColumnHeader} instances
+ * that are children of this NestedTableColumnHeader.
+ */
+ @ReturnsUnmodifiableCollection
+ public final ObservableList<TableColumnHeader> getColumnHeaders() {
+ if (columnHeaders == null) {
+ columnHeaders = FXCollections.<TableColumnHeader>observableArrayList();
+ unmodifiableColumnHeaders = FXCollections.unmodifiableObservableList(columnHeaders);
}
+ return unmodifiableColumnHeaders;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void layoutChildren() {
+ double w = getWidth() - snappedLeftInset() - snappedRightInset();
+ double h = getHeight() - snappedTopInset() - snappedBottomInset();
+
+ int labelHeight = (int) label.prefHeight(-1);
+
+ if (label.isVisible()) {
+ // label gets to span whole width and sits at top
+ label.resize(w, labelHeight);
+ label.relocate(snappedLeftInset(), snappedTopInset());
}
- @Override public void setTableHeaderRow(TableHeaderRow header) {
+ // children columns need to share the total available width
+ double x = snappedLeftInset();
+ int pos = 0;
+ for (int i = 0, max = getColumnHeaders().size(); i < max; i++) {
+ TableColumnHeader n = getColumnHeaders().get(i);
+ if (! n.isVisible()) continue;
+
+ double prefWidth = snapSize(n.prefWidth(-1));
+// double prefHeight = n.prefHeight(-1);
+
+ // position the column header in the default location...
+ n.resize(prefWidth, snapSize(h - labelHeight));
+ n.relocate(x, labelHeight + snappedTopInset());
+
+// // ...but, if there are no children of this column, we should ensure
+// // that it is resized vertically such that it goes to the very
+// // bottom of the table header row.
+// if (getTableHeaderRow() != null && n.getCol().getColumns().isEmpty()) {
+// Bounds bounds = getTableHeaderRow().sceneToLocal(n.localToScene(n.getBoundsInLocal()));
+// prefHeight = getTableHeaderRow().getHeight() - bounds.getMinY();
+// n.resize(prefWidth, prefHeight);
+// }
+
+ // shuffle along the x-axis appropriately
+ x += prefWidth;
+
+ // position drag overlay to intercept column resize requests
+ Rectangle dragRect = dragRects.get(n.getTableColumn());
+ if (dragRect != null) {
+ dragRect.setHeight(n.getDragRectHeight());
+ dragRect.relocate(x - DRAG_RECT_WIDTH / 2, snappedTopInset() + labelHeight);
+ }
+ }
+ }
+
+ // sum up all children columns
+ /** {@inheritDoc} */
+ @Override protected double computePrefWidth(double height) {
+ checkState();
+
+ double width = 0.0F;
+
+ if (getColumns() != null) {
+ for (TableColumnHeader c : getColumnHeaders()) {
+ if (c.isVisible()) {
+ width += snapSize(c.computePrefWidth(height));
+ }
+ }
+ }
+
+ return width;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected double computePrefHeight(double width) {
+ checkState();
+
+ double height = 0.0F;
+
+ if (getColumnHeaders() != null) {
+ for (TableColumnHeader n : getColumnHeaders()) {
+ height = Math.max(height, n.prefHeight(-1));
+ }
+ }
+
+ return height + label.prefHeight(-1) + snappedTopInset() + snappedBottomInset();
+ }
+
+
+
+ /***************************************************************************
+ * *
+ * Private Implementation *
+ * *
+ **************************************************************************/
+
+ @Override void setTableHeaderRow(TableHeaderRow header) {
super.setTableHeaderRow(header);
label.setTableHeaderRow(header);
// tell all children columns what TableHeader they belong to
for (TableColumnHeader c : getColumnHeaders()) {
c.setTableHeaderRow(header);
}
}
- @Override public void setParentHeader(NestedTableColumnHeader parentHeader) {
+ @Override void setParentHeader(NestedTableColumnHeader parentHeader) {
super.setParentHeader(parentHeader);
label.setParentHeader(parentHeader);
}
ObservableList<? extends TableColumnBase> getColumns() {
@@ -275,11 +401,11 @@
if (index >= 0 && index < parentColumnHeaders.size()) {
parentColumnHeaders.set(index, createColumnHeader(getTableColumn()));
}
} else {
// otherwise just remove all the columns
- getColumnHeaders().clear();
+ columnHeaders.clear();
}
} else {
List<TableColumnHeader> oldHeaders = new ArrayList<>(getColumnHeaders());
List<TableColumnHeader> newHeaders = new ArrayList<>();
@@ -302,11 +428,11 @@
if (!found) {
newHeaders.add(createColumnHeader(column));
}
}
- getColumnHeaders().setAll(newHeaders);
+ columnHeaders.setAll(newHeaders);
// dispose all old headers
oldHeaders.removeAll(newHeaders);
for (int i = 0; i < oldHeaders.size(); i++) {
oldHeaders.get(i).dispose();
@@ -320,134 +446,22 @@
for (TableColumnHeader header : getColumnHeaders()) {
header.applyCss();
}
}
- @Override void dispose() {
- super.dispose();
-
- if (label != null) {
- label.dispose();
- }
-
- if (getColumns() != null) {
- getColumns().removeListener(weakColumnsListener);
- }
-
- for (int i = 0; i < getColumnHeaders().size(); i++) {
- TableColumnHeader header = getColumnHeaders().get(i);
- header.dispose();
- }
-
- for (Rectangle rect : dragRects.values()) {
- if (rect != null) {
- rect.visibleProperty().unbind();
- }
- }
- dragRects.clear();
- getChildren().clear();
-
- changeListenerHandler.dispose();
- }
-
- public ObservableList<TableColumnHeader> getColumnHeaders() {
- if (columnHeaders == null) columnHeaders = FXCollections.<TableColumnHeader>observableArrayList();
- return columnHeaders;
- }
-
- @Override protected void layoutChildren() {
- double w = getWidth() - snappedLeftInset() - snappedRightInset();
- double h = getHeight() - snappedTopInset() - snappedBottomInset();
-
- int labelHeight = (int) label.prefHeight(-1);
-
- if (label.isVisible()) {
- // label gets to span whole width and sits at top
- label.resize(w, labelHeight);
- label.relocate(snappedLeftInset(), snappedTopInset());
- }
-
- // children columns need to share the total available width
- double x = snappedLeftInset();
- int pos = 0;
- for (int i = 0, max = getColumnHeaders().size(); i < max; i++) {
- TableColumnHeader n = getColumnHeaders().get(i);
- if (! n.isVisible()) continue;
-
- double prefWidth = snapSize(n.prefWidth(-1));
-// double prefHeight = n.prefHeight(-1);
-
- // position the column header in the default location...
- n.resize(prefWidth, snapSize(h - labelHeight));
- n.relocate(x, labelHeight + snappedTopInset());
-
-// // ...but, if there are no children of this column, we should ensure
-// // that it is resized vertically such that it goes to the very
-// // bottom of the table header row.
-// if (getTableHeaderRow() != null && n.getCol().getColumns().isEmpty()) {
-// Bounds bounds = getTableHeaderRow().sceneToLocal(n.localToScene(n.getBoundsInLocal()));
-// prefHeight = getTableHeaderRow().getHeight() - bounds.getMinY();
-// n.resize(prefWidth, prefHeight);
-// }
-
- // shuffle along the x-axis appropriately
- x += prefWidth;
-
- // position drag overlay to intercept column resize requests
- Rectangle dragRect = dragRects.get(n.getTableColumn());
- if (dragRect != null) {
- dragRect.setHeight(n.getDragRectHeight());
- dragRect.relocate(x - DRAG_RECT_WIDTH / 2, snappedTopInset() + labelHeight);
- }
- }
- }
-
- @Override
- double getDragRectHeight() {
+ /** {@inheritDoc} */
+ @Override double getDragRectHeight() {
return label.prefHeight(-1);
}
- // sum up all children columns
- @Override protected double computePrefWidth(double height) {
- checkState();
-
- double width = 0.0F;
-
- if (getColumns() != null) {
- for (TableColumnHeader c : getColumnHeaders()) {
- if (c.isVisible()) {
- width += snapSize(c.computePrefWidth(height));
- }
- }
- }
-
- return width;
- }
-
- @Override protected double computePrefHeight(double width) {
- checkState();
-
- double height = 0.0F;
-
- if (getColumnHeaders() != null) {
- for (TableColumnHeader n : getColumnHeaders()) {
- height = Math.max(height, n.prefHeight(-1));
- }
- }
-
- return height + label.prefHeight(-1) + snappedTopInset() + snappedBottomInset();
- }
-
- // protected to allow subclasses to customise the column header types
- protected TableColumnHeader createTableColumnHeader(TableColumnBase col) {
+ TableColumnHeader createTableColumnHeader(TableColumnBase col) {
return col.getColumns().isEmpty() ?
new TableColumnHeader(getTableViewSkin(), col) :
new NestedTableColumnHeader(getTableViewSkin(), col);
}
- // allowing subclasses to force an update on the headers
- protected void setHeadersNeedUpdate() {
+ void setHeadersNeedUpdate() {
updateColumns = true;
// go through children columns - they should update too
for (int i = 0; i < getColumnHeaders().size(); i++) {
TableColumnHeader header = getColumnHeaders().get(i);
@@ -456,18 +470,10 @@
}
}
requestLayout();
}
-
-
- /***************************************************************************
- * *
- * Private Implementation *
- * *
- **************************************************************************/
-
private void updateContent() {
// create a temporary list so we only do addAll into the main content
// observableArrayList once.
final List<Node> content = new ArrayList<Node>();
@@ -501,11 +507,11 @@
if (columns == null) {
return;
}
- final TableViewSkinBase<?,?,?,?,?,?> skin = getTableViewSkin();
+ final TableViewSkinBase<?,?,?,?,?> skin = getTableViewSkin();
Callback<ResizeFeaturesBase, Boolean> columnResizePolicy = skin.columnResizePolicyProperty().get();
boolean isConstrainedResize =
skin instanceof TableViewSkin ? TableView.CONSTRAINED_RESIZE_POLICY.equals(columnResizePolicy) :
skin instanceof TreeTableViewSkin ? TreeTableView.CONSTRAINED_RESIZE_POLICY.equals(columnResizePolicy) :
false;