From 7d041c3b8a0e97bbb62deb8dccc849a79870db5a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Feb 2024 13:38:33 +0100 Subject: [PATCH] The previous patch caused artifacts because it relied on an order of messages that turned out to not be guaranteed, this doesn't rely on such an order. Also removed SubclassSpinBox function and moved the code from that to be handled via the WM_PARENTNOTIFY message in RunProc. --- src/TabBar.cpp | 91 +++++++++++++++++++++++++------------------------- src/TabBar.h | 3 +- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/TabBar.cpp b/src/TabBar.cpp index d5e4d0b1..502241d1 100644 --- a/src/TabBar.cpp +++ b/src/TabBar.cpp @@ -55,7 +55,6 @@ CTabBar::CTabBar(HINSTANCE hInst) , m_tabBarDefaultProc(nullptr) , m_tabBarSpinDefaultProc(nullptr) , m_spin(nullptr) - , m_bIsSpinVisible(false) , m_currentHoverTabItem(-1) , m_bIsCloseHover(false) , m_whichCloseClickDown(-1) @@ -157,7 +156,6 @@ int CTabBar::InsertAtEnd(const wchar_t *subTabName) // remove the selection so it can be selected properly later. if (m_nItems == 1) TabCtrl_SetCurSel(*this, -1); - SubclassSpinBox(); InvalidateRect(*this, nullptr, FALSE); return index; } @@ -187,7 +185,6 @@ int CTabBar::InsertAfter(int index, const wchar_t *subTabName) if (m_nItems == 0) TabCtrl_SetCurSel(*this, -1); ++m_nItems; - SubclassSpinBox(); InvalidateRect(*this, nullptr, FALSE); return ret; } @@ -405,6 +402,28 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { + case WM_PARENTNOTIFY: + { + UINT childMessage = LOWORD(wParam); + + switch (childMessage) + { + case WM_CREATE: + { + wchar_t className[100]{}; + GetClassName((HWND)lParam, className, _countof(className)); + if (wcscmp(UPDOWN_CLASS, className) == 0) + { + SetWindowLongPtr((HWND)lParam, GWLP_USERDATA, reinterpret_cast(this)); + + m_tabBarSpinDefaultProc = reinterpret_cast(::SetWindowLongPtr((HWND)lParam, GWLP_WNDPROC, reinterpret_cast(TabBarSpin_Proc))); + m_spin = (HWND)lParam; + } + } + break; + } + } + break; case WM_ERASEBKGND: { HDC hDC = reinterpret_cast(wParam); @@ -484,7 +503,7 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) DrawMainBorder(&dis); - if (m_bIsSpinVisible) + if (IsSpinVisible()) { RECT rcSpin{}; GetWindowRect(m_spin, &rcSpin); @@ -517,7 +536,9 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { dis.rcItem.right = min(dis.rcItem.right, rPage.right); if (dis.rcItem.right != rPage.right || (dis.rcItem.right - dis.rcItem.left) > CDPIAware::Instance().Scale(*this, MIN_TAB_WIDTH)) + { DrawItem(&dis, static_cast(Animator::GetValue(m_animVars[GetIDFromIndex(nTab).GetValue()]))); + } } } } @@ -538,7 +559,9 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { dis.rcItem.right = min(dis.rcItem.right, rPage.right); if (dis.rcItem.right != rPage.right || (dis.rcItem.right - dis.rcItem.left) > CDPIAware::Instance().Scale(*this, MIN_TAB_WIDTH)) + { DrawItem(&dis, static_cast(Animator::GetValue(m_animVars[GetIDFromIndex(nSel).GetValue()]))); + } } } } @@ -678,7 +701,7 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) auto index = GetTabIndexAt(xPos, yPos); RECT rcItem{}; TabCtrl_GetItemRect(*this, index, &rcItem); - if (m_bIsSpinVisible) + if (IsSpinVisible()) { RECT rcSpin{}; GetWindowRect(m_spin, &rcSpin); @@ -755,7 +778,7 @@ LRESULT CTabBar::RunProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) RECT rPage{}; GetClientRect(*this, &rPage); TabCtrl_AdjustRect(*this, FALSE, &rPage); - if (m_bIsSpinVisible) + if (IsSpinVisible()) { RECT rcSpin{}; GetWindowRect(m_spin, &rcSpin); @@ -1273,13 +1296,6 @@ LRESULT CTabBar::TabBarSpin_Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM { case WM_PAINT: { - // as WM_SHOWWINDOW is only ever sent with wParam == FALSE, use WM_PAINT instead - // of WM_SHOWWINDOW with wParam == TRUE - // alternatives might be WM_NCPAINT or 1125, which also both seem to be only - // sent when updown control is unhidden - if (!pTab->m_bIsSpinVisible) - pTab->m_bIsSpinVisible = true; - PAINTSTRUCT ps; BeginPaint(hwnd, &ps); HDC hdc = ps.hdc; @@ -1479,17 +1495,8 @@ LRESULT CTabBar::TabBarSpin_Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM InvalidateRect(hwnd, nullptr, false); } break; - case WM_SHOWWINDOW: - { - if (wParam == FALSE) //< checked and is only ever sent with wParam == FALSE - pTab->m_bIsSpinVisible = false; - else - pTab->m_bIsSpinVisible = true; - break; - } case WM_DESTROY: { - pTab->m_bIsSpinVisible = false; pTab->m_spin = nullptr; break; } @@ -1497,6 +1504,22 @@ LRESULT CTabBar::TabBarSpin_Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM break; } return CallWindowProc(pTab->m_tabBarSpinDefaultProc, hwnd, message, wParam, lParam); +} + +bool CTabBar::IsSpinVisible() const +{ + // Tried WM_SHOWWINDOW in TabBarSpin_Proc, but this was only ever sent with wParam == FALSE + // Then tried using WM_PAINT in TabBarSpin_Proc instead of WM_SHOWWINDOW with wParam == TRUE + // This was sent after WM_PAINT in RunProc, thus would create artifacts + // As I couldn't find any proof of guaranteed order, the below should always work + + if (!m_spin) + { + return false; + } + + LONG spinStyle = GetWindowLong(m_spin, GWL_STYLE); + return (spinStyle & WS_VISIBLE) != 0 ? true : false; } DocID CTabBar::GetIDFromIndex(int index) const @@ -1534,30 +1557,6 @@ void CTabBar::NotifyTabDelete(int tab) ::SendMessage(m_hParent, WM_NOTIFY, 0, reinterpret_cast(&nmHdr)); } -void CTabBar::SubclassSpinBox() -{ - if (m_spin == nullptr) - { - EnumChildWindows( - *this, [](HWND hChild, LPARAM lParam) -> BOOL { - auto pThis = reinterpret_cast(lParam); - wchar_t className[100]{}; - GetClassName(hChild, className, _countof(className)); - if (wcscmp(UPDOWN_CLASS, className) == 0) - { - SetWindowLongPtr(hChild, GWLP_USERDATA, reinterpret_cast(pThis)); - - pThis->m_tabBarSpinDefaultProc = reinterpret_cast(::SetWindowLongPtr(hChild, GWLP_WNDPROC, reinterpret_cast(TabBarSpin_Proc))); - pThis->m_spin = hChild; - pThis->m_bIsSpinVisible = true; - return FALSE; - } - return TRUE; - }, - reinterpret_cast(this)); - } -} - bool CloseButtonZone::IsHit(int x, int y, const RECT &testZone) const { if (((x + m_width + m_fromRight) < testZone.right) || (x > (testZone.right - m_fromRight))) diff --git a/src/TabBar.h b/src/TabBar.h index 3fc9683a..5fba3d8b 100644 --- a/src/TabBar.h +++ b/src/TabBar.h @@ -124,7 +124,7 @@ class CTabBar : public CWindow int GetTabIndexAt(int x, int y) const; bool IsPointInParentZone(POINT screenPoint) const; void NotifyTabDelete(int tab); - void SubclassSpinBox(); + bool IsSpinVisible() const; private: int m_nItems; @@ -145,7 +145,6 @@ class CTabBar : public CWindow WNDPROC m_tabBarDefaultProc; WNDPROC m_tabBarSpinDefaultProc; HWND m_spin; - bool m_bIsSpinVisible; RECT m_currentHoverTabRect; int m_currentHoverTabItem;