From 3642afc46f2e1f575e4201eb6ec5883a28e63a68 Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Sun, 8 Mar 2026 18:51:24 +0100 Subject: [PATCH] Use readable text highlight colors in dark mode. --- src/applib/CMakeLists.txt | 1 + src/applib/gui_utils.cpp | 23 ++++++++ src/applib/gui_utils.h | 4 +- src/applib/warning_colors.cpp | 88 ++++++++++++++++++++++++++++ src/applib/warning_colors.h | 106 ++-------------------------------- src/gui/gsc_info_window.cpp | 12 ++-- src/gui/gsc_main_window.cpp | 2 +- 7 files changed, 128 insertions(+), 108 deletions(-) create mode 100644 src/applib/warning_colors.cpp diff --git a/src/applib/CMakeLists.txt b/src/applib/CMakeLists.txt index c70702b..ba8b558 100644 --- a/src/applib/CMakeLists.txt +++ b/src/applib/CMakeLists.txt @@ -72,6 +72,7 @@ target_sources(applib PRIVATE storage_property_repository.cpp storage_property_repository.h storage_settings.h + warning_colors.cpp warning_colors.h warning_level.h window_instance_manager.h diff --git a/src/applib/gui_utils.cpp b/src/applib/gui_utils.cpp index 0eee330..50a8364 100644 --- a/src/applib/gui_utils.cpp +++ b/src/applib/gui_utils.cpp @@ -165,6 +165,29 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes +bool gui_is_dark_theme_active() +{ + // Try to get the GTK settings to check for dark theme preference. + // If GTK is not available or not initialized, get_default() will return null. + const Glib::RefPtr settings = Gtk::Settings::get_default(); + if (settings) { + // Check if the application prefers dark theme + if (settings->property_gtk_application_prefer_dark_theme().get_value()) { + return true; + } + + // Check theme name for common dark theme identifiers + Glib::ustring theme_name; + settings->get_property("gtk-theme-name", theme_name); + const std::string theme_str = theme_name.lowercase(); + if (theme_str.find("dark") != std::string::npos || + theme_str.find("black") != std::string::npos) { + return true; + } + } + + return false; +} diff --git a/src/applib/gui_utils.h b/src/applib/gui_utils.h index f7ee726..b6f06dc 100644 --- a/src/applib/gui_utils.h +++ b/src/applib/gui_utils.h @@ -58,8 +58,8 @@ bool gui_show_text_entry_dialog(const std::string& title, const std::string& mes std::string& result, const std::string& default_str, Gtk::Window* parent = nullptr, bool sec_msg_markup = false); - - +/// Check if a dark GTK theme is currently active +bool gui_is_dark_theme_active(); #endif diff --git a/src/applib/warning_colors.cpp b/src/applib/warning_colors.cpp new file mode 100644 index 0000000..b648472 --- /dev/null +++ b/src/applib/warning_colors.cpp @@ -0,0 +1,88 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2008 - 2026 Alexander Shaduri +******************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +#include + +#include "warning_colors.h" +#include "gui_utils.h" + + +bool app_property_get_row_highlight_colors(bool dark_mode, WarningLevel warning, std::string& fg, std::string& bg) +{ + // Note: we're setting both fg and bg, to avoid theme conflicts. + if (warning == WarningLevel::Notice) { + fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes + bg = dark_mode ? "#6B2050" : "#FFD5EE"; // dark pinkish for dark themes, pinkish for light themes + + } else if (warning == WarningLevel::Warning) { + fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes + bg = dark_mode ? "#802020" : "#FFA0A0"; // dark red for dark themes, light red for light themes + + } else if (warning == WarningLevel::Alert) { + fg = dark_mode ? "#FFFFFF" : "#000000"; // white for dark themes, black for light themes + bg = dark_mode ? "#AA0000" : "#FF0000"; // darker red for dark themes, bright red for light themes + } + + return !(fg.empty()); +} + + + +bool app_property_get_label_highlight_color(bool dark_mode, WarningLevel warning, std::string& fg) +{ + if (warning == WarningLevel::None) { + return false; + } + + if (warning == WarningLevel::Notice) { + fg = dark_mode ? "#FF9999" : "#770000"; // lighter red for dark themes, very dark red for light themes + + } else if (warning == WarningLevel::Warning) { + fg = dark_mode ? "#FF6666" : "#C00000"; // lighter red for dark themes, dark red for light themes + + } else if (warning == WarningLevel::Alert) { + fg = dark_mode ? "#FF4444" : "#FF0000"; // lighter/pink red for dark themes, bright red for light themes + } + + return !(fg.empty()); +} + + + +std::string storage_property_get_warning_reason(const StorageProperty& p) +{ + std::string fg, start = "", stop = ""; + if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), p.warning_level, fg)) { + start += ""; + stop = "" + stop; + } + + switch (p.warning_level) { + case WarningLevel::None: + // nothing + break; + case WarningLevel::Notice: + /// Translators: %1 and %2 are HTML tags, %3 is a message. + return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); + case WarningLevel::Warning: + /// Translators: %1 and %2 are HTML tags, %3 is a message. + return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); + case WarningLevel::Alert: + /// Translators: %1 and %2 are HTML tags, %3 is a message. + return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); + } + + return {}; +} + + + +/// @} diff --git a/src/applib/warning_colors.h b/src/applib/warning_colors.h index 3f75cea..eea3e9d 100644 --- a/src/applib/warning_colors.h +++ b/src/applib/warning_colors.h @@ -1,7 +1,7 @@ /****************************************************************************** License: GNU General Public License v3.0 only Copyright: - (C) 2008 - 2021 Alexander Shaduri + (C) 2008 - 2026 Alexander Shaduri ******************************************************************************/ /// \file /// \author Alexander Shaduri @@ -12,118 +12,24 @@ Copyright: #ifndef WARNING_COLORS_H #define WARNING_COLORS_H -#include -#include +#include #include "storage_property.h" - - -/// Check if a dark GTK theme is currently active -inline bool is_dark_theme_active() -{ - // Try to get the GTK settings to check for dark theme preference. - // If GTK is not available or not initialized, get_default() will return null. - Glib::RefPtr settings = Gtk::Settings::get_default(); - if (settings) { - // Check if the application prefers dark theme - bool prefer_dark = false; - settings->get_property("gtk-application-prefer-dark-theme", prefer_dark); - if (prefer_dark) { - return true; - } - - // Check theme name for common dark theme identifiers - Glib::ustring theme_name; - settings->get_property("gtk-theme-name", theme_name); - std::string theme_str = theme_name.lowercase(); - if (theme_str.find("dark") != std::string::npos || - theme_str.find("black") != std::string::npos) { - return true; - } - } - - return false; -} +#include "warning_level.h" /// Get colors for tree rows according to warning severity. /// \return true if the colors were changed. -inline bool app_property_get_row_highlight_colors(WarningLevel warning, std::string& fg, std::string& bg) -{ - // Note: we're setting both fg and bg, to avoid theme conflicts. - if (warning == WarningLevel::Notice) { - fg = "#000000"; // black - bg = "#FFD5EE"; // pinkish - - } else if (warning == WarningLevel::Warning) { - fg = "#000000"; // black - bg = "#FFA0A0"; // even more pinkish - - } else if (warning == WarningLevel::Alert) { - fg = "#000000"; // black - bg = "#FF0000"; // red - } - - return !(fg.empty()); -} - +bool app_property_get_row_highlight_colors(bool dark_mode, WarningLevel warning, std::string& fg, std::string& bg); /// Get color for labels according to warning severity. /// \return true if the color was changed. -inline bool app_property_get_label_highlight_color(WarningLevel warning, std::string& fg) -{ - // Return early for None to avoid unnecessary theme detection - if (warning == WarningLevel::None) { - return false; - } - - bool dark_theme = is_dark_theme_active(); - - if (warning == WarningLevel::Notice) { - fg = dark_theme ? "#FF9999" : "#770000"; // lighter red for dark themes, very dark red for light themes - - } else if (warning == WarningLevel::Warning) { - fg = dark_theme ? "#FF6666" : "#C00000"; // lighter red for dark themes, dark red for light themes - - } else if (warning == WarningLevel::Alert) { - fg = dark_theme ? "#FF4444" : "#FF0000"; // lighter/pink red for dark themes, bright red for light themes - } - - return !(fg.empty()); -} - - +bool app_property_get_label_highlight_color(bool dark_mode, WarningLevel warning, std::string& fg); /// Format warning text, but without description -inline std::string storage_property_get_warning_reason(const StorageProperty& p) -{ - std::string fg, start = "", stop = ""; - if (app_property_get_label_highlight_color(p.warning_level, fg)) { - start += ""; - stop = "" + stop; - } - - switch (p.warning_level) { - case WarningLevel::None: - // nothing - break; - case WarningLevel::Notice: - /// Translators: %1 and %2 are HTML tags, %3 is a message. - return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); - case WarningLevel::Warning: - /// Translators: %1 and %2 are HTML tags, %3 is a message. - return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); - case WarningLevel::Alert: - /// Translators: %1 and %2 are HTML tags, %3 is a message. - return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); - } - - return {}; -} - - +std::string storage_property_get_warning_reason(const StorageProperty& p); diff --git a/src/gui/gsc_info_window.cpp b/src/gui/gsc_info_window.cpp index 8530a04..75d8007 100644 --- a/src/gui/gsc_info_window.cpp +++ b/src/gui/gsc_info_window.cpp @@ -81,6 +81,7 @@ namespace { // vbox->pack_start(*label, false, false); } else { + const bool dark_mode = gui_is_dark_theme_active(); // add one label per element for (const auto& label_string : label_strings) { @@ -95,7 +96,7 @@ namespace { label->set_can_focus(false); std::string fg; - if (app_property_get_label_highlight_color(label_string.property->warning_level, fg)) { + if (app_property_get_label_highlight_color(dark_mode, label_string.property->warning_level, fg)) { label->set_markup( std::string("") .append(label_text).append("") ); @@ -129,7 +130,7 @@ namespace { } std::string fg; - if (app_property_get_label_highlight_color(warning, fg)) + if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), warning, fg)) label->set_markup_with_mnemonic("" + original_label + ""); } @@ -1038,6 +1039,7 @@ void GscInfoWindow::fill_ui_general(const StoragePropertyRepository& property_re identity_table->hide(); WarningLevel max_tab_warning = WarningLevel::None; + const bool dark_mode = gui_is_dark_theme_active(); int row = 0; for (auto&& p : general_props) { @@ -1070,7 +1072,7 @@ void GscInfoWindow::fill_ui_general(const StoragePropertyRepository& property_re value->set_markup(Glib::Markup::escape_text(p.format_value())); std::string fg; - if (app_property_get_label_highlight_color(p.warning_level, fg)) { + if (app_property_get_label_highlight_color(dark_mode, p.warning_level, fg)) { name->set_markup("" + name->get_label() + ""); value->set_markup("" + value->get_label() + ""); } @@ -2067,7 +2069,7 @@ WarningLevel GscInfoWindow::fill_ui_directory(const StoragePropertyRepository& p inline void cell_renderer_set_warning_fg_bg(Gtk::CellRendererText* crt, const StorageProperty& p) { std::string fg, bg; - if (app_property_get_row_highlight_colors(p.warning_level, fg, bg)) { + if (app_property_get_row_highlight_colors(gui_is_dark_theme_active(), p.warning_level, fg, bg)) { // Note: property_cell_background makes horizontal tree lines disappear around it, // but property_background doesn't play nice with sorted column color. crt->property_cell_background() = bg; @@ -2446,7 +2448,7 @@ gboolean GscInfoWindow::test_idle_callback(void* data) if (!result_main_msg.empty()) { // Highlight in red std::string alert_color; // Use the same color as Alert level warnings for consistency - if (app_property_get_label_highlight_color(WarningLevel::Alert, alert_color) && !alert_color.empty()) { + if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), WarningLevel::Alert, alert_color) && !alert_color.empty()) { result_main_msg = ""s + result_main_msg + ""; } } diff --git a/src/gui/gsc_main_window.cpp b/src/gui/gsc_main_window.cpp index f784f39..b25873b 100644 --- a/src/gui/gsc_main_window.cpp +++ b/src/gui/gsc_main_window.cpp @@ -774,7 +774,7 @@ void GscMainWindow::update_status_widgets() if (health_prop.generic_name == "smart_status/passed") { health_label_->set_text(health_prop.format_value()); std::string fg; - if (app_property_get_label_highlight_color(health_prop.warning_level, fg)) { + if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), health_prop.warning_level, fg)) { health_label_->set_markup(""+ Glib::Markup::escape_text(health_label_->get_text()) + ""); } // don't set description tooltip - we already have the basic one.