From e6500a4e313e3ba11d1880a2897436cc637c8e30 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 24 Aug 2024 15:06:35 +0200 Subject: [PATCH] Compute wxStaticText best size ourselves if GTK does it wrongly GTK doesn't take into account changes to the font of a hidden wxStaticText and returns wrong preferred (i.e. best) size for it which still uses the old font in this case, which results in all sorts of problems when this wrong best size is used for the layout. Work around it by computing the best size ourselves in this case using the correct font. This seems to return exactly the same results as GTK returns as long as the font is not changed, but also works correctly when it is. This commit is best viewed ignoring whitespace-only changes. See #16088. --- include/wx/gtk/stattext.h | 3 ++ src/gtk/stattext.cpp | 86 +++++++++++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/include/wx/gtk/stattext.h b/include/wx/gtk/stattext.h index 13c2124c33..f1b7cab2f6 100644 --- a/include/wx/gtk/stattext.h +++ b/include/wx/gtk/stattext.h @@ -61,6 +61,9 @@ private: void GTKDoSetLabel(GTKLabelSetter setter, const wxString& label); + // If our font has been changed, we compute the best size ourselves because + // GTK doesn't always do it correctly, see DoGetBestSize(). + bool m_computeOurOwnBestSize = false; wxDECLARE_DYNAMIC_CLASS(wxStaticText); }; diff --git a/src/gtk/stattext.cpp b/src/gtk/stattext.cpp index 6c957dc9e8..04bf309de3 100644 --- a/src/gtk/stattext.cpp +++ b/src/gtk/stattext.cpp @@ -13,6 +13,14 @@ #include "wx/stattext.h" +#ifndef WX_PRECOMP + #include "wx/dcclient.h" +#endif + +#if wxUSE_MARKUP + #include "wx/generic/private/markuptext.h" +#endif // wxUSE_MARKUP + #include "wx/gtk/private.h" //----------------------------------------------------------------------------- @@ -177,6 +185,14 @@ bool wxStaticText::SetFont( const wxFont &font ) if ( !wxControl::SetFont(font) ) return false; + if ( !IsShownOnScreen() ) + { + // Setting the font of a hidden window doesn't update GTK style cache, + // see #16088, and the size computed by GTK will be wrong, so we will + // need to compute it ourselves. + m_computeOurOwnBestSize = true; + } + const bool isUnderlined = GetFont().GetUnderlined(); const bool isStrickenThrough = GetFont().GetStrikethrough(); @@ -227,32 +243,58 @@ wxSize wxStaticText::DoGetBestSize() const // Do not return any arbitrary default value... wxASSERT_MSG( m_widget, wxT("wxStaticText::DoGetBestSize called before creation") ); - // GetBestSize is supposed to return unwrapped size but calling - // gtk_label_set_line_wrap() from here is a bad idea as it queues another - // size request by calling gtk_widget_queue_resize() and we end up in - // infinite loop sometimes (notably when the control is in a toolbar) - // With GTK3 however, there is no simple alternative, and the sizing loop - // no longer seems to occur. -#ifdef __WXGTK3__ - gtk_label_set_line_wrap(GTK_LABEL(m_widget), false); -#else - GTK_LABEL(m_widget)->wrap = FALSE; + wxSize size; + if ( m_computeOurOwnBestSize ) + { + // GTK style cache may not be up to date, so we can't trust the results + // of wxControl::DoGetBestSize() and need to compute the best size + // ourselves here. + wxInfoDC dc(wxConstCast(this, wxStaticText)); - // Reset the ellipsize mode while computing the best size, otherwise it's - // going to be too small as the control knows that it can be shrunk to the - // bare minimum and just hide most of the text replacing it with ellipsis. - // This is especially important because we can enable ellipsization - // implicitly for GTK+ 2, see the code dealing with alignment in the ctor. - const PangoEllipsizeMode ellipsizeMode = gtk_label_get_ellipsize(GTK_LABEL(m_widget)); - gtk_label_set_ellipsize(GTK_LABEL(m_widget), PANGO_ELLIPSIZE_NONE); -#endif - wxSize size = wxStaticTextBase::DoGetBestSize(); + const wxString + label = wxString::FromUTF8(gtk_label_get_label(GTK_LABEL(m_widget))); + +#if wxUSE_MARKUP + if ( gtk_label_get_use_markup(GTK_LABEL(m_widget)) ) + { + wxMarkupText markupText(label); + size = markupText.Measure(dc); + } + else +#endif // wxUSE_MARKUP + { + size = dc.GetMultiLineTextExtent(label); + } + } + else + { + // GetBestSize is supposed to return unwrapped size but calling + // gtk_label_set_line_wrap() from here is a bad idea as it queues another + // size request by calling gtk_widget_queue_resize() and we end up in + // infinite loop sometimes (notably when the control is in a toolbar) + // With GTK3 however, there is no simple alternative, and the sizing loop + // no longer seems to occur. #ifdef __WXGTK3__ - gtk_label_set_line_wrap(GTK_LABEL(m_widget), true); + gtk_label_set_line_wrap(GTK_LABEL(m_widget), false); #else - gtk_label_set_ellipsize(GTK_LABEL(m_widget), ellipsizeMode); - GTK_LABEL(m_widget)->wrap = TRUE; // restore old value + GTK_LABEL(m_widget)->wrap = FALSE; + + // Reset the ellipsize mode while computing the best size, otherwise it's + // going to be too small as the control knows that it can be shrunk to the + // bare minimum and just hide most of the text replacing it with ellipsis. + // This is especially important because we can enable ellipsization + // implicitly for GTK+ 2, see the code dealing with alignment in the ctor. + const PangoEllipsizeMode ellipsizeMode = gtk_label_get_ellipsize(GTK_LABEL(m_widget)); + gtk_label_set_ellipsize(GTK_LABEL(m_widget), PANGO_ELLIPSIZE_NONE); #endif + size = wxStaticTextBase::DoGetBestSize(); +#ifdef __WXGTK3__ + gtk_label_set_line_wrap(GTK_LABEL(m_widget), true); +#else + gtk_label_set_ellipsize(GTK_LABEL(m_widget), ellipsizeMode); + GTK_LABEL(m_widget)->wrap = TRUE; // restore old value +#endif + } // Adding 1 to width to workaround GTK sometimes wrapping the text needlessly size.x++;