Unlike JTextArea, JTextPane has no option to turn line wrapping off. I found one solution to turning off line wrapping in JTextPanes, but it seems too verbose for such a simple problem. Is there a better way to do this?
See No Wrap Text Pane. Here's the code included from the link.
JTextPane textPane = new JTextPane();
JPanel noWrapPanel = new JPanel( new BorderLayout() );
noWrapPanel.add( textPane );
JScrollPane scrollPane = new JScrollPane( noWrapPanel );
The No Wrap Text Pane also provides an alternative solution that doesn't require wrapping the JTextPane in a JPanel, instead it overrides getScrollableTracksViewportWidth(). I prefer that solution, but it didn't quite work for me - I noticed that wrapping still occurs if the viewport becomes narrower than the minimum width of the JTextPane.
I found that JEditorPane is overriding getPreferredSize() to try and 'fix' things when the viewport is too narrow by returning the minimum width instead of the preferred width. This can be resolved by overriding getPreferredSize() again to say 'no, really - we always want the actual preferred size':
public class NoWrapJTextPane extends JTextPane {
#Override
public boolean getScrollableTracksViewportWidth() {
// Only track viewport width when the viewport is wider than the preferred width
return getUI().getPreferredSize(this).width
<= getParent().getSize().width;
};
#Override
public Dimension getPreferredSize() {
// Avoid substituting the minimum width for the preferred width when the viewport is too narrow
return getUI().getPreferredSize(this);
};
}
Related
I have a JScrollPane with a number of JLabel objects in a panel using a GridBagLayout. Each of the labels is displaying HTML text with rich elements which varies at run time.
I would like all labels to have the same width (driven by the width of the scroll pane) but vary in height depending on their content with the text wrapping (as is handled automatically by JLabel). If the labels exceed the scroll pane's height then a vertical scroll bar should appear.
Here is some sample code to demonstrate the problem:
public class ScrollLabels extends JFrame {
private final JPanel labelPanel = new JPanel(new GridBagLayout());
private final GridBagConstraints c = new GridBagConstraints();
public ScrollLabels() throws HeadlessException {
super("Scroll Labels");
}
public void createUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scroller = new JScrollPane(labelPanel);
add(scroller);
c.gridx = 0;
c.gridy = GridBagConstraints.RELATIVE;
c.fill = GridBagConstraints.HORIZONTAL;
addLabel("Here is <em>Rich Text</em>");
addLabel("Here is <ul><li>A</li><li>List</li></ul>");
addLabel("Here is <table><tr><th>A</th><th>Table></th></tr></table");
addLabel("Here is more <em>Rich Text</em>");
addLabel("Here is even more <b>Rich Text</b>");
addLabel("Here is a long sentence that should wrap when the panel "
+ "is too small for the text.");
pack();
}
private void addLabel(String text) {
JLabel label = new JLabel("<html>" + text + "</html>");
label.setBorder(BorderFactory.createEtchedBorder());
labelPanel.add(label, c);
}
public static void main(String[] args) {
ScrollLabels frame = new ScrollLabels();
frame.createUI();
frame.setVisible(true);
}
}
It correctly resizes the labels horizontally and shows scroll bars where appropriate. What it doesn't do is resize labels vertically to fit them within the scroll pane.
Here are the various things I have tried:
Changing the GridBagConstraint values. There are good controls for how to expand and contract components but I can't see any way to set a min or max width.
Setting the JScrollPane scroll bar policy to never show horizontal scroll bars. This just cuts off the label text rather than wrapping the text.
Manually setting the label size - i.e. setting the width from the scroll pane and the height depending on the text. I can't see an easy way to get the correct height of rich HTML text given a fixed width. In any case I'd prefer to have a layout manager that can do the job rather than manually coding preferred sizes.
The one thing I haven't tried yet is creating a custom layout manager. I suspect this might be the right answer but would like to see if any of you have an easier solution that I'm not seeing.
I would like all labels to have the same width (driven by the width of the scroll pane) but vary in height depending on their content
You need to implement the Scrollable interface on your panel and override the getScrollableTracksViewportWidth() method to return true. You will also need to provide default implementations for the other methods of the interface.
Or you can use the Scrollable Panel which provides method that allow you to set the scrolling properties.
I tried a lot of layout managers but none could solve my problem:
I want the items in a scrollPane to keep their size (preferred or minimum) and not being resized (reduced) to fit the viewport Panel. Since if it is a JTextArea, and if the text area has blank space and it is bigger then the viewport, it would reduce it so the blank text area won't be shown. I want the blank text area to be shown for appearance issues.
Im stacking one item after another using BoxLayout, and it seems to me that for text areas the setMinimum method fails.
If the text area has blank space, then the scrollbar of the ScrollPane won't appear, instead it only appears it there are no blank space left.
Any solution?
JScrollPane materialPane = new FScrollPane();
this.materialPaneView = new TPanel();
this.materialPaneView.setMinimumSize(new Dimension((int)(WIDTH*0.95), (int)(HEIGHT/2)));
this.materialPaneView.setLayout(new BoxLayout(this.materialPaneView, BoxLayout.Y_AXIS));
materialPane.setViewportView(materialPaneView);
materialPane.setMinimumSize(new Dimension((int)(WIDTH*0.95), (int)(HEIGHT/2)));
for(Material mat: this.unit.getMaterial()){
this.addMaterial(mat);
}
centerPanel.add(sectionPane);
centerPanel.add(exercisePane);
centerPanel.add(materialPane);
this.add(upperPanel, BorderLayout.NORTH);
this.add(centerPanel, BorderLayout.CENTER);
public void addMaterial(Material mat){
JTextField matName = new JTextField(30);
JPanel fieldButtonPanel = new TPanel();
fieldButtonPanel.setLayout(new GridLayout(1,2));
JPanel fieldPanel = new TPanel(new FlowLayout(FlowLayout.LEFT));
JPanel deleteMatButtonPanel = new TPanel(new FlowLayout(FlowLayout.RIGHT));
matName.setText(mat.getName());
matName.setMaximumSize(new Dimension(FFont.def.getSize()*20, 30));
fieldPanel.add(matName);
JButton deleteMat = new JButton("Delete Material");
deleteMatButtonPanel.add(deleteMat);
fieldButtonPanel.add(fieldPanel);
fieldButtonPanel.add(deleteMatButtonPanel);
fieldButtonPanel.setAlignmentX(LEFT_ALIGNMENT);
JTextArea matText = new FTextArea(mat.getDesc(), (int)(WIDTH*0.95), (int)(HEIGHT/3.4));
matText.setMinimumSize(new Dimension((int)(WIDTH*0.95), (int)(HEIGHT/3.5)));
/*matText.setMaximumSize(new Dimension((int)(WIDTH*0.95), (int)(HEIGHT/3.4)));*/
matText.setText(mat.getDesc());
matText.setAlignmentX(LEFT_ALIGNMENT);
this.materialPaneView.add(fieldButtonPanel);
this.materialPaneView.add(matText);
matName.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
mat.setName(matName.getText());
}
});
HEIGHT and WIDTH are constants, and TPanel FScrollPane are my predefined transparent panels. The BoxLayout panel is the viewport of a scrollPane, and still, it would resize the text areas.
I am not sure i get what you are asking for so please tell me if i totally missed the point...
As far as i know the Viewport size is controlled by the component inside the JScrollPane and the JScrollPane size wont change no matter what happens to the viewport.
You either want to:
A) Resize the JScrollPane to the same size as it's content.
I would implement listeners to look for the content size change and resize the ScrollPane accordingly but you need to pay attention to resize the whole Hierarchy too.
B) You want to resize the viewport so that it fits in the JScrollPane? Y'know without scrollbars.
I had this problem and fixed it by using a ScrollablePanel component. Check this answer, follow the link to download the .class and use it to use a JPanel that resizes to fit the ScrollPane.
Those arent very detailed answers but i will need more information about what you are trying to do before expanding on it. And your code isnt complete, always share a code that we can CTRL+C/V and readily verify the problem in our end.
I working on a application that manage image's filters etc.
I want to have scroll bars when the image is to big to be display.
I put my customize panel that extend JPanel in a JScrollPane and I add it in my JFrame.
My image is displayed but not the whole image and the scroll bars are not there.
How to get the scroll-bars to appear?
Here is my code :
CustomePanel test = new ImagePanel(new File("test.jpg"));
test.setPreferredSize(new Dimension(400, 400));
JScrollPane tmp = new JScrollPane(test);
this.getContentPane().add(tmp);
It is likely that your initial preferred size does not match that of your Image. Rather than using setPreferredSize, override getPreferredSize to reflect the size of the image in ImagePanel:
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(this), image.getHeight(this));
}
A JLabel would be a better approach here if the panel is not required as a container.
try to set the same preferred size to scroll pane as well and test it
JEditorPane seems to have a very interesting feature: it seems to track its parent width, and determines the preferred height accordingly, also if the parent IS NOT a JViewport.
By track i mean that the preferred width of the component is set to the one of its parent (maybe apart from some insets).
The ScrollableTracksViewportWidth is false.
This is the very simple code that demonstrates this fact (just copy and fix imports):
When the JFrame is resized, the preferred width of the JEditorPane (in my environement) is always frame.width-14 (of course 14 may be graphical-system specific).
q1) Tracking the parent (non viewport) width is good. Can I rely on it? As far as i know this is an undocumented feature.More! Just replace new JEditorPane() with new JTextPane(), a richer subclass of JEditorPane, and the feature disappear.
q2) It seems to me that this "tracking" happens through the "setting" of the JEditorPane size. This means that FIRST the size (width) must be set, then the preferred size height will be ok. Is it right?
q3) Why JTextPane has not this feature?
public class SSCE01 extends JFrame {
public static void main(String[] a) {
new SSCE01().setVisible(true);
}
public SSCE01() {
final JEditorPane ep = new JEditorPane();
add(ep);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
Dimension ps = getSize();
System.out.println("Frame size : " + ps.width + " x " + ps.height);
ps = ep.getPreferredSize();
System.out.println("JEditorPane preferredSize: " + ps.width + " x " + ps.height);
}
});
pack();
}
}
q4) More clear question. As hypothesized in q2, setting the size allows the tracking. But just for JEditorPane, NOT for JTextPane. How can I accomplish this for the JTextPane too?
this works:
public SSCE02() {
JEditorPane ep = new JEditorPane();
ep.setText("this is a very very long text. veeeeery long, so long that it will never fit into one 100 pixels width row");
ep.setSize(new Dimension(100,Integer.MAX_VALUE));
add(ep);
pack();
}
this doesn't. It has been used a JTextPane in place of JEditorPane:
public SSCE02() {
JEditorPane ep = new JTextPane();
ep.setText("this is a very very long text. veeeeery long, so long that it will never fit into one 100 pixels width row");
ep.setSize(new Dimension(100,Integer.MAX_VALUE));
add(ep);
pack();
}
UPDATE 1
Summary: the "track Size property" is observed in JEditorPane but nothing similar exists in JTextPane.
A little but significative step further:
Loading an HTML document into JEditorPane lets the feature disappear from JEditorPane too.
At this point, the feature seems implemented by the Document implementation and not by the JEditorPane (or JTextPane) itself! In the case of JEditorPane, the Document is javax.swing.text.PlainDocument. When you do:
URL url = HTMLInComponents01.class.getResource("sample.html");
jEditorPane1.setPage(url);
System.out.println(jEditorPane1.getDocument().getClass().getName());
you'll get:
javax.swing.text.html.HTMLDocument
I also notice that the good javax.swing.text.PlainDocument that gives us this great service of "calculating the height" of a component when the width is given through setSize" isn't assignable to a JTextPane, that expects a StyledDocument instance!
Now I'll verify which other text components are capable of using PlainDocument.
I recommend you add the component to the JFrame's content pane instead of using the add() method. Also set a layout on the content pane and it will all resize automatically.
JFrame f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new JTextPane());
Regards
Unlike JTextArea, JTextPane has no option to turn line wrapping off. I found one solution to turning off line wrapping in JTextPanes, but it seems too verbose for such a simple problem. Is there a better way to do this?
See No Wrap Text Pane. Here's the code included from the link.
JTextPane textPane = new JTextPane();
JPanel noWrapPanel = new JPanel( new BorderLayout() );
noWrapPanel.add( textPane );
JScrollPane scrollPane = new JScrollPane( noWrapPanel );
The No Wrap Text Pane also provides an alternative solution that doesn't require wrapping the JTextPane in a JPanel, instead it overrides getScrollableTracksViewportWidth(). I prefer that solution, but it didn't quite work for me - I noticed that wrapping still occurs if the viewport becomes narrower than the minimum width of the JTextPane.
I found that JEditorPane is overriding getPreferredSize() to try and 'fix' things when the viewport is too narrow by returning the minimum width instead of the preferred width. This can be resolved by overriding getPreferredSize() again to say 'no, really - we always want the actual preferred size':
public class NoWrapJTextPane extends JTextPane {
#Override
public boolean getScrollableTracksViewportWidth() {
// Only track viewport width when the viewport is wider than the preferred width
return getUI().getPreferredSize(this).width
<= getParent().getSize().width;
};
#Override
public Dimension getPreferredSize() {
// Avoid substituting the minimum width for the preferred width when the viewport is too narrow
return getUI().getPreferredSize(this);
};
}