[LyX/master] Unbreak foreground/background color adaptation with transparent backgrounds

Juergen Spitzmueller <[email protected]> Sun, 05 Jul 2026 11:19:11 +0000
Newsgroups gmane.editors.lyx.cvs
Message-ID <[email protected]>
commit efec7bd2e2b56c24a089c4c4d3188635ae33fdf5
Author: Juergen Spitzmueller <[email protected]>
Date:   Sun Jul 5 13:18:09 2026 +0200

    Unbreak foreground/background color adaptation with transparent backgrounds
---
 src/RowPainter.cpp                  |  9 ++++++---
 src/frontends/Application.h         |  5 +++--
 src/frontends/qt/ColorCache.cpp     | 27 +++++++++++++++++++++++----
 src/frontends/qt/ColorCache.h       |  5 ++++-
 src/frontends/qt/GuiApplication.cpp |  4 ++--
 src/frontends/qt/GuiApplication.h   |  2 +-
 src/frontends/qt/GuiWorkArea.cpp    | 12 +++++++-----
 src/insets/Inset.cpp                | 21 ++++++++++++++-------
 8 files changed, 60 insertions(+), 25 deletions(-)

diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp
index 0d01ac1bcc..fae564ce3b 100644
--- a/src/RowPainter.cpp
+++ b/src/RowPainter.cpp
@@ -258,12 +258,15 @@ void RowPainter::paintStringAndSel(Row::Element const & e) const
 
 	bool invert = false;
 	if (theApp() && theApp()->isInDarkMode()) {
+		ColorCode cc = pi_.backgroundColor(&pi_.base.bv->buffer().inset());
 		if (e.font.fontInfo().color() == Color_none
-		    && theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(pi_.background_color, true))))
+		    && theApp()->isLightColor(argbFromHexName(lcolor.get32bitHexName(cc, true)),
+					      argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
-		else if (pi_.background_color == Color_background
+		else if (cc == Color_background
 			 && lcolor.isStaticColor(e.font.fontInfo().color())
-			 && !theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(e.font.fontInfo().color()))))
+			 && !theApp()->isLightColor(argbFromHexName(lcolor.get32bitHexName(e.font.fontInfo().color())),
+						    argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
 	}
 
diff --git a/src/frontends/Application.h b/src/frontends/Application.h
index 3fa4fa4482..931de7849a 100644
--- a/src/frontends/Application.h
+++ b/src/frontends/Application.h
@@ -220,9 +220,10 @@ public:
 
 	/**
 	 * @return true this is a relatively light color
-	 * depending on relative luminance
+	 * depending on relative luminance.
+	 * Transparent colors are overlayed with the background.
 	 */
-	virtual bool isLightColor(RGBColor const & rgb) = 0;
+	virtual bool isLightColor(ARGBColor const & fg, ARGBColor const & bg) = 0;
 
 	/** Eg, passing Color_black returns "000000",
 	*      passing Color_white returns "ffffff".
diff --git a/src/frontends/qt/ColorCache.cpp b/src/frontends/qt/ColorCache.cpp
index 5cd1207e48..fadaa153c6 100644
--- a/src/frontends/qt/ColorCache.cpp
+++ b/src/frontends/qt/ColorCache.cpp
@@ -169,20 +169,23 @@ bool ColorCache::isDarkMode() const
 }
 
 
-bool ColorCache::isLightColor(RGBColor const & rgb)
+bool ColorCache::isLightColor(ARGBColor const & fg, ARGBColor const & bg)
 {
+	// Overlay on background (necessary with transparency)
+	ARGBColor const & argb = colorOverlay(fg, bg);
+
 	// RGB to sRGB
-	double r = rgb.r / 255.0;
+	double r = argb.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;
+	double g = argb.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;
+	double b = argb.b / 255.0;
 	if (b <= 0.04045)
 		b = b / 12.92;
 	else
@@ -224,6 +227,22 @@ QPixmap ColorCache::mergePixmaps(QPixmap const *fg, QPixmap const *bg)
 }
 
 
+ARGBColor ColorCache::colorOverlay(ARGBColor const & fg, ARGBColor const & bg)
+{
+	int red, green, blue, alpha;
+
+	auto fg_weight = fg.a;
+	auto bg_weight = 1 - fg.a;
+
+	red   = std::round((fg_weight * fg.r + bg_weight * bg.r) * 255);
+	green = std::round((fg_weight * fg.g + bg_weight * bg.g) * 255);
+	blue  = std::round((fg_weight*fg.b  + bg_weight * bg.b) * 255);
+	alpha = std::round((1 - (1 - fg.a) * (1 - bg.a) * 255));
+
+	return ARGBColor(red, green, blue, alpha);
+}
+
+
 QColor const rgb2qcolor(RGBColor const & rgb)
 {
 	return QColor(rgb.r, rgb.g, rgb.b);
diff --git a/src/frontends/qt/ColorCache.h b/src/frontends/qt/ColorCache.h
index 617bae8e56..81644f03c0 100644
--- a/src/frontends/qt/ColorCache.h
+++ b/src/frontends/qt/ColorCache.h
@@ -52,7 +52,7 @@ public:
 	 * a dark one as contrast) depending on relative luminance
 	 * (cf. https://www.w3.org/TR/WCAG20/#relativeluminancedef)
 	*/
-	bool isLightColor(RGBColor const & rgb);
+	bool isLightColor(ARGBColor const & fg, ARGBColor const & bg);
 
 	/// change the undelying palette
 	void setPalette(QPalette const pal) { pal_ = pal; clear(); }
@@ -72,6 +72,9 @@ private:
 	bool initialized_;
 	///
 	QPalette pal_;
+	/// Overlay fg color on bg color
+	/// This is necessary with transparency in fg color
+	ARGBColor colorOverlay(ARGBColor const & fg, ARGBColor const & bg);
 };
 
 ///
diff --git a/src/frontends/qt/GuiApplication.cpp b/src/frontends/qt/GuiApplication.cpp
index 532388a888..e48087bba6 100644
--- a/src/frontends/qt/GuiApplication.cpp
+++ b/src/frontends/qt/GuiApplication.cpp
@@ -3084,9 +3084,9 @@ bool GuiApplication::isInDarkMode()
 }
 
 
-bool GuiApplication::isLightColor(RGBColor const & rgb)
+bool GuiApplication::isLightColor(ARGBColor const & fg, ARGBColor const & bg)
 {
-	return colorCache().isLightColor(rgb);
+	return colorCache().isLightColor(fg, bg);
 }
 
 
diff --git a/src/frontends/qt/GuiApplication.h b/src/frontends/qt/GuiApplication.h
index 3f03ad1458..eff522eb3c 100644
--- a/src/frontends/qt/GuiApplication.h
+++ b/src/frontends/qt/GuiApplication.h
@@ -76,7 +76,7 @@ public:
 	bool getRgbColor(ColorCode col, RGBColor & rgbcol) override;
 	bool getArgbColor(ColorCode col, ARGBColor & argbcol) override;
 	bool isInDarkMode() override;
-	bool isLightColor(RGBColor const & rgb) override;
+	bool isLightColor(ARGBColor const & fg, ARGBColor const & bg) override;
 	std::string const hexName(ColorCode col) override;
 	void registerSocketCallback(int fd, SocketCallback func) override;
 	void unregisterSocketCallback(int fd) override;
diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp
index 3fb3f26e79..b4b4b7e401 100644
--- a/src/frontends/qt/GuiWorkArea.cpp
+++ b/src/frontends/qt/GuiWorkArea.cpp
@@ -581,12 +581,14 @@ void GuiWorkArea::Private::drawCaret(QPainter & painter, int horiz_offset) const
 		&& buffer_view_->getCaretBackground() != "none"
 		&& ((guiApp->colorCache().isDarkMode()
 	            && guiApp->colorCache().isLightColor(
-			   rgbFromHexName(lcolor.getX11HexName(
-						  buffer_view_->getCaretBackground(), true))))
+			     argbFromHexName(lcolor.get32bitHexName(
+						  buffer_view_->getCaretBackground(), true)),
+			     argbFromHexName(lcolor.get32bitHexName("background", true))))
 		|| (!guiApp->colorCache().isDarkMode()
-		    && !guiApp->colorCache().isLightColor(rgbFromHexName(
-							      lcolor.getX11HexName(
-								      buffer_view_->getCaretBackground()))))))
+		    && !guiApp->colorCache().isLightColor(argbFromHexName(
+							      lcolor.get32bitHexName(
+								      buffer_view_->getCaretBackground())),
+							  argbFromHexName(lcolor.get32bitHexName("background"))))))
 			? guiApp->colorCache().get(Color_cursor_inverted)
 			: guiApp->colorCache().get(Color_cursor);
 	painter.setPen(color);
diff --git a/src/insets/Inset.cpp b/src/insets/Inset.cpp
index 14f93c9d2b..0924136107 100644
--- a/src/insets/Inset.cpp
+++ b/src/insets/Inset.cpp
@@ -308,12 +308,15 @@ Color Inset::getModeDependentColor(PainterInfo & pi) const
 	FontInfo font = pi.base.font;
 	bool invert = false;
 	if (theApp() && theApp()->isInDarkMode()) {
+		ColorCode cc = pi.backgroundColor(this);
 		if (font.color() == Color_none
-		    && theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(pi.background_color, true))))
+		    && theApp()->isLightColor(argbFromHexName(lcolor.get32bitHexName(cc, true)),
+					      argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
-		else if (pi.background_color == Color_background
+		else if (cc == Color_background
 			 && font.color() != Color_none
-			 && !theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(font.color()))))
+			 && !theApp()->isLightColor(argbFromHexName(lcolor.get32bitHexName(font.color())),
+						    argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
 	}
 	Color rc = font.realColor(invert);
@@ -331,16 +334,20 @@ Color Inset::getModeDependentColor(ColorCode const fgc, ColorCode const bgc) con
 	bool darken = false;
 	if (theApp() && theApp()->isInDarkMode()) {
 		if (fgc == Color_none
-		    && theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(bgc, true))))
+		    && theApp()->isLightColor(argbFromHexName(lcolor.getX11HexName(bgc, true)),
+					      argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
 		else if (bgc == Color_background
 			 && fgc != Color_none
-			 && !theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(fgc, true))))
+			 && !theApp()->isLightColor(argbFromHexName(lcolor.getX11HexName(fgc, true)),
+						    argbFromHexName(lcolor.get32bitHexName("background", true))))
 			invert = true;
 		else if (bgc != Color_background
 			 && fgc != Color_none
-			 && theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(fgc, true)))
-			 && theApp()->isLightColor(rgbFromHexName(lcolor.getX11HexName(bgc, true))))
+			 && theApp()->isLightColor(argbFromHexName(lcolor.getX11HexName(fgc, true)),
+						   argbFromHexName(lcolor.get32bitHexName(bgc, true)))
+			 && theApp()->isLightColor(argbFromHexName(lcolor.getX11HexName(bgc, true)),
+						   argbFromHexName(lcolor.get32bitHexName("background", true))))
 			darken = true;
 	}
 	Color res = fgc;
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs