JDK-4890245 : CardLayout lays out cards visibly
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2003-07-14
  • Updated: 2003-07-23
  • Resolved: 2003-07-23
Related Reports
Duplicate :  
Description

Name: gm110360			Date: 07/14/2003


FULL PRODUCT VERSION :
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b19)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode)

On Zaurus:
Insignia Jeode 1.10.2 (Personal Java 1.2)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

ADDITIONAL OPERATING SYSTEMS :
Sharp Zaurus with linux 2.4.6-rmk1-np2-embedix (ROM 2.37)


A DESCRIPTION OF THE PROBLEM :
[1] When a CardLayout is requested to lay out (for instance
with a pack() call), it obtains its preferred size from its
cards and lays out the first visible card.
But it also lays out the invisible cards with an invalid
container with a size of 0x0.
1) This call is not necessary because the preferred size is
already known.
2) Not knowing the display size of the card makes some
layout manager unable to prepare laying out the components.

[2] When an invisible card becomes visible (call to
layout.show("card", this)), the call to lay out its
component is done after the card has been visible.
1) If the layout manager was not able to anticipate the
layout of the component (see [1].2), it has to do it again.
2) This operation is *visible* to the user on slow devices:
you see the components of the card changing position.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the test program
java -cp . TestCardLayout


EXPECTED VERSUS ACTUAL BEHAVIOR :
The test program creates a frame that has a CardLayout with
two cards. It displays the first card for 4s, then the next
card for 4s and exits.

The test program uses instrumented Layout managers and
components to trace how CardLayout behaves.

It shows that when the frame is displayed first, both cards
are requested to layout: the invisible one with an invalid
container size.

Then when the second card becomes visible, it lays out its
components *after* beeing visible.


I expected:
- First, only the visible card is laid out: no need to lay
out the invisible one.
- When the invisible becomes visible, a request to layout
its component is done before being visible.


ERROR MESSAGES/STACK TRACES THAT OCCUR :
C:\Development\Zepo\src>java -cp . TestCardLayout

TestPanel:panel2.setVisible(false)

TestGridLayout.preferredLayoutSize(TestPanel[panel0,0,0,0x0,invalid,layout=TestG
ridLayout])=java.awt.Dimension[width=230,height=46]

TestGridLayout.preferredLayoutSize(TestPanel[panel1,0,0,0x0,invalid,hidden,layou
t=TestGridLayout])=java.awt.Dimension[width=230,height=69]

TestGridLayout.layoutContainer(TestPanel[panel0,4,23,230x69,invalid,layout=TestG
ridLayout])
java.lang.Throwable
        at TestGridLayout.layoutContainer(TestCardLayout.java:100)
        at java.awt.Container.layout(Container.java:1017)
        at java.awt.Container.doLayout(Container.java:1007)
        at java.awt.Container.validateTree(Container.java:1089)
        at java.awt.Container.validateTree(Container.java:1096)
        at java.awt.Container.validate(Container.java:1064)
        at java.awt.Window.pack(Window.java:433)
        at TestCardLayout.<init>(TestCardLayout.java:39)
        at TestCardLayout.main(TestCardLayout.java:51)

TestGridLayout.layoutContainer(TestPanel[panel1,0,0,0x0,invalid,hidden,layout=Te
stGridLayout])
*** ==> The second card is requested to layout without knowing its size!
java.lang.Throwable
        at TestGridLayout.layoutContainer(TestCardLayout.java:100)
        at java.awt.Container.layout(Container.java:1017)
        at java.awt.Container.doLayout(Container.java:1007)
        at java.awt.Container.validateTree(Container.java:1089)
        at java.awt.Container.validateTree(Container.java:1096)
        at java.awt.Container.validate(Container.java:1064)
        at java.awt.Window.pack(Window.java:433)
        at TestCardLayout.<init>(TestCardLayout.java:39)
        at TestCardLayout.main(TestCardLayout.java:51)

Showing next pane


TestPanel:panel1.setVisible(false)

TestPanel:panel2.setVisible(true)
*** ==> Now the second card is visible when the container is requested to
layout again: the user can see the components moving...

TestGridLayout.layoutContainer(TestPanel[panel1,4,23,230x69,invalid,layout=TestG
ridLayout])
java.lang.Throwable
        at TestGridLayout.layoutContainer(TestCardLayout.java:100)
        at java.awt.Container.layout(Container.java:1017)
        at java.awt.Container.doLayout(Container.java:1007)
        at java.awt.Container.validateTree(Container.java:1089)
        at java.awt.Container.validateTree(Container.java:1096)
        at java.awt.Container.validate(Container.java:1064)
        at java.awt.CardLayout.show(CardLayout.java:465)
        at java.awt.CardLayout.last(CardLayout.java:429)
        at TestCardLayout.main(TestCardLayout.java:62)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.awt.*;
import java.awt.event.*;

/**
 * Test how a CardLayout lays out its cards.
 *
 * @author     GenePi
 * @created    26 septembre 2002
 */
public class TestCardLayout
	extends Frame
{
	/**
	 * Create a frame with a CardLayout containing 2 panes.
	 */
	TestCardLayout()
	{
		setLayout(new CardLayout());

		Panel panel1 = new TestPanel(new TestGridLayout(0,
2), "panel1");
		panel1.add(new Label("panel1.label1"));
		panel1.add(new TextField("panel1.field1"));
		panel1.add(new Label("panel1.label2"));
		panel1.add(new TextField("panel1.field2"));

		add(panel1, "panel1");

		Panel panel2 = new TestPanel(new TestGridLayout(0,
2), "panel2");
		panel2.add(new Label("panel2.label1"));
		panel2.add(new TextField("panel2.field1"));
		panel2.add(new Label("panel2.label2"));
		panel2.add(new TextField("panel2.field2"));
		panel2.add(new Label("panel2.label3"));
		panel2.add(new TextField("panel2.field3"));

		add(panel2, "panel2");

		pack();
	}


	/**
	 * The main program for the TestCardLayout. Display the frame for 4s
with the
	 * first pane, then shows the other one for 4s and exits.
	 *
	 * @param  args  The command line arguments
	 */
	public static void main(final String[] args)
	{
		TestCardLayout win = new TestCardLayout();
		win.show();
		try
		{
			Thread.sleep(4000L);
		}
		catch (InterruptedException ie)
		{
		}
		System.err.println("\nShowing next pane\n");
		CardLayout layout = (CardLayout) win.getLayout();
		layout.last(win);
		try
		{
			Thread.sleep(4000L);
		}
		catch (InterruptedException ie)
		{
		}
		System.exit(0);
	}
}

/**
 * Instrumented GridLayout class that prints calls to layoutContainer method.
 */
class TestGridLayout
	extends GridLayout
{
	/**
	 * Create the layout manager
	 *
	 * @param  row  Number of rows
	 * @param  col  Number of columns
	 */
	TestGridLayout(final int row, final int col)
	{
		super(row, col);
	}


	/**
	 * Print the state of the parent container and the call stack.
	 *
	 * @param  parent The parent container.
	 */
	public void layoutContainer(final Container parent)
	{
		System.err.println("\nTestGridLayout.layoutContainer(" + parent
+ ")");
		new Throwable().printStackTrace();
		super.layoutContainer(parent);
	}
	
	/**
	 * Print the preferred size of the layout.
	 *
	 * @param parent The parent container
	 */
	public Dimension preferredLayoutSize(final Container parent)
	{
		System.err.print("\nTestGridLayout.preferredLayoutSize(" +
parent + ")=");
		Dimension dim = super.preferredLayoutSize(parent);
		System.err.println(dim);
		return dim;
	}
}

/**
 * An instrumented panel to know its visibility state.
 */
class TestPanel
	extends Panel
{
	// Identifier of the panel
	private String _id;


	/**
	 * Create the panel.
	 *
	 * @param  layout  The layout manager
	 * @param  id      The identifier of the panel
	 */
	TestPanel(final LayoutManager layout, final String id)
	{
		super(layout);
		_id = id;
	}


	/**
	 * Displays the visibility of the panel.
	 *
	 * @param  visible  The new Visible value
	 */
	public void setVisible(final boolean visible)
	{
		System.err.println("\nTestPanel:" + _id + ".setVisible(" +
visible + ")");
		super.setVisible(visible);
	}
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Use extended layout managers and override method
layoutContainer to check the size of the parent container.

public void layoutContainer(final Container parent)
{
	Dimension dim = parent.getSize();
	if (dim.width == 0 && dim.height == 0)
	{
		return;
	}
	super.layoutContainer(parent);
}
(Incident Review ID: 165019) 
======================================================================

Comments
EVALUATION Name: osR10079 Date: 07/23/2003 The problem is not reproducible with 1.4.2b25 and current tiger. Looks like it was fixed by some of fix in 1.4.2. ###@###.### 2003-07-23 ====================================================================== Name: osR10079 Date: 07/23/2003 The problem was fixed by putback for 4546123 CardLayout becomes unusable after deleting an element 4689398 Inserting items in a Container with CardLayout does not work since Merlin 4690266 REGRESSION: Wizard Page does not move to the next page ###@###.### 2003-07-23 ======================================================================
23-07-2003