JFormDesigner / FlatLaf

FlatLaf - Swing Look and Feel (with Darcula/IntelliJ themes support)
https://www.formdev.com/flatlaf/
Apache License 2.0
3.42k stars 272 forks source link

JLabel with wrapped HTML is cutoff on the bottom with v3.5.0. #873

Closed iclkevin closed 3 months ago

iclkevin commented 3 months ago

In v3.5.0 of FlatLaF, including wrapped HTML in a JLabel (and probably other components) results in the bottom line or more being cutoff. This issue does not exist in v3.4.1.

public static void main(String[] args) throws UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(new FlatMacLightLaf());
        JFrame f = new JFrame();
        JPanel content = new JPanel(new GridBagLayout());
        content.setPreferredSize(new Dimension(500, 500));
        GridBagConstraints c = new GridBagConstraints();
        content.add(new JLabel("<html><div style=\"width:250px;\"><small>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis odio quis leo cursus, a scelerisque dui consequat. Nunc vel tincidunt erat. Cras non lacinia tellus. Donec semper ante in neque ornare, non vehicula dui consequat. Cras in posuere nulla. Duis tincidunt a neque at consequat. Proin et massa augue. Phasellus tempus, felis non varius vestibulum, nisl eros sagittis lacus, vel commodo ex massa non ipsum. Aliquam eget augue odio. Cras consequat posuere pharetra. Morbi vel est ultrices, iaculis orci id, aliquet ligula. Aenean posuere tellus id augue lacinia, ac vestibulum tellus sollicitudin. Phasellus dignissim justo sit amet quam pharetra pharetra. Curabitur ut lacus dignissim, egestas sem et, ullamcorper nibh. Fusce placerat bibendum sollicitudin. Mauris eleifend urna iaculis eros bibendum, id vulputate ante posuere.</small></html>"), c);
        f.setContentPane(content);
        f.pack();
        f.setVisible(true);
}
Screenshot 2024-07-25 at 3 09 27 PM

Please let me know if I can help further.

remcopoelstra commented 3 months ago

I was curious if I could maybe find the cause of this issue, this is what I have found so far:

Since 3.5 there is a new FlatHTML class that adds a BASE_SIZE to the stylesheet of the HTML (line 70):

        // BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
        styleSheet.addRule( "BASE_SIZE " + fontBaseSize );
        clearViewCaches( view );

When I remove this line the HTML is no longer cutoff.

I started playing around with the fontBaseSize value that is passed and I noticed that different values result in different minimum/preferred/maximum sizes of the JLabel but the actual rendering of the JLabel stays the same. I enabled the debug printing using dumpViews and this shows that the font size of the InlineView's (that render all the individual pieces of html) is being updated.

I don't yet understand why the updated font size is being used for calculating the layout of the label but not being used for the actual rendering, I will try to look into this further.

remcopoelstra commented 3 months ago

I added a mouse listener to the label which calls FlatHTML.dumpViews( view, 0 ) on the label, when I click it I see that the original font sizes from before the BASE_SIZE was set are now printed (except for the very last InlineView which still shows the desired font size). I verified that these are the fonts/sizes that are used by GlyphPainter1 for painting the HTML.

I also print the preferred size of the label in the mouse listener and this always prints the same dimension (which resulted from changing the BASE_SIZE), even when revalidating/forcing doLayout etc.

I am still learning about how Swing renders it's text components, but I think I'm getting closer.

remcopoelstra commented 3 months ago

This seems to be caused by the <div style=\"width:250px;\">, there is no closing </div> in the provided sample bit if I add it myself the html is still cutoff.

If I remove the <div> from the html everything works ok, the BASE_SIZE value that I was changing in my tests is now also being used.

iclkevin commented 3 months ago

I do not know a better way to force a particular wrap width on a JLabel without the div defined with the width attribute. I apologize about missing the closing div in the example; it should have no effect.

remcopoelstra commented 3 months ago

If the <div> was working in previous releases then I agree this is probably a new issue in 3.5, as a workaround maybe you could use a LayoutManager like GridBagLayout, in my tests I simply changed your code to:

GridBagLayout layout = new GridBagLayout();
layout.rowWeights = new double[] { 0.1, 0.1, 0.1 };
layout.rowHeights = new int[] { 10, 250, 10 };
layout.columnWeights = new double[] { 0.1, 0.0, 0.1 };
layout.columnWidths = new int[] { 10, 250, 10 };
content.setLayout(layout);

GridBagConstraints c = new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
iclkevin commented 3 months ago

I appreciate this. Unfortunately, this workaround is a bit too cumbersome for practical use, as layouts get more complex. I am still playing around too (adding extra return lines at the end and such). Hopefully this can be resolved in FlatLaF.

remcopoelstra commented 3 months ago

I also tried setting the width in the body of the html <html><body width=\"250px\"><small>Lorem ipsum... but this has the same issue. Yes I agree it would be nice if this html also works in combination with the BASE_SIZE fix in 3.5, or maybe a way to configure if the BASE_SIZE rule should be applied or not (because I think it might be due to a swing-bug when the html gets a little bit complex).

Personally I would try to avoid relying on the html for layout, if you want to show multiline wrapped text you could also try to use a JTextArea or a JTextPane, and you could also create a utility class which you can reuse for displaying a label with a fixed width, for example:

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JPanel;

public class FixedWidthPanel extends JPanel {

    public FixedWidthPanel(Component c, int w) {

        GridBagLayout layout = new GridBagLayout();
        layout.rowWeights = new double[] { 0.1 };
        layout.rowHeights = new int[] { 10 };
        layout.columnWeights = new double[] { 0.0 };
        layout.columnWidths = new int[] { w };
        setLayout(layout);

        add(c, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

    }

}

You can then simply wrap your label by adding it like so:

content.add(new FixedWidthPanel(label, 250), c);
iclkevin commented 3 months ago

Thank you, @remcopoelstra. This discussion was very helpful. The problem here seems mainly to be caused by a mismatch in sizing from the \ tag. If this tag is removed, the full text is visible, albeit larger in size. The HTML rendering engine for Swing inherits its style sheet from the current LaF of the JComponent. So using FlatLaF's styleClass property works well with a wrapped JLabel:

    public static void main(String[] args) throws UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(new FlatMacLightLaf());
        JFrame f = new JFrame();
        JPanel content = new JPanel(new GridBagLayout());
        content.setPreferredSize(new Dimension(500, 500));
        GridBagConstraints c = new GridBagConstraints();
        JLabel l = new JLabel("<html><div style=\"width:250px;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis odio quis leo cursus, a scelerisque dui consequat. Nunc vel tincidunt erat. Cras non lacinia tellus. Donec semper ante in neque ornare, non vehicula dui consequat. Cras in posuere nulla. Duis tincidunt a neque at consequat. Proin et massa augue. Phasellus tempus, felis non varius vestibulum, nisl eros sagittis lacus, vel commodo ex massa non ipsum. Aliquam eget augue odio. Cras consequat posuere pharetra. Morbi vel est ultrices, iaculis orci id, aliquet ligula. Aenean posuere tellus id augue lacinia, ac vestibulum tellus sollicitudin. Phasellus dignissim justo sit amet quam pharetra pharetra. Curabitur ut lacus dignissim, egestas sem et, ullamcorper nibh. Fusce placerat bibendum sollicitudin. Mauris eleifend urna iaculis eros bibendum, id vulputate ante posuere.</div></html>");
        l.putClientProperty("FlatLaf.styleClass", "mini");
        content.add(l, c);
        f.setContentPane(content);
        f.pack();
        f.setVisible(true);
    }

I am happy with this solution and I feel it makes more sense than using the tag. I don't know if the admins want to keep this issue open for the \ tag incompatibility, but I consider this a non-issue at this point.

KlemenDEV commented 3 months ago

We use small tag across our codebase and have now noticed this issue too. It would be good id LAF itself handled this correctly instead of having to avoid certain tags

DevCharly commented 3 months ago

Already working on this 😉

DevCharly commented 3 months ago

@KlemenDEV where can I see this issue in MCreator?

KlemenDEV commented 3 months ago

I have not personally replicated it yet, just heard from a dev. I will check asap and let you know

DevCharly commented 3 months ago

@iclkevin @remcopoelstra thanks for reporting and looking into this. 👍

This was difficult to find... Turned out that not the preferred size was too small, but the font used for painting was too large.

In FlatLaf 3.5, HTML is parsed and converted to a View hierarchy, then a BASE_SIZE rule is added to style sheet and caches of view hierarchy are cleared, which later recreates fonts using the new BASE_SIZE:

styleSheet.addRule( "BASE_SIZE " + fontBaseSize );
clearViewCaches( view );

Unfortunately, class FlowView holds a copy of some views in field FlowView.layoutPool and it is not possible to clear font cache in those views. On relayout, views from FlowView.layoutPool are cloned (in GlyphView.createFragment()) and new views use wrong font size. The relayout is triggered by painting the view.

The fix for 3.5.1 now inserts BASE_SIZE rule into HTML.

fixed in latest 3.5.1-SNAPSHOT: https://github.com/JFormDesigner/FlatLaf#snapshots

KlemenDEV commented 3 months ago

I will do more testing and report back if anything is found, but I am assuming our case was fixed too