However, the height of the dialogue seems to be incorrectly based on the
Textarea being one line high, (it is two lines high when wrapped).
The println statements show
Pref java.awt.Dimension[width=352,height=16]
Wrap set
Pref java.awt.Dimension[width=330,height=16]
Note that the width is reduced but the height is wrong.
Packed
Size java.awt.Dimension[width=330,height=16]
No change due to pack()
Size java.awt.Dimension[width=330,height=32]
But after closing, the height is correct (32)!
Qs:
Am I doing something wrong?
Is there a better way to word-wrap some text in a dialogue?
------------------------- 8< ----------------------
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TestErrorDialog {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestErrorDialog();
}
});
}
private JFrame f;
TestErrorDialog() {
JPanel p = new JPanel();
p.add(new JLabel("Testing ..."));
f = new JFrame("Test Error Dialog");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(p);
f.pack();
f.setVisible(true);
triggerAnError();
}
public void triggerAnError() {
System.out.println("Error triggered");
new ErrorDialog(f, "Unable to unwind flanges!",
"Please contact the help desk "
+ "and tell them absolutely everything.");
System.exit(0);
}
}
// ==================================================================
class ErrorDialog extends JDialog implements ActionListener {
JTextArea messageComponent = new JTextArea(1, 30);
static final String CLOSE = "Close";
public ErrorDialog(JFrame parent, String title, String message) {
super(parent, "Error", true);
System.out.println("Constructing dialog");
messageComponent.setText(message);
messageComponent.setEditable(false);
System.out.println("Pref "
+ messageComponent.getPreferredSize());
messageComponent.setLineWrap(true);
messageComponent.setWrapStyleWord(true);
System.out.println("Wrap set");
System.out.println("Pref "
+ messageComponent.getPreferredSize());
JButton closeButton = new JButton(CLOSE);
closeButton.addActionListener(this);
JLabel titleLabel = new JLabel("<html><body><h2>" + title
+ "</h2></body></html>");
JPanel innerPane = new JPanel();
innerPane.setLayout(
new BoxLayout(innerPane, BoxLayout.PAGE_AXIS));
innerPane.setBorder(
BorderFactory.createEmptyBorder(10,10,10,10));
for (JComponent component : new JComponent[] { titleLabel,
messageComponent, closeButton })
component.setAlignmentX(Component.LEFT_ALIGNMENT);
innerPane.add(titleLabel);
innerPane.add(Box.createRigidArea(new Dimension(0, 5)));
innerPane.add(messageComponent);
innerPane.add(Box.createRigidArea(new Dimension(0, 5)));
innerPane.add(closeButton);
add(innerPane);
getRootPane().setDefaultButton(closeButton);
setLocationRelativeTo(parent);
pack();
System.out.println("Packed");
System.out.println("Size " + messageComponent.getSize());
setVisible(true);
System.out.println("Size " + messageComponent.getSize());
}
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
} // ErrorDialog
---------------------------- 8< -------------------------
---
* Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24
During layout, the text area doesn't know what its eventual width will
be, so it has no way to provide a useful preferred height. It's a problem
for any component for which the preferred size in one dimension
depends on the actual size in the other. This might be good enough to
solve your problem (replacement for pack()):
pack();
messageComponent.setSize(messageComponent.getPreferredSize());
pack();
You could write your own word-wrapping label class from scratch,
but there is no way to solve the basic problem of height/width
dependence. A double pack(), at least, would still be required.
A simple solution for multi-line component is to use a JLabel and put
HTML in it.
--
Knute Johnson
email s/nospam/knute/
That has the same problem, if you put in enough text for wrap
to occur. There is no way for a component to work around this.
JTextAreas layout funny there are a lot of issues. The program below
uses a JDialog with a JLabel in it and it sizes just fine. If he really
wants to use a JTextArea for the text, he can make it work by specifying
the rows and columns and setting the font to Monospaced. It will work
then too, see test3 below.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class test {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton b = new JButton("press me");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JDialog d = new JDialog(f,true);
d.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(10,10,10,10);
JLabel l = new JLabel(
"<html>this is a test<br>" +
"of multi-line label<br>" +
"it has 3 lines");
d.add(l,c);
d.pack();
d.setLocationRelativeTo(f);
d.setVisible(true);
d.dispose();
}
});
f.add(b);
f.pack();
f.setVisible(true);
}
};
EventQueue.invokeLater(r);
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class test3 {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
final JFrame f = new JFrame();
f.setLayout(new GridBagLayout());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton b = new JButton("press me again");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JDialog d = new JDialog(f,true);
JTextArea ta = new JTextArea(
"Enough text to wrap and be visible on two lines",
2,30);
ta.setFont(new Font("Monospaced",Font.PLAIN,16));
ta.setLineWrap(true);
d.add(ta);
d.pack();
d.setLocationRelativeTo(f);
d.setVisible(true);
d.dispose();
}
});
f.add(b);
f.pack();
f.setVisible(true);
}
};
EventQueue.invokeLater(r);
Thanks Knute, thanks Larry.
Use the row,columns feature in the TextArea constructor. If that does
not work use the component-expanding features of the GridBagLayout.
Make sure the you have recently validated after changing the size of
the TextArea.
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com
Yes, but your example uses a label with 3 forced lines of text and
no wrapping. The problem is not with JTextArea, it is with laying
out a component for which the preferred width is flexible and
preferred height depends on width, or vice versa. If a component
can fit the text "exactly" at 4000x10, 200x200, 50x800, and lots
of sizes in between, what should it report for its preferred size?
What should it do when it doesn't get it? Lets say it chooses
200x200...
component: I want to be 200x200.
layout manager: The row you are in will be 400 pixels wide,
as will you.
component: In that case, I only need to be 100 pixels high.
layout manager: Too late.
programmer: What's this big empty space?
To see this general behavior, add
messageComponent.setSize(50, Integer.MAX_VALUE)
before the pack() in RGB's original example above.
My solution above would do this:
component: I want to be 200x200.
layout manager: The row you are in will be 400 pixels wide,
as will you.
component: In that case, I only need to be 100 pixels high.
layout manager: Too late.
program: OK, lets go again, with the knowledge that component
should be 400 pixels wide.
Which might be more obvious if you code it this way:
pack();
messageComponent.setSize(messageComponent.getWidth(),
Integer.MAX_VALUE);
pack();
Larry:
I also included an example that works just fine with a JTextArea.
knute...
Of course you can get it to work if you know ahead of time
how many lines the wrapped text will require. That is unlikely
in a real-world situation, and was not the case in the example
in the OP.
He said it was two lines. If you want it to do any number of lines then
you have to call pack() again after you know how many lines it will take.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class test10 {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton b = new JButton("press me one more time");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JDialog d = new JDialog(f,true);
JTextArea ta = new JTextArea(
"Enough text to wrap and be visible on two
lines");
ta.setColumns(15);
ta.setLineWrap(true);
d.add(ta);
d.pack();
ta.setRows(ta.getLineCount());
The error message was passed in as a parameter to the error dialog
constructor, so it would be reasonable to assume he wanted a solution
that works for any length of text. Besides, unless the number of rows
are fixed and the width flexible or the text is one character long, the
number of wrapped lines for any text in any component that wraps
text in any gui layout is always system dependent in principle.
> If you want it to do any number of lines then you have to call pack()
> again after you know how many lines it will take.
I have already posted two solutions that are equivalent to the one
below.
Exactly, you made an assumption that he wanted an indeterminate number
of lines and I made the assumption that he wanted two. Somewhere along
the line one of the several decisions must be made. Once it is, the
appropriate solution is obvious.