Skip to content

Commit 935e99d

Browse files
Hadzic Samirkevinrushforth
Hadzic Samir
authored andcommitted
8207957: TableSkinUtils should not contain actual code implementation
Reviewed-by: fastegal, kcr, aghaisas
1 parent 4e005e4 commit 935e99d

File tree

5 files changed

+423
-191
lines changed

5 files changed

+423
-191
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/skin/NestedTableColumnHeader.java

+15-11
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,28 @@
2525

2626
package javafx.scene.control.skin;
2727

28-
import com.sun.javafx.scene.control.skin.Utils;
29-
import javafx.beans.property.ObjectProperty;
30-
import javafx.collections.WeakListChangeListener;
31-
import java.util.ArrayList;
32-
import java.util.List;
33-
import java.util.Map;
34-
import java.util.WeakHashMap;
35-
import java.util.concurrent.atomic.AtomicBoolean;
36-
3728
import javafx.collections.FXCollections;
3829
import javafx.collections.ListChangeListener;
3930
import javafx.collections.ObservableList;
31+
import javafx.collections.WeakListChangeListener;
4032
import javafx.event.EventHandler;
4133
import javafx.geometry.NodeOrientation;
4234
import javafx.scene.Cursor;
4335
import javafx.scene.Node;
44-
import javafx.scene.control.*;
36+
import javafx.scene.control.ResizeFeaturesBase;
37+
import javafx.scene.control.TableColumnBase;
38+
import javafx.scene.control.TableView;
39+
import javafx.scene.control.TreeTableView;
4540
import javafx.scene.input.MouseEvent;
4641
import javafx.scene.paint.Color;
4742
import javafx.scene.shape.Rectangle;
4843
import javafx.util.Callback;
4944

45+
import java.util.ArrayList;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.WeakHashMap;
49+
5050
/**
5151
* <p>This class is used to construct the header of a TableView. We take the approach
5252
* that every TableView header is nested - even if it isn't. This allows for us
@@ -165,7 +165,11 @@ public NestedTableColumnHeader(final TableColumnBase tc) {
165165
if (me.getClickCount() == 2 && me.isPrimaryButtonDown()) {
166166
// the user wants to resize the column such that its
167167
// width is equal to the widest element in the column
168-
TableSkinUtils.resizeColumnToFitContent(header.getTableSkin(), column, -1);
168+
TableHeaderRow tableHeader = header.getTableHeaderRow();
169+
TableColumnHeader columnHeader = tableHeader.getColumnHeaderFor(column);
170+
if (columnHeader != null) {
171+
columnHeader.resizeColumnToFitContent(-1);
172+
}
169173
} else {
170174
// rather than refer to the rect variable, we just grab
171175
// it from the source to prevent a small memory leak.

modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableColumnHeader.java

+192-7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
2929
import com.sun.javafx.scene.control.Properties;
3030
import com.sun.javafx.scene.control.TableColumnBaseHelper;
31+
import com.sun.javafx.scene.control.TreeTableViewBackingList;
32+
import com.sun.javafx.scene.control.skin.Utils;
3133
import javafx.beans.property.DoubleProperty;
3234
import javafx.beans.property.ReadOnlyObjectProperty;
3335
import javafx.beans.property.ReadOnlyObjectWrapper;
@@ -40,6 +42,7 @@
4042
import javafx.css.Styleable;
4143
import javafx.css.StyleableDoubleProperty;
4244
import javafx.css.StyleableProperty;
45+
import javafx.css.converter.SizeConverter;
4346
import javafx.event.EventHandler;
4447
import javafx.geometry.HPos;
4548
import javafx.geometry.Insets;
@@ -50,22 +53,27 @@
5053
import javafx.scene.Node;
5154
import javafx.scene.control.ContextMenu;
5255
import javafx.scene.control.Label;
56+
import javafx.scene.control.TableCell;
5357
import javafx.scene.control.TableColumn;
5458
import javafx.scene.control.TableColumnBase;
59+
import javafx.scene.control.TableView;
60+
import javafx.scene.control.TreeTableCell;
61+
import javafx.scene.control.TreeTableColumn;
62+
import javafx.scene.control.TreeTableRow;
63+
import javafx.scene.control.TreeTableView;
5564
import javafx.scene.input.ContextMenuEvent;
5665
import javafx.scene.input.MouseEvent;
5766
import javafx.scene.layout.GridPane;
5867
import javafx.scene.layout.HBox;
5968
import javafx.scene.layout.Priority;
6069
import javafx.scene.layout.Region;
70+
import javafx.util.Callback;
6171

6272
import java.util.ArrayList;
6373
import java.util.Collections;
6474
import java.util.List;
6575
import java.util.Locale;
6676

67-
import javafx.css.converter.SizeConverter;
68-
6977
import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeName;
7078
import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeProperty;
7179
import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.isAscending;
@@ -529,7 +537,7 @@ private void updateScene() {
529537
if (getTableColumn() == null || getTableColumn().getWidth() != DEFAULT_COLUMN_WIDTH || getScene() == null) {
530538
return;
531539
}
532-
doColumnAutoSize(getTableColumn(), n);
540+
doColumnAutoSize(n);
533541
autoSizeComplete = true;
534542
}
535543
}
@@ -581,13 +589,190 @@ private void initUI() {
581589
}
582590
}
583591

584-
private void doColumnAutoSize(TableColumnBase<?,?> column, int cellsToMeasure) {
585-
double prefWidth = column.getPrefWidth();
592+
private void doColumnAutoSize(int cellsToMeasure) {
593+
double prefWidth = getTableColumn().getPrefWidth();
586594

587595
// if the prefWidth has been set, we do _not_ autosize columns
588596
if (prefWidth == DEFAULT_COLUMN_WIDTH) {
589-
TableSkinUtils.resizeColumnToFitContent(getTableSkin(), column, cellsToMeasure);
590-
// getTableViewSkin().resizeColumnToFitContent(column, cellsToMeasure);
597+
resizeColumnToFitContent(cellsToMeasure);
598+
}
599+
}
600+
601+
/**
602+
* Resizes this {@code TableColumnHeader}'s column to fit the width of its content.
603+
*
604+
* @implSpec The resulting column width for this implementation is the maximum of the preferred width of the header
605+
* cell and the preferred width of the first {@code maxRow} cells.
606+
* <p>
607+
* Subclasses can either use this method or override it (without the need to call {@code super()}) to provide their
608+
* custom implementation (such as ones that exclude the header, exclude {@code null} content, compute the minimum
609+
* width, etc.).
610+
*
611+
* @param maxRows the number of rows considered when resizing. If -1 is given, all rows are considered.
612+
* @since 14
613+
*/
614+
protected void resizeColumnToFitContent(int maxRows) {
615+
TableColumnBase<?, ?> tc = getTableColumn();
616+
if (!tc.isResizable()) return;
617+
618+
Object control = this.getTableSkin().getSkinnable();
619+
if (control instanceof TableView) {
620+
resizeColumnToFitContent((TableView) control, (TableColumn) tc, this.getTableSkin(), maxRows);
621+
} else if (control instanceof TreeTableView) {
622+
resizeColumnToFitContent((TreeTableView) control, (TreeTableColumn) tc, this.getTableSkin(), maxRows);
623+
}
624+
}
625+
626+
private <T,S> void resizeColumnToFitContent(TableView<T> tv, TableColumn<T, S> tc, TableViewSkinBase tableSkin, int maxRows) {
627+
List<?> items = tv.getItems();
628+
if (items == null || items.isEmpty()) return;
629+
630+
Callback/*<TableColumn<T, ?>, TableCell<T,?>>*/ cellFactory = tc.getCellFactory();
631+
if (cellFactory == null) return;
632+
633+
TableCell<T,?> cell = (TableCell<T, ?>) cellFactory.call(tc);
634+
if (cell == null) return;
635+
636+
// set this property to tell the TableCell we want to know its actual
637+
// preferred width, not the width of the associated TableColumnBase
638+
cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);
639+
640+
// determine cell padding
641+
double padding = 10;
642+
Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
643+
if (n instanceof Region) {
644+
Region r = (Region) n;
645+
padding = r.snappedLeftInset() + r.snappedRightInset();
646+
}
647+
648+
int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
649+
double maxWidth = 0;
650+
for (int row = 0; row < rows; row++) {
651+
cell.updateTableColumn(tc);
652+
cell.updateTableView(tv);
653+
cell.updateIndex(row);
654+
655+
if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
656+
tableSkin.getChildren().add(cell);
657+
cell.applyCss();
658+
maxWidth = Math.max(maxWidth, cell.prefWidth(-1));
659+
tableSkin.getChildren().remove(cell);
660+
}
661+
}
662+
663+
// dispose of the cell to prevent it retaining listeners (see RT-31015)
664+
cell.updateIndex(-1);
665+
666+
// RT-36855 - take into account the column header text / graphic widths.
667+
// Magic 10 is to allow for sort arrow to appear without text truncation.
668+
TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
669+
double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
670+
Node graphic = header.label.getGraphic();
671+
double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
672+
double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
673+
maxWidth = Math.max(maxWidth, headerWidth);
674+
675+
// RT-23486
676+
maxWidth += padding;
677+
if (tv.getColumnResizePolicy() == TableView.CONSTRAINED_RESIZE_POLICY && tv.getWidth() > 0) {
678+
if (maxWidth > tc.getMaxWidth()) {
679+
maxWidth = tc.getMaxWidth();
680+
}
681+
682+
int size = tc.getColumns().size();
683+
if (size > 0) {
684+
TableColumnHeader columnHeader = getTableHeaderRow().getColumnHeaderFor(tc.getColumns().get(size - 1));
685+
if (columnHeader != null) {
686+
columnHeader.resizeColumnToFitContent(maxRows);
687+
}
688+
return;
689+
}
690+
691+
TableSkinUtils.resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
692+
} else {
693+
TableColumnBaseHelper.setWidth(tc, maxWidth);
694+
}
695+
}
696+
697+
private <T,S> void resizeColumnToFitContent(TreeTableView<T> ttv, TreeTableColumn<T, S> tc, TableViewSkinBase tableSkin, int maxRows) {
698+
List<?> items = new TreeTableViewBackingList(ttv);
699+
if (items == null || items.isEmpty()) return;
700+
701+
Callback cellFactory = tc.getCellFactory();
702+
if (cellFactory == null) return;
703+
704+
TreeTableCell<T,S> cell = (TreeTableCell) cellFactory.call(tc);
705+
if (cell == null) return;
706+
707+
// set this property to tell the TableCell we want to know its actual
708+
// preferred width, not the width of the associated TableColumnBase
709+
cell.getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE);
710+
711+
// determine cell padding
712+
double padding = 10;
713+
Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
714+
if (n instanceof Region) {
715+
Region r = (Region) n;
716+
padding = r.snappedLeftInset() + r.snappedRightInset();
717+
}
718+
719+
TreeTableRow<T> treeTableRow = new TreeTableRow<>();
720+
treeTableRow.updateTreeTableView(ttv);
721+
722+
int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
723+
double maxWidth = 0;
724+
for (int row = 0; row < rows; row++) {
725+
treeTableRow.updateIndex(row);
726+
treeTableRow.updateTreeItem(ttv.getTreeItem(row));
727+
728+
cell.updateTreeTableColumn(tc);
729+
cell.updateTreeTableView(ttv);
730+
cell.updateTreeTableRow(treeTableRow);
731+
cell.updateIndex(row);
732+
733+
if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
734+
tableSkin.getChildren().add(cell);
735+
cell.applyCss();
736+
737+
double w = cell.prefWidth(-1);
738+
739+
maxWidth = Math.max(maxWidth, w);
740+
tableSkin.getChildren().remove(cell);
741+
}
742+
}
743+
744+
// dispose of the cell to prevent it retaining listeners (see RT-31015)
745+
cell.updateIndex(-1);
746+
747+
// RT-36855 - take into account the column header text / graphic widths.
748+
// Magic 10 is to allow for sort arrow to appear without text truncation.
749+
TableColumnHeader header = tableSkin.getTableHeaderRow().getColumnHeaderFor(tc);
750+
double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
751+
Node graphic = header.label.getGraphic();
752+
double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
753+
double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
754+
maxWidth = Math.max(maxWidth, headerWidth);
755+
756+
// RT-23486
757+
maxWidth += padding;
758+
if (ttv.getColumnResizePolicy() == TreeTableView.CONSTRAINED_RESIZE_POLICY && ttv.getWidth() > 0) {
759+
760+
if (maxWidth > tc.getMaxWidth()) {
761+
maxWidth = tc.getMaxWidth();
762+
}
763+
764+
int size = tc.getColumns().size();
765+
if (size > 0) {
766+
TableColumnHeader columnHeader = getTableHeaderRow().getColumnHeaderFor(tc.getColumns().get(size - 1));
767+
if (columnHeader != null) {
768+
columnHeader.resizeColumnToFitContent(maxRows);
769+
}
770+
return;
771+
}
772+
773+
TableSkinUtils.resizeColumn(tableSkin, tc, Math.round(maxWidth - tc.getWidth()));
774+
} else {
775+
TableColumnBaseHelper.setWidth(tc, maxWidth);
591776
}
592777
}
593778

0 commit comments

Comments
 (0)