Skip to content

Commit d0a5c6f

Browse files
committed
Add Dark Mode support for Windows
1 parent f0199f3 commit d0a5c6f

File tree

10 files changed

+200
-86
lines changed

10 files changed

+200
-86
lines changed

3rdParty/vcpkg_ports/configs/msbuild/vcpkg.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"rappture",
1212
{
1313
"name": "wxwidgets",
14+
"version>=": "3.3.1",
1415
"features": ["webview"],
1516
"default-features": false
1617
},

clientgui/BOINCGUIApp.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,40 @@ typedef unsigned int (*RsvgErrorQuark) (void);
8484
typedef void (*RsvgHandleGetPixbuf) (void*);
8585
#endif
8686

87+
#if SUPPORTDARKMODE && defined(__WXMSW__) && wxCHECK_VERSION(3,3,0)
88+
// wxWidgets' IsDark() returns false on Win 11 now even if dark mode is enabled
89+
// so we need to additional function to check the registry for the dark mode setting
90+
bool IsWindowsInDarkMode() {
91+
HKEY hKey;
92+
DWORD dwValue;
93+
DWORD dwSize = sizeof(dwValue);
94+
95+
// Open the registry key where the app mode theme is stored
96+
LONG result = RegOpenKeyEx(HKEY_CURRENT_USER,
97+
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
98+
0, KEY_READ, &hKey);
99+
100+
if (result != ERROR_SUCCESS) {
101+
// Handle error, e.g., key not found
102+
return false;
103+
}
104+
105+
// Query the value of "AppsUseLightTheme"
106+
result = RegQueryValueEx(hKey, L"AppsUseLightTheme", nullptr, nullptr,
107+
reinterpret_cast<LPBYTE>(&dwValue), &dwSize);
108+
109+
RegCloseKey(hKey);
110+
111+
if (result != ERROR_SUCCESS) {
112+
// Handle error, e.g., value not found
113+
return false;
114+
}
115+
116+
// If AppsUseLightTheme is 0, dark mode is enabled for apps
117+
return (dwValue == 0);
118+
}
119+
#endif
120+
87121
bool CBOINCGUIApp::OnInit() {
88122
#if defined(__WXGTK__) && defined(BUILD_WITH_VCPKG)
89123
try {
@@ -108,6 +142,13 @@ bool CBOINCGUIApp::OnInit() {
108142
#if SUPPORTDARKMODE
109143
wxSystemAppearance appearance = wxSystemSettings::GetAppearance();
110144
m_isDarkMode = appearance.IsDark();
145+
// Dark theme only makes sense on Win if WxWidgets has version >3.3
146+
#if defined(__WXMSW__) && wxCHECK_VERSION(3,3,0)
147+
// wxWidgets' IsDark() returns false on Win 11 now even if dark mode is enabled
148+
// so we need to additionally check the registry for the dark mode setting
149+
m_isDarkMode = m_isDarkMode || IsWindowsInDarkMode();
150+
MSWEnableDarkMode(wxApp::DarkMode_Auto);
151+
#endif
111152
#endif
112153

113154
s_bSkipExitConfirmation = false;

clientgui/BOINCGUIApp.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@
3434
#define BOINC_ADVANCEDGUI 1
3535
#define BOINC_SIMPLEGUI 2
3636

37+
#include <wx/version.h>
38+
3739
// NOTE: MacOS automatically adjusts all standard OS-drawn UI items
3840
// in Dark Mode, so BOINC must not modify their colors for Dark Mode.
3941
// For MacOS, we adjust Dark Mode colors only for our custom UI items.
4042
// If you implement Dark Mode support for another OS which requires
4143
// BOINC to adjust standard UI items for Dark Mode, be sure to guard
4244
// those changes so they do not affect the Mac implementation.
4345
//
44-
#if (defined(__WXMAC__) || defined(__WXGTK__))
46+
#if (defined(__WXMAC__) || defined(__WXGTK__) || (defined(__WXMSW__) && wxCHECK_VERSION(3,3,0)))
4547
#define SUPPORTDARKMODE true
4648
#else
4749
#define SUPPORTDARKMODE false

clientgui/BOINCListCtrl.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,23 @@ void CBOINCListCtrl::DrawProgressBars()
524524
int n = (int)m_iRowsNeedingProgressBars.GetCount();
525525
if (n <= 0) return;
526526

527-
wxColour progressColor = isDarkMode ? wxColour(0, 64, 128) : wxColour(192, 217, 217);
527+
wxColour progressColor;
528+
wxColour remainderColor;
529+
wxColour textColor;
530+
531+
if (isDarkMode) {
532+
progressColor = wxColour(96, 96, 96);
533+
remainderColor = wxColour(24, 24, 24);
534+
textColor = wxColour(230, 230, 230);
535+
} else {
536+
progressColor = wxColour(192, 217, 217);
537+
remainderColor = *wxWHITE;
538+
textColor = *wxBLACK;
539+
}
540+
528541
wxBrush progressBrush(progressColor);
542+
wxBrush remainderBrush(remainderColor);
543+
wxPen remainderPen(remainderColor);
529544

530545
numItems = GetItemCount();
531546
if (numItems) {
@@ -606,12 +621,13 @@ void CBOINCListCtrl::DrawProgressBars()
606621
dc.SetPen(bkgd);
607622
dc.SetBrush(bkgd);
608623
#else
609-
dc.SetPen(isDarkMode ? *wxBLACK_PEN : *wxWHITE_PEN);
610-
dc.SetBrush(isDarkMode ? *wxBLACK_BRUSH : *wxWHITE_BRUSH);
624+
dc.SetPen(remainderPen);
625+
dc.SetBrush(remainderBrush);
611626
#endif
612627
dc.DrawRectangle( rr );
613628

614629
dc.SetPen(*wxBLACK_PEN);
630+
dc.SetTextForeground(textColor);
615631
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
616632
if (xx > (r.width - 7)) {
617633
dc.DrawText(progressString, r.x, r.y);

clientgui/NoticeListCtrl.cpp

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,48 @@
3434
#include "MainDocument.h"
3535
#include "NoticeListCtrl.h"
3636

37+
namespace {
38+
39+
#if wxUSE_WEBVIEW
40+
wxString BuildNoticeHtmlStart(bool darkMode) {
41+
const wxString style = darkMode ?
42+
wxT("<style>"
43+
"body { background-color:#000000; color:#f2f2f2; font-family:Helvetica,Arial,sans-serif; }"
44+
"a { color:#4fb3ff !important; }"
45+
"hr { border:0; border-top:1px solid #303030; }"
46+
".notice-footer { color:#a0a0a0 !important; font-size:0.8em; }"
47+
"</style>")
48+
:
49+
wxT("<style>"
50+
"body { background-color:#ffffff; color:#000000; font-family:Helvetica,Arial,sans-serif; }"
51+
"a { color:#0645ad; }"
52+
"hr { border:0; border-top:1px solid #dcdcdc; }"
53+
".notice-footer { color:#6f6f6f; font-size:0.8em; }"
54+
"</style>");
55+
return wxT("<html><head>") + style + wxT("</head><body>");
56+
}
57+
#else
58+
wxString BuildNoticeHtmlStart(bool darkMode) {
59+
const wxString linkColour = darkMode ? wxT("#4fb3ff") : wxT("#0645ad");
60+
const wxString ruleColour = darkMode ? wxT("#303030") : wxT("#dcdcdc");
61+
const wxString footerColour = darkMode ? wxT("#a0a0a0") : wxT("#6f6f6f");
62+
const wxString bodyAttrs = darkMode
63+
? wxT(" bgcolor=\"#000000\" text=\"#f2f2f2\"")
64+
: wxT(" bgcolor=\"#ffffff\" text=\"#000000\"");
65+
const wxString style =
66+
wxT("<style type=\"text/css\">")
67+
wxT("body { font-family:Helvetica,Arial,sans-serif; }")
68+
wxT("a { color:") + linkColour + wxT("; }")
69+
wxT("hr { border:0; border-top:1px solid ") + ruleColour + wxT("; }")
70+
wxT(".notice-footer { color:") + footerColour + wxT("; font-size:0.8em; }")
71+
wxT("</style>");
72+
return wxT("<html><head>") + style + wxT("</head><body") + bodyAttrs + wxT(">");
73+
}
74+
#endif // wxUSE_WEBVIEW
75+
76+
} // namespace
77+
78+
3779
////@begin XPM images
3880
////@end XPM images
3981

@@ -101,20 +143,9 @@ bool CNoticeListCtrl::Create( wxWindow* parent ) {
101143
SetSizer(topsizer);
102144

103145
m_itemCount = 0;
104-
bool isWindowsDarkMode = false;
105-
#ifdef __WXMSW__
106-
const wxSystemAppearance appearance = wxSystemSettings::GetAppearance();
107-
isWindowsDarkMode = appearance.IsSystemDark();
108-
#endif
109-
if (wxGetApp().GetIsDarkMode() || isWindowsDarkMode){
110-
#if wxUSE_WEBVIEW
111-
m_noticesBody = wxT("<html><style>body{background-color:black;color:white;}</style><head></head><body></body></html>");
112-
#else
113-
m_noticesBody = wxT("<html><head></head><body bgcolor=black></body></html>");
114-
#endif
115-
} else {
116-
m_noticesBody = wxT("<html><head></head><body></body></html>");
117-
}
146+
147+
const bool darkMode = wxGetApp().GetIsDarkMode();
148+
m_noticesBody = BuildNoticeHtmlStart(darkMode) + wxT("</body></html>");
118149

119150
// In Dark Mode, paint the window black immediately
120151
#if wxUSE_WEBVIEW
@@ -158,21 +189,8 @@ void CNoticeListCtrl::SetItemCount(int newCount) {
158189
wxASSERT(pSkinAdvanced);
159190
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
160191

161-
m_itemCount = newCount;
162-
bool isWindowsDarkMode = false;
163-
#ifdef __WXMSW__
164-
const wxSystemAppearance appearance = wxSystemSettings::GetAppearance();
165-
isWindowsDarkMode = appearance.IsSystemDark();
166-
#endif
167-
if (wxGetApp().GetIsDarkMode() || isWindowsDarkMode){
168-
#if wxUSE_WEBVIEW
169-
m_noticesBody = wxT("<html><style>body{background-color:black;color:white;}</style><head></head><body><font face=helvetica>");
170-
#else
171-
m_noticesBody = wxT("<html><head></head><body bgcolor=black><font face=helvetica color=white bgcolor=black>");
172-
#endif
173-
} else {
174-
m_noticesBody = wxT("<html><head></head><body><font face=helvetica>");
175-
}
192+
const bool darkMode = wxGetApp().GetIsDarkMode();
193+
m_noticesBody = BuildNoticeHtmlStart(darkMode);
176194

177195
for (i=0; i<newCount; ++i) {
178196
if (pDoc->IsConnected()) {
@@ -245,7 +263,7 @@ void CNoticeListCtrl::SetItemCount(int newCount) {
245263
strBuffer = wxT("<hr>");
246264
}
247265

248-
strBuffer += wxT("<table border=0 cellpadding=5><tr><td>");
266+
strBuffer += wxT("<table class=\"notice\" border=0 cellpadding=5 cellspacing=0><tr><td>");
249267

250268
if (!strTitle.IsEmpty()) {
251269
strTemp.Printf(
@@ -257,7 +275,7 @@ void CNoticeListCtrl::SetItemCount(int newCount) {
257275

258276
strBuffer += strDescription;
259277

260-
strBuffer += wxT("<br><font size=-2 color=#8f8f8f>");
278+
strBuffer += wxT("<div class=\"notice-footer\">");
261279

262280
strBuffer += strCreateTime;
263281

@@ -270,11 +288,11 @@ void CNoticeListCtrl::SetItemCount(int newCount) {
270288
strBuffer += strTemp;
271289
}
272290

273-
strBuffer += wxT("</font></td></tr></table>");
291+
strBuffer += wxT("</div></td></tr></table>");
274292
}
275293
m_noticesBody += strBuffer;
276294
}
277-
m_noticesBody += wxT("</font></body></html>");
295+
m_noticesBody += wxT("</body></html>");
278296
// baseURL is not needed here (see comments above) and it
279297
// must be an empty string for this to work under OS 10.12.4
280298
#if wxUSE_WEBVIEW

clientgui/ViewResources.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,28 @@ END_EVENT_TABLE ()
4040

4141

4242
CViewResources::CViewResources()
43+
: m_BOINCwasEmpty(false)
44+
, m_isDarkTheme(wxGetApp().GetIsDarkMode())
4345
{}
4446

4547
CViewResources::CViewResources(wxNotebook* pNotebook) :
4648
CBOINCBaseView(pNotebook)
49+
, m_BOINCwasEmpty(false)
50+
, m_isDarkTheme(wxGetApp().GetIsDarkMode())
4751
{
48-
bool isDarkMode = wxGetApp().GetIsDarkMode();
49-
m_BOINCwasEmpty=false;
50-
5152
wxGridSizer* itemGridSizer = new wxGridSizer(2, 0, 3);
5253
wxASSERT(itemGridSizer);
5354

5455
// create pie chart ctrl for total disk usage
5556
m_pieCtrlTotal = new wxPieCtrl(this, ID_PIECTRL_RESOURCEUTILIZATIONVIEWTOTAL, wxDefaultPosition, wxDefaultSize);
5657
wxASSERT(m_pieCtrlTotal);
58+
m_pieCtrlTotal->ApplyTheme(m_isDarkTheme);
5759

5860
// setup the legend
5961
m_pieCtrlTotal->SetTransparent(true);
6062
m_pieCtrlTotal->SetHorLegendBorder(10);
6163
m_pieCtrlTotal->SetLabelFont(*wxSWISS_FONT);
62-
m_pieCtrlTotal->SetLabelColour(isDarkMode ? *wxWHITE : *wxBLACK);
64+
m_pieCtrlTotal->SetLabelColour(m_isDarkTheme ? *wxWHITE : *wxBLACK);
6365
m_pieCtrlTotal->SetLabel(_("Total disk usage"));
6466

6567
// initialize pie control
@@ -75,12 +77,13 @@ CViewResources::CViewResources(wxNotebook* pNotebook) :
7577
// create pie chart ctrl for BOINC disk usage
7678
m_pieCtrlBOINC = new wxPieCtrl(this, ID_PIECTRL_RESOURCEUTILIZATIONVIEW, wxDefaultPosition, wxDefaultSize);
7779
wxASSERT(m_pieCtrlBOINC);
80+
m_pieCtrlBOINC->ApplyTheme(m_isDarkTheme);
7881

7982
//setup the legend
8083
m_pieCtrlBOINC->SetTransparent(true);
8184
m_pieCtrlBOINC->SetHorLegendBorder(10);
8285
m_pieCtrlBOINC->SetLabelFont(*wxSWISS_FONT);
83-
m_pieCtrlTotal->SetLabelColour(isDarkMode ? *wxWHITE : *wxBLACK);
86+
m_pieCtrlBOINC->SetLabelColour(m_isDarkTheme ? *wxWHITE : *wxBLACK);
8487
m_pieCtrlBOINC->SetLabel(_("Disk usage by BOINC projects"));
8588

8689
// initialize pie control
@@ -173,7 +176,6 @@ void CViewResources::OnListRender() {
173176
wxString diskspace;
174177
static double project_total=0.0;
175178
unsigned int i;
176-
bool isDarkMode = wxGetApp().GetIsDarkMode();
177179

178180
wxASSERT(pDoc);
179181
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
@@ -228,7 +230,7 @@ void CViewResources::OnListRender() {
228230
wxPiePart part;
229231
part.SetLabel(_("no projects: 0 bytes used"));
230232
part.SetValue(1);
231-
part.SetColour(isDarkMode ? wxColour(255, 255, 255) : wxColour(0,0,0));
233+
part.SetColour(m_isDarkTheme ? wxColour(255, 255, 255) : wxColour(0,0,0));
232234
m_pieCtrlBOINC->m_Series.Add(part);
233235
m_pieCtrlBOINC->Refresh();
234236
m_BOINCwasEmpty=true;
@@ -263,8 +265,7 @@ void CViewResources::OnListRender() {
263265
FormatDiskSpace(boinc_total, diskspace);
264266
part.SetLabel(_("used by BOINC: ") + diskspace);
265267
part.SetValue(boinc_total);
266-
part.SetColour(isDarkMode ? wxColour(255, 255, 255) : wxColour(0,0,0));
267-
part.SetColour(isDarkMode ? *wxWHITE : *wxBLACK);
268+
part.SetColour(m_isDarkTheme ? *wxWHITE : *wxBLACK);
268269
m_pieCtrlTotal->m_Series.Add(part);
269270

270271
if (pDoc->disk_usage.d_allowed > 0) {
@@ -274,15 +275,15 @@ void CViewResources::OnListRender() {
274275
FormatDiskSpace(avail, diskspace);
275276
part.SetLabel(_("free, available to BOINC: ") + diskspace);
276277
part.SetValue(avail == 0 ? 1 : avail);
277-
part.SetColour(isDarkMode ? wxColour(108, 108, 108) : wxColour(128, 128, 128));
278+
part.SetColour(m_isDarkTheme ? wxColour(108, 108, 108) : wxColour(128, 128, 128));
278279
m_pieCtrlTotal->m_Series.Add(part);
279280

280281
double not_avail = free - avail;
281282
if (not_avail > 0) {
282283
FormatDiskSpace(not_avail, diskspace);
283284
part.SetLabel(_("free, not available to BOINC: ") + diskspace);
284285
part.SetValue(not_avail);
285-
part.SetColour(isDarkMode ? wxColour(172,172,172) : wxColour(238,238,238));
286+
part.SetColour(m_isDarkTheme ? wxColour(172,172,172) : wxColour(238,238,238));
286287
m_pieCtrlTotal->m_Series.Add(part);
287288
}
288289
} else {
@@ -292,7 +293,7 @@ void CViewResources::OnListRender() {
292293
FormatDiskSpace(free, diskspace);
293294
part.SetLabel(_("free: ") + diskspace);
294295
part.SetValue(free);
295-
part.SetColour(isDarkMode ? wxColour(172,172,172) : wxColour(238,238,238));
296+
part.SetColour(m_isDarkTheme ? wxColour(172,172,172) : wxColour(238,238,238));
296297
m_pieCtrlTotal->m_Series.Add(part);
297298
}
298299

@@ -301,7 +302,7 @@ void CViewResources::OnListRender() {
301302
FormatDiskSpace(used_by_others, diskspace);
302303
part.SetLabel(_("used by other programs: ") + diskspace);
303304
part.SetValue(used_by_others);
304-
part.SetColour(isDarkMode ? wxColour(140,140,140) : wxColour(192,192,192));
305+
part.SetColour(m_isDarkTheme ? wxColour(140,140,140) : wxColour(192,192,192));
305306
m_pieCtrlTotal->m_Series.Add(part);
306307
m_pieCtrlTotal->Refresh();
307308
}

clientgui/ViewResources.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class CViewResources : public CBOINCBaseView
5252
wxPieCtrl* m_pieCtrlTotal;
5353

5454
bool m_BOINCwasEmpty;
55+
bool m_isDarkTheme;
5556

5657
virtual void UpdateSelection();
5758

0 commit comments

Comments
 (0)