diff --git a/include/wx/gtk/toplevel.h b/include/wx/gtk/toplevel.h index 8f0ad658b1..88abfd03cf 100644 --- a/include/wx/gtk/toplevel.h +++ b/include/wx/gtk/toplevel.h @@ -136,6 +136,20 @@ public: void GTKUpdateDecorSize(const DecorSize& decorSize); + void GTKDoAfterShow(); + +#ifdef __WXGTK3__ + void GTKUpdateClientSizeIfNecessary(); + + virtual void SetMinSize(const wxSize& minSize) wxOVERRIDE; + + 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 f852c6670c..999dfa580c 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 @@ -614,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, @@ -1163,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; @@ -1272,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); @@ -1499,9 +1520,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 +1543,54 @@ wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize() return size[index]; } +void wxTopLevelWindowGTK::GTKDoAfterShow() +{ +#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__ + +void wxTopLevelWindowGTK::GTKUpdateClientSizeIfNecessary() +{ + if ( m_pendingFittingClientSizeFlags ) + { + WXSetInitialFittingClientSize(m_pendingFittingClientSizeFlags); + + m_pendingFittingClientSizeFlags = 0; + } +} + +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. + 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 // ----------------------------------------------------------------------------