[LyX/master] Adapt cursor color to background color (part of #13312)

Juergen Spitzmueller <[email protected]> Sat, 16 May 2026 07:05:03 +0000
Newsgroups gmane.editors.lyx.cvs
Message-ID <[email protected]>
commit 78754b0d9c09dafe551ed719c976792173065a01
Author: Juergen Spitzmueller <[email protected]>
Date:   Sat May 16 09:03:41 2026 +0200

    Adapt cursor color to background color (part of #13312)
    
    I.e., in dark mode with a light inset background, use a dark cursor
    
    Co-production with JMarc
---
 src/BufferView.cpp               | 13 +++++++++++++
 src/BufferView.h                 |  3 +++
 src/Color.cpp                    |  1 +
 src/ColorCode.h                  |  2 ++
 src/Cursor.cpp                   |  6 +++---
 src/CursorSlice.cpp              | 14 +++++++++++++-
 src/CursorSlice.h                |  4 +++-
 src/DocIterator.cpp              | 22 ++++++++++------------
 src/DocIterator.h                |  5 ++++-
 src/frontends/qt/GuiWorkArea.cpp | 16 ++++++++++++++--
 src/insets/InsetText.cpp         |  4 ++--
 src/mathed/InsetMath.h           |  2 ++
 src/mathed/InsetMathHull.cpp     |  2 +-
 13 files changed, 71 insertions(+), 23 deletions(-)

diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 995ea8a85d..c64eb8a8ae 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -21,6 +21,7 @@
 #include "BufferList.h"
 #include "BufferParams.h"
 #include "BiblioInfo.h"
+#include "ColorSet.h"
 #include "CoordCache.h"
 #include "Cursor.h"
 #include "CutAndPaste.h"
@@ -3479,6 +3480,18 @@ int BufferView::updateMetrics(bool force)
 }
 
 
+string BufferView::getCaretBackground()
+{
+	Inset const * covering_inset = cursor().realInset();
+	if (!covering_inset)
+		return "background";
+
+	frontend::NullPainter np;
+	PainterInfo pi(this, np);
+	return lcolor.getLyXName(covering_inset->backgroundColor(pi));
+}
+
+
 void BufferView::updatePosCache()
 {
 	// this is the "nodraw" drawing stage: only set the positions of the
diff --git a/src/BufferView.h b/src/BufferView.h
index 9823fccea7..91fe571b96 100644
--- a/src/BufferView.h
+++ b/src/BufferView.h
@@ -416,6 +416,9 @@ public:
 	/// Inserted label from ref dialog
 	std::string const & insertedLabel() const { return inserted_label_; }
 
+	///
+	std::string getCaretBackground();
+
 private:
 	/// noncopyable
 	BufferView(BufferView const &);
diff --git a/src/Color.cpp b/src/Color.cpp
index 36629c015b..4e33541db8 100644
--- a/src/Color.cpp
+++ b/src/Color.cpp
@@ -261,6 +261,7 @@ ColorSet::ColorSet()
 	{ Color_violet, N_("violet"), "violet", violet, violet, "violet" },
 	{ Color_yellow, N_("yellow"), "yellow", yellow, yellow, "yellow" },
 	{ Color_cursor, N_("cursor"), "cursor", black, Linen, "cursor" },
+	{ Color_cursor_inverted, N_("cursor (inverted background)"), "cursor_inverted", Linen, black, "cursor_inverted" },
 	{ Color_background, N_("background"), "background", Linen, black, "background" },
 	{ Color_foreground, N_("text"), "foreground", black, Linen, "foreground" },
 	{ Color_foreground_inverted, N_("text (inverted background)"), "foreground_inverted", Linen, black, "foreground_inverted" },
diff --git a/src/ColorCode.h b/src/ColorCode.h
index 60837a3c2c..4a20ca2955 100644
--- a/src/ColorCode.h
+++ b/src/ColorCode.h
@@ -65,6 +65,8 @@ enum ColorCode {
 	Color_foreground,
 	/// Foreground color in inverted background
 	Color_foreground_inverted,
+	/// Cursor color on inverted background
+	Color_cursor_inverted,
 	/// Background color of selected text
 	Color_selection,
 	/// Foreground color of selected math
diff --git a/src/Cursor.cpp b/src/Cursor.cpp
index 52c8bf2821..3141660515 100644
--- a/src/Cursor.cpp
+++ b/src/Cursor.cpp
@@ -2456,7 +2456,7 @@ bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur)
 	// find inset in common
 	size_type i;
 	for (i = 0; i < old.depth() && i < cur.depth(); ++i) {
-		if (&old[i].inset() != &cur[i].inset())
+		if (old[i].realInset() != cur[i].realInset())
 			break;
 	}
 
@@ -2473,14 +2473,14 @@ bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur)
 	for (size_type j = i; j < old.depth(); ++j) {
 		Cursor inset_pos = old;
 		inset_pos.resize(j + 1);
-		if (old[j].inset().notifyCursorLeaves(inset_pos, cur))
+		if (old[j].realInset()->notifyCursorLeaves(inset_pos, cur))
 			return true;
 	}
 
 	// notify everything on top of the common part in new cursor,
 	// but stop if the inset claims the cursor to be invalid now
 	for (; i < cur.depth(); ++i) {
-		if (cur[i].inset().notifyCursorEnters(old, cur))
+		if (cur[i].realInset()->notifyCursorEnters(old, cur))
 			return true;
 	}
 
diff --git a/src/CursorSlice.cpp b/src/CursorSlice.cpp
index 522e35b7b8..e6f941e1c5 100644
--- a/src/CursorSlice.cpp
+++ b/src/CursorSlice.cpp
@@ -19,12 +19,13 @@
 #include "Paragraph.h"
 #include "ParagraphList.h"
 
-#include "support/debug.h"
+#include "insets/InsetTabular.h"
 
 #include "mathed/InsetMath.h"
 #include "mathed/InsetMathMacro.h"
 #include "mathed/MathData.h"
 
+#include "support/debug.h"
 #include "support/lassert.h"
 
 #include <ostream>
@@ -52,6 +53,17 @@ MathData & CursorSlice::cell() const
 }
 
 
+Inset * CursorSlice::realInset() const
+{
+	// if we are in a tabular, we need the cell
+	if (inset().lyxCode() == TABULAR_CODE) {
+		InsetTabular * tabular = inset().asInsetTabular();
+		return tabular->cell(idx()).get();
+	}
+	return &inset();
+}
+
+
 Paragraph & CursorSlice::paragraph() const
 {
 	return text()->getPar(pit_);
diff --git a/src/CursorSlice.h b/src/CursorSlice.h
index 6eb6ff5aba..794c5b1e91 100644
--- a/src/CursorSlice.h
+++ b/src/CursorSlice.h
@@ -59,8 +59,10 @@ public:
 
 	/// return true if the slice has not been initialized
 	bool empty() const { return !inset_; }
-	/// the current inset
+	/// the containing inset
 	Inset & inset() const { return *inset_; }
+	/// the containing inset or the (tabular) cell, respectively
+	Inset * realInset() const;
 	/// return the cell this cursor is in
 	idx_type idx() const { return idx_; }
 	/// return the cell this cursor is in
diff --git a/src/DocIterator.cpp b/src/DocIterator.cpp
index 7d18c36547..2024bcc00c 100644
--- a/src/DocIterator.cpp
+++ b/src/DocIterator.cpp
@@ -127,18 +127,6 @@ Inset * DocIterator::prevInset() const
 }
 
 
-Inset * DocIterator::realInset() const
-{
-	LASSERT(inTexted(), return nullptr);
-	// if we are in a tabular, we need the cell
-	if (inset().lyxCode() == TABULAR_CODE) {
-		InsetTabular * tabular = inset().asInsetTabular();
-		return tabular->cell(idx()).get();
-	}
-	return &inset();
-}
-
-
 InsetMath & DocIterator::nextMath()
 {
 	return *nextAtom().nucleus();
@@ -714,6 +702,16 @@ size_type DocIterator::find(Inset const * inset) const
 }
 
 
+size_type DocIterator::findInsetOrRealInset(Inset const * inset) const
+{
+	for (size_t l = 0; l != slices_.size(); ++l) {
+		if (&slices_[l].inset() == inset || slices_[l].realInset() == inset)
+			return l;
+	}
+	return lyx::npos;
+}
+
+
 void DocIterator::resize(size_type count, vector<CursorSlice> & cut)
 {
 	LASSERT(count <= depth(), return);
diff --git a/src/DocIterator.h b/src/DocIterator.h
index f46d57598f..61ad46ab97 100644
--- a/src/DocIterator.h
+++ b/src/DocIterator.h
@@ -182,7 +182,7 @@ public:
 	///
 	Text * text() const;
 	/// the containing inset or the cell, respectively
-	Inset * realInset() const;
+	Inset * realInset() const { return top().realInset(); }
 	///
 	Inset * innerInsetOfType(int code) const;
 	///
@@ -265,6 +265,9 @@ public:
 	size_type find(MathData const & cell) const;
 	/// find index of CursorSlice with inset() == inset (or lyx::npos of not found)
 	size_type find(Inset const * inset) const;
+	/// find index of CursorSlice with inset() == inset or realInset() == inset
+	/// (or lyx::npos of not found)
+	size_type findInsetOrRealInset(Inset const * inset) const;
 	/// push CursorSlices on top
 	void append(std::vector<CursorSlice> const & x);
 	/// push one CursorSlice on top and set its index and position
diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp
index 9e1cbb4655..5a067ef9aa 100644
--- a/src/frontends/qt/GuiWorkArea.cpp
+++ b/src/frontends/qt/GuiWorkArea.cpp
@@ -15,7 +15,6 @@
 #include "GuiWorkArea.h"
 #include "GuiWorkArea_Private.h"
 
-#include "ColorCache.h"
 #include "GuiApplication.h"
 #include "GuiCompleter.h"
 #include "GuiKeySymbol.h"
@@ -28,6 +27,7 @@
 #include "BufferList.h"
 #include "BufferParams.h"
 #include "BufferView.h"
+#include "ColorCache.h"
 #include "CoordCache.h"
 #include "Cursor.h"
 #include "Font.h"
@@ -576,7 +576,19 @@ void GuiWorkArea::Private::drawCaret(QPainter & painter, int horiz_offset) const
 	if (buffer_view_->caretGeometry().shapes.empty())
 		return;
 
-	QColor const color = guiApp->colorCache().get(Color_cursor);
+	QColor const color = (
+		!buffer_view_->getCaretBackground().empty()
+		&& buffer_view_->getCaretBackground() != "none"
+		&& ((guiApp->colorCache().isDarkMode()
+	            && guiApp->colorCache().isLightColor(
+			   rgbFromHexName(lcolor.getX11HexName(
+						  buffer_view_->getCaretBackground(), true))))
+		|| (!guiApp->colorCache().isDarkMode()
+		    && !guiApp->colorCache().isLightColor(rgbFromHexName(
+							      lcolor.getX11HexName(
+								      buffer_view_->getCaretBackground()))))))
+			? guiApp->colorCache().get(Color_cursor_inverted)
+			: guiApp->colorCache().get(Color_cursor);
 	painter.setPen(color);
 	painter.setRenderHint(QPainter::Antialiasing, true);
 	for (auto const & shape : buffer_view_->caretGeometry().shapes) {
diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp
index f01b0fbf3c..4e92a1236d 100644
--- a/src/insets/InsetText.cpp
+++ b/src/insets/InsetText.cpp
@@ -1324,12 +1324,12 @@ bool InsetText::notifyCursorLeaves(Cursor const & old, Cursor & cur)
 
 	// find text inset in old cursor
 	Cursor insetCur = old;
-	size_type scriptSlice = insetCur.find(this);
+	size_type scriptSlice = insetCur.findInsetOrRealInset(this);
 	// we can try to continue here. returning true means
 	// the cursor is "now" invalid. which it was.
 	LASSERT(scriptSlice != lyx::npos, return true);
 	insetCur.resize(scriptSlice + 1);
-	LASSERT(&insetCur.inset() == this, return true);
+	LASSERT(&insetCur.inset() == this || insetCur.realInset() == this, return true);
 
 	// update the old paragraph's words
 	insetCur.paragraph().updateWords();
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 6f580c3def..03adeb820f 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -277,6 +277,8 @@ public:
 	InsetCode lyxCode() const override { return MATH_CODE; }
 	///
 	uid_type id() const { return this; }
+	///
+	ColorCode backgroundColor(PainterInfo const &) const override { return Color_mathbg; };
 };
 
 ///
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index 8e98b75e46..9ba086a715 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -617,7 +617,7 @@ ColorCode InsetMathHull::backgroundColor(PainterInfo const & pi) const
 			return Color_error;
 		return graphics::PreviewLoader::backgroundColor();
 	}
-	return Color_mathbg;
+	return InsetMath::backgroundColor(pi);
 }
 
 
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs