Use readable text highlight colors in dark mode.

This commit is contained in:
Alexander Shaduri
2026-03-08 18:52:27 +01:00
parent e4c9afeed0
commit 3642afc46f
7 changed files with 128 additions and 108 deletions
+1
View File
@@ -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
+23
View File
@@ -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<Gtk::Settings> 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;
}
+2 -2
View File
@@ -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
+88
View File
@@ -0,0 +1,88 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2026 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
/// \ingroup applib
/// \weakgroup applib
/// @{
#include <glibmm.h>
#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 = "<b>", stop = "</b>";
if (app_property_get_label_highlight_color(gui_is_dark_theme_active(), p.warning_level, fg)) {
start += "<span color=\"" + fg + "\">";
stop = "</span>" + 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 {};
}
/// @}
+6 -100
View File
@@ -1,7 +1,7 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
(C) 2008 - 2026 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
/// \file
/// \author Alexander Shaduri
@@ -12,118 +12,24 @@ Copyright:
#ifndef WARNING_COLORS_H
#define WARNING_COLORS_H
#include <glibmm.h>
#include <gtkmm.h>
#include <string>
#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<Gtk::Settings> 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 = "<b>", stop = "</b>";
if (app_property_get_label_highlight_color(p.warning_level, fg)) {
start += "<span color=\"" + fg + "\">";
stop = "</span>" + 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);
+7 -5
View File
@@ -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("<span color=\"").append(fg).append("\">")
.append(label_text).append("</span>") );
@@ -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("<span color=\"" + fg + "\">" + original_label + "</span>");
}
@@ -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("<span color=\"" + fg + "\">" + name->get_label() + "</span>");
value->set_markup("<span color=\"" + fg + "\">" + value->get_label() + "</span>");
}
@@ -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 = "<span color=\"" + alert_color + "\">"s + result_main_msg + "</span>";
}
}
+1 -1
View File
@@ -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("<span color=\"" + fg + "\">"+ Glib::Markup::escape_text(health_label_->get_text()) + "</span>");
}
// don't set description tooltip - we already have the basic one.