From 329f60d7f3beb88da49dc05fb1c9be37e66f3eb2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 17:27:34 +0200 Subject: [PATCH 1/5] Factor out wxTopLevelWindowGTK::GTKDoAfterShow() Just extract the code generating wxEVT_SHOW for TLWs in wxGTK in its own function before modifying it to avoid having to do it in two places. No real changes, this is a pure refactoring. --- include/wx/gtk/toplevel.h | 2 ++ src/gtk/toplevel.cpp | 16 ++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/wx/gtk/toplevel.h b/include/wx/gtk/toplevel.h index 8f0ad658b1..2cf22d8e41 100644 --- a/include/wx/gtk/toplevel.h +++ b/include/wx/gtk/toplevel.h @@ -136,6 +136,8 @@ public: void GTKUpdateDecorSize(const DecorSize& decorSize); + void GTKDoAfterShow(); + protected: // give hints to the Window Manager for how the size // of the TLW can be changed by dragging diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index f852c6670c..113da04eaf 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -445,9 +445,7 @@ gtk_frame_map_callback( GtkWidget*, // it is possible for m_isShown to be false here, see bug #9909 if (win->wxWindowBase::Show(true)) { - wxShowEvent eventShow(win->GetId(), true); - eventShow.SetEventObject(win); - win->GetEventHandler()->ProcessEvent(eventShow); + win->GTKDoAfterShow(); } // restore focus-on-map setting in case ShowWithoutActivating() was called @@ -1499,9 +1497,8 @@ void wxTopLevelWindowGTK::GTKUpdateDecorSize(const DecorSize& decorSize) SendSizeEvent(); } #endif - wxShowEvent showEvent(GetId(), true); - showEvent.SetEventObject(this); - HandleWindowEvent(showEvent); + + GTKDoAfterShow(); } #endif // GDK_WINDOWING_X11 } @@ -1523,6 +1520,13 @@ wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize() return size[index]; } +void wxTopLevelWindowGTK::GTKDoAfterShow() +{ + wxShowEvent showEvent(GetId(), true); + showEvent.SetEventObject(this); + HandleWindowEvent(showEvent); +} + // ---------------------------------------------------------------------------- // frame title/icon // ---------------------------------------------------------------------------- From fd7386ed837147afbab92ef3338f0cfdf677873d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 17:52:23 +0200 Subject: [PATCH 2/5] 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 // ---------------------------------------------------------------------------- From 645e1d2fd0eaaa1db4a3ae4590d303784584aad2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 17:58:31 +0200 Subject: [PATCH 3/5] Don't overwrite explicitly set size with pending client size Calls to SetSize() were ignored in wxGTK if they happened after calling wxSizer::Fit() since the changes of f655a52fba (Allow wxSizer::Fit() to work properly when called from TLW ctor on GTK3, 2020-04-20) because we overwrote the explicitly set size with the client size computed earlier by the sizer. Don't do this by explicitly forgetting the client size we were going to set when SetSize() is called. Closes #19134. --- src/gtk/toplevel.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index e68cf9de0d..d5cf3268e3 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -1280,6 +1280,19 @@ void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int si m_deferShowAllowed = true; m_useCachedClientSize = false; +#ifdef __WXGTK3__ + // Reset pending client size, it is not relevant any more and shouldn't + // be set when the window is shown, as we don't want it to replace the + // size explicitly specified here. Note that we do still want to set + // the minimum client size, as increasing the total size shouldn't + // allow shrinking the frame beyond its minimum fitting size. + // + // Also note that if we're called from WXSetInitialFittingClientSize() + // itself, this will be overwritten again with the pending flags when + // we return. + m_pendingFittingClientSizeFlags &= ~wxSIZE_SET_CURRENT; +#endif // __WXGTK3__ + int w, h; GTKDoGetSize(&w, &h); gtk_window_resize(GTK_WINDOW(m_widget), w, h); From b5a78fbac6b1cfd56861d123a3e5617ac2854c96 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 18:09:37 +0200 Subject: [PATCH 4/5] Also make SetMinSize() after wxSizer::Fit() work correctly This fixes a problem similar to that fixed in the previous commit but for SetMinSize(), which was also ignored if done after calling wxSizer::Fit() and before showing the window, as the explicitly set min size was also overwritten by the pending min size computed from the client size corresponding to the sizer fitting size. The fix is similar too: just invalidate the pending minimum size if SetMinSize() is called. --- include/wx/gtk/toplevel.h | 2 ++ src/gtk/toplevel.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/wx/gtk/toplevel.h b/include/wx/gtk/toplevel.h index ac2e27a7fd..88abfd03cf 100644 --- a/include/wx/gtk/toplevel.h +++ b/include/wx/gtk/toplevel.h @@ -141,6 +141,8 @@ public: #ifdef __WXGTK3__ void GTKUpdateClientSizeIfNecessary(); + virtual void SetMinSize(const wxSize& minSize) wxOVERRIDE; + virtual void WXSetInitialFittingClientSize(int flags) wxOVERRIDE; private: diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index d5cf3268e3..ec2b6e5b51 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -1568,6 +1568,14 @@ void wxTopLevelWindowGTK::GTKUpdateClientSizeIfNecessary() } } +void wxTopLevelWindowGTK::SetMinSize(const wxSize& minSize) +{ + wxTopLevelWindowBase::SetMinSize(minSize); + + // Explicitly set minimum size should override the pending size, if any. + m_pendingFittingClientSizeFlags &= ~wxSIZE_SET_MIN; +} + void wxTopLevelWindowGTK::WXSetInitialFittingClientSize(int flags) { // In any case, update the size immediately. From 2e1641870785e037abaaea3f12c265dde3ca1dad Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Apr 2021 21:27:32 +0200 Subject: [PATCH 5/5] Update TLW client size in wxGTK before sending wxEVT_SHOW If we have to change the client size, it's better to do this before sending the event whose handler might rely on the size being already correct. Co-Authored-By: Paul Cornett --- src/gtk/toplevel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index ec2b6e5b51..999dfa580c 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -1545,15 +1545,15 @@ wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize() 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__ + + wxShowEvent showEvent(GetId(), true); + showEvent.SetEventObject(this); + HandleWindowEvent(showEvent); } #ifdef __WXGTK3__