Hi everyone,
I’m still pretty new to contributing here, so please bear with me if I am
misunderstanding something or missing some history behind this.
I have been using the Qt6/Wayland build on a HiDPI laptop, and I kept noticing
dialogs where text was either getting cut off or wrapping in strange places.
The one that finally made me look into it was the “Select Your Preferred User
Interface” dialog. The description on the right gets clipped, and the
“Toolbars” tab breaks into “Toolba / rs.”
It is a small issue, but it kept bothering me.
I was able to fix that specific dialog by changing the .ui file, and the result
looked fine. The problem is that this only fixes one dialog. If the text
changes later, or another dialog has the same issue, it can easily happen
again. Because of that, I started trying to understand why this is happening
instead of only fixing the individual UI file.
From what I can tell, the main issue is that VCL’s box and grid layouts only
ask a widget for one preferred size through GetOptimalSize() or
get_preferred_size(). The layout then uses that size when deciding how much
space everything should get.
For a wrapping label, FixedText::GetOptimalSize() calculates the preferred size
once, based partly on max-width-chars. It goes through CalcMinimumSize,
CalcMinimumTextSize, and then GetTextRect with WordBreak enabled.
What seems to be missing is a way for the layout to go back afterward and ask
the widget, “Now that you only have this much width, how much height do you
actually need?”
Right now, if VclBox::setAllocation gives a wrapping label less width than its
preferred size, the label does not seem to recalculate its height based on the
new width. It keeps the wider layout and then gets clipped by the smaller
allocation.
The main thing that made me think this is the actual cause is that the same
dialogs look fine when I use SAL_USE_VCLPLUGIN=gtk3. GTK already supports
height-for-width, so it recalculates the required height after the width is
known. The issue seems to mainly affect VCL’s own layout path, which is being
used by qt6, kf6, gen, Windows, and macOS.
What I am thinking about is adding basic height-for-width support to VCL,
similar to what GTK already does.
Something roughly like this:
virtual tools::Long get_preferred_height_for_width(
tools::Long nWidth) const;
virtual bool has_height_for_width() const;
The default get_preferred_height_for_width() implementation could just return
GetOptimalSize().Height(), and has_height_for_width() could return false by
default.
That way, nothing changes for an existing widget unless it specifically opts
into the new behavior.
FixedText could be the first widget to implement it. It could reuse the
GetTextRect wrapping code it already has, but calculate the height using the
actual width passed in by the container.
Later, other wrapping widgets, such as multiline edits or check box and radio
button labels, could add support in the same way.
The containers, such as VclBox and VclGrid, already seem to settle the child
widths before the final allocation. Once the width is known, the container
could check whether the child has height-for-width support and ask for the
correct height at the width it actually received.
The same idea could also be used in setOptimalLayoutSize so the dialog reserves
enough height from the beginning.
I think having a no-op default and making this opt-in per widget would make the
change safer. A widget that does not support height-for-width would continue
behaving exactly the way it does now. It could also be added gradually,
starting with FixedText, instead of changing the behavior of every widget at
once.
The part I am honestly nervous about is that this would touch layout code used
by basically every dialog in the suite. If I make a mistake, it could cause
dialogs to be incorrectly sized in many places.
A few things might make that easier to control.
First, because the behavior would be opt-in, the first patch could be limited
to the new API, FixedText, and maybe VclBox. During testing, the second layout
pass could also be placed behind a runtime flag so the current and new behavior
are easy to compare.
Second, GTK3 gives us a useful reference. Since the same dialogs already lay
out correctly there, I could compare the VCL output against GTK3 across
different dialogs, display scales, and text lengths instead of only checking
one dialog by eye.
Before I start putting a lot of time into this, I wanted to ask a few questions.
-
Is height-for-width support something that would actually be wanted in VCL, or
was it left out intentionally because of performance or another problem?
-
Does the API above make sense, or should this be handled through the existing
GetOptimalSize() or get_preferred_size() system instead?
-
Does an opt-in approach with a no-op default seem reasonable for introducing it
gradually?
-
Has anyone attempted something similar before, or is there an old branch or
patch that I should look at?
-
Would comparing the results against GTK3 be a reasonable testing method, or is
there already a dialog layout testing setup that would be better to extend?
If this sounds like a reasonable direction, I would start small. My first goal
would only be the new API, FixedText, and VclBox, tested against the UI-picker
dialog and a few other dialogs that currently have wrapping or clipping
problems.
I am not trying to submit a huge layout rewrite all at once. I mostly want to
find out whether this is worth working on before I spend a lot of time going
further into it.
Thanks for reading, and I would appreciate any advice or background people can
provide.
Jamie Fitzgerald
[email protected]