-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Enable text wrapping inside a vertical sizer for wxStaticText #25753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…to wxWrapSizer Changed the wxWrapSizer code from using a variable "m_usedLast" to two different functions CalcMinFirstPass() and CalcMin() for two step min size calculation. Added the same logic and naming to controls using GetEffectiveMinSizeFirstPass() Implemented this for the generic code in wxStaticText Added test to wrapsizer sample
So far only tested on GTK+. Will test on OSX later the week. I would appreciate testing on MSW. |
Before looking at it, could you please explain the relationship between this and Also, I don't know if we want to do this unconditionally or if some flag is specified. While people probably don't want their labels being truncated, they may not want them to take more than one line too... And the interaction of this with ellipsize flags is not clear at all. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, there are a number of things to fix here, but more importantly I see 2 changes here that should ideally correspond to 2 commits:
- Addition of
GetEffectiveMinSizeFirstPass()
and changingwxWrapSizer
to use it. - Changes to
wxStaticText
.
(1) is probably fine, but it really needs to be documented.
For (2) I'm still not sure if we want to do it unconditionally or depending on a flag. If nothing else, this almost certainly shouldn't override explicit calls to Wrap()
in the user code as it, I believe, does now.
include/wx/sizer.h
Outdated
// Can be overidden for two step process, e.g. by wxWrapSizer | ||
virtual wxSize CalcMinFirstPass() { return CalcMin(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to explain what the two step process is, because this is really not obvious.
Also, wxWrapSizer
doesn't really need it, as it worked before adding it, so this makes things even more confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wxWrapSizer called CalcMin() several times, but with different behaviour, but instead of using either a flag or two different calls, it set a flag in wxSizer::InformFirstDirection() which altered the behaviour of CalcMin(). I tried to use CalcMinFirstPass() or CalcMin( bool firstpass = false ); for both wrapsizer and a wrapped control, but there sizers and controls are handled differently in wxSizer::Layout()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wxWrapSizer
was, AFAIR, somewhat of a hack and it's great to replace it with something better but this something should be documented/explained.
Maybe documenting it would also give some idea for a better name because, frankly, "first pass" is not great: it doesn't mean anything per se and implies the existence of "second pass" which doesn't actually exist.
|
||
sizerMidWrap->Add(chk, wxSizerFlags().Centre().Border()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid mixing whitespace-only changes with significant ones if possible.
src/common/stattextcmn.cpp
Outdated
// While wxWrapSizer can only wrap entire controls, a text paragraph | ||
// could theoretically wrap at a few letters, so we start with | ||
// requesting very little space in the first pass | ||
return wxSize( 10, 10 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we avoid hardcoding magic numbers please? It would make sense to use GetCharWidth()
or something else based it.
src/common/wincmn.cpp
Outdated
// merge the best size with the min size, giving priority to the min size | ||
wxSize min = GetMinSize(); | ||
|
||
const wxStaticText *text = dynamic_cast<const wxStaticText*>(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this and all the other changes to this file.
The code in wxSizer() always assumed this behaviour for wxWrapSizer and wxStaticText. It just https://docs.wxwidgets.org/3.2/classwx_window.html#a9fd5b6520c1b30eb8e82bb5d56bc24c0
This only happens inside of a vertical sizer, which calls InformsFirstDirection() to for the sole |
Co-authored-by: VZ <[email protected]>
Co-authored-by: VZ <[email protected]>
Co-authored-by: VZ <[email protected]>
Wait, does this mean that wrapping still won't work in a Can we make it work for 2D sizers too? |
It should work there as well. I meant any vertical compartment in any sizer. And with my code it works for controls as well, although I cannot think of any apart from display of text. Well, a text editor or a HTML window, maybe. |
Use GetCharHeight() and GetCharWidth() as minimal size for wrapping wxStaticText Removed accidentally committed debug code Added clarifying comments to new wrapping code in wxStaticText
I can confirm that the PR works on GTK, OSX and MSW. Thanks for the technical and mental support from VZ for setting git up and making this possible. |
Could you please check the CI failures? There are some trivial to fix compile errors, but this also broke |
I will look at the compile errors, but doing further test scenarios I encountered a situation that might constitute an unexpected change of behaviour. If you put a vertical wxStaticBoxSizer in a dialog and you add two wxStaticText elements into it, the previous code stretched the wxStaticBoxSizer horizontally until the wxStaticText would fit it using a single line each. Previously, there was no way to make the wxStaticText wrap elegantly if the text was too long - you could just set a hard wrap at a e.g. 200px. But the point is: if your dialog layout relied on an expectation that the text would force the owning sizer to stretch, then that layout will be broken with the new code. Here is my artistic explanation Now I need to think if we need a wxST_WRAP_IN_SIZER or a wxWrapStaticText in order not to break anything. |
FWIW, I didn't have time to really think about this yet, but since the very beginning I thought we would add a style ( |
This make it possible to choose between the three wrap modes: - Don't wrap at all (and force owning size to stretch out to accomodate whole length on a single line) - Fixed wrapping when falling wxStaticText::Wrap() - Dynamic wrapping, typically with very long text Added test for all three modes side by side in the wrapsizer sample Documented new style flag
Added wxST_WRAP, fixed tests and spelling mistakes |
Also added test for this in wrapsizer sample
std::string::find_last_of does not work with complex characters in UTF8 mode AFAIK
They were semantically broken by the recent changes.
Try to make it more clear and correct various typos.
Use wxSize::SetDefaults() instead of reimplementing it to make it more obvious what happens here. No real changes.
Rename them to have a name actually meaning something to people not familiar with the details of the wxSizer layout algorithm who don't even know that it makes 2 passes. Also call the new wxWindow function GetMinSizeUsingLayoutDirection(), without the "Effective" part, as GetEffectiveMinSize() is supposed to be just a trivial wrapper around GetMinSize() and should have never been virtual and overridable in the first place, even if it was unfortunately made so back in a20a357 (Make GetEffectiveMinSize virtual as not being able to override the method, which the sizer system depends on, is inflexible, 2009-01-30).
Use multiple lines for the long label to prevent it from making the contents of the page too wide. Do make the static box take all available space, if there is any.
The text always fits initially, as the page size is computed using it, so choosing to ellipsize it has no effect.
Add a checkbox to allow turning on wxST_WRAP.
No real changes, just don't use "dummy text" which doesn't mean anything even though it has been present since 39bc034 (added support for ellipsization and markup in wxStaticText (modified patch 1629946), 2007-04-01) and call the flags used for plain text and text with markup just "flagsText" and "flagsMarkupText" respectively. This makes the code a bit more understandable.
…imilar to wxWrapSizer
Don't return ridiculously small width and, even more importantly, don't return single line height when a wrapped label typically has more than one line.
…imilar to wxWrapSizer
…imilar to wxWrapSizer
This reverts 9d7c647 (Incorporated PR wxWidgets#751 to work with zero width spaces, 2025-09-23) and 6f03759 (Replaced the code with one that works in UTF8 mode, 2025-09-23) before reimplementing this in a better and simpler way.
Break lines on zero width space and other Unicode characters with the "white space" property, but not non-breaking ones. Closes wxWidgets#751.
I've done some changes here to make this more or less mergeable. Not counting minor corrections (including those needed to make CI builds pass), they are:
I'm still unhappy about several things but I don't have time to work on them myself:
|
Hi again, what is the current plan on this PR? I read your comments that not everything is perfect, but I always get nervous if a PR is waiting in queue. Further improvements could also be in later PR and the current PR does solve the issue it is supposed to solve (and the code was actually planning to solve 15 years ago). |
I hoped to work on (1) this weekend but I unfortunately couldn't find time to do it for unrelated reasons. Thinking more about this, we really shouldn't build more bad API on the existing bad |
This is the weekly reminder that the API was good enough between 2008 and 2025 - and maybe can be improved after merging? |
Yes, the existing API is poor but let's at least avoid adding a new function which is equally poor or worse. I'll try to get this a.s.a.p., I was busy with other things the last 2 weeks but hope to have more time for wx again soon. |
Changed the wxWrapSizer code from using a variable "m_usedLast" to two different functions
CalcMinFirstPass() and CalcMin() for the two step min size calculation.
Added the same logic and naming to controls using GetEffectiveMinSizeFirstPass()
Implemented this for the generic code in wxStaticTextBase.
Added test to wrapsizer sample.