From 3d47518697e794bf4afb577f16afb278fbf3d30a Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Sat, 15 Aug 2026 08:16:46 +0000 Subject: [PATCH] 8387267: Editor for the last column in JTable is hard to activate after AUTO_RESIZE_LAST_COLUMN was configured 8388467: Test "api/javax_swing/interactive/JTableTests.html" JTable freezes during rapid column resize Reviewed-by: angorya, kizune, jdv --- .../share/classes/javax/swing/JTable.java | 70 ++++++++++---- .../JTable/TestAutoResizeLastColumn.java | 92 +++++++++++++++++++ 2 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index fa8110d1517..a13059f93b4 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -453,6 +453,14 @@ public class JTable extends JComponent implements TableModelListener, Scrollable * needed. */ private boolean columnSelectionAdjusting; + + /* + * True after column widths have been initialized/synchronized by layout. + * Used to distinguish the first preferred-width layout from later normal + * AUTO_RESIZE_LAST_COLUMN layouts. + */ + private boolean columnWidthsInitialized; + /** * The last value of getValueIsAdjusting from the row selection models * valueChanged notification. Used to test if a repaint is needed. @@ -1264,12 +1272,6 @@ public class JTable extends JComponent implements TableModelListener, Scrollable autoResizeMode = mode; resizeAndRepaint(); if (tableHeader != null) { - if (mode == JTable.AUTO_RESIZE_LAST_COLUMN) { - int colCnt = columnModel.getColumnCount(); - if (colCnt > 0) { - tableHeader.setResizingColumn(columnModel.getColumn(colCnt - 1)); - } - } tableHeader.resizeAndRepaint(); } firePropertyChange("autoResizeMode", old, autoResizeMode); @@ -3193,21 +3195,8 @@ public class JTable extends JComponent implements TableModelListener, Scrollable */ public void doLayout() { - boolean prefWidthSet = false; TableColumn resizingColumn = getResizingColumn(); - // doLayout is called for both pack and show - // so if initial preferred width is set by user then - // it needs to be honoured even if resizingColumn - // is set to last column on account of - // AUTO_RESIZE_LAST_COLUMN autoResizeMode - for (int i = 0; i < columnModel.getColumnCount(); i++) { - if (columnModel.getColumn(i).getPreferredWidth() != 75 - && columnModel.getColumn(i).getWidth() == 75) { - prefWidthSet = true; - break; - } - } - if (resizingColumn == null || prefWidthSet) { + if (resizingColumn == null) { setWidthsFromPreferredWidths(false); } else { @@ -3294,7 +3283,42 @@ public class JTable extends JComponent implements TableModelListener, Scrollable } } + private void accommodateLastColumnOnly() { + int columnCount = getColumnCount(); + if (columnCount == 0) { + return; + } + + int delta = getWidth() - getColumnModel().getTotalColumnWidth(); + if (delta != 0) { + accommodateDelta(columnCount - 1, delta); + } + } + + private void setWidthsFromPreferredWidthsLastColumnOnly() { + int columnCount = getColumnCount(); + if (columnCount == 0) { + return; + } + + for (int i = 0; i < columnCount - 1; i++) { + TableColumn column = columnModel.getColumn(i); + column.setWidth(column.getPreferredWidth()); + } + + accommodateLastColumnOnly(); + } + private void setWidthsFromPreferredWidths(final boolean inverse) { + if (!inverse && autoResizeMode == AUTO_RESIZE_LAST_COLUMN) { + if (!columnWidthsInitialized) { + setWidthsFromPreferredWidthsLastColumnOnly(); + } else { + accommodateLastColumnOnly(); + } + columnWidthsInitialized = true; + return; + } int totalWidth = getWidth(); int totalPreferred = getPreferredSize().width; int target = !inverse ? totalWidth : totalPreferred; @@ -3323,6 +3347,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable }; adjustSizes(target, r, inverse); + columnWidthsInitialized = true; } @@ -3821,6 +3846,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable if (columnModel == null) { throw new IllegalArgumentException("Cannot set a null ColumnModel"); } + columnWidthsInitialized = false; TableColumnModel old = this.columnModel; if (columnModel != old) { if (old != null) { @@ -4634,6 +4660,10 @@ public class JTable extends JComponent implements TableModelListener, Scrollable * @see TableColumnModelListener */ public void columnAdded(TableColumnModelEvent e) { + if (columnWidthsInitialized) { + TableColumn column = columnModel.getColumn(e.getToIndex()); + column.setWidth(column.getPreferredWidth()); + } // If I'm currently editing, then I should stop editing if (isEditing()) { removeEditor(); diff --git a/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java new file mode 100644 index 00000000000..2cb1b9ae32e --- /dev/null +++ b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 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. + */ + +/* + * @test + * @bug 8234071 8387267 + * @summary AUTO_RESIZE_LAST_COLUMN should resize only the last column during table layout + * @run main TestAutoResizeLastColumn + */ + +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableColumnModel; + +public class TestAutoResizeLastColumn { + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(TestAutoResizeLastColumn::testLastColumnOnly); + } + + private static void testLastColumnOnly() { + JTable table = new JTable(3, 3); + table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + + if (table.getTableHeader().getResizingColumn() != null) { + throw new RuntimeException( + "AUTO_RESIZE_LAST_COLUMN must not set resizingColumn"); + } + + TableColumnModel cm = table.getColumnModel(); + for (int i = 0; i < cm.getColumnCount(); i++) { + cm.getColumn(i).setMinWidth(10); + cm.getColumn(i).setPreferredWidth(100); + cm.getColumn(i).setWidth(100); + } + + table.setSize(300, 100); + table.doLayout(); + + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 100); + + table.setSize(360, 100); + table.doLayout(); + + /* + * AUTO_RESIZE_LAST_COLUMN means the +60 delta is absorbed by + * the last column only. + * Without fix all columns width will change to 120. + */ + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 160); + + table.setSize(330, 100); + table.doLayout(); + + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 130); + } + + private static void assertWidth(TableColumnModel cm, int column, int expected) { + int actual = cm.getColumn(column).getWidth(); + if (actual != expected) { + throw new RuntimeException( + "Unexpected width for column " + column + + ": expected " + expected + + ", actual " + actual); + } + } +}