From c0140d12c9bb828d576648e5d87d5ae13d0a25b0 Mon Sep 17 00:00:00 2001 From: AliKet Date: Sat, 6 Jan 2024 22:05:57 +0100 Subject: [PATCH 01/14] Remove unnecessary override from wxOverlayImpl under wxMSW The base class version already returns true by default. --- src/msw/overlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/msw/overlay.cpp b/src/msw/overlay.cpp index 7df05aca45..fd94364ad6 100644 --- a/src/msw/overlay.cpp +++ b/src/msw/overlay.cpp @@ -61,7 +61,6 @@ public: wxOverlayImpl() = default; ~wxOverlayImpl() = default; - virtual bool IsNative() const override { return true; } virtual void Reset() override; virtual bool IsOk() override; virtual void Init(wxDC* dc, int x , int y , int width , int height) override; From e79466f02b62a0612e3704a52e21307a07580689 Mon Sep 17 00:00:00 2001 From: AliKet Date: Fri, 9 Feb 2024 21:17:24 +0100 Subject: [PATCH 02/14] Fix wxOverlay when used with a TLW under wxMSW Correctly position the overlay window over the TLW's client area. --- src/msw/overlay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/msw/overlay.cpp b/src/msw/overlay.cpp index fd94364ad6..62298891c8 100644 --- a/src/msw/overlay.cpp +++ b/src/msw/overlay.cpp @@ -99,8 +99,9 @@ void wxOverlayImpl::Init(wxDC* dc, int , int , int , int ) m_window = dc->GetWindow(); - m_rect.SetSize(m_window->GetClientSize()); - m_rect.SetPosition(m_window->GetScreenPosition()); + // The rectangle must be in screen coordinates + m_rect = m_window->GetClientRect(); + m_window->ClientToScreen(&m_rect.x, &m_rect.y); m_bitmap.CreateWithLogicalSize(m_rect.GetSize(), m_window->GetDPIScaleFactor()); From 7b75d0a25635478ff38397991caf0c5aadbe6ab7 Mon Sep 17 00:00:00 2001 From: AliKet Date: Fri, 9 Feb 2024 21:45:48 +0100 Subject: [PATCH 03/14] Added per-pixel alpha blending capability to wxOverlay under wxMSW By default, wxMSW overlay uses a constant alpha for the window's opacity which can be changed by the added wxOverlay::SetOpacity() function. For some applications, that's all they need. But for others, per-pixel alpha blending is a requirement. This capability can be enabled by calling SetOpacity(-1) before initializing the overlay. But the drawing must be done via a graphics context because the standard DC under MSW does not support alpha drawing. Also request that the overlay window be positioned below any floating windows on the target (if any). --- include/wx/overlay.h | 2 + include/wx/private/overlay.h | 1 + interface/wx/overlay.h | 19 ++++++ src/common/overlaycmn.cpp | 6 ++ src/msw/overlay.cpp | 124 ++++++++++++++++++++++++++++++++--- 5 files changed, 142 insertions(+), 10 deletions(-) diff --git a/include/wx/overlay.h b/include/wx/overlay.h index dcac779c3e..4885900eaa 100644 --- a/include/wx/overlay.h +++ b/include/wx/overlay.h @@ -34,6 +34,8 @@ public: bool IsNative() const; + void SetOpacity(int alpha); + private: friend class WXDLLIMPEXP_FWD_CORE wxDCOverlay; diff --git a/include/wx/private/overlay.h b/include/wx/private/overlay.h index fed674b014..e76fe26152 100644 --- a/include/wx/private/overlay.h +++ b/include/wx/private/overlay.h @@ -38,6 +38,7 @@ public: virtual void EndDrawing(wxDC* dc) = 0; virtual void Clear(wxDC* dc) = 0; virtual void Reset() = 0; + virtual void SetOpacity(int WXUNUSED(alpha)) { } }; #endif // _WX_PRIVATE_OVERLAY_H_ diff --git a/interface/wx/overlay.h b/interface/wx/overlay.h index c03732ce08..38953597a1 100644 --- a/interface/wx/overlay.h +++ b/interface/wx/overlay.h @@ -27,6 +27,25 @@ public: example, when the window content has been changed and repainted. */ void Reset(); + + /** + Sets or unsets constant opacity of the overlay window. + + If @a alpha is `-1`, use per-pixel alpha blending, otherwise use the + given alpha value for all pixels. + + @note Drawing on the overlay window is opaque by default under wxMSW. + You have to call SetOpacity(-1) before initializing the overlay and use + a graphics context to do the actual drawing if you want per-pixel alpha + blending. Or, assuming that SetOpacity(-1) wasn't called, explicitly set + the alpha value (at any time) to change the overlay window's opacity. + + @note This is currently only implemented for wxMSW and does nothing in + the other ports. + + @since 3.3.0 + */ + void SetOpacity(int alpha); }; diff --git a/src/common/overlaycmn.cpp b/src/common/overlaycmn.cpp index a9cfce1899..19226a199e 100644 --- a/src/common/overlaycmn.cpp +++ b/src/common/overlaycmn.cpp @@ -74,6 +74,12 @@ void wxOverlay::Reset() wxASSERT_MSG(m_inDrawing==false,wxT("cannot reset overlay during drawing")); m_impl->Reset(); } + +void wxOverlay::SetOpacity(int alpha) +{ + m_impl->SetOpacity(alpha); +} + // ---------------------------------------------------------------------------- wxOverlay::Impl::~Impl() diff --git a/src/msw/overlay.cpp b/src/msw/overlay.cpp index 62298891c8..d720182e46 100644 --- a/src/msw/overlay.cpp +++ b/src/msw/overlay.cpp @@ -13,10 +13,40 @@ #include "wx/msw/dc.h" #include "wx/msw/private.h" +// A note about updating the overlay window : +// ------------------------------------------ +// Although both SetLayeredWindowAttributes and UpdateLayeredWindow can be used +// to update the overlay window, the latter is more flexible because it can perform +// per-pixel alpha blending like the other ports do. But this requires the DC to +// support alpha drawing, which can only be done via graphics contexts. That's why +// it's not used by default and is only enabled by SetOpacity(-1) call before the +// overlay window is initialized. Like this for example: +// ... +// m_overlay.SetOpacity(-1); +// wxClientDC dc(win); +// wxDCOverlay overlaydc(m_overlay, &dc); +// wxGCDC gdc(dc); +// "use gdc for drawing" +// ... +// +// Notice that the opacity of the overlay window is opaque by default, assuming that +// SetOpacity(-1) wasn't called. Call SetOpacity(alpha) at any time to change that. +// E.g.: SetOpacity(128) will make the overlay window semi-transparent. +// +// Notice also that SetOpacity(-1) has no effect if called after the overlay window +// is initialized. Likewise, calling SetOpacity(alpha) has no effect if SetOpacity(-1) +// was effectively called before the overlay window is initialized. +// +// From MSDN remarks: +// ------------------ +// Note that once SetLayeredWindowAttributes has been called for a +// layered window, subsequent UpdateLayeredWindow calls will fail until +// the layering style bit is cleared and set again. +// + namespace // anonymous { - -wxWindow* wxCreateOverlayWindow(const wxRect& rect) +wxWindow* wxCreateOverlayWindow(const wxRect& rect, int alpha, HWND hWndInsertAfter) { auto overlayWin = new wxWindow(); @@ -34,15 +64,19 @@ wxWindow* wxCreateOverlayWindow(const wxRect& rect) overlayWin->SetBackgroundStyle(wxBG_STYLE_PAINT); - if ( !::SetLayeredWindowAttributes(GetHwndOf(overlayWin), 0, 128, - LWA_COLORKEY | LWA_ALPHA) ) + if ( alpha >= 0 ) { - wxLogLastError(wxS("SetLayeredWindowAttributes()")); + if ( !::SetLayeredWindowAttributes(GetHwndOf(overlayWin), 0, alpha, + LWA_COLORKEY | LWA_ALPHA) ) + { + wxLogLastError(wxS("SetLayeredWindowAttributes()")); + } } + //else: UpdateLayeredWindow will be used to update the overlay window // We intentionally don't use WS_VISIBLE when creating this window to avoid // stealing activation from the parent, so show it using SWP_NOACTIVATE now. - ::SetWindowPos(GetHwndOf(overlayWin), nullptr, 0, 0, 0, 0, + ::SetWindowPos(GetHwndOf(overlayWin), hWndInsertAfter, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | @@ -68,6 +102,13 @@ public: virtual void EndDrawing(wxDC* dc) override; virtual void Clear(wxDC* dc) override; + // To use per-pixel alpha blending, call this function with -1 before the + // overlay is initialized. Or with another value to change the opacity of + // the overlay window if you like a constant opacity. + virtual void SetOpacity(int alpha) override; + + bool IsUsingConstantOpacity() const { return m_alpha >= 0; } + public: // window the overlay is associated with wxWindow* m_window = nullptr; @@ -77,6 +118,15 @@ public: wxRect m_rect; + // This variable defines how our overlay window is updated: + // - If set to -1, UpdateLayeredWindow will be used, which means the overlay + // window will use per-pixel alpha blending with the values in the source + // bitmap. + // - If set to a value between (0..255) a constant opacity will be applied + // to the entire overlay window. SetLayeredWindowAttributes will be used + // to update the overlay window in this case. + int m_alpha = wxALPHA_OPAQUE; + // Drawing on the overlay window is achieved by hijacking the existing wxDC. // i.e. any drawing done through wxDC should go to the offscreen bitmap m_bitmap // which will eventually be drawn on the overlay window. @@ -103,9 +153,24 @@ void wxOverlayImpl::Init(wxDC* dc, int , int , int , int ) m_rect = m_window->GetClientRect(); m_window->ClientToScreen(&m_rect.x, &m_rect.y); - m_bitmap.CreateWithLogicalSize(m_rect.GetSize(), m_window->GetDPIScaleFactor()); + if ( IsUsingConstantOpacity() ) + { + m_bitmap.CreateWithDIPSize(m_rect.GetSize(), m_window->GetDPIScaleFactor()); + } + else + { + m_bitmap.CreateWithDIPSize(m_rect.GetSize(), m_window->GetDPIScaleFactor(), 32); + m_bitmap.UseAlpha(); + } - m_overlayWindow = wxCreateOverlayWindow(m_rect); + // We want the overlay window precede the associated window in Z-order, but + // under any floating window i.e. be positioned between them. And to achieve + // this, we must insert it after the _previous_ window in the same order, as + // then it will be just before this one. + // GW_HWNDPREV: Returns a handle to the window above the given window. (MSDN) + const auto win = wxGetTopLevelParent(m_window); + HWND hWndInsertAfter = ::GetNextWindow(GetHwndOf(win), GW_HWNDPREV); + m_overlayWindow = wxCreateOverlayWindow(m_rect, m_alpha, hWndInsertAfter); } void wxOverlayImpl::BeginDrawing(wxDC* dc) @@ -127,6 +192,20 @@ void wxOverlayImpl::EndDrawing(wxDC* dc) { wxCHECK_RET( IsOk(), wxS("overlay not initialized") ); + if ( !IsUsingConstantOpacity() ) + { + POINT ptSrc{0, 0}; + POINT ptDst{m_rect.x, m_rect.y}; + SIZE size{m_rect.width, m_rect.height}; + BLENDFUNCTION blendFunc{AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + + if ( !::UpdateLayeredWindow(GetHwndOf(m_overlayWindow), nullptr, &ptDst, &size, + GetHdcOf(m_memDC), &ptSrc, 0, &blendFunc, ULW_ALPHA) ) + { + wxLogLastError(wxS("UpdateLayeredWindow()")); + } + } + m_memDC.SelectObject(wxNullBitmap); auto impl = dc->GetImpl(); @@ -135,8 +214,11 @@ void wxOverlayImpl::EndDrawing(wxDC* dc) msw_impl->SetHDC(m_hdc); // restore the original hdc msw_impl->UpdateClipBox(); - wxWindowDC winDC(m_overlayWindow); - winDC.DrawBitmap(m_bitmap, wxPoint(0, 0)); + if ( IsUsingConstantOpacity() ) + { + wxWindowDC winDC(m_overlayWindow); + winDC.DrawBitmap(m_bitmap, wxPoint(0, 0)); + } } void wxOverlayImpl::Clear(wxDC* WXUNUSED(dc)) @@ -156,6 +238,28 @@ void wxOverlayImpl::Reset() { m_overlayWindow->Destroy(); m_overlayWindow = nullptr; + + m_alpha = wxALPHA_OPAQUE; + } +} + +void wxOverlayImpl::SetOpacity(int alpha) +{ +#if wxUSE_GRAPHICS_CONTEXT + if ( !IsOk() ) + { + m_alpha = wxClip(alpha, -1, 255); + } + else if ( IsUsingConstantOpacity() ) +#endif // wxUSE_GRAPHICS_CONTEXT + { + m_alpha = wxClip(alpha, 0, 255); + + if ( !::SetLayeredWindowAttributes(GetHwndOf(m_overlayWindow), 0, m_alpha, + LWA_COLORKEY | LWA_ALPHA) ) + { + wxLogLastError(wxS("SetLayeredWindowAttributes()")); + } } } From fafc7140570dfc6a369a990bf8e375bc9ac9aa69 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Thu, 25 Jan 2024 02:49:02 +0100 Subject: [PATCH 04/14] Use wxOverlay to show sash feedback in non-live resize mode in wxAUI Make non-live resizing possible on all supported platforms thanks to wxOverlay. Previously, live resizing was the only supported mode under these platforms: wxMac, wxQt and wxGTK3 under Wayland. --- include/wx/aui/framemanager.h | 3 +++ src/aui/framemanager.cpp | 41 ++++++++++++++--------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/include/wx/aui/framemanager.h b/include/wx/aui/framemanager.h index e19325968f..07240e924e 100644 --- a/include/wx/aui/framemanager.h +++ b/include/wx/aui/framemanager.h @@ -24,6 +24,7 @@ #include "wx/timer.h" #include "wx/sizer.h" #include "wx/bmpbndl.h" +#include "wx/overlay.h" enum wxAuiManagerDock { @@ -615,6 +616,8 @@ protected: wxByte m_hintFadeAmt; // transparent fade amount wxByte m_hintFadeMax; // maximum value of hint fade + wxOverlay m_overlay; + void* m_reserved; private: diff --git a/src/aui/framemanager.cpp b/src/aui/framemanager.cpp index c62b1d848d..6c93ae4bfc 100644 --- a/src/aui/framemanager.cpp +++ b/src/aui/framemanager.cpp @@ -271,31 +271,23 @@ wxIMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame, wxFrame); static wxBitmap wxPaneCreateStippleBitmap() { - // TODO: Provide x1.5 and x2.0 versions. - unsigned char data[] = { 0,0,0,192,192,192, 192,192,192,0,0,0 }; + // Notice that wxOverlay, under wxMSW, uses the wxBLACK colour i.e.(0,0,0) + // as the key colour for transparency. and using it for the stipple bitmap + // will make the sash feedback totaly invisible if the window's background + // colour is (192,192,192) or so. (1,1,1) is used instead. + unsigned char data[] = { 1,1,1,192,192,192, 192,192,192,1,1,1 }; wxImage img(2,2,data,true); return wxBitmap(img); } static void DrawResizeHint(wxDC& dc, const wxRect& rect) { -#ifdef __WXMSW__ wxBitmap stipple = wxPaneCreateStippleBitmap(); wxBrush brush(stipple); dc.SetBrush(brush); - wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); - PatBlt(GetHdcOf(*impl), rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight(), PATINVERT); -#else - // 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.SetPen(*wxTRANSPARENT_PEN); - dc.SetLogicalFunction(wxINVERT); dc.DrawRectangle(rect); -#endif } @@ -767,11 +759,9 @@ unsigned int wxAuiManager::GetFlags() const return m_flags; } -/* static */ bool wxAuiManager::AlwaysUsesLiveResize(const wxWindow* window) +/* static */ bool wxAuiManager::AlwaysUsesLiveResize(const wxWindow* WXUNUSED(window)) { - // 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); + return false; } bool wxAuiManager::HasLiveResize() const @@ -4453,8 +4443,7 @@ void wxAuiManager::OnLeftUp(wxMouseEvent& event) if (!HasLiveResize()) { // get rid of the hint rectangle - wxClientDC dc{m_frame}; - DrawResizeHint(dc, m_actionHintRect); + m_overlay.Reset(); } if (m_currentDragItem != -1 && HasLiveResize()) m_actionPart = & (m_uiParts.Item(m_currentDragItem)); @@ -4569,18 +4558,20 @@ void wxAuiManager::OnMotion(wxMouseEvent& event) else { wxRect rect(pos, m_actionPart->rect.GetSize()); - wxClientDC dc{m_frame}; if (!m_actionHintRect.IsEmpty()) { - // remove old resize hint - DrawResizeHint(dc, m_actionHintRect); m_actionHintRect = wxRect(); } - // draw new resize hint - DrawResizeHint(dc, rect); + wxClientDC dc{m_frame}; + wxDCOverlay overlaydc(m_overlay, &dc); + overlaydc.Clear(); + + // draw resize hint m_actionHintRect = rect; + rect.SetPosition(rect.GetPosition() + m_frame->GetClientAreaOrigin()); + DrawResizeHint(dc, rect); } } } From a38175885c2d3ac224950009cd2c64f0dcf1a8a4 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Thu, 25 Jan 2024 03:10:23 +0100 Subject: [PATCH 05/14] Use wxOverlay to show sash feedback in non-live resize mode in wxSplitterWindow Make non-live resizing possible on all supported platforms thanks to wxOverlay. Previously, live resizing was the only supported mode under these platforms: wxMac, wxQt and wxGTK3 under Wayland. Also remove no longer needed function AlwaysUsesLiveUpdate and its documentation. --- include/wx/generic/splitter.h | 6 +-- interface/wx/splitter.h | 14 ------ samples/splitter/splitter.cpp | 8 +--- src/generic/splitter.cpp | 86 +++++++++++++++++------------------ 4 files changed, 44 insertions(+), 70 deletions(-) diff --git a/include/wx/generic/splitter.h b/include/wx/generic/splitter.h index f2fbe2a906..2d632993ac 100644 --- a/include/wx/generic/splitter.h +++ b/include/wx/generic/splitter.h @@ -12,6 +12,7 @@ #include "wx/window.h" // base class declaration #include "wx/containr.h" // wxControlContainer +#include "wx/overlay.h" class WXDLLIMPEXP_FWD_CORE wxSplitterEvent; @@ -131,9 +132,6 @@ 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)) { } @@ -293,7 +291,7 @@ protected: int m_minimumPaneSize; wxCursor m_sashCursorWE; wxCursor m_sashCursorNS; - wxPen *m_sashTrackerPen; + wxOverlay m_overlay; // when in live mode, set this to true to resize children in idle bool m_needUpdating:1; diff --git a/interface/wx/splitter.h b/interface/wx/splitter.h index 22d564a7ea..4ff835b2ee 100644 --- a/interface/wx/splitter.h +++ b/interface/wx/splitter.h @@ -142,20 +142,6 @@ 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. diff --git a/samples/splitter/splitter.cpp b/samples/splitter/splitter.cpp index cee9eafc03..3d100be780 100644 --- a/samples/splitter/splitter.cpp +++ b/samples/splitter/splitter.cpp @@ -254,7 +254,7 @@ MyFrame::MyFrame() "Toggle sash invisibility"); splitMenu->AppendSeparator(); - auto itemLive = splitMenu->AppendCheckItem(SPLIT_LIVE, + splitMenu->AppendCheckItem(SPLIT_LIVE, "&Live update\tCtrl-L", "Toggle live update mode"); splitMenu->AppendCheckItem(SPLIT_BORDER, @@ -306,12 +306,6 @@ 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 diff --git a/src/generic/splitter.cpp b/src/generic/splitter.cpp index 5636704951..fdc34e879b 100644 --- a/src/generic/splitter.cpp +++ b/src/generic/splitter.cpp @@ -69,19 +69,34 @@ 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 -- 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); + // dragging is finished + return wnd->HasFlag(wxSP_LIVE_UPDATE); +} + +static wxBitmap wxPaneCreateStippleBitmap() +{ + // Notice that wxOverlay, under wxMSW, uses the wxBLACK colour i.e.(0,0,0) + // as the key colour for transparency. and using it for the stipple bitmap + // will make the sash feedback totaly invisible if the window's background + // colour is (192,192,192) or so. (1,1,1) is used instead. + unsigned char data[] = { 1,1,1,192,192,192, 192,192,192,1,1,1 }; + wxImage img(2,2,data,true); + return wxBitmap(img); +} + +static void DrawResizeHint(wxDC& dc, const wxRect& rect) +{ + wxBitmap stipple = wxPaneCreateStippleBitmap(); + wxBrush brush(stipple); + dc.SetBrush(brush); + dc.SetPen(*wxTRANSPARENT_PEN); + + dc.DrawRectangle(rect); } bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id, @@ -134,7 +149,6 @@ void wxSplitterWindow::Init() m_minimumPaneSize = 0; m_sashCursorWE = wxCursor(wxCURSOR_SIZEWE); m_sashCursorNS = wxCursor(wxCURSOR_SIZENS); - m_sashTrackerPen = new wxPen(*wxBLACK, 2, wxPENSTYLE_SOLID); m_needUpdating = false; m_isHot = false; @@ -142,7 +156,6 @@ void wxSplitterWindow::Init() wxSplitterWindow::~wxSplitterWindow() { - delete m_sashTrackerPen; } // ---------------------------------------------------------------------------- @@ -280,10 +293,10 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event) return; } - // Erase old tracker + // Hide sash tracker if ( !isLive ) { - DrawSashTracker(m_oldX, m_oldY); + m_overlay.Reset(); } // the position of the click doesn't exactly correspond to @@ -361,9 +374,6 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event) m_sashPositionCurrent = posSashNew; - // Erase old tracker - DrawSashTracker(m_oldX, m_oldY); - m_oldX = (m_splitMode == wxSPLIT_VERTICAL ? m_sashPositionCurrent : x); m_oldY = (m_splitMode != wxSPLIT_VERTICAL ? m_sashPositionCurrent : y); @@ -380,7 +390,6 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event) m_oldY = 0; #endif // __WXMSW__ - // Draw new one DrawSashTracker(m_oldX, m_oldY); } else @@ -414,10 +423,10 @@ void wxSplitterWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(even SetCursor(* wxSTANDARD_CURSOR); - // Erase old tracker + // Erase sash tracker if ( !IsLive(this) ) { - DrawSashTracker(m_oldX, m_oldY); + m_overlay.Reset(); } } @@ -612,43 +621,30 @@ void wxSplitterWindow::DrawSashTracker(int x, int y) int x1, y1; int x2, y2; + const int sashTrackerWidth = GetDefaultSashSize(); + if ( m_splitMode == wxSPLIT_VERTICAL ) { - x1 = x2 = wxClip(x, 0, w) + m_sashTrackerPen->GetWidth()/2; - y1 = 2; - y2 = h-2; + x1 = wxClip(x, 0, w); + x2 = sashTrackerWidth; + y1 = 0; + y2 = h; } else { - y1 = y2 = wxClip(y, 0, h) + m_sashTrackerPen->GetWidth()/2; - x1 = 2; - x2 = w-2; + y1 = wxClip(y, 0, h); + y2 = sashTrackerWidth; + x1 = 0; + x2 = w; } -#if defined(__WXGTK3__) wxClientDC dc(this); + wxDCOverlay overlaydc( m_overlay, &dc ); + overlaydc.Clear(); - // In the ports with wxGraphicsContext-based wxDC, such as wxGTK3 or wxOSX, - // wxINVERT only works for inverting the background when using white - // foreground (note that this code is not used anyhow for __WXMAC__ due to - // always using live-resizing there, see IsLive()). - dc.SetPen(*wxWHITE_PEN); -#else - // We need to use wxScreenDC and not wxClientDC at least for wxMSW where - // drawing in this window itself would be hidden by its children windows, - // that cover it almost entirely. - wxScreenDC dc; - ClientToScreen(&x1, &y1); - ClientToScreen(&x2, &y2); + const wxRect rect{x1, y1, x2, y2}; - dc.SetPen(*m_sashTrackerPen); -#endif - - dc.SetLogicalFunction(wxINVERT); - - dc.SetBrush(*wxTRANSPARENT_BRUSH); - - dc.DrawLine(x1, y1, x2, y2); + DrawResizeHint(dc, rect); } int wxSplitterWindow::GetWindowSize() const From 7491ac5b7a44e2f37989b9384a344d8f9b85d379 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Thu, 25 Jan 2024 03:17:45 +0100 Subject: [PATCH 06/14] Make wxAUI and wxSplitterWindow work properly with wxOverlay under wxGTK2 This Workaround is needed because the DC used to draw on the wxOverlay clips children by default under wxGTK2. This means that the sash will always appear behind the sub-windows. making wxOverlay worthless under this platforms. Notice that calling gdk_gc_set_subwindow() unconditionally in Destroy() is cheap when DontClipSubWindows() has never been called. --- include/wx/gtk/dcclient.h | 1 + src/common/overlaycmn.cpp | 15 ++++++++++++++ src/gtk/dcclient.cpp | 43 +++++++++++++++++++++++++++++++-------- src/gtk/dcscreen.cpp | 9 +------- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/include/wx/gtk/dcclient.h b/include/wx/gtk/dcclient.h index f6d6f3ed5d..a19e736d94 100644 --- a/include/wx/gtk/dcclient.h +++ b/include/wx/gtk/dcclient.h @@ -122,6 +122,7 @@ public: PangoFontDescription *m_fontdesc; void SetUpDC( bool ismem = false ); + void DontClipSubWindows(); void Destroy(); virtual void ComputeScaleAndOrigin() override; diff --git a/src/common/overlaycmn.cpp b/src/common/overlaycmn.cpp index 19226a199e..f7f06dd6cb 100644 --- a/src/common/overlaycmn.cpp +++ b/src/common/overlaycmn.cpp @@ -146,6 +146,10 @@ void wxDCOverlay::Clear() #include "wx/window.h" +#if defined(__WXGTK__) && !defined(__WXGTK3__) +#include "wx/gtk/dcclient.h" +#endif + namespace { class wxOverlayImpl: public wxOverlay::Impl { @@ -191,6 +195,14 @@ bool wxOverlayImpl::IsOk() void wxOverlayImpl::Init( wxDC* dc, int x , int y , int width , int height ) { +#if defined(__WXGTK__) && !defined(__WXGTK3__) + // Workaround! to include sub-windows in the final drawing (on the overlay) + // as drawing on a DC under wxGTK2 clips children by default. + const auto dcimpl = static_cast(dc->GetImpl()); + if ( dcimpl ) + dcimpl->DontClipSubWindows(); +#endif + if (m_bmpSaved.IsOk()) { if (x != m_x || y != m_y || width != m_width || height != m_height) @@ -241,6 +253,9 @@ void wxOverlayImpl::Clear(wxDC* dc) void wxOverlayImpl::Reset() { m_bmpSaved.UnRef(); + + if ( m_window ) + m_window->Refresh(); } void wxOverlayImpl::BeginDrawing(wxDC* dc) diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 69478afc92..f02997977d 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -454,6 +454,14 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC ) gdk_gc_set_clip_rectangle( m_bgGC, nullptr ); } +void wxWindowDCImpl::DontClipSubWindows() +{ + gdk_gc_set_subwindow( m_penGC, GDK_INCLUDE_INFERIORS ); + gdk_gc_set_subwindow( m_brushGC, GDK_INCLUDE_INFERIORS ); + gdk_gc_set_subwindow( m_textGC, GDK_INCLUDE_INFERIORS ); + gdk_gc_set_subwindow( m_bgGC, GDK_INCLUDE_INFERIORS ); +} + void wxWindowDCImpl::DoGetSize( int* width, int* height ) const { wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); @@ -2018,14 +2026,33 @@ void wxWindowDCImpl::DestroyClippingRegion() void wxWindowDCImpl::Destroy() { - if (m_penGC) wxFreePoolGC( m_penGC ); - m_penGC = nullptr; - if (m_brushGC) wxFreePoolGC( m_brushGC ); - m_brushGC = nullptr; - if (m_textGC) wxFreePoolGC( m_textGC ); - m_textGC = nullptr; - if (m_bgGC) wxFreePoolGC( m_bgGC ); - m_bgGC = nullptr; + if (m_penGC) + { + gdk_gc_set_subwindow( m_penGC, GDK_CLIP_BY_CHILDREN ); + wxFreePoolGC( m_penGC ); + m_penGC = nullptr; + } + + if (m_brushGC) + { + gdk_gc_set_subwindow( m_brushGC, GDK_CLIP_BY_CHILDREN ); + wxFreePoolGC( m_brushGC ); + m_brushGC = nullptr; + } + + if (m_textGC) + { + gdk_gc_set_subwindow( m_textGC, GDK_CLIP_BY_CHILDREN ); + wxFreePoolGC( m_textGC ); + m_textGC = nullptr; + } + + if (m_bgGC) + { + gdk_gc_set_subwindow( m_bgGC, GDK_CLIP_BY_CHILDREN ); + wxFreePoolGC( m_bgGC ); + m_bgGC = nullptr; + } } void wxWindowDCImpl::SetDeviceOrigin( wxCoord x, wxCoord y ) diff --git a/src/gtk/dcscreen.cpp b/src/gtk/dcscreen.cpp index 8e14e70f44..0ee335dca2 100644 --- a/src/gtk/dcscreen.cpp +++ b/src/gtk/dcscreen.cpp @@ -42,18 +42,11 @@ void wxScreenDCImpl::Init() SetUpDC(); - gdk_gc_set_subwindow( m_penGC, GDK_INCLUDE_INFERIORS ); - gdk_gc_set_subwindow( m_brushGC, GDK_INCLUDE_INFERIORS ); - gdk_gc_set_subwindow( m_textGC, GDK_INCLUDE_INFERIORS ); - gdk_gc_set_subwindow( m_bgGC, GDK_INCLUDE_INFERIORS ); + DontClipSubWindows(); } wxScreenDCImpl::~wxScreenDCImpl() { - gdk_gc_set_subwindow( m_penGC, GDK_CLIP_BY_CHILDREN ); - gdk_gc_set_subwindow( m_brushGC, GDK_CLIP_BY_CHILDREN ); - gdk_gc_set_subwindow( m_textGC, GDK_CLIP_BY_CHILDREN ); - gdk_gc_set_subwindow( m_bgGC, GDK_CLIP_BY_CHILDREN ); } void wxScreenDCImpl::DoGetSize(int *width, int *height) const From 29c8bfc24925f1f0c67b7f9a8645a03b65e412a4 Mon Sep 17 00:00:00 2001 From: AliKet Date: Wed, 31 Jan 2024 18:39:45 +0100 Subject: [PATCH 07/14] Use wxOverlay to show docking hint instead of transparent wxFrame The main advantage of using wxOverlay over wxFrame, besides reduced code complexity, is that the docking hint now works correctly under Wayland which it didn't work before. wxAUI_MGR_RECTANGLE_HINT now works everywhere (including wxGTK and wxOSX) We simply use a bitmap to draw the venetian blinds hint instead of the exotic and esoteric code using wxRegion. wxGTK2 now uses graphics context for transparency. If not available, the hint will be drawn using wxINVERT raster operation mode. --- include/wx/aui/framemanager.h | 3 - src/aui/framemanager.cpp | 551 +++++++++------------------------- 2 files changed, 135 insertions(+), 419 deletions(-) diff --git a/include/wx/aui/framemanager.h b/include/wx/aui/framemanager.h index 07240e924e..fdb412eaba 100644 --- a/include/wx/aui/framemanager.h +++ b/include/wx/aui/framemanager.h @@ -497,8 +497,6 @@ public: protected: - void UpdateHintWindowConfig(); - void DoFrameLayout(); void LayoutAddPane(wxSizer* container, @@ -611,7 +609,6 @@ protected: double m_dockConstraintX; // 0.0 .. 1.0; max pct of window width a dock can consume double m_dockConstraintY; // 0.0 .. 1.0; max pct of window height a dock can consume - wxFrame* m_hintWnd; // transparent hint window, if supported by platform wxTimer m_hintFadeTimer; // transparent fade timer wxByte m_hintFadeAmt; // transparent fade amount wxByte m_hintFadeMax; // maximum value of hint fade diff --git a/src/aui/framemanager.cpp b/src/aui/framemanager.cpp index 6c93ae4bfc..901a47835d 100644 --- a/src/aui/framemanager.cpp +++ b/src/aui/framemanager.cpp @@ -33,7 +33,7 @@ #include "wx/settings.h" #include "wx/app.h" #include "wx/dcclient.h" - #include "wx/dcscreen.h" + #include "wx/dcmemory.h" #include "wx/toolbar.h" #include "wx/image.h" #include "wx/statusbr.h" @@ -64,6 +64,8 @@ wxDEFINE_EVENT( wxEVT_AUI_FIND_MANAGER, wxAuiManagerEvent ); #include "wx/msw/dc.h" #endif +#include "wx/dcgraph.h" + #include wxIMPLEMENT_DYNAMIC_CLASS(wxAuiManagerEvent, wxEvent); @@ -73,202 +75,18 @@ wxIMPLEMENT_CLASS(wxAuiManager, wxEvtHandler); const int auiToolBarLayer = 10; -#ifndef __WXGTK__ - - -class wxPseudoTransparentFrame : public wxFrame -{ -public: - wxPseudoTransparentFrame(wxWindow* parent = nullptr, - wxWindowID id = wxID_ANY, - const wxString& title = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxDEFAULT_FRAME_STYLE, - const wxString &name = wxT("frame")) - : wxFrame(parent, id, title, pos, size, style | wxFRAME_SHAPED, name) - { - SetBackgroundStyle(wxBG_STYLE_PAINT); - m_amount=0; - m_maxWidth=0; - m_maxHeight=0; - m_lastWidth=0; - m_lastHeight=0; - m_canSetShape = true; - m_region = wxRegion(0, 0, 0, 0); - SetTransparent(0); - } - - virtual bool SetTransparent(wxByte alpha) override - { - if (m_canSetShape) - { - wxSize size = GetClientSize(); - - m_maxWidth = size.x; - m_maxHeight = size.y; - m_amount = alpha; - m_region.Clear(); -// m_region.Union(0, 0, 1, m_maxWidth); - if (m_amount) - { - for (int y=0; yAddChild(this); - - g_signal_connect( m_widget, "realize", - G_CALLBACK (gtk_pseudo_window_realized_callback), this ); - - m_backgroundColour.Set(128, 192, 255); - GTKApplyWidgetStyle(); - } - - bool SetTransparent(wxByte WXUNUSED(alpha)) override - { - return true; - } - -protected: - virtual void DoSetSizeHints( int minW, int minH, - int maxW, int maxH, - int incW, int incH) override - { - // the real wxFrame method doesn't work for us because we're not really - // a top level window so skip it - wxWindow::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH); - } - -private: - wxDECLARE_DYNAMIC_CLASS(wxPseudoTransparentFrame); -}; - -wxIMPLEMENT_DYNAMIC_CLASS(wxPseudoTransparentFrame, wxFrame); - -#endif // !__WXGTK__/__WXGTK__ - - - // -- static utility functions -- +static wxBitmap wxCreateVenetianBlindsBitmap(wxByte r, wxByte g, wxByte b, wxByte a) +{ + unsigned char data[] = { r,g,b, 0,0,0, r,g,b }; + unsigned char alpha[] = { a, 128, a }; + + wxImage img(1,3,data,true); + img.SetAlpha(alpha,true); + return wxBitmap(img); +} + static wxBitmap wxPaneCreateStippleBitmap() { // Notice that wxOverlay, under wxMSW, uses the wxBLACK colour i.e.(0,0,0) @@ -586,12 +404,13 @@ wxAuiManager::wxAuiManager(wxWindow* managed_wnd, unsigned int flags) m_actionWindow = nullptr; m_hoverButton = nullptr; m_art = new wxAuiDefaultDockArt; - m_hintWnd = nullptr; m_flags = flags; m_hasMaximized = false; m_frame = nullptr; m_dockConstraintX = 0.3; m_dockConstraintY = 0.3; + m_hintFadeMax = 128; + m_reserved = nullptr; m_currentDragItem = -1; @@ -736,22 +555,8 @@ wxAuiDockUIPart* wxAuiManager::HitTest(int x, int y) // options which are global to wxAuiManager void wxAuiManager::SetFlags(unsigned int flags) { - // find out if we have to call UpdateHintWindowConfig() - bool update_hint_wnd = false; - unsigned int hint_mask = wxAUI_MGR_TRANSPARENT_HINT | - wxAUI_MGR_VENETIAN_BLINDS_HINT | - wxAUI_MGR_RECTANGLE_HINT; - if ((flags & hint_mask) != (m_flags & hint_mask)) - update_hint_wnd = true; - - // set the new flags m_flags = flags; - - if (update_hint_wnd) - { - UpdateHintWindowConfig(); - } } unsigned int wxAuiManager::GetFlags() const @@ -766,8 +571,7 @@ unsigned int wxAuiManager::GetFlags() const bool wxAuiManager::HasLiveResize() const { - return AlwaysUsesLiveResize(m_frame) || - (GetFlags() & wxAUI_MGR_LIVE_RESIZE) == wxAUI_MGR_LIVE_RESIZE; + return (GetFlags() & wxAUI_MGR_LIVE_RESIZE) == wxAUI_MGR_LIVE_RESIZE; } // don't use these anymore as they are deprecated @@ -800,79 +604,6 @@ wxAuiManager* wxAuiManager::GetManager(wxWindow* window) } -void wxAuiManager::UpdateHintWindowConfig() -{ - // find out if the system can do transparent frames - bool can_do_transparent = false; - - wxWindow* w = m_frame; - while (w) - { - if (wxDynamicCast(w, wxFrame)) - { - wxFrame* f = static_cast(w); - can_do_transparent = f->CanSetTransparent(); - - break; - } - - w = w->GetParent(); - } - - // if there is an existing hint window, delete it - if (m_hintWnd) - { - m_hintWnd->Destroy(); - m_hintWnd = nullptr; - } - - m_hintFadeMax = 50; - m_hintWnd = nullptr; - - if ((m_flags & wxAUI_MGR_TRANSPARENT_HINT) && can_do_transparent) - { - // Make a window to use for a transparent hint - m_hintWnd = new wxFrame(m_frame, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxSize(1,1), - wxFRAME_TOOL_WINDOW | - wxFRAME_FLOAT_ON_PARENT | - wxFRAME_NO_TASKBAR | - wxNO_BORDER); - #ifdef __WXMAC__ - // Do nothing so this event isn't handled in the base handlers. - - // Letting the hint window activate without this handler can lead to - // weird behaviour on Mac where the menu is switched out to the top - // window's menu in MDI applications when it shouldn't be. So since - // we don't want user interaction with the hint window anyway, we just - // prevent it from activating here. - m_hintWnd->Bind(wxEVT_ACTIVATE, [](wxActivateEvent&) {}); - #endif - - m_hintWnd->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HOTLIGHT)); - } - else - { - if ((m_flags & wxAUI_MGR_TRANSPARENT_HINT) != 0 || - (m_flags & wxAUI_MGR_VENETIAN_BLINDS_HINT) != 0) - { - // system can't support transparent fade, or the venetian - // blinds effect was explicitly requested - m_hintWnd = new wxPseudoTransparentFrame(m_frame, - wxID_ANY, - wxEmptyString, - wxDefaultPosition, - wxSize(1,1), - wxFRAME_TOOL_WINDOW | - wxFRAME_FLOAT_ON_PARENT | - wxFRAME_NO_TASKBAR | - wxNO_BORDER); - m_hintFadeMax = 128; - } - } -} - - // SetManagedWindow() is usually called once when the frame // manager class is being initialized. "frame" specifies // the frame which should be managed by the frame manager @@ -913,8 +644,6 @@ void wxAuiManager::SetManagedWindow(wxWindow* wnd) } #endif - - UpdateHintWindowConfig(); } @@ -3269,10 +2998,9 @@ bool wxAuiManager::DoDrop(wxAuiDockInfoArray& docks, return false; } - void wxAuiManager::OnHintFadeTimer(wxTimerEvent& WXUNUSED(event)) { - if (!m_hintWnd || m_hintFadeAmt >= m_hintFadeMax) + if (m_hintFadeAmt >= m_hintFadeMax) { m_hintFadeTimer.Stop(); Unbind(wxEVT_TIMER, &wxAuiManager::OnHintFadeTimer, this, @@ -3281,132 +3009,114 @@ void wxAuiManager::OnHintFadeTimer(wxTimerEvent& WXUNUSED(event)) } m_hintFadeAmt++; - m_hintWnd->SetTransparent(m_hintFadeAmt); + + ShowHint(m_lastHint); } void wxAuiManager::ShowHint(const wxRect& rect) { - if (m_hintWnd) + wxClientDC dc(m_frame); + wxDCOverlay overlaydc(m_overlay, &dc); + overlaydc.Clear(); + + wxDCClipper clip(dc, rect); + + if ( m_flags & wxAUI_MGR_RECTANGLE_HINT ) { - // if the hint rect is the same as last time, don't do anything - if (m_lastHint == rect) - return; - m_lastHint = rect; - - // Decide if we want to fade in the hint and set it to the end value if - // we don't. - if ((m_flags & wxAUI_MGR_HINT_FADE) - && !((m_flags & wxAUI_MGR_VENETIAN_BLINDS_HINT) && - (m_flags & wxAUI_MGR_NO_VENETIAN_BLINDS_FADE)) - ) - m_hintFadeAmt = 0; - else - m_hintFadeAmt = m_hintFadeMax; - - m_hintWnd->SetSize(rect); - m_hintWnd->SetTransparent(m_hintFadeAmt); - - if (!m_hintWnd->IsShown()) - m_hintWnd->Show(); - - // if we are dragging a floating pane, set the focus - // back to that floating pane (otherwise it becomes unfocused) - if (m_action == actionDragFloatingPane && m_actionWindow) - m_actionWindow->SetFocus(); - - m_hintWnd->Raise(); - - - if (m_hintFadeAmt != m_hintFadeMax) // Only fade if we need to - { - // start fade in timer - m_hintFadeTimer.SetOwner(this); - m_hintFadeTimer.Start(15); - Bind(wxEVT_TIMER, &wxAuiManager::OnHintFadeTimer, this, - m_hintFadeTimer.GetId()); - } - } - else // Not using a transparent hint window... - { - if (!(m_flags & wxAUI_MGR_RECTANGLE_HINT)) - return; - - if (m_lastHint != rect) - { - // remove the last hint rectangle - m_lastHint = rect; - m_frame->Refresh(); - m_frame->Update(); - } - - wxScreenDC screendc; - wxRegion clip(1, 1, 10000, 10000); - - // clip all floating windows, so we don't draw over them - int i, pane_count; - for (i = 0, pane_count = m_panes.GetCount(); i < pane_count; ++i) - { - wxAuiPaneInfo& pane = m_panes.Item(i); - - if (pane.IsFloating() && - pane.frame && - pane.frame->IsShown()) - { - wxRect r = pane.frame->GetRect(); -#ifdef __WXGTK__ - // wxGTK returns the client size, not the whole frame size - r.width += pane.frame->FromDIP(15); - r.height += pane.frame->FromDIP(35); - r.Inflate(pane.frame->FromDIP(wxSize(5, 5))); -#endif - - clip.Subtract(r); - } - } - - // As we can only hide the hint by redrawing the managed window, we - // need to clip the region to the managed window too or we get - // nasty redrawn problems. - clip.Intersect(m_frame->GetRect()); - - screendc.SetDeviceClippingRegion(clip); - + // Not using a transparent hint window... wxBitmap stipple = wxPaneCreateStippleBitmap(); wxBrush brush(stipple); - screendc.SetBrush(brush); - screendc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(brush); + dc.SetPen(*wxTRANSPARENT_PEN); - screendc.DrawRectangle(rect.x, rect.y, m_frame->FromDIP(5), rect.height); - screendc.DrawRectangle(rect.x + m_frame->FromDIP(5), rect.y, rect.width - m_frame->FromDIP(10), m_frame->FromDIP(5)); - screendc.DrawRectangle(rect.x + rect.width - m_frame->FromDIP(5), rect.y, m_frame->FromDIP(5), rect.height); - screendc.DrawRectangle(rect.x + m_frame->FromDIP(5), rect.y + rect.height - m_frame->FromDIP(5), rect.width - m_frame->FromDIP(10), m_frame->FromDIP(5)); + const int d = m_frame->FromDIP(5); + const int dd = m_frame->FromDIP(10); + + dc.DrawRectangle(rect.x, rect.y, d, rect.height); + dc.DrawRectangle(rect.x + d, rect.y, rect.width - dd, d); + dc.DrawRectangle(rect.x + rect.width - d, rect.y, d, rect.height); + dc.DrawRectangle(rect.x + d, rect.y + rect.height - d, rect.width - dd, d); + + return; + } + +#ifdef __WXGTK3__ + // The standard DC under wxGTK3 supports alpha drawing, whether the overlay + // is native (Wayland) or generic (X11). + const bool canDrawTransparentHint = true; +#else + const bool canDrawTransparentHint = m_overlay.IsNative(); +#endif + + const auto hintCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HOTLIGHT); + const unsigned char r = hintCol.GetRed(); + const unsigned char g = hintCol.GetGreen(); + const unsigned char b = hintCol.GetBlue(); + const unsigned char a = m_hintFadeAmt; + + const auto makeBrush = [=]() + { + return (m_flags & wxAUI_MGR_VENETIAN_BLINDS_HINT) != 0 + ? wxCreateVenetianBlindsBitmap(r, g, b, a) + : wxBrush(wxColour(r, g, b, a)); + }; + + if ( canDrawTransparentHint ) + { +#ifdef __WXMSW__ + m_overlay.SetOpacity(m_hintFadeAmt); +#endif // __WXMSW__ + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(makeBrush()); + dc.DrawRectangle(rect); + } + else + { + wxBitmap bmp(rect.GetSize(), 32); + wxMemoryDC mdc(bmp); + mdc.Blit(0, 0, rect.width, rect.height, &dc, rect.x, rect.y); + wxRasterOperationMode logicalFunc = wxINVERT; + +#if wxUSE_GRAPHICS_CONTEXT + { + wxGCDC gdc(mdc); + gdc.SetPen(*wxTRANSPARENT_PEN); + gdc.SetBrush(makeBrush()); + gdc.DrawRectangle(wxPoint(0, 0), rect.GetSize()); + } + + logicalFunc = wxCOPY; +#endif // wxUSE_GRAPHICS_CONTEXT + + dc.Blit(rect.x, rect.y, rect.width, rect.height, &mdc, 0, 0, logicalFunc); + } + + // if we are dragging a floating pane, set the focus + // back to that floating pane (otherwise it becomes unfocused) + if (m_action == actionDragFloatingPane && m_actionWindow) + m_actionWindow->SetFocus(); + + if (m_hintFadeAmt != m_hintFadeMax) // Only fade if we need to + { + // start fade in timer + m_hintFadeTimer.SetOwner(this); + m_hintFadeTimer.Start(10); + Bind(wxEVT_TIMER, &wxAuiManager::OnHintFadeTimer, this, + m_hintFadeTimer.GetId()); } } void wxAuiManager::HideHint() { - // hides a transparent window hint, if there is one - if (m_hintWnd) - { - if (m_hintWnd->IsShown()) - m_hintWnd->Show(false); - m_hintWnd->SetTransparent(0); - m_hintFadeTimer.Stop(); - // In case this is called while a hint fade is going, we need to - // disconnect the event handler. - Unbind(wxEVT_TIMER, &wxAuiManager::OnHintFadeTimer, this, - m_hintFadeTimer.GetId()); - m_lastHint = wxRect(); - return; - } + m_overlay.Reset(); + m_hintFadeTimer.Stop(); + // In case this is called while a hint fade is going, we need to + // disconnect the event handler. + Unbind(wxEVT_TIMER, &wxAuiManager::OnHintFadeTimer, this, + m_hintFadeTimer.GetId()); - // hides a painted hint by redrawing the frame window - if (!m_lastHint.IsEmpty()) - { - m_frame->Refresh(); - m_frame->Update(); - m_lastHint = wxRect(); - } + m_lastHint = wxRect(); } @@ -3445,7 +3155,7 @@ void wxAuiManager::StartPaneDrag(wxWindow* pane_window, // first calls DoDrop() to determine the exact position the pane would // be at were if dropped. If the pane would indeed become docked at the // specified drop point, the rectangle hint will be returned in -// screen coordinates. Otherwise, an empty rectangle is returned. +// client coordinates. Otherwise, an empty rectangle is returned. // |pane_window| is the window pointer of the pane being dragged, |pt| is // the mouse position, in client coordinates. |offset| describes the offset // that the mouse is from the upper-left corner of the item being dragged @@ -3516,18 +3226,15 @@ wxRect wxAuiManager::CalculateHintRect(wxWindow* pane_window, delete sizer; - if (rect.IsEmpty()) + if ( !rect.IsEmpty() ) { - return rect; - } + rect.Offset( m_frame->GetClientAreaOrigin() ); - // actually show the hint rectangle on the screen - m_frame->ClientToScreen(&rect.x, &rect.y); - - if ( m_frame->GetLayoutDirection() == wxLayout_RightToLeft ) - { - // Mirror rectangle in RTL mode - rect.x -= rect.GetWidth(); + if ( m_frame->GetLayoutDirection() == wxLayout_RightToLeft ) + { + // Mirror rectangle in RTL mode + rect.x -= rect.GetWidth(); + } } return rect; @@ -3547,10 +3254,22 @@ void wxAuiManager::DrawHintRect(wxWindow* pane_window, { HideHint(); } - else + else if (m_lastHint != rect) // if the hint rect is the same as last time, don't do anything { - ShowHint(rect); - } + m_lastHint = rect; + + // Decide if we want to fade in the hint and set it to the end value if + // we don't. + if ((m_flags & wxAUI_MGR_HINT_FADE) + && !((m_flags & wxAUI_MGR_VENETIAN_BLINDS_HINT) && + (m_flags & wxAUI_MGR_NO_VENETIAN_BLINDS_FADE)) + ) + m_hintFadeAmt = 0; + else + m_hintFadeAmt = m_hintFadeMax; + + ShowHint(rect); + } } void wxAuiManager::OnFloatingPaneMoveStart(wxWindow* wnd) @@ -3886,7 +3605,7 @@ void wxAuiManager::Repaint(wxDC* dc) // make a client dc if (!dc) { - if ( AlwaysUsesLiveResize(m_frame) ) + if ( !wxClientDC::CanBeUsedForDrawing(m_frame) ) { // We can't use wxClientDC in these ports. m_frame->Refresh() ; From bad07efa7017bc96f5733b52b16697c24abdfe6a Mon Sep 17 00:00:00 2001 From: AliKet Date: Thu, 8 Feb 2024 19:27:07 +0100 Subject: [PATCH 08/14] wxAuiManager::AlwaysUsesLiveResize() documentation updated This is no longer true as wxAUI now uses wxOverlay to display the resizing hint in non-live resize mode, which should work on all platforms. --- interface/wx/aui/framemanager.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/wx/aui/framemanager.h b/interface/wx/aui/framemanager.h index 14f573eeb1..ead1c1c559 100644 --- a/interface/wx/aui/framemanager.h +++ b/interface/wx/aui/framemanager.h @@ -228,6 +228,8 @@ public: @param window The associated window, may be null (this parameter was added in wxWidgets 3.3.0) + @note As of wxWidgets 3.3.0 this function always returns false. + @since 3.1.4 */ static bool AlwaysUsesLiveResize(const wxWindow* window); From c1325983cf8e8d2b02316993cb96017a564d3f2d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Feb 2024 22:23:40 +0100 Subject: [PATCH 09/14] Avoid using "x2" and "y2" for sash width and height This was confusing, so get rid of these variables and use more clearly named "posSash" and "sizeSash" instead. No real changes. --- src/generic/splitter.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/generic/splitter.cpp b/src/generic/splitter.cpp index fdc34e879b..3eb853ff87 100644 --- a/src/generic/splitter.cpp +++ b/src/generic/splitter.cpp @@ -615,34 +615,32 @@ void wxSplitterWindow::DrawSash(wxDC& dc) // Draw the sash tracker (for whilst moving the sash) void wxSplitterWindow::DrawSashTracker(int x, int y) { - int w, h; - GetClientSize(&w, &h); + // One of the components of this size will be modified below, the other one + // will stay unchanged as the tracker goes across the entire window. + wxSize sizeSash = GetClientSize(); - int x1, y1; - int x2, y2; + // One of the components of this position will be modified below, the other + // one is always 0 as the tracker starts at the left/top of the window. + wxPoint posSash; const int sashTrackerWidth = GetDefaultSashSize(); if ( m_splitMode == wxSPLIT_VERTICAL ) { - x1 = wxClip(x, 0, w); - x2 = sashTrackerWidth; - y1 = 0; - y2 = h; + posSash.x = wxClip(x, 0, sizeSash.x); + sizeSash.x = sashTrackerWidth; } else { - y1 = wxClip(y, 0, h); - y2 = sashTrackerWidth; - x1 = 0; - x2 = w; + posSash.y = wxClip(y, 0, sizeSash.y); + sizeSash.y = sashTrackerWidth; } wxClientDC dc(this); wxDCOverlay overlaydc( m_overlay, &dc ); overlaydc.Clear(); - const wxRect rect{x1, y1, x2, y2}; + const wxRect rect{posSash, sizeSash}; DrawResizeHint(dc, rect); } From 7584c04ff87540240a0f61aace194e27e5a014ed Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Feb 2024 22:25:39 +0100 Subject: [PATCH 10/14] Inline DrawResizeHint() function into its only caller It seems to be more clear to just draw the sash in DrawSashTracker() itself instead of calling a function which does it. --- src/generic/splitter.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/generic/splitter.cpp b/src/generic/splitter.cpp index 3eb853ff87..d741ddf1ce 100644 --- a/src/generic/splitter.cpp +++ b/src/generic/splitter.cpp @@ -89,16 +89,6 @@ static wxBitmap wxPaneCreateStippleBitmap() return wxBitmap(img); } -static void DrawResizeHint(wxDC& dc, const wxRect& rect) -{ - wxBitmap stipple = wxPaneCreateStippleBitmap(); - wxBrush brush(stipple); - dc.SetBrush(brush); - dc.SetPen(*wxTRANSPARENT_PEN); - - dc.DrawRectangle(rect); -} - bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, @@ -640,9 +630,12 @@ void wxSplitterWindow::DrawSashTracker(int x, int y) wxDCOverlay overlaydc( m_overlay, &dc ); overlaydc.Clear(); - const wxRect rect{posSash, sizeSash}; + wxBitmap stipple = wxPaneCreateStippleBitmap(); + wxBrush brush(stipple); + dc.SetBrush(brush); + dc.SetPen(*wxTRANSPARENT_PEN); - DrawResizeHint(dc, rect); + dc.DrawRectangle(posSash, sizeSash); } int wxSplitterWindow::GetWindowSize() const From c4658ec8f281341f9c795e10436e02c6a1c8d352 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 12 Feb 2024 22:27:25 +0100 Subject: [PATCH 11/14] Get rid of IsLive() helper in wxSplitterWindow code This was useful when we had AlwaysUsesLiveResize() and the related complications, but now that live resizing is governed solely by wxSP_LIVE_UPDATE again, it's not needed any longer. No real changes. --- src/generic/splitter.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/generic/splitter.cpp b/src/generic/splitter.cpp index d741ddf1ce..c163993446 100644 --- a/src/generic/splitter.cpp +++ b/src/generic/splitter.cpp @@ -69,15 +69,6 @@ wxBEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow) #endif // wxMSW wxEND_EVENT_TABLE() -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 - return wnd->HasFlag(wxSP_LIVE_UPDATE); -} - static wxBitmap wxPaneCreateStippleBitmap() { // Notice that wxOverlay, under wxMSW, uses the wxBLACK colour i.e.(0,0,0) @@ -239,7 +230,11 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event) return; } - bool isLive = IsLive(this); + // 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 + const bool isLive = HasFlag(wxSP_LIVE_UPDATE); if (event.LeftDown()) { @@ -414,7 +409,7 @@ void wxSplitterWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(even SetCursor(* wxSTANDARD_CURSOR); // Erase sash tracker - if ( !IsLive(this) ) + if ( HasFlag(wxSP_LIVE_UPDATE) ) { m_overlay.Reset(); } From 0217f548944ff3c97c6d5cc24783f3308ca05881 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 13 Feb 2024 00:43:54 +0100 Subject: [PATCH 12/14] Initialize wxOverlayImpl members in their declarations Ensure that all member variables are initialized instead of initializing just some of them in the ctor and leaving others uninitialized, it's simpler to just do this rather than to check if uninitialized values can or not be used. --- src/osx/cocoa/overlay.mm | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/osx/cocoa/overlay.mm b/src/osx/cocoa/overlay.mm index 127f82edea..c8a8e9fe28 100644 --- a/src/osx/cocoa/overlay.mm +++ b/src/osx/cocoa/overlay.mm @@ -126,7 +126,7 @@ class wxOverlayImpl: public wxOverlay::Impl { public: - wxOverlayImpl(); + wxOverlayImpl() = default; ~wxOverlayImpl(); virtual void Reset() override; @@ -138,16 +138,16 @@ public: void CreateOverlayWindow(wxDC* dc); - WXWindow m_overlayWindow; - WXWindow m_overlayParentWindow; - CGContextRef m_overlayContext; + WXWindow m_overlayWindow = nullptr; + WXWindow m_overlayParentWindow = nullptr; + CGContextRef m_overlayContext = nullptr; // we store the window in case we would have to issue a Refresh() - wxWindow* m_window; + wxWindow* m_window = nullptr; - int m_x; - int m_y; - int m_width; - int m_height; + int m_x = 0; + int m_y = 0; + int m_width = 0; + int m_height = 0; } ; wxOverlay::Impl* wxOverlay::Create() @@ -155,13 +155,6 @@ wxOverlay::Impl* wxOverlay::Create() return new wxOverlayImpl; } -wxOverlayImpl::wxOverlayImpl() -{ - m_window = nullptr ; - m_overlayContext = nullptr ; - m_overlayWindow = nullptr ; -} - wxOverlayImpl::~wxOverlayImpl() { Reset(); From 4261e1d07d52c0703a5adf4a436937eb0f8896e2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 13 Feb 2024 01:00:48 +0100 Subject: [PATCH 13/14] Erase overlay contents when resetting it in wxOSX Without this, the old contents continues to be shown for some time before the display was refreshed. --- src/osx/cocoa/overlay.mm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/osx/cocoa/overlay.mm b/src/osx/cocoa/overlay.mm index c8a8e9fe28..6452e6026c 100644 --- a/src/osx/cocoa/overlay.mm +++ b/src/osx/cocoa/overlay.mm @@ -157,6 +157,11 @@ wxOverlay::Impl* wxOverlay::Create() wxOverlayImpl::~wxOverlayImpl() { + // Set it to null before calling Reset() to prevent it from drawing + // anything: this is not needed when destroying the overlay and would + // result in problems. + m_window = nullptr; + Reset(); } @@ -275,6 +280,15 @@ void wxOverlayImpl::Clear(wxDC* dc) void wxOverlayImpl::Reset() { + if ( m_window ) + { + // erase whatever was drawn on the overlay the last time + wxClientDC dc(m_window); + BeginDrawing(&dc); + Clear(&dc); + EndDrawing(&dc); + } + if ( m_overlayContext ) { [(id)m_overlayContext release]; From acee397e4c5db67188951578cea9f786c210ddbd Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 13 Feb 2024 01:31:07 +0100 Subject: [PATCH 14/14] Avoid implicit "this" capture in a lambda to avoid C++20 warning Implicitly capturing "this" is deprecated in C++20, so do it explicitly, even if it also requires explicitly capturing everything else as combining implicit capture with explicitly capturing "this" is not allowed in the previous C++ versions. --- src/aui/framemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aui/framemanager.cpp b/src/aui/framemanager.cpp index 901a47835d..939de7b4d3 100644 --- a/src/aui/framemanager.cpp +++ b/src/aui/framemanager.cpp @@ -3054,7 +3054,7 @@ void wxAuiManager::ShowHint(const wxRect& rect) const unsigned char b = hintCol.GetBlue(); const unsigned char a = m_hintFadeAmt; - const auto makeBrush = [=]() + const auto makeBrush = [this, r, g, b, a]() { return (m_flags & wxAUI_MGR_VENETIAN_BLINDS_HINT) != 0 ? wxCreateVenetianBlindsBitmap(r, g, b, a)