Skip to content
Permalink
Browse files
8207957: TableSkinUtils should not contain actual code implementation
Reviewed-by: fastegal, kcr, aghaisas
  • Loading branch information
Hadzic Samir authored and kevinrushforth committed Dec 18, 2019
1 parent 4e005e4 commit 935e99d241fd971dc92c4357a5b170fb4ac29380
Showing 5 changed files with 423 additions and 191 deletions.
@@ -25,28 +25,28 @@

package javafx.scene.control.skin;

import com.sun.javafx.scene.control.skin.Utils;
import javafx.beans.property.ObjectProperty;
import javafx.collections.WeakListChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.NodeOrientation;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.ResizeFeaturesBase;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TableView;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Callback;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

/**
* <p>This class is used to construct the header of a TableView. We take the approach
* that every TableView header is nested - even if it isn't. This allows for us
@@ -165,7 +165,11 @@ public NestedTableColumnHeader(final TableColumnBase tc) {
if (me.getClickCount() == 2 && me.isPrimaryButtonDown()) {
// the user wants to resize the column such that its
// width is equal to the widest element in the column
TableSkinUtils.resizeColumnToFitContent(header.getTableSkin(), column, -1);
TableHeaderRow tableHeader = header.getTableHeaderRow();
TableColumnHeader columnHeader = tableHeader.getColumnHeaderFor(column);
if (columnHeader != null) {
columnHeader.resizeColumnToFitContent(-1);
}
} else {
// rather than refer to the rect variable, we just grab
// it from the source to prevent a small memory leak.
@@ -28,6 +28,8 @@
import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
import com.sun.javafx.scene.control.Properties;
import com.sun.javafx.scene.control.TableColumnBaseHelper;
import com.sun.javafx.scene.control.TreeTableViewBackingList;
import com.sun.javafx.scene.control.skin.Utils;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
@@ -40,6 +42,7 @@
import javafx.css.Styleable;
import javafx.css.StyleableDoubleProperty;
import javafx.css.StyleableProperty;
import javafx.css.converter.SizeConverter;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
@@ -50,22 +53,27 @@
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TableView;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.util.Callback;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import javafx.css.converter.SizeConverter;

import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeName;
import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeProperty;
import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.isAscending;
@@ -529,7 +537,7 @@ private void updateScene() {
if (getTableColumn() == null || getTableColumn().getWidth() != DEFAULT_COLUMN_WIDTH || getScene() == null) {
return;
}
doColumnAutoSize(getTableColumn(), n);
doColumnAutoSize(n);
autoSizeComplete = true;
}
}
@@ -581,13 +589,190 @@ private void initUI() {
}
}

private void doColumnAutoSize(TableColumnBase<?,?> column, int cellsToMeasure) {
double prefWidth = column.getPrefWidth();
private void doColumnAutoSize(int cellsToMeasure) {
double prefWidth = getTableColumn().getPrefWidth();

// if the prefWidth has been set, we do _not_ autosize columns
if (prefWidth == DEFAULT_COLUMN_WIDTH) {
TableSkinUtils.resizeColumnToFitContent(getTableSkin(), column, cellsToMeasure);
// getTableViewSkin().resizeColumnToFitContent(column, cellsToMeasure);
resizeColumnToFitContent(cellsToMeasure);
}
}

/**
* Resizes this {@code TableColumnHeader}'s column to fit the width of its content.
*
* @implSpec The resulting column width for this implementation is the maximum of the preferred width of the header
* cell and the preferred width of the first {@code maxRow} cells.
* <p>
* Subclasses can either use this method or override it (without the need to call {@code super()}) to provide their
* custom implementation (such as ones that exclude the header, exclude {@code null} content, compute the minimum
* width, etc.).
*
* @param maxRows the number of rows considered when resizing. If -1 is given, all rows are considered.
* @since 14
*/
protected void resizeColumnToFitContent(int maxRows) {
TableColumnBase<?, ?> tc = getTableColumn();
if (!tc.isResizable()) return;

Object control = this.getTableSkin().getSkinnable();
if (control instanceof TableView) {
resizeColumnToFitContent((TableView) control, (TableColumn) tc, this.getTableSkin(), maxRows);
} else if (control instanceof TreeTableView) {
resizeColumnToFitContent((TreeTableView) control, (TreeTableColumn) tc, this.getTableSkin(), maxRows);
}
}

private <T,S> void resizeColumnToFitContent(TableView<T> tv, TableColumn<T, S> tc, TableViewSkinBase tableSkin, int maxRows) {
List<?> items = tv.getItems();
if (items == null || items.isEmpty()) return;

Callback/*<TableColumn<T, ?>, TableCell<T,?>>*/ cellFactory = tc.getCellFactory();
if (cellFactory == null) return;

TableCell<T,?> cell = (TableCell<T, ?>) cellFactory.call(tc);
if (cell == null) return;

// set this property to tell the TableCell we want to know its actual
// preferred width, not the width of the associated TableColumnBase
cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);

// determine cell padding
double padding = 10;
Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
if (n instanceof Region) {
Region r = (Region) n;
padding = r.snappedLeftInset() + r.snappedRightInset();
}

int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
double maxWidth = 0;
for (int row = 0; row < rows; row++) {
cell.updateTableColumn(tc);
cell.updateTableView(tv);
cell.updateIndex(row);

if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
tableSkin.getChildren().add(cell);
cell.applyCss();
maxWidth = Math.max(maxWidth, cell.prefWidth(-1));
tableSkin.getChildren().remove(cell);
}
}

// dispose of the cell to prevent it retaining listeners (see RT-31015)
cell.updateIndex(-1);

// RT-36855 - take into account the column header text / graphic widths.
// Magic 10 is to allow for sort arrow to appear without text truncation.
TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
Node graphic = header.label.getGraphic();
double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
maxWidth = Math.max(maxWidth, headerWidth);

// RT-23486
maxWidth += padding;
if (tv.getColumnResizePolicy() == TableView.CONSTRAINED_RESIZE_POLICY && tv.getWidth() > 0) {
if (maxWidth > tc.getMaxWidth()) {
maxWidth = tc.getMaxWidth();
}

int size = tc.getColumns().size();
if (size > 0) {
TableColumnHeader columnHeader = getTableHeaderRow().getColumnHeaderFor(tc.getColumns().get(size - 1));
if (columnHeader != null) {
columnHeader.resizeColumnToFitContent(maxRows);
}
return;
}

TableSkinUtils.resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
} else {
TableColumnBaseHelper.setWidth(tc, maxWidth);
}
}

private <T,S> void resizeColumnToFitContent(TreeTableView<T> ttv, TreeTableColumn<T, S> tc, TableViewSkinBase tableSkin, int maxRows) {
List<?> items = new TreeTableViewBackingList(ttv);
if (items == null || items.isEmpty()) return;

Callback cellFactory = tc.getCellFactory();
if (cellFactory == null) return;

TreeTableCell<T,S> cell = (TreeTableCell) cellFactory.call(tc);
if (cell == null) return;

// set this property to tell the TableCell we want to know its actual
// preferred width, not the width of the associated TableColumnBase
cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);

// determine cell padding
double padding = 10;
Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
if (n instanceof Region) {
Region r = (Region) n;
padding = r.snappedLeftInset() + r.snappedRightInset();
}

TreeTableRow<T> treeTableRow = new TreeTableRow<>();
treeTableRow.updateTreeTableView(ttv);

int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
double maxWidth = 0;
for (int row = 0; row < rows; row++) {
treeTableRow.updateIndex(row);
treeTableRow.updateTreeItem(ttv.getTreeItem(row));

cell.updateTreeTableColumn(tc);
cell.updateTreeTableView(ttv);
cell.updateTreeTableRow(treeTableRow);
cell.updateIndex(row);

if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
tableSkin.getChildren().add(cell);
cell.applyCss();

double w = cell.prefWidth(-1);

maxWidth = Math.max(maxWidth, w);
tableSkin.getChildren().remove(cell);
}
}

// dispose of the cell to prevent it retaining listeners (see RT-31015)
cell.updateIndex(-1);

// RT-36855 - take into account the column header text / graphic widths.
// Magic 10 is to allow for sort arrow to appear without text truncation.
TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
Node graphic = header.label.getGraphic();
double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
maxWidth = Math.max(maxWidth, headerWidth);

// RT-23486
maxWidth += padding;
if (ttv.getColumnResizePolicy() == TreeTableView.CONSTRAINED_RESIZE_POLICY && ttv.getWidth() > 0) {

if (maxWidth > tc.getMaxWidth()) {
maxWidth = tc.getMaxWidth();
}

int size = tc.getColumns().size();
if (size > 0) {
TableColumnHeader columnHeader = getTableHeaderRow().getColumnHeaderFor(tc.getColumns().get(size - 1));
if (columnHeader != null) {
columnHeader.resizeColumnToFitContent(maxRows);
}
return;
}

TableSkinUtils.resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
} else {
TableColumnBaseHelper.setWidth(tc, maxWidth);
}
}

0 comments on commit 935e99d

Please sign in to comment.