|
28 | 28 | import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
|
29 | 29 | import com.sun.javafx.scene.control.Properties;
|
30 | 30 | import com.sun.javafx.scene.control.TableColumnBaseHelper;
|
| 31 | +import com.sun.javafx.scene.control.TreeTableViewBackingList; |
| 32 | +import com.sun.javafx.scene.control.skin.Utils; |
31 | 33 | import javafx.beans.property.DoubleProperty;
|
32 | 34 | import javafx.beans.property.ReadOnlyObjectProperty;
|
33 | 35 | import javafx.beans.property.ReadOnlyObjectWrapper;
|
|
40 | 42 | import javafx.css.Styleable;
|
41 | 43 | import javafx.css.StyleableDoubleProperty;
|
42 | 44 | import javafx.css.StyleableProperty;
|
| 45 | +import javafx.css.converter.SizeConverter; |
43 | 46 | import javafx.event.EventHandler;
|
44 | 47 | import javafx.geometry.HPos;
|
45 | 48 | import javafx.geometry.Insets;
|
|
50 | 53 | import javafx.scene.Node;
|
51 | 54 | import javafx.scene.control.ContextMenu;
|
52 | 55 | import javafx.scene.control.Label;
|
| 56 | +import javafx.scene.control.TableCell; |
53 | 57 | import javafx.scene.control.TableColumn;
|
54 | 58 | 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; |
55 | 64 | import javafx.scene.input.ContextMenuEvent;
|
56 | 65 | import javafx.scene.input.MouseEvent;
|
57 | 66 | import javafx.scene.layout.GridPane;
|
58 | 67 | import javafx.scene.layout.HBox;
|
59 | 68 | import javafx.scene.layout.Priority;
|
60 | 69 | import javafx.scene.layout.Region;
|
| 70 | +import javafx.util.Callback; |
61 | 71 |
|
62 | 72 | import java.util.ArrayList;
|
63 | 73 | import java.util.Collections;
|
64 | 74 | import java.util.List;
|
65 | 75 | import java.util.Locale;
|
66 | 76 |
|
67 |
| -import javafx.css.converter.SizeConverter; |
68 |
| - |
69 | 77 | import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeName;
|
70 | 78 | import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.getSortTypeProperty;
|
71 | 79 | import static com.sun.javafx.scene.control.TableColumnSortTypeWrapper.isAscending;
|
@@ -529,7 +537,7 @@ private void updateScene() {
|
529 | 537 | if (getTableColumn() == null || getTableColumn().getWidth() != DEFAULT_COLUMN_WIDTH || getScene() == null) {
|
530 | 538 | return;
|
531 | 539 | }
|
532 |
| - doColumnAutoSize(getTableColumn(), n); |
| 540 | + doColumnAutoSize(n); |
533 | 541 | autoSizeComplete = true;
|
534 | 542 | }
|
535 | 543 | }
|
@@ -581,13 +589,190 @@ private void initUI() {
|
581 | 589 | }
|
582 | 590 | }
|
583 | 591 |
|
584 |
| - private void doColumnAutoSize(TableColumnBase<?,?> column, int cellsToMeasure) { |
585 |
| - double prefWidth = column.getPrefWidth(); |
| 592 | + private void doColumnAutoSize(int cellsToMeasure) { |
| 593 | + double prefWidth = getTableColumn().getPrefWidth(); |
586 | 594 |
|
587 | 595 | // if the prefWidth has been set, we do _not_ autosize columns
|
588 | 596 | 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); |
591 | 776 | }
|
592 | 777 | }
|
593 | 778 |
|
|
0 commit comments