Skip to content

Commit a1981f4

Browse files
lostindarkCopilot
andcommitted
Dark mode: themed message boxes and dark scrollbars
Address PR zufuliu#1203 feedback: scroll bars and the save-changes dialog were rendered in the light theme even when dark mode is active. - Enable scroll bar fix in darkmodelib (mode 2: per-window) and rely on the existing enableDarkScrollBarForWindowAndChildren(hwndMain) call so the Scintilla edit window's scroll bars use the dark theme. - Route Notepad4's MsgBox() through dmlib::darkMessageBoxW(), which renders message boxes as themed task dialogs when dark mode is on. - Fix two upstream darkmodelib namespacing bugs that surface when _DARKMODELIB_USE_SCROLLBAR_FIX is defined (DmlibHook.cpp uses ModuleHandle / LoadFn without the dmlib_module:: prefix). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 58bf4e2 commit a1981f4

8 files changed

Lines changed: 57 additions & 7 deletions

File tree

build/VisualStudio/Notepad4.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
<ItemDefinitionGroup>
159159
<ClCompile>
160160
<AdditionalIncludeDirectories>..\..\scintilla\include;..\..\scintilla\lexlib;..\..\scintilla\src;..\..\src;..\..\darkmodelib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
161-
<PreprocessorDefinitions>_WINDOWS;NOMINMAX;WIN32_LEAN_AND_MEAN;STRICT_TYPED_ITEMIDS;BOOST_REGEX_STANDALONE;NO_CXX11_REGEX;UNICODE;_UNICODE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DARKMODELIB_NO_INI_CONFIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
161+
<PreprocessorDefinitions>_WINDOWS;NOMINMAX;WIN32_LEAN_AND_MEAN;STRICT_TYPED_ITEMIDS;BOOST_REGEX_STANDALONE;NO_CXX11_REGEX;UNICODE;_UNICODE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DARKMODELIB_NO_INI_CONFIG;_DARKMODELIB_USE_SCROLLBAR_FIX=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
162162
<WarningLevel>Level4</WarningLevel>
163163
<LanguageStandard>stdcpplatest</LanguageStandard>
164164
<LanguageStandard_C>stdc17</LanguageStandard_C>

build/mingw/notepad4.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ INCDIR = \
1414
-I"$(scintilla_dir)/include" \
1515
-I"$(darkmodelib_dir)"
1616

17-
CPPFLAGS += -D_DARKMODELIB_NO_INI_CONFIG
17+
CPPFLAGS += -D_DARKMODELIB_NO_INI_CONFIG -D_DARKMODELIB_USE_SCROLLBAR_FIX=2
1818

1919
LDFLAGS += -L"$(BINFOLDER)/obj"
2020

darkmodelib/DmlibHook.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#if defined(_DARKMODELIB_USE_SCROLLBAR_FIX) && (_DARKMODELIB_USE_SCROLLBAR_FIX > 0)
2525
#include <mutex>
26+
#include <string_view>
2627
#include <unordered_set>
2728
#endif
2829

@@ -168,7 +169,7 @@ static fnOpenNcThemeData pfOpenNcThemeData = nullptr;
168169

169170
bool dmlib_hook::loadOpenNcThemeData(const HMODULE& hUxtheme) noexcept
170171
{
171-
return LoadFn(hUxtheme, pfOpenNcThemeData, 49);
172+
return dmlib_module::LoadFn(hUxtheme, pfOpenNcThemeData, 49);
172173
}
173174

174175
#if defined(_DARKMODELIB_USE_SCROLLBAR_FIX) && (_DARKMODELIB_USE_SCROLLBAR_FIX > 1)
@@ -214,7 +215,7 @@ static bool isWindowOrParentUsingDarkScrollBar(HWND hWnd)
214215
static HTHEME WINAPI MyOpenNcThemeData(HWND hWnd, LPCWSTR pszClassList)
215216
{
216217
static constexpr std::wstring_view scrollBarClassName = WC_SCROLLBAR;
217-
if (scrollBarClassName == pszClassList)
218+
if (scrollBarClassName == pszClassList && dmlib_win32api::IsDarkModeActive())
218219
{
219220
#if defined(_DARKMODELIB_USE_SCROLLBAR_FIX) && (_DARKMODELIB_USE_SCROLLBAR_FIX > 1)
220221
if (isWindowOrParentUsingDarkScrollBar(hWnd))
@@ -229,7 +230,7 @@ static HTHEME WINAPI MyOpenNcThemeData(HWND hWnd, LPCWSTR pszClassList)
229230

230231
void dmlib_hook::fixDarkScrollBar()
231232
{
232-
const ModuleHandle moduleComctl(L"comctl32.dll");
233+
const dmlib_module::ModuleHandle moduleComctl(L"comctl32.dll");
233234
if (moduleComctl.isLoaded())
234235
{
235236
auto* addr = iat_hook::FindDelayLoadThunkInModule(moduleComctl.get(), "uxtheme.dll", 49); // OpenNcThemeData

src/DarkMode.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@
1010

1111
// Declared in Styles.cpp
1212
extern int np2StyleTheme;
13+
// Declared in Notepad4.cpp - this app's module handle
14+
extern HINSTANCE g_hInstance;
1315

1416
// Thread-level hook to intercept WM_INITDIALOG and apply dark mode to dialogs
1517
static HHOOK s_hCallWndProcRetHook = nullptr;
1618

1719
static LRESULT CALLBACK DarkMode_CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept {
1820
if (nCode == HC_ACTION) {
1921
const CWPRETSTRUCT *cwpret = reinterpret_cast<const CWPRETSTRUCT *>(lParam);
20-
if (cwpret->message == WM_INITDIALOG) {
22+
// Only theme dialogs created by this app while dark mode is active.
23+
// This avoids touching system dialogs (e.g. the common file open/save
24+
// dialog) that are hosted in our process but owned by comdlg32/shell.
25+
if (cwpret->message == WM_INITDIALOG
26+
&& dmlib::isExperimentalActive()
27+
&& reinterpret_cast<HINSTANCE>(GetWindowLongPtr(cwpret->hwnd, GWLP_HINSTANCE)) == g_hInstance) {
2128
DarkMode_ApplyToDialog(cwpret->hwnd);
2229
}
2330
}
@@ -113,10 +120,36 @@ void DarkMode_OnThemeChanged(int newTheme) noexcept {
113120
}
114121
}
115122

123+
// Broadcast WM_THEMECHANGED to a window and all its descendants so that
124+
// themed controls (including scroll bars) re-open their theme handles.
125+
static BOOL CALLBACK DarkMode_SendThemeChangedProc(HWND hwnd, LPARAM /*lParam*/) noexcept {
126+
SendMessage(hwnd, WM_THEMECHANGED, 0, 0);
127+
return TRUE;
128+
}
129+
130+
void DarkMode_BroadcastThemeChanged(HWND hwnd) noexcept {
131+
if (hwnd == nullptr) {
132+
return;
133+
}
134+
SendMessage(hwnd, WM_THEMECHANGED, 0, 0);
135+
EnumChildWindows(hwnd, DarkMode_SendThemeChangedProc, 0);
136+
}
137+
116138
bool DarkMode_HandleSettingChange([[maybe_unused]] HWND hwnd, LPARAM lParam) noexcept {
117139
return dmlib::handleSettingChange(lParam);
118140
}
119141

120142
bool DarkMode_IsEnabled() noexcept {
121143
return dmlib::isExperimentalActive();
122144
}
145+
146+
int DarkMode_MessageBox(HWND hwnd, LPCWSTR text, LPCWSTR caption, UINT uType, WORD wLanguageId) noexcept {
147+
if (dmlib::isExperimentalActive()) {
148+
const HRESULT hr = dmlib::darkMessageBoxW(hwnd, text, caption, uType);
149+
if (hr > 0) {
150+
return static_cast<int>(hr);
151+
}
152+
// Fall through to MessageBoxEx on failure.
153+
}
154+
return MessageBoxEx(hwnd, text, caption, uType, wLanguageId);
155+
}

src/DarkMode.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ bool DarkMode_HandleSettingChange(HWND hwnd, LPARAM lParam) noexcept;
2828

2929
// Check if dark mode UI is currently active.
3030
bool DarkMode_IsEnabled() noexcept;
31+
32+
// Show a message box that respects dark mode (uses Task Dialog when dark mode is active).
33+
// Returns the same kind of value as MessageBoxEx (IDOK, IDYES, IDNO, IDCANCEL, ...).
34+
int DarkMode_MessageBox(HWND hwnd, LPCWSTR text, LPCWSTR caption, UINT uType, WORD wLanguageId) noexcept;
35+
36+
// Broadcast WM_THEMECHANGED to the window and all its descendants so that
37+
// themed controls (including scroll bars) re-open their theme handles.
38+
void DarkMode_BroadcastThemeChanged(HWND hwnd) noexcept;

src/Dialogs.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "Styles.h"
3535
#include "Dlapi.h"
3636
#include "Dialogs.h"
37+
#include "DarkMode.h"
3738
#include "resource.h"
3839
#include "Version.h"
3940

@@ -114,7 +115,7 @@ int MsgBox(UINT uType, UINT uIdMsg, ...) noexcept {
114115

115116
HWND hwnd = GetMsgBoxParent();
116117
PostMessage(hwndMain, APPM_CENTER_MESSAGE_BOX, AsInteger<WPARAM>(hwnd), 0);
117-
return MessageBoxEx(hwnd, szText, szTitle, uType, lang);
118+
return DarkMode_MessageBox(hwnd, szText, szTitle, uType, lang);
118119
}
119120

120121
//=============================================================================

src/Notepad4.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,10 @@ void CreateBars(HWND hwnd, HINSTANCE hInstance) noexcept {
19921992
dmlib::setDarkExplorerTheme(hwndToolbar);
19931993
dmlib::setStatusBarCtrlSubclass(hwndStatus);
19941994
dmlib::setWindowEraseBgSubclass(hwndReBar);
1995+
// Subclass the main window so darkmodelib can custom-draw toolbar buttons
1996+
// (dark hover/checked states) via the toolbar's NM_CUSTOMDRAW notifications,
1997+
// which the rebar forwards up to its parent.
1998+
dmlib::setWindowNotifyCustomDrawSubclass(hwnd);
19951999
dmlib::enableDarkScrollBarForWindowAndChildren(hwnd);
19962000

19972001
// Theme toolbar tooltips

src/Styles.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,9 @@ void Style_OnStyleThemeChanged(int theme) noexcept {
12011201
DarkMode_OnThemeChanged(theme);
12021202
if (hwndMain) {
12031203
DarkMode_ApplyToWindow(hwndMain);
1204+
// Make controls (including scroll bars) re-open theme handles so
1205+
// they pick up the new light/dark scroll bar style.
1206+
DarkMode_BroadcastThemeChanged(hwndMain);
12041207
RedrawWindow(hwndMain, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_FRAME);
12051208
}
12061209

0 commit comments

Comments
 (0)