[LyX/master] Temporary fix for #11723.

Richard Kimberly Heck rikiheck at lyx.org
Mon Dec 25 15:18:56 UTC 2023


commit 1e6b5448c1b18e7bf3a636bf27686c5773a1ccdd
Author: Koji Yakota <yokota6 at gmail.com>
Date:   Mon Dec 25 11:44:21 2023 -0500

    Temporary fix for #11723.
---
 src/frontends/qt/GuiPainter.cpp        |    2 +-
 src/frontends/qt/GuiWorkArea.cpp       |   83 +++++++++++++++++++++++++++----
 src/frontends/qt/GuiWorkArea_Private.h |    4 ++
 3 files changed, 77 insertions(+), 12 deletions(-)

diff --git a/src/frontends/qt/GuiPainter.cpp b/src/frontends/qt/GuiPainter.cpp
index 7110910..48c19f9 100644
--- a/src/frontends/qt/GuiPainter.cpp
+++ b/src/frontends/qt/GuiPainter.cpp
@@ -464,7 +464,7 @@ int GuiPainter::preeditText(int x, int y, char_type c,
 			break;
 		case preedit_selecting:
 			// We are in selecting mode: white text on black background.
-			fillRectangle(x, y - height + 1, width, height, Color_black);
+			fillRectangle(x, y - height + 1, width, height, Color_darkgray);
 			temp_font.setColor(Color_white);
 			break;
 		case preedit_cursor:
diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp
index 099b129..978819c 100644
--- a/src/frontends/qt/GuiWorkArea.cpp
+++ b/src/frontends/qt/GuiWorkArea.cpp
@@ -54,6 +54,7 @@
 #include <QContextMenuEvent>
 #include <QDrag>
 #include <QHelpEvent>
+#include <QInputMethod>
 #ifdef Q_OS_MAC
 #include <QProxyStyle>
 #endif
@@ -218,6 +219,8 @@ void GuiWorkArea::init()
 	// Enables input methods for asian languages.
 	// Must be set when creating custom text editing widgets.
 	setAttribute(Qt::WA_InputMethodEnabled, true);
+	// obtain transformation information to reset it when LyX gets refocus
+	d->im_item_trans_ = d->im_->inputItemTransform();
 }
 
 
@@ -1126,8 +1129,18 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
 	Point point;
 	Dimension dim;
 	buffer_view_->caretPosAndDim(point, dim);
-	int cur_x = point.x_;
+	int cur_x = point.x_ - dim.width();
 	int cur_y = point.y_ + dim.height();
+	// lower margin of the preedit area to separate the candidate window
+	// report to IM the height of preedit rectangle larger than the actual by
+	// preedit_lower_margin so that the conversion suggestion window does not
+	// hide the underline of the preedit text
+	int preedit_lower_margin = 3;
+	// reset item transformation since it gets wrong after the item get
+	// lost and regain focus.
+	im_->setInputItemTransform(im_item_trans_);
+	// force fulldraw to remove previous paint remaining on screen
+	buffer_view_->processUpdateFlags(Update::ForceDraw);
 
 	// get attributes of input method cursor.
 	// cursor_pos : cursor position in preedit string.
@@ -1168,7 +1181,8 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
 		rLength = 0;
 	}
 
-	int const right_margin = buffer_view_->rightMargin();
+	int const text_width = p->viewport()->width() - buffer_view_->rightMargin()
+			- buffer_view_->leftMargin();
 	Painter::preedit_style ps;
 	// Most often there would be only one line:
 	preedit_lines_ = 1;
@@ -1178,8 +1192,8 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
 		ps = Painter::preedit_default;
 
 		// if we reached the right extremity of the screen, go to next line.
-		if (cur_x + fm.width(typed_char) > p->viewport()->width() - right_margin) {
-			cur_x = right_margin;
+		if (cur_x + fm.width(typed_char) > p->viewport()->width() - buffer_view_->rightMargin()) {
+			cur_x = buffer_view_->leftMargin();
 			cur_y += dim.height() + 1;
 			++preedit_lines_;
 		}
@@ -1190,8 +1204,9 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
 		// FIXME: should be put out of the loop.
 		if (pos >= rStart
 			&& pos < rStart + rLength
-			&& !(cursor_pos < rLength && rLength == preedit_length))
+			&& !(cursor_pos < rLength && rLength == preedit_length)) {
 			ps = Painter::preedit_selecting;
+		}
 
 		if (pos == cursor_pos
 			&& (cursor_pos < rLength && rLength == preedit_length))
@@ -1200,6 +1215,49 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
 		// draw one character and update cur_x.
 		cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
 	}
+
+	// the selection candidate window follows the position of Qt::ImCursorRectangle
+	// so we set it here in preparation for InputMethodQuery.
+	if(rLength > 0) {
+		// candidate selection mode
+		div_t mod_cur, mod_anc;
+		// width of preedit string in pixels
+		int preedit_width = fm.width(preedit_string_);
+		// calculate for modular so that its remainder and quotient become horizontal
+		// and vertical adjustment factor respectively
+		// FIXME: divider should be (text_width - font width of the end-of-line character)
+		//        however, since it's very rare that preedit becomes so long that it goes over
+		//        more than two lines, current error of position is more or less negligible
+		mod_cur	= div(text_width + cur_x - (preedit_width - fm.width(preedit_string_.substr(0, rStart))),
+				text_width);
+		mod_anc = div(text_width + cur_x - (preedit_width - fm.width(preedit_string_.substr(0, rStart + rLength))),
+				text_width);
+		// Obtain cursor and anchor rectangles to bind starting and ending points of selection.
+		// Since the remainder of div moves from positive to negative as the line becomes longer
+		// while its quotient repeats zero twice, we need to take it into account by conditioning.
+		if (mod_cur.rem >= 0)
+			im_cursor_rect_ = QRectF(mod_cur.rem,
+					(cur_y - dim.height()) + (dim.height() + 1) * (mod_cur.quot - 1), 1,
+					dim.height() + preedit_lower_margin);
+		else
+			im_cursor_rect_ = QRectF(text_width + mod_cur.rem,
+					(cur_y - dim.height()) + (dim.height() + 1) * (mod_cur.quot - 2), 1,
+					dim.height() + preedit_lower_margin);
+		if (mod_anc.rem >= 0)
+			im_anchor_rect_ = QRectF(mod_anc.rem,
+					point.y_ + (dim.height() + 1) * (mod_anc.quot - 1), 1,
+					dim.height() + preedit_lower_margin );
+		else
+			im_anchor_rect_ = QRectF(text_width + mod_anc.rem,
+					point.y_ + (dim.height() + 1) * (mod_anc.quot - 2), 1,
+					dim.height() + preedit_lower_margin );
+	} else {
+		im_cursor_rect_ =	QRectF(point.x_, point.y_, 1, dim.height() + preedit_lower_margin);
+		im_anchor_rect_ = im_cursor_rect_;
+	}
+	// Urge platform input method to make inputMethodQuery to check the values
+	// set above
+	im_->update(Qt::ImQueryInput);
 }
 
 
@@ -1352,14 +1410,17 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
 
 QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
 {
+	LYXERR(Debug::INFO, "incoming InputMethodQuery Value: 0x" << std::hex << query);
 	switch (query) {
-		// this is the CJK-specific composition window position and
-		// the context menu position when the menu key is pressed.
+	// this is the CJK-specific composition window position and
+	// the context menu position when the menu key is pressed.
 	case Qt::ImCursorRectangle: {
-		CaretGeometry const & cg = bufferView().caretGeometry();
-		return QRect(cg.left - 10 * (d->preedit_lines_ != 1),
-		             cg.top + cg.height() * d->preedit_lines_,
-		             cg.width(), cg.height());
+		return QVariant(d->im_cursor_rect_);
+		break;
+	}
+	case Qt::ImAnchorRectangle: {
+		return QVariant(d->im_anchor_rect_);
+		break;
 	}
 	default:
 		return QWidget::inputMethodQuery(query);
diff --git a/src/frontends/qt/GuiWorkArea_Private.h b/src/frontends/qt/GuiWorkArea_Private.h
index 6acd988..9da4cfa 100644
--- a/src/frontends/qt/GuiWorkArea_Private.h
+++ b/src/frontends/qt/GuiWorkArea_Private.h
@@ -136,12 +136,16 @@ struct GuiWorkArea::Private
 	///
 	bool need_resize_ = false;
 
+	QInputMethod * im_ = QGuiApplication::inputMethod();
 	/// the current preedit text of the input method
 	docstring preedit_string_;
 	/// Number of lines used by preedit text
 	int preedit_lines_ = 1;
 	/// the attributes of the preedit text
 	QList<QInputMethodEvent::Attribute> preedit_attr_;
+	QRectF im_cursor_rect_;
+	QRectF im_anchor_rect_;
+	QTransform im_item_trans_;
 
 	/// Ratio between physical pixels and device-independent pixels
 	/// We save the last used value to detect changes of the


More information about the lyx-cvs mailing list