JDK-4238932 : a JTextField in gridBagLayout does not properly set MinimumSize
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.2.0,1.3.0,6
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic,linux
  • CPU: generic,x86
  • Submitted: 1999-05-17
  • Updated: 2008-04-16
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
Name: vi73552			Date: 05/17/99


/* The following class generates a typical use case for swing. A label and two textfields, which display the length of the requested input.
When there is enough space, everything works fine. But if you narrow the frame so that there is not enough width for the columns of the textfields, both textfields will be crippled to their minimal size although more room is left.
That was not the case in swing 1.0.1 and I think the patch for bug 4118855 is the reason for that.
I think it is correct that JTextfield doesn't return its preferredSize as minimumSize. But it is a bug, that gridBagLayout calculates with minimumSize when there is not enough space for preferredSize.*/

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class GridBagTest{

  public static void main (String[] args){

    JFrame frame = new JFrame();
    frame.addWindowListener(new WindowAdapter(){
      public void windowClosing(WindowEvent e){
        System.exit(0);
      }
    });

    Container container = frame.getContentPane();
    container.setLayout(new GridBagLayout());
    GridBagConstraints gBC = new GridBagConstraints();

    gBC.gridx      = 0;
    gBC.gridy      = 0;
    gBC.gridwidth  = 1;
    gBC.gridheight = 1;
    gBC.weightx    = 0.0;
    gBC.weighty    = 0.0;
    gBC.fill       = GridBagConstraints.NONE;
    gBC.anchor     = GridBagConstraints.NORTHWEST;
    container.add(new JLabel("Text"), gBC);    

    gBC.gridx      = 1;
    gBC.gridy      = 0;
    gBC.gridwidth  = 1;
    gBC.gridheight = 1;
    gBC.weightx    = 1.0;
    gBC.weighty    = 0.0;
    gBC.fill       = GridBagConstraints.NONE;
    gBC.anchor     = GridBagConstraints.NORTHWEST;
    container.add(new JTextField(16), gBC);    

    gBC.gridx      = 1;
    gBC.gridy      = 1;
    gBC.gridwidth  = 1;
    gBC.gridheight = 1;
    gBC.weightx    = 1.0;
    gBC.weighty    = 0.0;
    gBC.fill       = GridBagConstraints.NONE;
    gBC.anchor     = GridBagConstraints.NORTHWEST;
    container.add(new JTextField(8), gBC);    

    frame.pack();
    frame.setVisible(true);
  }
}

/* Do not tell me to use fill, I wan't the textfields two have different widths.*/
(Review ID: 63228) 
======================================================================

Name: krC82822			Date: 11/28/2000


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

We've got an application which has some custom database connected JTextFields.
The columns property of the JTextFields is set to the columnDisplaySize of the
ResultSetMetaData. The panels primarily use GridBagLayout as the LayoutManager.
Sometimes there isn't enough room for a JTextField to have the size of it's
preferredSize. What the GridBagLayout manager then does is just reduce it's
size to the minimumSize! This is totally unacceptable (minimumSize doesn't
allow for display of a single character!).
What we expect to see is that GridBagLayout gives the available space (smaller
than the preferredSize but greater than the minimumSize) to the JTextField!

There is a bug about this exact problem already on the Bug Parade: 4238932, but
in the evaluation, the ball is first passed to the AWT group and then passed
back to the Swing group. This does not help us (developers) very much.

Please fix this or provide a workaround. The only workaround we currently can
think of is to just manually set the minimumSize of the JTextField's (ugly &
platform dependant).
(Review ID: 112859)
======================================================================

Comments
EVALUATION Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=11998&forumID=1463
15-03-2006

EVALUATION There is a lot of complains for new layouting policy and after huge investigation we found that we may not keep 4238932 fixed and avoid these regressions. For now we back this fix out and probably will introduce new java property for new layouting policy: (-Djava.awt.gridbaglayout.preferredsizepolicy = true ?)
23-09-2005

SUGGESTED FIX --- GridBagLayout.java Tue Jun 7 14:44:24 2005 *** 1129,1144 **** xMaxArray[i] = px; } /* Cache the current slave's size. */ ! if (sizeflag == PREFERREDSIZE) d = comp.getPreferredSize(); ! else d = comp.getMinimumSize(); constraints.minWidth = d.width; constraints.minHeight = d.height; if (calculateBaseline(comp, constraints, d)) { hasBaseline = true; } /* Zero width and height must mean that this is the last item (or --- 1129,1152 ---- xMaxArray[i] = px; } /* Cache the current slave's size. */ ! // SM (1): ! // depending on the sizeflag cache the preferredSize or minimumSize, ! // but store values in different members so (4) and (5) have access ! // to the cached preferred size ! if (sizeflag == PREFERREDSIZE) { d = comp.getPreferredSize(); ! constraints.prefWidth = d.width; ! constraints.prefHeight = d.height; ! } else { d = comp.getMinimumSize(); constraints.minWidth = d.width; constraints.minHeight = d.height; + } + if (calculateBaseline(comp, constraints, d)) { hasBaseline = true; } /* Zero width and height must mean that this is the last item (or *** 1319,1338 **** } break; case GridBagConstraints.ABOVE_BASELINE: case GridBagConstraints.ABOVE_BASELINE_LEADING: case GridBagConstraints.ABOVE_BASELINE_TRAILING: ! pixels_diff = constraints.minHeight + constraints.insets.top + constraints.insets.bottom + constraints.ipady; maxAscent[curY] = Math.max(maxAscent[curY], pixels_diff); break; case GridBagConstraints.BELOW_BASELINE: case GridBagConstraints.BELOW_BASELINE_LEADING: case GridBagConstraints.BELOW_BASELINE_TRAILING: ! pixels_diff = constraints.minHeight + constraints.insets.top + constraints.insets.bottom + constraints.ipady; maxDescent[curY] = Math.max(maxDescent[curY], pixels_diff); break; --- 1327,1346 ---- } break; case GridBagConstraints.ABOVE_BASELINE: case GridBagConstraints.ABOVE_BASELINE_LEADING: case GridBagConstraints.ABOVE_BASELINE_TRAILING: ! pixels_diff = constraints.prefHeight + constraints.insets.top + constraints.insets.bottom + constraints.ipady; maxAscent[curY] = Math.max(maxAscent[curY], pixels_diff); break; case GridBagConstraints.BELOW_BASELINE: case GridBagConstraints.BELOW_BASELINE_LEADING: case GridBagConstraints.BELOW_BASELINE_TRAILING: ! pixels_diff = constraints.prefHeight + constraints.insets.top + constraints.insets.bottom + constraints.ipady; maxDescent[curY] = Math.max(maxDescent[curY], pixels_diff); break; *** 1408,1420 **** * First, figure out how wide the current slave needs to be. * Then, see if it will fit within the current minWidth values. * If it will not fit, add the difference according to the * weightX array. */ ! pixels_diff = ! constraints.minWidth + constraints.ipadx + constraints.insets.left + constraints.insets.right; for (k = constraints.tempX; k < px; k++) pixels_diff -= r.minWidth[k]; if (pixels_diff > 0) { --- 1416,1432 ---- * First, figure out how wide the current slave needs to be. * Then, see if it will fit within the current minWidth values. * If it will not fit, add the difference according to the * weightX array. */ ! // SM (2): ! // calculate pixels_diff depending on the sizeflag, ! // using 'constraints.prefWidth' or 'constraints.minWidth' ! // respectively, this is needed because of change (1) ! int sizeflagWidth = (sizeflag == PREFERREDSIZE) ? constraints.prefWidth : constraints.minWidth; pixels_diff = ! sizeflagWidth + constraints.ipadx + constraints.insets.left + constraints.insets.right; for (k = constraints.tempX; k < px; k++) pixels_diff -= r.minWidth[k]; if (pixels_diff > 0) { *** 1469,1478 **** --- 1481,1495 ---- * First, figure out how tall the current slave needs to be. * Then, see if it will fit within the current minHeight values. * If it will not fit, add the difference according to the * weightY array. */ + // SM (3): + // calculate pixels_diff depending on the sizeflag, + // using 'constraints.prefHeight' or 'constraints.minHeight' + // respectively, this is needed because of change (1) + int sizeflagHeight = (sizeflag == PREFERREDSIZE) ? constraints.prefHeight: constraints.minHeight; pixels_diff = -1; if (hasBaseline) { switch(constraints.anchor) { case GridBagConstraints.BASELINE: *** 1500,1525 **** break; case GridBagConstraints.ABOVE_BASELINE: case GridBagConstraints.ABOVE_BASELINE_LEADING: case GridBagConstraints.ABOVE_BASELINE_TRAILING: pixels_diff = constraints.insets.top + ! constraints.minHeight + maxDescent[constraints.tempY]; break; case GridBagConstraints.BELOW_BASELINE: case GridBagConstraints.BELOW_BASELINE_LEADING: case GridBagConstraints.BELOW_BASELINE_TRAILING: pixels_diff = maxAscent[constraints.tempY] + ! constraints.minHeight + constraints.insets.bottom + constraints.ipady; break; } } if (pixels_diff == -1) { pixels_diff = ! constraints.minHeight + constraints.ipady + constraints.insets.top + constraints.insets.bottom; } for (k = constraints.tempY; k < py; k++) pixels_diff -= r.minHeight[k]; --- 1517,1542 ---- break; case GridBagConstraints.ABOVE_BASELINE: case GridBagConstraints.ABOVE_BASELINE_LEADING: case GridBagConstraints.ABOVE_BASELINE_TRAILING: pixels_diff = constraints.insets.top + ! sizeflagHeight + maxDescent[constraints.tempY]; break; case GridBagConstraints.BELOW_BASELINE: case GridBagConstraints.BELOW_BASELINE_LEADING: case GridBagConstraints.BELOW_BASELINE_TRAILING: pixels_diff = maxAscent[constraints.tempY] + ! sizeflagHeight + constraints.insets.bottom + constraints.ipady; break; } } if (pixels_diff == -1) { pixels_diff = ! sizeflagHeight + constraints.ipady + constraints.insets.top + constraints.insets.bottom; } for (k = constraints.tempY; k < py; k++) pixels_diff -= r.minHeight[k]; *** 1626,1648 **** r.width -= (constraints.insets.left + constraints.insets.right); r.y += constraints.insets.top; r.height -= (constraints.insets.top + constraints.insets.bottom); diffx = 0; if ((constraints.fill != GridBagConstraints.HORIZONTAL && ! constraints.fill != GridBagConstraints.BOTH) ! && (r.width > (constraints.minWidth + constraints.ipadx))) { ! diffx = r.width - (constraints.minWidth + constraints.ipadx); ! r.width = constraints.minWidth + constraints.ipadx; } diffy = 0; if ((constraints.fill != GridBagConstraints.VERTICAL && ! constraints.fill != GridBagConstraints.BOTH) ! && (r.height > (constraints.minHeight + constraints.ipady))) { ! diffy = r.height - (constraints.minHeight + constraints.ipady); ! r.height = constraints.minHeight + constraints.ipady; } switch (constraints.anchor) { case GridBagConstraints.BASELINE: r.x += diffx/2; --- 1643,1673 ---- r.width -= (constraints.insets.left + constraints.insets.right); r.y += constraints.insets.top; r.height -= (constraints.insets.top + constraints.insets.bottom); diffx = 0; + // SM (4): + // if component isn't filled, give it its preferred width + // but not wider than the available width: + // - now possible because of change (1) + // - same result as original calculation for container which size is greated than its preferred size if ((constraints.fill != GridBagConstraints.HORIZONTAL && ! constraints.fill != GridBagConstraints.BOTH)) { ! diffx = r.width - Math.min(r.width, constraints.prefWidth + constraints.ipadx); ! r.width -= diffx; } diffy = 0; + // SM (5): + // if component isn't filled, give it its preferred height + // but not taller than the available height + // - now possible because of change (1) + // - same result as original calculation for container which size is greated than its preferred size if ((constraints.fill != GridBagConstraints.VERTICAL && ! constraints.fill != GridBagConstraints.BOTH)) { ! diffy = r.height - Math.min(r.height, constraints.prefHeight + constraints.ipady); ! r.height -= diffy; } switch (constraints.anchor) { case GridBagConstraints.BASELINE: r.x += diffx/2; *** 1775,1786 **** int maxY = cellY + cellHeight - layoutInfo.maxDescent[cons.tempY + cons.tempHeight - 1] + cons.descent; r.height = maxY - r.y; if (!cons.isVerticallyResizable()) { ! r.y = maxY - cons.minHeight; ! r.height = cons.minHeight; } } else { int baseline; // baseline, relative to cellY int ascent = cons.ascent; // baseline for component --- 1800,1811 ---- int maxY = cellY + cellHeight - layoutInfo.maxDescent[cons.tempY + cons.tempHeight - 1] + cons.descent; r.height = maxY - r.y; if (!cons.isVerticallyResizable()) { ! r.y = maxY - cons.prefHeight; ! r.height = cons.prefHeight; } } else { int baseline; // baseline, relative to cellY int ascent = cons.ascent; // baseline for component *** 1819,1852 **** } } if (!fits) { // Doesn't fit, use min size and original ascent ascent = cons.ascent; ! r.width = cons.minWidth; ! r.height = cons.minHeight; } } r.y = cellY + baseline - ascent; if (cons.isVerticallyResizable()) { switch(cons.baselineResizeBehavior) { case CONSTANT_ASCENT: ! r.height = Math.max(cons.minHeight,cellY + cellHeight - r.y - cons.insets.bottom); break; case CENTER_OFFSET: { int upper = r.y - cellY - cons.insets.top; int lower = cellY + cellHeight - r.y - ! cons.minHeight - cons.insets.bottom; int delta = Math.min(upper, lower); delta += delta; ! if ((cons.minHeight + cons.centerPadding + delta) / 2 + cons.centerOffset != baseline) { // Off by 1 delta--; } ! r.height = cons.minHeight + delta; r.y = cellY + baseline - (r.height + cons.centerPadding) / 2 - cons.centerOffset; } break; --- 1844,1877 ---- } } if (!fits) { // Doesn't fit, use min size and original ascent ascent = cons.ascent; ! r.width = cons.prefWidth; ! r.height = cons.prefHeight; } } r.y = cellY + baseline - ascent; if (cons.isVerticallyResizable()) { switch(cons.baselineResizeBehavior) { ###@###.### 2005-06-16 09:24:12 GMT
16-06-2005

EVALUATION The bug is against GridBagLayout, passing this to awt. timothy.prinzing@eng 1999-09-16 This is a problem of JTextField not properly setting Minimum and Preferred Size richard.ray@eng 2000-07-28 This still appears to be a swing bug. ###@###.### 2001-08-27 With GridBagLayout if you do not specify a fill other than GridBagConstraints.NONE, the size of the component will be either the min or pref. For example, lets say you have a Component with a min of 10 and a pref of 100. If the parent Container, with a GridBagLayout, only has 20 pixels available, it will adjust the Component to a size of 10 (its pref, 100, is too big). The submitter of this bug is asking for GridBagLayout, if the min/pref doesn't exactly match, that the size by scaled between the min and pref regardless of the fill. As this is a fundamental change in GridBagLayout, I'm reassigning to awt to evaluate if they want to make this change. ###@###.### 2001-08-27 AWT certainly does not want to make this change in the Merlin timeframe. Downgrading the priority. ###@###.### 2001-09-05 Name: osR10079 Date: 04/11/2004 The problem is still reproducible with tiger b45. I wonder why this is not RFE. ###@###.### 2004-04-12 ====================================================================== a wrong and high-visible behaviour. ###@###.### 2005-05-14 10:43:41 GMT We receieved the attached contribution from java.net user (svenmeier). After applying the patch, it does look like if fixed the problem. However, due to a conflict we had to get a new version from Sven. This new version seems to fix both the BaseLine tests that Scott added as well as the original bug. Andrei also provided a regression test for it. The attachment named sven.txt is the original version, the second one (GridBagLayout.java) is the final one after several back and forths to fix various problems. This problem is now fixed. ###@###.### 2005-06-15 18:09:12 GMT
15-06-2005

WORK AROUND Name: vi73552 Date: 05/17/99 Subclass JTextField an override getMinimalSize: public Dimension getMinimalSize(){ return (getPreferredSize()); } ======================================================================
22-09-2004