Concepts / Dialogs and Modal Windows

Dialogs and Modal Windows

A window can be defined as a subclass of JFrame, and its constructor can set up the window's contents, title, and menu bar.

  • Programming

A Window That Builds Itself

A common first way to build a window in Java is to create a JFrame object inside main(), attach a panel as its content pane, and then show it. That pattern works, and many programs use it. Because it is so common, learners often come to believe it is the only way to build a window. Under that belief, every piece of frame setup must live in main(), and the window is treated as a plain container that someone else configures from the outside.

There is another approach. A window can be defined as its own class, a subclass of JFrame, that sets up its own contents when it is constructed. This is more object-oriented because the window describes itself. As you read the rest of this article, keep two questions in mind: what does the subclass gain, and what has to happen before the window is ready to be seen?

Walking Through the Constructor

When a class extends JFrame, the class itself is a frame. Its constructor is where the window is assembled. The constructor of the subclass always begins by calling the constructor of the superclass with super(). In the MosaicDraw example from the source, that call passes the string "Mosaic Draw" to the JFrame constructor, and that string becomes the text shown in the title bar.

Code walkthrough
A minimal window class in the same shape as MosaicDraw. The constructor sets the title through super(), installs a content pane, calls pack(), and centers the window. The main() method only creates the window, makes it close the program on exit, and makes it visible.
  1. 1
    Line 1 imports the Swing and AWT classes needed for a window and for screen measurement.
  2. 2
    Line 3 declares MosaicDraw as a subclass of JFrame, so every MosaicDraw object is a frame.
  3. 3
    Line 5 calls super with the title string. This runs the JFrame constructor first, so the superclass part of the object is established before the rest of the constructor runs.
  4. 4
    Lines 6 and 7 build a panel holding a button and install it as the content pane.
  5. 5
    Line 8 calls pack(), which is equivalent to this.pack(), meaning the window being constructed.
  6. 6
    Lines 9 and 10 center the window using the size that pack() just settled.
  7. 7
    In main(), line 14 creates the window, line 15 makes closing the window end the program, and line 16 makes the window visible.
  • super("Mosaic Draw") passes the title up to JFrame
  • setContentPane installs the panel that holds the window's components
  • pack() resizes the window to match the preferred size of its contents
  • Centering uses getWidth() and getHeight(), so it must run after pack()
  • main() only creates, configures close behavior, and shows the window
java
  1. 1
    The caller invokes new MosaicDraw(), which starts the subclass constructor.
  2. 2
    super("Mosaic Draw") runs first, so the JFrame part of the object is set up and the title is stored.
  3. 3
    The rest of the constructor installs the contents and attaches the menu bar if one is defined.
  4. 4
    pack() runs, so the window takes its size from the components it now holds.
  5. 5
    The centering calculation runs, because the size it reads is now settled.
  6. 6
    The constructor returns a fully configured window, and main() makes it visible.
main()MosaicDrawconstructorJFrame superclassContent pane andcomponentspack()setVisible(true)1. new MosaicDraw()2. super("Mosaic Draw")3. install panel4. pack()5. ask preferred sizes6. after constructor returns
In what order do the constructor's setup steps, pack(), and making the window visible run, and why does that order matter?

Follow the diagram from top to bottom. The superclass is set up first, through super(). Then the constructor installs the contents, calls pack(), and centers the window. Only after the constructor returns does main() make the window visible. The benefit of this structure is that anyone who creates a MosaicDraw object receives a fully configured window in one step, and main() needs only to create it, set its close behavior, and show it.

What pack() Actually Does

The pack() method resizes the window so that its size matches the preferred size of its contents. It does not decide the size on its own. It asks the components inside the window how large they would like to be, and it adjusts the window to fit them. The source describes this method as usually the best way to set the size of a window, which is why it appears in nearly every well-built frame constructor.

Read the diagram below as a change of target rather than a change of number. Before pack() runs, the window has not been fitted to what it contains. After pack() runs, its size has been set to the preferred size of its contents. The contents themselves are not altered by the call. Only the window's own dimensions are adjusted.

Window not yet fittedWindow fitted tocontentsComponents withpreferred sizesSame components,unchangednot yet appliedpack() matches size
What is the window's size before and after pack() is called, and how does each component's preferred size change the final result?

What do you think happens?

The constructor could center the window before calling pack(). Why does the example call pack() first?

Reveal answer
Answer:

The centering calculation uses getWidth() and getHeight(), so the window's size must be settled before centering runs.

If centering ran before pack(), it would read the window's size before pack() had fitted it to the contents, so the window would be centered for the wrong size.

In the source, the call is written as a bare pack(), which is equivalent to this.pack(). That refers to the window being built by the constructor. If a variable named window of type JFrame or JDialog held the object instead, the same resizing would be written window.pack().

Where pack() Cannot Find the Right Size

Because pack() depends on each component reporting a correct preferred size, it only works reliably when those sizes are available. The source identifies exactly two situations in which the system cannot find a correct preferred size on its own. In both cases the panel has no layout logic that can compute a size from its children, so the system has nothing reliable to report.

Panel as drawingsurfaceSet preferred size byhandPanel with nulllayout managerrequires manual sizerequires manual size
How does pack() decide which sizes to use, and in which two cases does that decision produce a wrong result?

Setting a preferred size by hand means telling a component how large it would like to be before pack() asks. Once that value is supplied, pack() has something correct to match. The rule is simple: when a panel is a drawing surface, or a panel holds components with a null layout manager, do not rely on pack() alone to discover the right size.

java
Notes on the code
  • A drawing panel has no layout logic that reports a size
  • setPreferredSize supplies the size by hand before pack() asks

Setting a Preferred Size by Hand

MEDIUM

A window's content pane holds a JPanel that is used only to draw a mosaic pattern, and no layout manager arranges its children. Describe the change you would make to the panel's setup before calling pack(), and explain why pack() alone would not produce the correct window size.

Hints
  • Ask which of the two situations in the source this panel matches.
  • Think about what pack() asks each component for before it resizes the window.
  • The fix is to supply a preferred size before pack() runs, not after.

Mistakes to Avoid

  • Common mistake

    Believing that a window can only be built inside main()

    Why it fails

    The main() pattern works, but a window can also define itself as a subclass of JFrame and set up its contents in its constructor.

    Do this instead

    Choose the design that makes the window's configuration clearest, knowing both are valid.

  • Common mistake

    Omitting the super() call in a JFrame subclass constructor

    Why it fails

    The subclass constructor begins by calling super(), which passes the title to the JFrame superclass. Without it, the title is not set through that path.

    Do this instead

    Begin the constructor with super("Title") and then configure the rest of the window.

  • Common mistake

    Assuming pack() always produces the correct window size

    Why it fails

    pack() only works correctly when every component reports a correct preferred size. A drawing-surface panel or a null-layout panel reports nothing reliable.

    Do this instead

    Set the preferred size by hand on those panels before calling pack().

  • Common mistake

    Centering the window before calling pack()

    Why it fails

    Centering reads getWidth() and getHeight(), which depend on the size pack() sets.

    Do this instead

    Call pack() first, then compute the position from the settled size.

  • Common mistake

    Thinking that pack() changes the contents of the window; it only adjusts the window's own dimensions.

Summary of Window Construction

  1. 1
    A window can be a subclass of JFrame whose constructor sets up its contents, title, and menu bar.
  2. 2
    The constructor calls super() first, passing the title to JFrame, and then configures the rest of the window.
  3. 3
    pack() resizes the window to match the preferred size of its contents and is usually the best way to size a window.
  4. 4
    pack() cannot be trusted for drawing-surface panels or panels with a null layout manager, so set their preferred sizes by hand before calling pack().

At a glance

  • A window can be defined as a subclass of JFrame, so the class itself is a frame whose constructor assembles the window.
  • The subclass constructor calls super() first, which passes the title to JFrame before the rest of the setup runs.
  • pack() resizes the window to match the preferred size of its contents, and it only works correctly when those preferred sizes are correct.
  • Drawing-surface panels and panels with a null layout manager require a preferred size set by hand before pack() is called.