From fd7386ed837147afbab92ef3338f0cfdf677873d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 17:52:23 +0200 Subject: [PATCH] Move workaround for initial TLW size to wxGTK itself Add wxWindow::WXSetInitialFittingClientSize() instead of handling wxGTK TLWs specially in the common wxSizer code and override it in wxGTK to remember that we need to reset the client size once the window is shown. This commit shouldn't result in any changes in the observed behaviour. --- include/wx/gtk/toplevel.h | 10 ++++++++ include/wx/window.h | 14 +++++++++++ src/common/sizer.cpp | 50 ++------------------------------------- src/common/wincmn.cpp | 19 +++++++++++++++ src/gtk/toplevel.cpp | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 48 deletions(-) diff --git a/include/wx/gtk/toplevel.h b/include/wx/gtk/toplevel.h index 2cf22d8e41..ac2e27a7fd 100644 --- a/include/wx/gtk/toplevel.h +++ b/include/wx/gtk/toplevel.h @@ -138,6 +138,16 @@ public: void GTKDoAfterShow(); +#ifdef __WXGTK3__ + void GTKUpdateClientSizeIfNecessary(); + + virtual void WXSetInitialFittingClientSize(int flags) wxOVERRIDE; + +private: + // Flags to call WXSetInitialFittingClientSize() with if != 0. + int m_pendingFittingClientSizeFlags; +#endif // __WXGTK3__ + protected: // give hints to the Window Manager for how the size // of the TLW can be changed by dragging diff --git a/include/wx/window.h b/include/wx/window.h index ece8b65451..4bc21b75b9 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -139,6 +139,13 @@ enum wxSEND_EVENT_POST = 1 }; +// Flags for WXSetInitialFittingClientSize(). +enum +{ + wxSIZE_SET_CURRENT = 0x0001, // Set the current size. + wxSIZE_SET_MIN = 0x0002 // Set the size as the minimum allowed size. +}; + // ---------------------------------------------------------------------------- // (pseudo)template list classes // ---------------------------------------------------------------------------- @@ -1541,6 +1548,13 @@ public: // window is part of a composite control. bool WXSendContextMenuEvent(const wxPoint& posInScreenCoords); + // This internal function needs to be called to set the fitting client size + // (i.e. the minimum size determined by the window sizer) when the size + // that we really need to use is not known until the window is actually + // shown, as is the case for TLWs with recent GTK versions, as it will + // update the size again when it does become known, if necessary. + virtual void WXSetInitialFittingClientSize(int flags); + // get the handle of the window for the underlying window system: this // is only used for wxWin itself or for user code which wants to call // platform-specific APIs diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index 110a5d69c3..127a557ed6 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -999,31 +999,12 @@ wxSize wxSizer::ComputeFittingWindowSize(wxWindow *window) return window->ClientToWindowSize(ComputeFittingClientSize(window)); } -#ifdef __WXGTK3__ -static void FitOnShow(wxShowEvent& event) -{ - wxWindow* win = static_cast(event.GetEventObject()); - wxSizer* sizer = win->GetSizer(); - if (sizer) - sizer->Fit(win); - win->Unbind(wxEVT_SHOW, FitOnShow); -} -#endif - wxSize wxSizer::Fit( wxWindow *window ) { wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); -#ifdef __WXGTK3__ - // GTK3 updates cached style information before showing a TLW, - // which may affect best size calculations, so add a handler to - // redo the calculations at that time - if (!window->IsShown() && window->IsTopLevel()) - window->Bind(wxEVT_SHOW, FitOnShow); -#endif - // set client size - window->SetClientSize(ComputeFittingClientSize(window)); + window->WXSetInitialFittingClientSize(wxSIZE_SET_CURRENT); // return entire size return window->GetSize(); @@ -1059,38 +1040,11 @@ void wxSizer::Layout() RepositionChildren(minSize); } -#ifdef __WXGTK3__ -static void SetSizeHintsOnShow(wxShowEvent& event) -{ - wxWindow* win = static_cast(event.GetEventObject()); - wxSizer* sizer = win->GetSizer(); - if (sizer) - sizer->SetSizeHints(win); - win->Unbind(wxEVT_SHOW, SetSizeHintsOnShow); -} -#endif - void wxSizer::SetSizeHints( wxWindow *window ) { // Preserve the window's max size hints, but set the // lower bound according to the sizer calculations. - - // This is equivalent to calling Fit(), except that we need to set - // the size hints _in between_ the two steps performed by Fit - // (1. ComputeFittingClientSize, 2. SetClientSize). That's because - // otherwise SetClientSize() could have no effect if there already are - // size hints in effect that forbid requested client size. - -#ifdef __WXGTK3__ - // see comment in Fit() - if (!window->IsShown() && window->IsTopLevel()) - window->Bind(wxEVT_SHOW, SetSizeHintsOnShow); -#endif - - const wxSize clientSize = ComputeFittingClientSize(window); - - window->SetMinClientSize(clientSize); - window->SetClientSize(clientSize); + window->WXSetInitialFittingClientSize(wxSIZE_SET_CURRENT | wxSIZE_SET_MIN); } #if WXWIN_COMPATIBILITY_2_8 diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index cad8c190bf..c43f6f813d 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -1002,6 +1002,25 @@ wxSize wxWindowBase::WindowToClientSize(const wxSize& size) const size.y == -1 ? -1 : size.y - diff.y); } +void wxWindowBase::WXSetInitialFittingClientSize(int flags) +{ + wxSizer* const sizer = GetSizer(); + if ( !sizer ) + return; + + const wxSize + size = sizer->ComputeFittingClientSize(static_cast(this)); + + // It is important to set the min client size before changing the size + // itself as the existing size hints could prevent SetClientSize() from + // working otherwise. + if ( flags & wxSIZE_SET_MIN ) + SetMinClientSize(size); + + if ( flags & wxSIZE_SET_CURRENT ) + SetClientSize(size); +} + void wxWindowBase::SetWindowVariant( wxWindowVariant variant ) { if ( m_windowVariant != variant ) diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index 113da04eaf..e68cf9de0d 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -612,6 +612,10 @@ void wxTopLevelWindowGTK::Init() m_incWidth = m_incHeight = 0; m_urgency_hint = -2; + +#ifdef __WXGTK3__ + m_pendingFittingClientSizeFlags = 0; +#endif // __WXGTK3__ } bool wxTopLevelWindowGTK::Create( wxWindow *parent, @@ -1161,6 +1165,12 @@ bool wxTopLevelWindowGTK::Show( bool show ) bool change = base_type::Show(show); #ifdef __WXGTK3__ + if (change && show) + { + // We may need to redo it after showing the window. + GTKUpdateClientSizeIfNecessary(); + } + if (m_needSizeEvent) { m_needSizeEvent = false; @@ -1525,8 +1535,41 @@ void wxTopLevelWindowGTK::GTKDoAfterShow() wxShowEvent showEvent(GetId(), true); showEvent.SetEventObject(this); HandleWindowEvent(showEvent); + +#ifdef __WXGTK3__ + // Set the client size again if necessary, we should be able to do it + // correctly by now as the style cache should be up to date. + GTKUpdateClientSizeIfNecessary(); +#endif // __WXGTK3__ } +#ifdef __WXGTK3__ + +void wxTopLevelWindowGTK::GTKUpdateClientSizeIfNecessary() +{ + if ( m_pendingFittingClientSizeFlags ) + { + WXSetInitialFittingClientSize(m_pendingFittingClientSizeFlags); + + m_pendingFittingClientSizeFlags = 0; + } +} + +void wxTopLevelWindowGTK::WXSetInitialFittingClientSize(int flags) +{ + // In any case, update the size immediately. + wxTopLevelWindowBase::WXSetInitialFittingClientSize(flags); + + // But if we're not shown yet, the fitting size may be wrong because GTK + // style cache hasn't been updated yet and we need to do it again when the + // window becomes visible as we can be sure that by then we'll be able to + // compute the best size correctly. + if ( !IsShown() ) + m_pendingFittingClientSizeFlags = flags; +} + +#endif // __WXGTK3__ + // ---------------------------------------------------------------------------- // frame title/icon // ----------------------------------------------------------------------------