Merge branch 'gtk-initial-size'

Fix initial size of TLWs in wxGTK when using both sizers and explicit
SetSize() calls.

See https://github.com/wxWidgets/wxWidgets/pull/2322

See #16088, #19134.
This commit is contained in:
Vadim Zeitlin
2021-04-13 00:23:58 +02:00
5 changed files with 123 additions and 54 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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<wxWindow*>(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<wxWindow*>(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

View File

@@ -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<wxWindow *>(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 )

View File

@@ -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
// ----------------------------------------------------------------------------