-
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.
@@ -411,6 +411,7 @@ class WXDLLIMPEXP_CORE wxWindowBase : public wxEvtHandler | |||
// minimum size, giving priority to the min size components, and | |||
// returns the results. | |||
virtual wxSize GetEffectiveMinSize() const; | |||
virtual wxSize GetEffectiveMinSizeFirstPass() const { return GetEffectiveMinSize(); } |
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.
This should really be documented (in a comment here and in interface/wx/window.h
) because I just have no idea how to use it right now.
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.
Right, will do
@@ -72,6 +77,12 @@ class WXDLLIMPEXP_CORE wxStaticTextBase : public wxControl | |||
// display. | |||
void UpdateLabel(); | |||
|
|||
// save unwrapped label to allow to call Wrap() several times and | |||
// always starting from the original, unwrapped label | |||
wxString m_unwrappedLabel; |
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.
Is this different from wxControl::m_labelOrig
?
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.
I could not figure out of the stripping of markup and/or mnemonics and/or the ellipsis code would alter m_labelOrig. I think m_labelOrig is the before any change, and m_unwrappedLabel is after the other changes, but before wrapping.
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.
I think you're right, but is it really worth storing it separately instead of just stripping the other stuff from m_labelOrig
?
It's not just about optimizing memory usage but also, in more places we store redundant information, more places we need to remember to update later.
// save unwrapped label to allow to call Wrap() several times and | ||
// always starting from the original, unwrapped label | ||
wxString m_unwrappedLabel; | ||
int m_currentWrap = 0; |
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.
What is the value of this? Wrap width? With 0
meaning "none"? Please comment it too.
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.
0 means no wrapping (will add comment). The current value needs to be saved to prevent uncountable recalculations of the wrapping code during the wxSizer Layout() process (as you probably guessed).
@@ -118,10 +118,13 @@ WrapSizerFrame::WrapSizerFrame() | |||
|
|||
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
@@ -859,21 +859,39 @@ wxSize wxWindowBase::GetEffectiveMinSize() const | |||
// 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 |
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.