Skip to content

Commit 083936e

Browse files
authored
Add Windows dark mode detection and automatic GTK theme switching (#111)
1 parent 5343ba9 commit 083936e

3 files changed

Lines changed: 84 additions & 1 deletion

File tree

src/gui/gsc_init.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,32 @@ bool app_init_and_loop(int& argc, char**& argv)
505505
}
506506
*/
507507

508+
// Detect Windows dark mode and set GTK theme preference accordingly
509+
if constexpr(BuildEnv::is_kernel_family_windows()) {
510+
Glib::RefPtr<Gtk::Settings> gtk_settings = Gtk::Settings::get_default();
511+
if (gtk_settings) {
512+
bool use_dark_theme = false;
513+
#ifdef _WIN32
514+
// Check Windows registry for dark mode preference
515+
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
516+
// AppsUseLightTheme = 0 means dark mode, 1 means light mode
517+
DWORD apps_use_light_theme = 1; // Default to light mode
518+
if (hz::win32_get_registry_value_dword(HKEY_CURRENT_USER,
519+
R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)",
520+
"AppsUseLightTheme", apps_use_light_theme)) {
521+
use_dark_theme = (apps_use_light_theme == 0);
522+
debug_out_dump("app", "Windows theme detected: " << (use_dark_theme ? "dark" : "light") << "\n");
523+
} else {
524+
debug_out_dump("app", "Could not read Windows theme preference, defaulting to light mode.\n");
525+
}
526+
#endif
527+
// Apply the dark theme preference to GTK
528+
gtk_settings->property_gtk_application_prefer_dark_theme().set_value(use_dark_theme);
529+
debug_out_dump("app", "GTK dark theme preference set to: " << (use_dark_theme ? "dark" : "light") << "\n");
530+
}
531+
}
532+
533+
508534
// The application is dpi-aware in Windows.
509535
// However, Gtk3 does not support fractional scaling, so at 250% scaling in system settings, the UI will use 200%.
510536
//

src/gui/gsc_main_window_iconview.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ bool GscMainWindowIconView::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
120120
return true;
121121
}
122122
if (empty_view_message_ != Message::None && this->num_icons_ == 0) { // no icons
123-
Glib::RefPtr<Pango::Layout> layout = this->create_pango_layout("");
123+
const Glib::RefPtr<Pango::Layout> layout = this->create_pango_layout("");
124124
layout->set_alignment(Pango::ALIGN_CENTER);
125125
layout->set_markup(get_message_string(empty_view_message_));
126126

@@ -131,6 +131,12 @@ bool GscMainWindowIconView::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
131131
const int pos_y = (get_allocation().get_height() - layout_h) / 2;
132132
cr->move_to(pos_x, pos_y);
133133

134+
// Use the foreground color from the widget's style context so
135+
// the text is visible in both light and dark themes.
136+
const auto style_context = get_style_context();
137+
const Gdk::RGBA fg_color = style_context->get_color(style_context->get_state());
138+
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), fg_color.get_alpha());
139+
134140
layout->show_in_cairo_context(cr);
135141

136142
return true;

src/hz/win32_tools.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ inline bool win32_set_registry_value_string(HKEY base,
7979
const std::string& keydir, const std::string& key, const std::string& value);
8080

8181

82+
/// Get registry value as a DWORD.
83+
/// Base may be e.g. HKEY_CURRENT_USER.
84+
/// Note that this works only with REG_DWORD types.
85+
/// False is returned for all other types.
86+
inline bool win32_get_registry_value_dword(HKEY base,
87+
const std::string& keydir, const std::string& key, DWORD& put_here);
88+
89+
8290
/// Redirect stdout and stderr to console window (if open). Requires winxp (at compile-time).
8391
/// \param create_if_none if true, create a new console if none was found and attach to it.
8492
/// \return false if failed or unsupported.
@@ -340,6 +348,49 @@ inline bool win32_set_registry_value_string(HKEY base,
340348

341349

342350

351+
// Get registry value as a DWORD.
352+
// Note that this works only with REG_DWORD types.
353+
inline bool win32_get_registry_value_dword(HKEY base,
354+
const std::string& keydir, const std::string& key, DWORD& put_here)
355+
{
356+
std::wstring wkeydir = win32_utf8_to_utf16(keydir);
357+
if (wkeydir.empty())
358+
return false;
359+
360+
HKEY reg_key = nullptr;
361+
bool open_status = (RegOpenKeyExW(base, wkeydir.c_str(), 0, KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS);
362+
363+
if (!open_status)
364+
return false;
365+
366+
bool ok = false;
367+
std::wstring wkey = win32_utf8_to_utf16(key, &ok);
368+
if (!ok) { // conversion error. Note that an empty string is not an error.
369+
if (reg_key)
370+
RegCloseKey(reg_key);
371+
return false;
372+
}
373+
374+
DWORD type = 0;
375+
DWORD value = 0;
376+
DWORD nbytes = sizeof(DWORD);
377+
bool status = (RegQueryValueExW(reg_key, wkey.c_str(), nullptr, &type,
378+
reinterpret_cast<BYTE*>(&value), &nbytes) == ERROR_SUCCESS);
379+
380+
if (status && type == REG_DWORD) {
381+
put_here = value;
382+
} else {
383+
status = false;
384+
}
385+
386+
if (reg_key)
387+
RegCloseKey(reg_key);
388+
389+
return status;
390+
}
391+
392+
393+
343394
// Redirect stdout and stderr to console window (if open).
344395
inline bool win32_redirect_stdio_to_console(bool create_if_none)
345396
{

0 commit comments

Comments
 (0)