Merge branch 'aui-repaint'

Fix some wxAUI repaint problems and switch to using live resize
everywhere by default.

See #24166.
This commit is contained in:
Vadim Zeitlin 2024-01-10 18:43:24 +01:00
commit afc635f845
19 changed files with 174 additions and 81 deletions

View file

@ -51,7 +51,8 @@ enum wxAuiManagerOption
wxAUI_MGR_DEFAULT = wxAUI_MGR_ALLOW_FLOATING |
wxAUI_MGR_TRANSPARENT_HINT |
wxAUI_MGR_HINT_FADE |
wxAUI_MGR_NO_VENETIAN_BLINDS_FADE
wxAUI_MGR_NO_VENETIAN_BLINDS_FADE |
wxAUI_MGR_LIVE_RESIZE
};
@ -416,7 +417,7 @@ public:
void SetFlags(unsigned int flags);
unsigned int GetFlags() const;
static bool AlwaysUsesLiveResize();
static bool AlwaysUsesLiveResize(const wxWindow* window = nullptr);
bool HasLiveResize() const;
void SetManagedWindow(wxWindow* managedWnd);

View file

@ -128,6 +128,8 @@ public:
virtual wxDCImpl* CreatePrinterDC( wxPrinterDC *owner, const wxPrintData &data ) = 0;
#endif
virtual bool CanDrawUsingClientDC(const wxWindow* window) const = 0;
static void Set(wxDCFactory *factory);
static wxDCFactory *Get();
@ -154,6 +156,8 @@ public:
#if wxUSE_PRINTING_ARCHITECTURE
virtual wxDCImpl* CreatePrinterDC( wxPrinterDC *owner, const wxPrintData &data ) override;
#endif
virtual bool CanDrawUsingClientDC(const wxWindow* window) const override;
};
//-----------------------------------------------------------------------------

View file

@ -36,6 +36,8 @@ class WXDLLIMPEXP_CORE wxClientDC : public wxWindowDC
public:
wxClientDC(wxWindow *win);
static bool CanBeUsedForDrawing(const wxWindow* window);
protected:
wxClientDC(wxDCImpl *impl) : wxWindowDC(impl) { }

View file

@ -51,6 +51,9 @@ public:
wxClientDCImpl(wxDC *owner) : wxWindowDCImpl(owner) { }
wxClientDCImpl(wxDC *owner, wxWindow *win);
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; }
wxDECLARE_DYNAMIC_CLASS(wxClientDCImpl);
wxDECLARE_NO_COPY_CLASS(wxClientDCImpl);
};

View file

@ -131,6 +131,9 @@ public:
// Is the window split?
bool IsSplit() const { return (m_windowTwo != nullptr); }
// Return true if wxSP_LIVE_UPDATE is always used.
bool AlwaysUsesLiveUpdate() const;
// Sets the border size
void SetBorderSize(int WXUNUSED(width)) { }

View file

@ -64,6 +64,8 @@ class wxClientDCImpl: public wxGTKCairoDCImpl
public:
wxClientDCImpl(wxClientDC* owner, wxWindow* window);
static bool CanBeUsedForDrawing(const wxWindow* window);
wxDECLARE_NO_COPY_CLASS(wxClientDCImpl);
};
//-----------------------------------------------------------------------------

View file

@ -150,6 +150,9 @@ public:
virtual void DoGetSize(int *width, int *height) const override;
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; }
wxDECLARE_ABSTRACT_CLASS(wxClientDCImpl);
};

View file

@ -56,6 +56,9 @@ public:
virtual void DoGetSize(int *width, int *height) const override;
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; }
protected:
void InitDC();

View file

@ -54,6 +54,9 @@ public:
wxClientDCImpl( wxDC *owner, wxWindow *window );
virtual ~wxClientDCImpl();
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return false; }
private:
wxDECLARE_CLASS(wxClientDCImpl);
wxDECLARE_NO_COPY_CLASS(wxClientDCImpl);

View file

@ -40,6 +40,9 @@ public:
wxClientDCImpl( wxDC *owner );
wxClientDCImpl( wxDC *owner, wxWindow *win );
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return false; }
private:
wxDECLARE_CLASS(wxClientDCImpl);
wxDECLARE_NO_COPY_CLASS(wxClientDCImpl);

View file

@ -153,6 +153,9 @@ public:
wxClientDCImpl( wxDC *owner ) : wxWindowDCImpl( owner ) { }
wxClientDCImpl( wxDC *owner, wxWindow *win );
static bool
CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; }
protected:
virtual void DoGetSize(int *width, int *height) const;

View file

@ -45,12 +45,14 @@ enum wxAuiManagerOption
wxAUI_MGR_NO_VENETIAN_BLINDS_FADE = 1 << 7,
/// When a docked pane is resized, its content is refreshed in live (instead of moving
/// the border alone and refreshing the content at the end).
/// Since wxWidgets 3.3.0 this flag is included in the default flags.
wxAUI_MGR_LIVE_RESIZE = 1 << 8,
/// Default behaviour.
wxAUI_MGR_DEFAULT = wxAUI_MGR_ALLOW_FLOATING |
wxAUI_MGR_TRANSPARENT_HINT |
wxAUI_MGR_HINT_FADE |
wxAUI_MGR_NO_VENETIAN_BLINDS_FADE
wxAUI_MGR_NO_VENETIAN_BLINDS_FADE |
wxAUI_MGR_LIVE_RESIZE
};
/**
@ -144,7 +146,11 @@ enum wxAuiManagerOption
docking hint immediately.
@style{wxAUI_MGR_LIVE_RESIZE}
When a docked pane is resized, its content is refreshed in live (instead of moving
the border alone and refreshing the content at the end).
the border alone and refreshing the content at the end). Note that
this flag is included in wxAUI_MGR_DEFAULT and so needs to be
explicitly turned off if you don't need. Also note that it is
always enabled in wxGTK3 and wxOSX ports as non-live resizing is not
implemented in them.
@style{wxAUI_MGR_DEFAULT}
Default behaviour, combines: wxAUI_MGR_ALLOW_FLOATING | wxAUI_MGR_TRANSPARENT_HINT |
wxAUI_MGR_HINT_FADE | wxAUI_MGR_NO_VENETIAN_BLINDS_FADE.
@ -215,12 +221,16 @@ public:
If this function returns true, ::wxAUI_MGR_LIVE_RESIZE flag is ignored
and live resize is always used, whether it's specified or not.
Currently this is the case for wxOSX and wxGTK3 ports, as live resizing
is the only implemented method there.
Currently this is the case for wxOSX and wxGTK3 when using Wayland, as
live resizing is the only implemented method there. See
wxClientDC::CanBeUsedForDrawing() for more details.
@param window The associated window, may be null (this parameter was
added in wxWidgets 3.3.0)
@since 3.1.4
*/
static bool AlwaysUsesLiveResize();
static bool AlwaysUsesLiveResize(const wxWindow* window);
/**
This function is used by controls to calculate the drop hint rectangle.

View file

@ -63,12 +63,13 @@ public:
window from outside an EVT_PAINT() handler in some ports, this does @em not
work on most of the platforms: neither wxOSX nor wxGTK with GTK 3 Wayland
backend support this at all, so drawing using wxClientDC simply doesn't
have any effect there, while wxMSW doesn't support using it for composited
windows, so wxWindow::MSWDisableComposited() must be called to allow it to
work. The only supported way of drawing on a window is via wxPaintDC. To
redraw a small part of the window, use wxWindow::RefreshRect() to
invalidate just this part and check wxWindow::GetUpdateRegion() in the
paint event handler to redraw this part only.
have any effect there. CanBeUsedForDrawing() can be used to determine
whether wxClientDC can be used for drawing in the current environment, but
it is recommended to only draw on the window using wxPaintDC, as this is
guaranteed to work everywhere. To redraw a small part of the window, use
wxWindow::RefreshRect() to invalidate just this part and check
wxWindow::GetUpdateRegion() in the paint event handler to redraw this part
only.
wxClientDC objects should normally be constructed as temporary stack
objects, i.e. don't store a wxClientDC object.
@ -88,6 +89,23 @@ public:
Constructor. Pass a pointer to the window on which you wish to paint.
*/
wxClientDC(wxWindow* window);
/**
Return true if drawing on wxClientDC actually works.
In many environments (currently this includes wxGTK when using Wayland
backend, wxMSW when using double buffering and wxOSX in all cases),
wxClientDC can be only used for obtaining information about the device
context, but not for actually drawing on it. Portable code should avoid
using wxClientDC completely, as explained in the class documentation,
but it is also possible to optionally use it only when it does work,
i.e. when this function returns @true.
@param window The window that would be used with wxClientDC.
@since 3.3.0
*/
static bool CanBeUsedForDrawing(const wxWindow* window);
};

View file

@ -142,6 +142,20 @@ public:
*/
virtual ~wxSplitterWindow();
/**
Returns true if splitter always behaves as if wxSP_LIVE_UPDATE were
specified.
This function returns true if non-live update mode is not supported and
live update is always used, even if wxSP_LIVE_UPDATE was not explicitly
specified.
@see wxClientDC::CanBeUsedForDrawing()
@since 3.3.0
*/
bool AlwaysUsesLiveUpdate() const;
/**
Creation function, for two-step construction.
See wxSplitterWindow() for details.

View file

@ -254,7 +254,7 @@ MyFrame::MyFrame()
"Toggle sash invisibility");
splitMenu->AppendSeparator();
splitMenu->AppendCheckItem(SPLIT_LIVE,
auto itemLive = splitMenu->AppendCheckItem(SPLIT_LIVE,
"&Live update\tCtrl-L",
"Toggle live update mode");
splitMenu->AppendCheckItem(SPLIT_BORDER,
@ -306,6 +306,12 @@ MyFrame::MyFrame()
menuBar->Check(SPLIT_LIVE, true);
m_splitter = new MySplitterWindow(this);
if ( m_splitter->AlwaysUsesLiveUpdate() )
{
// Only live update mode is supported, so this menu item can't be used.
itemLive->Enable(false);
}
// If you use non-zero gravity you must initialize the splitter with its
// correct initial size, otherwise it will change the sash position by a
// huge amount when it's resized from its initial default size to its real

View file

@ -64,6 +64,8 @@ wxDEFINE_EVENT( wxEVT_AUI_FIND_MANAGER, wxAuiManagerEvent );
#include "wx/msw/dc.h"
#endif
#include <memory>
wxIMPLEMENT_DYNAMIC_CLASS(wxAuiManagerEvent, wxEvent);
wxIMPLEMENT_CLASS(wxAuiManager, wxEvtHandler);
@ -277,16 +279,21 @@ static wxBitmap wxPaneCreateStippleBitmap()
static void DrawResizeHint(wxDC& dc, const wxRect& rect)
{
#ifdef __WXMSW__
wxBitmap stipple = wxPaneCreateStippleBitmap();
wxBrush brush(stipple);
dc.SetBrush(brush);
#ifdef __WXMSW__
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
PatBlt(GetHdcOf(*impl), rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight(), PATINVERT);
#else
dc.SetPen(*wxTRANSPARENT_PEN);
// Note that we have to use white colour for wxINVERT to work with
// wxGraphicsContext-based wxDC implementations, such as used by wxGTK3
// (and wxOSX, but this code is never used for the latter because it always
// uses live resize).
dc.SetPen(*wxWHITE_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetLogicalFunction(wxXOR);
dc.SetLogicalFunction(wxINVERT);
dc.DrawRectangle(rect);
#endif
}
@ -760,26 +767,17 @@ unsigned int wxAuiManager::GetFlags() const
return m_flags;
}
// With Core Graphics on Mac or GTK 3, it's not possible to show sash feedback,
// so we'll always use live update instead.
#if defined(__WXMAC__) || defined(__WXGTK3__) || defined(__WXQT__)
#define wxUSE_AUI_LIVE_RESIZE_ALWAYS 1
#else
#define wxUSE_AUI_LIVE_RESIZE_ALWAYS 0
#endif
/* static */ bool wxAuiManager::AlwaysUsesLiveResize()
/* static */ bool wxAuiManager::AlwaysUsesLiveResize(const wxWindow* window)
{
return wxUSE_AUI_LIVE_RESIZE_ALWAYS;
// Not using live resize relies on wxClientDC being usable for drawing, so
// we have to use live resize if it can't be used on the current platform.
return !wxClientDC::CanBeUsedForDrawing(window);
}
bool wxAuiManager::HasLiveResize() const
{
#if wxUSE_AUI_LIVE_RESIZE_ALWAYS
return true;
#else
return (GetFlags() & wxAUI_MGR_LIVE_RESIZE) == wxAUI_MGR_LIVE_RESIZE;
#endif
return AlwaysUsesLiveResize(m_frame) ||
(GetFlags() & wxAUI_MGR_LIVE_RESIZE) == wxAUI_MGR_LIVE_RESIZE;
}
// don't use these anymore as they are deprecated
@ -3891,28 +3889,28 @@ void wxAuiManager::Render(wxDC* dc)
void wxAuiManager::Repaint(wxDC* dc)
{
#if defined(__WXMAC__) || defined(__WXGTK3__) || defined(__WXQT__)
// We can't use wxClientDC in these ports.
if ( dc == nullptr )
{
m_frame->Refresh() ;
m_frame->Update() ;
return ;
}
#endif
int w, h;
m_frame->GetClientSize(&w, &h);
std::unique_ptr<wxClientDC> client_dc;
// figure out which dc to use; if one
// has been specified, use it, otherwise
// make a client dc
wxClientDC* client_dc = nullptr;
if (!dc)
{
client_dc = new wxClientDC(m_frame);
dc = client_dc;
if ( AlwaysUsesLiveResize(m_frame) )
{
// We can't use wxClientDC in these ports.
m_frame->Refresh() ;
m_frame->Update() ;
return ;
}
client_dc.reset(new wxClientDC(m_frame));
dc = client_dc.get();
}
int w, h;
m_frame->GetClientSize(&w, &h);
// if the frame has a toolbar, the client area
// origin will not be (0,0).
wxPoint pt = m_frame->GetClientAreaOrigin();
@ -3921,10 +3919,6 @@ void wxAuiManager::Repaint(wxDC* dc)
// render all the items
Render(dc);
// if we created a client_dc, delete it
if (client_dc)
delete client_dc;
}
void wxAuiManager::OnDestroy(wxWindowDestroyEvent& event)
@ -4070,7 +4064,14 @@ void wxAuiManager::UpdateButtonOnScreen(wxAuiDockUIPart* button_ui_part,
state = wxAUI_BUTTON_STATE_HOVER;
}
// now repaint the button with hover state
// now repaint the button with hover state -- or everything if we can't
// repaint just it
if ( !wxClientDC::CanBeUsedForDrawing(m_frame) )
{
m_frame->Refresh();
m_frame->Update();
}
wxClientDC cdc(m_frame);
// if the frame has a toolbar, the client area
@ -4452,7 +4453,7 @@ void wxAuiManager::OnLeftUp(wxMouseEvent& event)
if (!HasLiveResize())
{
// get rid of the hint rectangle
wxScreenDC dc;
wxClientDC dc{m_frame};
DrawResizeHint(dc, m_actionHintRect);
}
if (m_currentDragItem != -1 && HasLiveResize())
@ -4567,9 +4568,8 @@ void wxAuiManager::OnMotion(wxMouseEvent& event)
}
else
{
wxRect rect(m_frame->ClientToScreen(pos),
m_actionPart->rect.GetSize());
wxScreenDC dc;
wxRect rect(pos, m_actionPart->rect.GetSize());
wxClientDC dc{m_frame};
if (!m_actionHintRect.IsEmpty())
{
@ -4578,13 +4578,9 @@ void wxAuiManager::OnMotion(wxMouseEvent& event)
m_actionHintRect = wxRect();
}
// draw new resize hint, if it's inside the managed frame
wxRect frameScreenRect = m_frame->GetScreenRect();
if (frameScreenRect.Contains(rect))
{
DrawResizeHint(dc, rect);
m_actionHintRect = rect;
}
// draw new resize hint
DrawResizeHint(dc, rect);
m_actionHintRect = rect;
}
}
}

View file

@ -174,6 +174,11 @@ wxDCImpl *wxNativeDCFactory::CreatePrinterDC( wxPrinterDC *owner, const wxPrintD
}
#endif
bool wxNativeDCFactory::CanDrawUsingClientDC(const wxWindow* window) const
{
return wxClientDCImpl::CanBeUsedForDrawing(window);
}
//-----------------------------------------------------------------------------
// wxWindowDC
//-----------------------------------------------------------------------------
@ -196,6 +201,12 @@ wxClientDC::wxClientDC(wxWindow *win)
{
}
/* static */
bool wxClientDC::CanBeUsedForDrawing(const wxWindow* window)
{
return wxDCFactory::Get()->CanDrawUsingClientDC(window);
}
//-----------------------------------------------------------------------------
// wxMemoryDC
//-----------------------------------------------------------------------------

View file

@ -69,30 +69,19 @@ wxBEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
#endif // wxMSW
wxEND_EVENT_TABLE()
bool wxSplitterWindow::AlwaysUsesLiveUpdate() const
{
return !wxClientDC::CanBeUsedForDrawing(this);
}
static bool IsLive(wxSplitterWindow* wnd)
{
// with wxSP_LIVE_UPDATE style the splitter windows are always resized
// following the mouse movement while it drags the sash, without it we only
// draw the sash at the new position but only resize the windows when the
// dragging is finished
#if defined( __WXMAC__ ) && defined(TARGET_API_MAC_OSX) && TARGET_API_MAC_OSX == 1
return true; // Mac can't paint outside paint event - always need live mode
#else
// wxClientDC doesn't work with Wayland either, so check if we're using it.
#if defined(__WXGTK3__) && defined(__UNIX__)
switch ( wxGetDisplayInfo().type )
{
case wxDisplayNone:
case wxDisplayX11:
break;
case wxDisplayWayland:
return true;
}
#endif // wxGTK3
return wnd->HasFlag(wxSP_LIVE_UPDATE);
#endif
// dragging is finished -- but drawing the sash is done using wxClientDC,
// so check if we can use it and always use live resizing if we can't
return wnd->AlwaysUsesLiveUpdate() || wnd->HasFlag(wxSP_LIVE_UPDATE);
}
bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id,

View file

@ -486,6 +486,25 @@ wxClientDCImpl::wxClientDCImpl(wxClientDC* owner, wxWindow* window)
else
SetGraphicsContext(wxGraphicsContext::Create());
}
/* static */
bool wxClientDCImpl::CanBeUsedForDrawing(const wxWindow* WXUNUSED(window))
{
#ifdef __UNIX__
switch ( wxGetDisplayInfo().type )
{
case wxDisplayNone:
case wxDisplayX11:
break;
case wxDisplayWayland:
return false;
}
#endif // __UNIX__
return true;
}
//-----------------------------------------------------------------------------
wxPaintDCImpl::wxPaintDCImpl(wxPaintDC* owner, wxWindow* window)