diff --git a/PowerEditor/src/NppDarkMode.cpp b/PowerEditor/src/NppDarkMode.cpp index cd9077cf9..ac8fdd9ae 100644 --- a/PowerEditor/src/NppDarkMode.cpp +++ b/PowerEditor/src/NppDarkMode.cpp @@ -1761,102 +1761,98 @@ namespace NppDarkMode SetWindowSubclass(hwnd, GroupboxSubclass, g_groupboxSubclassID, pButtonData); } - constexpr UINT_PTR g_tabSubclassID = 42; - - static LRESULT CALLBACK TabSubclass( - HWND hWnd, - UINT uMsg, - WPARAM wParam, - LPARAM lParam, - UINT_PTR uIdSubclass, - DWORD_PTR /*dwRefData*/ - ) + static void paintTab(HWND hWnd, HDC hdc, const RECT& rect) { - switch (uMsg) + ::FillRect(hdc, &rect, NppDarkMode::getDlgBackgroundBrush()); + + auto holdPen = static_cast(::SelectObject(hdc, NppDarkMode::getEdgePen())); + + auto holdClip = ::CreateRectRgn(0, 0, 0, 0); + if (::GetClipRgn(hdc, holdClip) != 1) { - case WM_ERASEBKGND: + ::DeleteObject(holdClip); + holdClip = nullptr; + } + + auto hFont = reinterpret_cast(::SendMessage(hWnd, WM_GETFONT, 0, 0)); + auto holdFont = ::SelectObject(hdc, hFont); + + POINT ptCursor{}; + ::GetCursorPos(&ptCursor); + ::ScreenToClient(hWnd, &ptCursor); + + bool hasFocusRect = false; + if (::GetFocus() == hWnd) + { + const auto uiState = static_cast(::SendMessage(hWnd, WM_QUERYUISTATE, 0, 0)); + hasFocusRect = ((uiState & UISF_HIDEFOCUS) != UISF_HIDEFOCUS); + } + + const int iSelTab = TabCtrl_GetCurSel(hWnd); + const int nTabs = TabCtrl_GetItemCount(hWnd); + for (int i = 0; i < nTabs; ++i) + { + RECT rcItem{}; + TabCtrl_GetItemRect(hWnd, i, &rcItem); + RECT rcFrame{ rcItem }; + + RECT rcIntersect{}; + if (::IntersectRect(&rcIntersect, &rect, &rcItem) == TRUE) { - if (NppDarkMode::isEnabled()) + const bool isHot = ::PtInRect(&rcItem, ptCursor) == TRUE; + const bool isSelectedTab = (i == iSelTab); + + ::SetBkMode(hdc, TRANSPARENT); + + HRGN hClip = ::CreateRectRgnIndirect(&rcItem); + ::SelectClipRgn(hdc, hClip); + + ::InflateRect(&rcItem, -1, -1); + rcItem.right += 1; + + std::wstring label(MAX_PATH, L'\0'); + TCITEM tci{}; + tci.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_STATE; + tci.dwStateMask = TCIS_HIGHLIGHTED; + tci.pszText = label.data(); + tci.cchTextMax = MAX_PATH - 1; + + TabCtrl_GetItem(hWnd, i, &tci); + + const auto nStyle = ::GetWindowLongPtr(hWnd, GWL_STYLE); + const bool isBtn = (nStyle & TCS_BUTTONS) == TCS_BUTTONS; + if (isBtn) { - return TRUE; + const bool isHighlighted = (tci.dwState & TCIS_HIGHLIGHTED) == TCIS_HIGHLIGHTED; + ::FillRect(hdc, &rcItem, isHighlighted ? NppDarkMode::getHotBackgroundBrush() : NppDarkMode::getDlgBackgroundBrush()); + ::SetTextColor(hdc, isHighlighted ? NppDarkMode::getLinkTextColor() : NppDarkMode::getDarkerTextColor()); } - break; - } - - case WM_PAINT: - { - if (!NppDarkMode::isEnabled()) - { - break; - } - - LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE); - if ((dwStyle & TCS_BUTTONS) || (dwStyle & TCS_VERTICAL)) - { - break; - } - - PAINTSTRUCT ps{}; - HDC hdc = ::BeginPaint(hWnd, &ps); - ::FillRect(hdc, &ps.rcPaint, NppDarkMode::getDlgBackgroundBrush()); - - auto holdPen = static_cast(::SelectObject(hdc, NppDarkMode::getEdgePen())); - - HRGN holdClip = CreateRectRgn(0, 0, 0, 0); - if (1 != GetClipRgn(hdc, holdClip)) - { - DeleteObject(holdClip); - holdClip = nullptr; - } - - HFONT hFont = reinterpret_cast(SendMessage(hWnd, WM_GETFONT, 0, 0)); - auto hOldFont = SelectObject(hdc, hFont); - - POINT ptCursor{}; - ::GetCursorPos(&ptCursor); - ScreenToClient(hWnd, &ptCursor); - - int nTabs = TabCtrl_GetItemCount(hWnd); - - int nSelTab = TabCtrl_GetCurSel(hWnd); - for (int i = 0; i < nTabs; ++i) - { - RECT rcItem{}; - TabCtrl_GetItemRect(hWnd, i, &rcItem); - RECT rcFrame = rcItem; - - RECT rcIntersect{}; - if (IntersectRect(&rcIntersect, &ps.rcPaint, &rcItem)) + else { - bool bHot = PtInRect(&rcItem, ptCursor); - bool isSelectedTab = (i == nSelTab); - - HRGN hClip = CreateRectRgnIndirect(&rcItem); - - SelectClipRgn(hdc, hClip); - - SetTextColor(hdc, (bHot || isSelectedTab ) ? NppDarkMode::getTextColor() : NppDarkMode::getDarkerTextColor()); - - ::InflateRect(&rcItem, -1, -1); - rcItem.right += 1; - // for consistency getBackgroundBrush() // would be better, than getCtrlBackgroundBrush(), // however default getBackgroundBrush() has same color // as getDlgBackgroundBrush() - ::FillRect(hdc, &rcItem, isSelectedTab ? NppDarkMode::getDlgBackgroundBrush() : bHot ? NppDarkMode::getHotBackgroundBrush() : NppDarkMode::getCtrlBackgroundBrush()); + auto getBrush = [&]() -> HBRUSH { + if (isSelectedTab) + { + return NppDarkMode::getDlgBackgroundBrush(); + } - SetBkMode(hdc, TRANSPARENT); + if (isHot) + { + return NppDarkMode::getHotBackgroundBrush(); + } + return NppDarkMode::getCtrlBackgroundBrush(); + }; - wchar_t label[MAX_PATH]{}; - TCITEM tci{}; - tci.mask = TCIF_TEXT; - tci.pszText = label; - tci.cchTextMax = MAX_PATH - 1; + ::FillRect(hdc, &rcItem, getBrush()); + ::SetTextColor(hdc, (isHot || isSelectedTab) ? NppDarkMode::getTextColor() : NppDarkMode::getDarkerTextColor()); + } - ::SendMessage(hWnd, TCM_GETITEM, i, reinterpret_cast(&tci)); - - RECT rcText = rcItem; + RECT rcText{ rcItem }; + if (!isBtn) + { if (isSelectedTab) { ::OffsetRect(&rcText, 0, -1); @@ -1867,62 +1863,184 @@ namespace NppDarkMode { rcFrame.right += 1; } - - ::FrameRect(hdc, &rcFrame, NppDarkMode::getEdgeBrush()); - - DrawText(hdc, label, -1, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE); - - DeleteObject(hClip); - - SelectClipRgn(hdc, holdClip); } - } - SelectObject(hdc, hOldFont); - - SelectClipRgn(hdc, holdClip); - if (holdClip) - { - DeleteObject(holdClip); - holdClip = nullptr; - } - - SelectObject(hdc, holdPen); - - EndPaint(hWnd, &ps); - return 0; - } - - case WM_NCDESTROY: - { - ::RemoveWindowSubclass(hWnd, TabSubclass, uIdSubclass); - break; - } - - case WM_PARENTNOTIFY: - { - switch (LOWORD(wParam)) - { - case WM_CREATE: + if (tci.iImage != -1) { - auto hwndUpdown = reinterpret_cast(lParam); - if (NppDarkMode::subclassTabUpDownControl(hwndUpdown)) + int cx = 0; + int cy = 0; + auto hImagelist = TabCtrl_GetImageList(hWnd); + static constexpr int offset = 2; + ::ImageList_GetIconSize(hImagelist, &cx, &cy); + ::ImageList_Draw(hImagelist, tci.iImage, hdc, rcText.left + offset, rcText.top + (((rcText.bottom - rcText.top) - cy) / 2), ILD_NORMAL); + rcText.left += cx; + } + + ::DrawText(hdc, label.c_str(), -1, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE); + + ::FrameRect(hdc, &rcFrame, NppDarkMode::getEdgeBrush()); + + if (isSelectedTab && hasFocusRect) + { + ::InflateRect(&rcFrame, -2, -1); + ::DrawFocusRect(hdc, &rcFrame); + } + + ::SelectClipRgn(hdc, holdClip); + ::DeleteObject(hClip); + } + } + + ::SelectObject(hdc, holdFont); + ::SelectClipRgn(hdc, holdClip); + if (holdClip != nullptr) + { + ::DeleteObject(holdClip); + holdClip = nullptr; + } + ::SelectObject(hdc, holdPen); + } + + static constexpr UINT_PTR g_tabSubclassID = 42; + + static LRESULT CALLBACK TabSubclass( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam, + UINT_PTR uIdSubclass, + DWORD_PTR dwRefData + ) + { + auto* pTabBufferData = reinterpret_cast(dwRefData); + const auto& hMemDC = pTabBufferData->_hMemDC; + + switch (uMsg) + { + case WM_NCDESTROY: + { + ::RemoveWindowSubclass(hWnd, TabSubclass, uIdSubclass); + delete pTabBufferData; + break; + } + + case WM_ERASEBKGND: + { + if (!NppDarkMode::isEnabled()) + { + break; + } + + const auto* hdc = reinterpret_cast(wParam); + if (hdc != hMemDC) + { + return FALSE; + } + return TRUE; + } + + case WM_PAINT: + { + if (!NppDarkMode::isEnabled()) + { + break; + } + + const auto nStyle = ::GetWindowLongPtr(hWnd, GWL_STYLE); + if ((nStyle & TCS_VERTICAL) == TCS_VERTICAL) + { + break; + } + + PAINTSTRUCT ps{}; + HDC hdc = ::BeginPaint(hWnd, &ps); + + if (ps.rcPaint.right <= ps.rcPaint.left || ps.rcPaint.bottom <= ps.rcPaint.top) + { + ::EndPaint(hWnd, &ps); + return 0; + } + + RECT rcClient{}; + ::GetClientRect(hWnd, &rcClient); + + if (pTabBufferData->ensureBuffer(hdc, rcClient)) + { + const int savedState = ::SaveDC(hMemDC); + ::IntersectClipRect( + hMemDC, + ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom + ); + + NppDarkMode::paintTab(hWnd, hMemDC, rcClient); + + ::RestoreDC(hMemDC, savedState); + + ::BitBlt( + hdc, + ps.rcPaint.left, ps.rcPaint.top, + ps.rcPaint.right - ps.rcPaint.left, + ps.rcPaint.bottom - ps.rcPaint.top, + hMemDC, + ps.rcPaint.left, ps.rcPaint.top, + SRCCOPY + ); + } + + ::EndPaint(hWnd, &ps); + return 0; + } + + case WM_UPDATEUISTATE: + { + if ((HIWORD(wParam) & (UISF_HIDEACCEL | UISF_HIDEFOCUS)) != 0) + { + ::InvalidateRect(hWnd, nullptr, FALSE); + } + break; + } + + case WM_PARENTNOTIFY: + { + if (LOWORD(wParam) == WM_CREATE) + { + auto hUpDown = reinterpret_cast(lParam); + if (NppDarkMode::subclassTabUpDownControl(hUpDown)) { return 0; } - break; } + break; } - return 0; - } + default: + { + break; + } } - return DefSubclassProc(hWnd, uMsg, wParam, lParam); + return ::DefSubclassProc(hWnd, uMsg, wParam, lParam); } - void subclassTabControl(HWND hwnd) + void subclassTabControl(HWND hWnd) { - SetWindowSubclass(hwnd, TabSubclass, g_tabSubclassID, 0); + if (::GetWindowSubclass(hWnd, TabSubclass, g_tabSubclassID, nullptr) == FALSE) + { + auto pTabBufferData = reinterpret_cast(new BufferData()); + ::SetWindowSubclass(hWnd, TabSubclass, g_tabSubclassID, pTabBufferData); + } + } + + static void setTabCtrlSubclassAndTheme(HWND hWnd, NppDarkModeParams p) + { + if (p._theme) + { + NppDarkMode::setDarkTooltips(hWnd, ToolTipsType::tabbar); + } + + if (p._subclass) + { + NppDarkMode::subclassTabControl(hWnd); + } } struct BorderMetricsData @@ -2829,6 +2947,12 @@ namespace NppDarkMode return TRUE; } + if (wcscmp(className, WC_TABCONTROL) == 0) + { + NppDarkMode::setTabCtrlSubclassAndTheme(hwnd, p); + return TRUE; + } + // Plugin might use rich edit control version 2.0 and later if (wcscmp(className, L"RichEdit20W") == 0 || wcscmp(className, L"RICHEDIT50W") == 0) { diff --git a/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp b/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp index b05237a2e..4b7f31607 100644 --- a/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp +++ b/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp @@ -1013,7 +1013,6 @@ intptr_t CALLBACK UserDefineDialog::run_dlgProc(UINT message, WPARAM wParam, LPA _pUserLang = _pCurrentUserLang; _ctrlTab.init(_hInst, _hSelf); - NppDarkMode::subclassTabControl(_ctrlTab.getHSelf()); _folderStyleDlg.init(_hInst, _hSelf); _folderStyleDlg.create(IDD_FOLDER_STYLE_DLG); diff --git a/PowerEditor/src/WinControls/Grid/ShortcutMapper.cpp b/PowerEditor/src/WinControls/Grid/ShortcutMapper.cpp index d2971ca62..a1dafc7ec 100644 --- a/PowerEditor/src/WinControls/Grid/ShortcutMapper.cpp +++ b/PowerEditor/src/WinControls/Grid/ShortcutMapper.cpp @@ -24,7 +24,6 @@ using namespace std; void ShortcutMapper::initTabs() { _hTabCtrl = ::GetDlgItem(_hSelf, IDC_BABYGRID_TABBAR); - NppDarkMode::subclassTabControl(_hTabCtrl); TCITEM tie{}; tie.mask = TCIF_TEXT; diff --git a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp index 3d5cc187b..783eb7681 100644 --- a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp +++ b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp @@ -130,7 +130,6 @@ void PluginsAdminDlg::create(int dialogID, bool isRTL, bool msgDestParent) RECT rect{}; getClientRect(rect); _tab.init(_hInst, _hSelf, false, true); - NppDarkMode::subclassTabControl(_tab.getHSelf()); const wchar_t *available = L"Available"; const wchar_t *updates = L"Updates";