[LyX/master] Fix use of light inset background colors in dark mode (#13312)
Juergen Spitzmueller <[email protected]> Sat, 09 May 2026 06:46:32 +0000
| Newsgroups | gmane.editors.lyx.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit d4ef3b0045da048d4834284c31fb133526dc4fba Author: Juergen Spitzmueller <[email protected]> Date: Sat May 9 08:42:13 2026 +0200 Fix use of light inset background colors in dark mode (#13312) We cannot use the default (light) font color in this case, as text is not readable (or even invisible with white background). Hence we define a new color "foreground_inverted" (black by default) that is used if the inset background color is light (determined by the W3C relative luminosity threshold) We do not need an analogouos setting in light mode I think as this conforms with the output font/paper colors --- src/Color.cpp | 1 + src/ColorCode.h | 2 ++ src/FontInfo.cpp | 4 ++-- src/FontInfo.h | 2 +- src/RowPainter.cpp | 45 ++++++++++++++++++++++++++++++++++++++--- src/frontends/NullPainter.h | 4 ++-- src/frontends/Painter.h | 8 +++++--- src/frontends/qt/GuiPainter.cpp | 19 ++++++++--------- src/frontends/qt/GuiPainter.h | 13 +++++++----- 9 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/Color.cpp b/src/Color.cpp index ecb72eca87..6744221f5c 100644 --- a/src/Color.cpp +++ b/src/Color.cpp @@ -263,6 +263,7 @@ ColorSet::ColorSet() { Color_cursor, N_("cursor"), "cursor", black, Linen, "cursor" }, { Color_background, N_("background"), "background", Linen, black, "background" }, { Color_foreground, N_("text"), "foreground", black, Linen, "foreground" }, + { Color_foreground_inverted, N_("text"), "foreground_inverted", Linen, black, "foreground_inverted" }, { Color_selection, N_("selection"), "selection", "#add8e6", "#add8e6", "selection" }, { Color_selectionmath, N_("selected math"), "selectionmath", "#00008B", "#00008B", "selectionmath" }, { Color_selectiontext, N_("selected text"), "selectiontext", black, black, "selectiontext" }, diff --git a/src/ColorCode.h b/src/ColorCode.h index c0cdaaadee..60837a3c2c 100644 --- a/src/ColorCode.h +++ b/src/ColorCode.h @@ -63,6 +63,8 @@ enum ColorCode { Color_background, /// Foreground color Color_foreground, + /// Foreground color in inverted background + Color_foreground_inverted, /// Background color of selected text Color_selection, /// Foreground color of selected math diff --git a/src/FontInfo.cpp b/src/FontInfo.cpp index 867b1e66e0..7133836949 100644 --- a/src/FontInfo.cpp +++ b/src/FontInfo.cpp @@ -450,12 +450,12 @@ bool FontInfo::resolved() const } -Color FontInfo::realColor() const +Color FontInfo::realColor(bool const invert) const { if (paint_color_ != Color_none) return paint_color_; if (color_ == Color_none) - return Color_foreground; + return invert ? Color_foreground_inverted : Color_foreground; return color_; } diff --git a/src/FontInfo.h b/src/FontInfo.h index 008504653e..bcbe596e98 100644 --- a/src/FontInfo.h +++ b/src/FontInfo.h @@ -112,7 +112,7 @@ public: /// The real color of the font. This can be the color that is /// set for painting, the color of the font or a default color. - Color realColor() const; + Color realColor(bool const invert = false) const; /// Sets the color which is used during painting void setPaintColor(Color c) { paint_color_ = c; } diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 3ee26b6096..cced976d6f 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -14,6 +14,7 @@ #include "RowPainter.h" #include "Buffer.h" +#include "ColorSet.h" #include "CoordCache.h" #include "Cursor.h" #include "BufferParams.h" @@ -32,6 +33,7 @@ #include "TextMetrics.h" #include "frontends/FontMetrics.h" +#include "frontends/Application.h" #include "frontends/Painter.h" #include "support/debug.h" @@ -39,6 +41,7 @@ #include "support/lassert.h" #include <algorithm> +#include <cmath> using namespace std; @@ -245,6 +248,37 @@ void RowPainter::paintMisspelledMark(Row::Element const & e) const } +namespace { + +// check whether a color is relatively light (i.e., needs +// a dark one as contrast) depending on relative luminance +// (cf. https://www.w3.org/TR/WCAG20/#relativeluminancedef) +bool isLightColor(RGBColor const & rgb) +{ + // RGB to sRGB + double r = rgb.r / 255.0; + if (r <= 0.04045) + r = r / 12.92; + else + r = pow((r + 0.055) / 1.055, 2.4); + double g = rgb.g / 255.0; + if (g <= 0.04045) + g = g / 12.92; + else + g = pow((g + 0.055) / 1.055, 2.4); + double b = rgb.b / 255.0; + if (b <= 0.04045) + b = b / 12.92; + else + b = pow((b + 0.055) / 1.055, 2.4); + // Calculate luminance + double L = 0.2126 * r + 0.7152 * g + 0.0722 * b; + + return (L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05); +} +} // namespace anon + + void RowPainter::paintStringAndSel(Row::Element const & e) const { // at least part of text selected? @@ -254,6 +288,11 @@ void RowPainter::paintStringAndSel(Row::Element const & e) const bool const all_sel = (e.pos >= row_.sel_beg && e.endpos < row_.sel_end) || pi_.selected; + bool invert = false; + if (theApp() && theApp()->isInDarkMode() + && isLightColor(rgbFromHexName(lcolor.getX11HexName(pi_.background_color, true)))) + invert = true; + if (e.type == Row::PREEDIT) { // the case of the preedit element pi_.pain.text(int(x_), yo_, e.str, e.im, e.char_format_index); @@ -262,14 +301,14 @@ void RowPainter::paintStringAndSel(Row::Element const & e) const Color const col = e.change.changed() ? e.change.color() : Color_selectiontext; copy.fontInfo().setPaintColor(col); - pi_.pain.text(int(x_), yo_, e.str, copy, e.extra, e.full_width()); + pi_.pain.text(int(x_), yo_, e.str, copy, e.extra, e.full_width(), invert); } else if (!some_sel) { - pi_.pain.text(int(x_), yo_, e.str, e.font, e.extra, e.full_width()); + pi_.pain.text(int(x_), yo_, e.str, e.font, e.extra, e.full_width(), invert); } else { pi_.pain.text(int(x_), yo_, e.str, e.font, Color_selectiontext, max(row_.sel_beg, e.pos) - e.pos, min(row_.sel_end, e.endpos) - e.pos, - e.extra, e.full_width()); + e.extra, e.full_width(), invert); } } diff --git a/src/frontends/NullPainter.h b/src/frontends/NullPainter.h index 05716322ee..f3113eb65f 100644 --- a/src/frontends/NullPainter.h +++ b/src/frontends/NullPainter.h @@ -71,11 +71,11 @@ public: void text(int, int, char_type, FontInfo const &, Direction const = Auto) override {} /// draw a string - void text(int, int, docstring const &, Font const &, double, double) override {} + void text(int, int, docstring const &, Font const &, double, double, bool) override {} /// void text(int, int, docstring const &, Font const &, - Color, size_type, size_type, double, double) override {} + Color, size_type, size_type, double, double, bool) override {} /// draw a char with input method, FontInfo is used in mathed void text(int, int, char_type, InputMethod const *, pos_type const, diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h index f6054068cf..c9f90ecb90 100644 --- a/src/frontends/Painter.h +++ b/src/frontends/Painter.h @@ -150,15 +150,17 @@ public: * text direction is enforced by the \c Font. */ virtual void text(int x, int y, docstring const & str, Font const & f, - double wordspacing, double textwidth) = 0; + double wordspacing, double textwidth, + bool const invert = false) = 0; /** draw a string at position x, y (y is the baseline), but * make sure that the part between \c from and \c to is in * \c other color. The text direction is enforced by the \c Font. */ virtual void text(int x, int y, docstring const & str, Font const & f, - Color other, size_type from, size_type to, - double wordspacing, double textwidth) = 0; + Color other, size_type from, size_type to, + double wordspacing, double textwidth, + bool const invert = false) = 0; /// draw a char at position x, y (y is the baseline) using input method /// f is used to get the font size in the text mode of mathed diff --git a/src/frontends/qt/GuiPainter.cpp b/src/frontends/qt/GuiPainter.cpp index 8920e95425..cf00f0257e 100644 --- a/src/frontends/qt/GuiPainter.cpp +++ b/src/frontends/qt/GuiPainter.cpp @@ -296,7 +296,8 @@ void GuiPainter::text(int x, int y, docstring const & s, FontInfo const & f, Dir void GuiPainter::text(int x, int y, docstring const & s, FontInfo const & f, Direction const dir, - double const wordspacing, double const tw) + double const wordspacing, double const tw, + bool const invert) { //LYXERR0("text: x=" << x << ", s=" << s); if (s.empty()) @@ -329,7 +330,7 @@ void GuiPainter::text(int x, int y, docstring const & s, textDecoration(f, x, y, textwidth); - setQPainterPen(computeColor(f.realColor())); + setQPainterPen(computeColor(f.realColor(invert))); if (dir != Auto) { auto ptl = fm.getTextLayout(s, dir == RtL, wordspacing); QTextLine const & tline = ptl->lineForTextPosition(0); @@ -391,16 +392,16 @@ void GuiPainter::text(int x, int y, docstring const & s, } void GuiPainter::text(int x, int y, docstring const & str, Font const & f, - double const wordspacing, double const tw) + double const wordspacing, double const tw, bool const invert) { text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft() ? RtL : LtR, - wordspacing, tw); + wordspacing, tw, invert); } void GuiPainter::text(int x, int y, docstring const & str, Font const & f, Color other, size_type const from, size_type const to, - double const wordspacing, double const tw) + double const wordspacing, double const tw, bool const invert) { GuiFontMetrics const & fm = getFontMetrics(f.fontInfo()); FontInfo fi = f.fontInfo(); @@ -413,17 +414,17 @@ void GuiPainter::text(int x, int y, docstring const & str, Font const & f, int xmax = fm.pos2x(str, to, dir == RtL, wordspacing); // Avoid this case, since it would make the `other' text spill in some cases if (xmin == xmax) { - text(x, y, str, fi, dir, wordspacing, tw); + text(x, y, str, fi, dir, wordspacing, tw, invert); return; } else if (xmin > xmax) swap(xmin, xmax); // First the part in other color - Color const orig = fi.realColor(); + Color const orig = fi.realColor(invert); fi.setPaintColor(other); QRegion const clip(x + xmin, y - ascent, xmax - xmin, height); setClipRegion(clip); - text(x, y, str, fi, dir, wordspacing, tw); + text(x, y, str, fi, dir, wordspacing, tw, invert); // Then the part in normal color // Note that in Qt5, it is not possible to use Qt::UniteClip, @@ -431,7 +432,7 @@ void GuiPainter::text(int x, int y, docstring const & str, Font const & f, fi.setPaintColor(orig); QRegion region(viewport()); setClipRegion(region - clip); - text(x, y, str, fi, dir, wordspacing, tw); + text(x, y, str, fi, dir, wordspacing, tw, invert); setClipping(false); } diff --git a/src/frontends/qt/GuiPainter.h b/src/frontends/qt/GuiPainter.h index 9988e43569..b53e4392b5 100644 --- a/src/frontends/qt/GuiPainter.h +++ b/src/frontends/qt/GuiPainter.h @@ -126,15 +126,17 @@ public: * text direction is enforced by the \c Font. */ void text(int x, int y, docstring const & str, Font const & f, - double wordspacing, double textwidth) override; + double wordspacing, double textwidth, + bool const invert = false) override; /** draw a string at position x, y (y is the baseline), but * make sure that the part between \c from and \c to is in * \c other color. The text direction is enforced by the \c Font. */ void text(int x, int y, docstring const & str, Font const & f, - Color other, size_type from, size_type to, - double wordspacing, double textwidth) override; + Color other, size_type from, size_type to, + double wordspacing, double textwidth, + bool const invert = false) override; /// draw a char at position x, y (y is the baseline) using input method /// char_format_index specifies preedit format @@ -200,8 +202,9 @@ private: // Real text() method void text(int x, int y, docstring const & s, - FontInfo const & f, Direction const dir, - double const wordspacing, double tw); + FontInfo const & f, Direction const dir, + double const wordspacing, double tw, + bool const invert = false); QColor current_color_; Painter::line_style current_ls_; -- lyx-cvs mailing list [email protected] https://lists.lyx.org/mailman/listinfo/lyx-cvs