[LyX/master] Change the reference point of the preedit caret
Koji Yokota
yokota at lyx.org
Tue May 12 10:17:44 UTC 2026
commit c2d2c2ca14fb89f976abfa6e9f2ea1fa413d7b67
Author: Koji Yokota <yokota at lyx.org>
Date: Tue May 12 19:13:46 2026 +0900
Change the reference point of the preedit caret
The coordinates of the preedit caret is now relative to rows broken by
TextMetrics::breakParagraph(), instead of the cursor coordinates.
This significantly simplifies the calculation of the caret coordinates.
---
src/MetricsInfo.cpp | 2 +-
src/MetricsInfo.h | 2 +-
src/TextMetrics.cpp | 4 +-
src/frontends/InputMethod.h | 6 +-
src/frontends/NullPainter.h | 4 +-
src/frontends/Painter.h | 4 +-
src/frontends/qt/GuiInputMethod.cpp | 289 ++++++++++++++++-----------------
src/frontends/qt/GuiInputMethod.h | 33 ++--
src/frontends/qt/GuiPainter.cpp | 10 +-
src/frontends/qt/GuiPainter.h | 9 +-
src/frontends/qt/GuiWorkArea.cpp | 54 ++++--
src/frontends/qt/GuiWorkArea_Private.h | 4 +-
src/mathed/InsetMathChar.cpp | 2 +-
src/mathed/InsetMathChar.h | 4 +-
14 files changed, 230 insertions(+), 197 deletions(-)
diff --git a/src/MetricsInfo.cpp b/src/MetricsInfo.cpp
index 3b4dd70f82..faca0ea183 100644
--- a/src/MetricsInfo.cpp
+++ b/src/MetricsInfo.cpp
@@ -218,7 +218,7 @@ void PainterInfo::draw(int x, int y, docstring const & str)
void PainterInfo::draw(int x, int y, char_type c,
- frontend::InputMethod const * im,
+ frontend::InputMethod * im,
pos_type const char_format_index, FontInfo const * f)
{
pain.text(x, y, c, im, char_format_index, f);
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index 8804f09b23..1cb4554b69 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -128,7 +128,7 @@ public:
void draw(int x, int y, docstring const & str);
/// Draw preedit char using char_format_index for format
/// f is used to get the font size in the text mode of mathed
- void draw(int x, int y, char_type c, frontend::InputMethod const * im,
+ void draw(int x, int y, char_type c, frontend::InputMethod * im,
pos_type const char_format_index, FontInfo const * f = nullptr);
/// Determines the background color based on the
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index c7ee6769d5..b8dd127987 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -895,7 +895,7 @@ Row TextMetrics::tokenizeParagraph(pit_type const pit) const
// loop for differently presented segments of preedit string
// note that a sequence of preedit segments should have the
// same position since they have zero length
- for (size_type j=0; j < im_->segmentSize(); j++) {
+ for (size_type j=0; j < im_->segmentsSize(); j++) {
row.addPreedit(
i, preedit.substr(im_->segmentStart(j), im_->segmentLength(j)),
*fi, im_, (pos_type)j, Change());
@@ -2004,7 +2004,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
theSession().bookmarks().bookmarksInPar(bv_->buffer().fileName(), pm.id());
for (size_t i = 0; i != nrows; ++i) {
-
Row const & row = pm.rows()[i];
// Adapt to cursor row scroll offset if applicable.
int row_x = x - bv_->horizScrollOffset(text_, pit, row.pos());
@@ -2122,7 +2121,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
row.changed(false);
}
-
//LYXERR(Debug::PAINTING, ".");
}
diff --git a/src/frontends/InputMethod.h b/src/frontends/InputMethod.h
index 60393c138b..bb4a56da63 100644
--- a/src/frontends/InputMethod.h
+++ b/src/frontends/InputMethod.h
@@ -64,7 +64,7 @@ public:
/// Lengths of each preedit snippet
virtual size_type & segmentLength(size_type seg_id) const = 0;
- virtual size_type segmentSize() const = 0;
+ virtual size_type segmentsSize() const = 0;
/// the index in char_formats_ vector for given pos in preedit string
virtual pos_type charFormatIndex(pos_type pos) const = 0;
@@ -83,6 +83,10 @@ public:
virtual void enableInputMethod() = 0;
///
virtual void disableInputMethod() = 0;
+ ///
+ virtual void setElemsCoordinates(int x, int y) = 0;
+ ///
+ virtual void clearElemsCoordinates() = 0;
#ifdef Q_DEBUG
///
virtual void setHint(Hint) = 0;
diff --git a/src/frontends/NullPainter.h b/src/frontends/NullPainter.h
index f3113eb65f..0397a3cd43 100644
--- a/src/frontends/NullPainter.h
+++ b/src/frontends/NullPainter.h
@@ -78,11 +78,11 @@ public:
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,
+ void text(int, int, char_type, InputMethod *, pos_type const,
FontInfo const * = nullptr, Direction const = Auto) override {}
/// draw a string with input method, FontInfo is used in mathed
- void text(int, int, docstring const &, InputMethod const *, pos_type const,
+ void text(int, int, docstring const &, InputMethod *, pos_type const,
FontInfo const * = nullptr,Direction const = Auto) override {}
/// This painter does not paint
diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h
index c9f90ecb90..3fc6adaec9 100644
--- a/src/frontends/Painter.h
+++ b/src/frontends/Painter.h
@@ -164,13 +164,13 @@ public:
/// 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
- virtual void text(int, int, char_type, InputMethod const *, pos_type const,
+ virtual void text(int, int, char_type, InputMethod *, pos_type const,
FontInfo const * = nullptr, Direction const = Auto) = 0;
/// draw a string 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
virtual void text(int x, int y, docstring const & str,
- InputMethod const * im, pos_type const char_format_index,
+ InputMethod * im, pos_type const char_format_index,
FontInfo const * f = nullptr, Direction const dir = Auto) = 0;
// Returns true if the painter does not actually paint.
diff --git a/src/frontends/qt/GuiInputMethod.cpp b/src/frontends/qt/GuiInputMethod.cpp
index 2ad68efb73..b9d8786824 100644
--- a/src/frontends/qt/GuiInputMethod.cpp
+++ b/src/frontends/qt/GuiInputMethod.cpp
@@ -75,21 +75,30 @@ struct GuiInputMethod::Private
Rows::iterator rows_;
size_type rows_size_;
+ /// position of the cursor
pos_type cur_pos_ = 0;
+ /// the index of the cursor row
pos_type cur_row_idx_;
+ /// the row index of the end of preedit
+ pos_type preedit_end_row_idx_;
+ /// position of the caret
pos_type caret_pos_;
+ /// the row index and pos of the caret
+ PreeditElement caret_row_;
+ /// caret index and pos in preedit elements
+ PreeditElement caret_elem_;
pos_type anchor_pos_ = 0;
pos_type abs_pos_;
+ std::vector<std::array<int,2>> elems_coords_;
+
bool real_boundary_ = false;
bool virtual_boundary_ = false;
bool initial_tf_entry_ = false;
Point init_point_;
Dimension cur_dim_;
- /// pixel offset of the caret within preedit text
- std::array<int, 2> caret_offset_ = {0, 0};
};
@@ -242,7 +251,6 @@ void GuiInputMethod::inputMethodEvent(QInputMethodEvent* ev)
d->cur_->undoAction();
d->cur_->endUndoGroup();
d->has_selection_ = false;
- d->im_state_.preediting_ = false;
}
d->preedit_str_ = qstring_to_ucs4(ev->preeditString());
@@ -256,32 +264,13 @@ void GuiInputMethod::inputMethodEvent(QInputMethodEvent* ev)
d->init_point_ =
initializeCaretCoords(d->cur_row_idx_ + d->real_boundary_,
d->real_boundary_ && !d->im_state_.composing_mode_);
+ setElemsCoordinates(d->init_point_.x, d->init_point_.y);
// Push preedit texts into row elements, which can shift the anchor
// point of the preedit texts in a centered or right-flushed row.
// Check such a shift immediately below.
updateMetrics(d->cur_);
- /*
- * Draw caret
- */
-
- // set offset of the virtual preedit caret from the real position
- d->caret_offset_ = setCaretOffset(d->caret_pos_);
-
- // set graphical geometry of the caret
- d->im_state_.cursor_rect_.setCoords(
- d->init_point_.x + d->caret_offset_[0],
- d->init_point_.y + d->caret_offset_[1],
- d->init_point_.x + d->caret_offset_[0] + d->cur_dim_.width(),
- d->init_point_.y + d->caret_offset_[1] + d->cur_dim_.height());
- // while preedit exists, this is just coords of real cursor
- d->im_state_.anchor_rect_.setCoords(
- d->init_point_.x,
- d->init_point_.y,
- d->init_point_.x + d->cur_dim_.width(),
- d->init_point_.y + d->cur_dim_.height());
-
// if preedit string is not empty, we are still working on it
d->im_state_.preediting_ = d->preedit_str_.empty() ? false : true;
@@ -722,126 +711,117 @@ void GuiInputMethod::setParagraphMetrics(ParagraphMetrics & pm)
d->pm_ptr_ = ±
}
-std::array<int,2> GuiInputMethod::setCaretOffset(pos_type caret_pos)
+void GuiInputMethod::setCaretRectangle()
{
- // Note that preedit elements are virtual and not counted in pos().
- // pos: 0 1 2 3 4 5 6 7 8 | 8 8 8 8 8 8 8 8 8 8 | 9 10 11 ...
- // <- non-virtual ->|<- preedit element ->|<- non-virtual
- //
- // This is also true for next_row_pos.
- // On the other hand d->caret_pos_ counts preedit elements.
-
- if (d->preedit_str_.empty() || d->cur_->reverseDirectionNeeded() ||
- d->cur_->inMathed()) {
- // reset shift of the virtual caret as the preedit string is cancelled
- // this part is also visited right before starting preedit input
- return {0, 0};
+ d->caret_elem_ = setCaretPreeditElement();
+ int x,y;
+ if (d->im_state_.composing_mode_) {
+ x = d->elems_coords_[d->caret_elem_.index][0] + preeditCaretOffset();
+ y = d->elems_coords_[d->caret_elem_.index][1];
+ } else {
+ x = d->elems_coords_[d->caret_elem_.index][0];
+ y = d->elems_coords_[d->caret_elem_.index][1];
}
- // when preedit cursor is available, string fonts are all common
- // so pick up the last one
- QTextCharFormat qtcf;
- if (!d->style_.segments_.empty())
- qtcf = d->style_.segments_.back().char_format_;
- else
- conformToSurroundingFont(qtcf);
- QFontMetrics qfm(qtcf.font());
- QString str_before_caret = "";
+ d->im_state_.anchor_rect_.setRect(d->elems_coords_[0][0],
+ d->elems_coords_[0][1],
+ d->cur_dim_.width(),
+ d->cur_dim_.height());
+ d->im_state_.cursor_rect_.setRect(x, y, d->cur_dim_.width(),
+ d->cur_dim_.height());
+ QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
+}
-#if defined(Q_OS_MACOS) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+QRectF GuiInputMethod::caretRectangle()
+{
if (d->im_state_.composing_mode_)
- str_before_caret =
- toqstr(d->preedit_str_.substr(0, caret_pos - d->cur_pos_));
+ return d->im_state_.cursor_rect_;
else
- // adjust for the reported caret position in the completion mode in Qt5
- str_before_caret = toqstr(
- d->preedit_str_.substr(0, caret_pos - d->cur_pos_ -
- shiftFromCaretToSegmentHead()));
-#else
- str_before_caret =
- toqstr(d->preedit_str_.substr(0, caret_pos - d->cur_pos_));
-#endif
-
- // process line wrapping
- //
- // NOTE:
- // preedits added on boundary goes to the beginning of the next line
- // -> real_boundary = true virtual_boundary = false
- // preedits added on boundary is appended to the same line
- // -> real_boundary = false virtual_boundary = true
-
- PreeditRow caret_row = getCaretInfo();
- std::array<int,2> caret_offset {};
-
- // caret_row.index doesn't decrease with virtual_boundary
- // has multiple preedit rows
- if (caret_row.index > d->cur_row_idx_ || d->real_boundary_) {
- QString lastline_str;
+ return d->im_state_.anchor_rect_;
+}
- if (d->real_boundary_ && caret_row.index == d->cur_row_idx_ + d->real_boundary_)
- lastline_str = str_before_caret;
- else
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- lastline_str = str_before_caret.sliced(
- caret_row.pos - d->cur_pos_,
- d->cur_pos_ + str_before_caret.length() - caret_row.pos);
-#else
- lastline_str = str_before_caret.mid(
- caret_row.pos - d->cur_pos_,
- d->cur_pos_ + str_before_caret.length() - caret_row.pos);
-#endif
- //
- // calculate left margin
- //
- int left_margin;
- int inset_offset;
- if (d->cur_->depth() > 1) {
- left_margin = d->cur_->inset().leftOffset(&d->cur_->bv());
- inset_offset = d->cur_->inset().xo(d->cur_->bv());
- } else {
- left_margin = d->rows_[caret_row.index].left_margin;
- inset_offset = 0;
- }
-#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
- if (d->real_boundary_ && !d->im_state_.composing_mode_)
- caret_offset[0] = qfm.horizontalAdvance(lastline_str);
- else
- caret_offset[0] = - d->init_point_.x + left_margin
- + inset_offset + qfm.horizontalAdvance(lastline_str);
-#else
- if (d->real_boundary_ && !d->im_state_.composing_mode_)
- caret_offset[0] = qfm.width(lastline_str);
+GuiInputMethod::PreeditElement GuiInputMethod::setCaretPreeditElement()
+{
+ int caret_elem_idx = 0;
+ int elem_pos = -1;
+ docstring elem_str;
+
+ if (d->cur_->inMathed()) {
+ // in mathed, each char ocuppies an element
+ elem_pos = d->caret_pos_;
+ if (d->im_state_.composing_mode_)
+ caret_elem_idx = d->caret_pos_ - d->cur_pos_ - 1;
else
- caret_offset[0] = - d->init_point_.x + left_margin
- + inset_offset + qfm.width(lastline_str);
-#endif
+ caret_elem_idx = d->caret_pos_ - d->cur_pos_;
} else {
- int left_margin_diff = d->rows_[caret_row.index].left_margin -
- d->rows_[d->cur_row_idx_].left_margin;
- // the case in which the preedit caret is in the first row of the preedit
-#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
- caret_offset[0] =
- left_margin_diff + qfm.horizontalAdvance(str_before_caret);
-#else
- caret_offset[0] =
- left_margin_diff + qfm.width(str_before_caret);
-#endif
+ TextMetrics & tm = d->cur_->bv().textMetrics(d->cur_->innerText());
+ ParagraphMetrics & pm = tm.parMetrics(d->cur_->top().pit());
+
+ Rows & cur_rows = pm.rows();
+ bool break_all = false;
+ for (auto&& row : cur_rows) {
+ for (auto&& elem : row) {
+ if (elem.type != Row::Type::PREEDIT)
+ continue;
+ // element type is preedit
+ if (elem_pos == -1)
+ elem_pos = elem.pos;
+ // note that caret is at the end of the element in the composing
+ // mode whereas it is at the start in the completing mode
+ if ((d->im_state_.composing_mode_ &&
+ elem_pos + (int)elem.str.length() >= d->caret_pos_) ||
+ (!d->im_state_.composing_mode_ && elem_pos >= d->caret_pos_))
+ {
+ elem_str = elem.str;
+ break_all = true;
+ break;
+ }
+ elem_pos += elem.str.length();
+ ++caret_elem_idx;
+ }
+ if (break_all)
+ break;
+ }
}
+ return {caret_elem_idx, elem_pos, elem_str};
+}
- // vertical offset only applicable to main text
- caret_offset[1] = 0;
- for (pos_type i = d->cur_row_idx_ +
- (d->real_boundary_ && !d->im_state_.composing_mode_);
- i < caret_row.index; ++i)
- caret_offset[1] += d->rows_[i].descent() + d->rows_[i+1].ascent();
- return caret_offset;
+void GuiInputMethod::setElemsCoordinates(int x, int y)
+{
+ d->elems_coords_.push_back({x,y});
}
-// returns (x_offset, y_offset) array
-std::array<int, 2> GuiInputMethod::preeditCaretOffset() const
+void GuiInputMethod::clearElemsCoordinates()
{
- return d->caret_offset_;
+ d->elems_coords_.clear();
+}
+
+
+int GuiInputMethod::preeditCaretOffset()
+{
+ if (d->preedit_str_.empty() || d->cur_->reverseDirectionNeeded())
+ return 0;
+
+ // when preedit cursor is available, string fonts are all common
+ // so pick up the last one
+ QTextCharFormat qtcf;
+ if (!d->style_.segments_.empty())
+ qtcf = d->style_.segments_.back().char_format_;
+ else
+ conformToSurroundingFont(qtcf);
+ QFontMetrics qfm(qtcf.font());
+
+ docstring line;
+ if (d->cur_->inMathed())
+ // texts in math text mode cannot be accessed via preedit elements,
+ // so that d->caret_elem_ becomes empty.
+ // accessing it via d->preedit_str_
+ line = d->preedit_str_.substr(d->preedit_str_.length()-1, 1);
+ else
+ line = d->caret_elem_.str.substr(0, d->caret_pos_ - d->caret_elem_.pos);
+
+ return qfm.horizontalAdvance(toqstr(line));
}
@@ -947,14 +927,15 @@ QVariant GuiInputMethod::inputMethodQuery(Qt::InputMethodQuery query)
} else {
QRectF * rect_ptr;
if (d->im_state_.composing_mode_) {
- // in the editing mode, cursor_rect_ follows the position of
+ // in the composing mode, cursor_rect_ follows the position of
// the virtual caret, but the drop down of predicted candidates
// wants the starting point of the preedit, so respond with
// anchor_rect_ that points the starting point during the
- // editing mode
+ // composing mode
rect_ptr = &d->im_state_.anchor_rect_;
- } else
+ } else {
rect_ptr = &d->im_state_.cursor_rect_;
+ }
LYXERR(Debug::KEY, msg << " x:" << rect_ptr->x() <<
" y:" << rect_ptr->y() <<
@@ -1042,7 +1023,7 @@ QVariant GuiInputMethod::inputMethodQuery(Qt::InputMethodQuery query)
d->work_area_->viewport()->y(),
d->work_area_->viewport()->width(),
d->work_area_->viewport()->height());
- LYXERR(Debug::KEY, msg << "(x,y,w,h) = " <<
+ LYXERR(Debug::KEY, msg << "(x,y,w,h) = (" <<
viewport.x() << ", " << viewport.y() << ", " <<
viewport.width() << ", " << viewport.height() << ")");
return viewport;
@@ -1132,6 +1113,8 @@ pos_type GuiInputMethod::initializePositions(Cursor * cur)
// position of the real cursor (also the start of the preedit)
updatePosAndSurroundingText();
+ d->preedit_end_row_idx_ = d->pm_ptr_->getRowIndex(d->cur_pos_, false);
+
// Note that getRowIndex(., false) gives the row index *after* preedit
// strings since they are virtual, so it increases as preedit strings go
// over multiple rows. To fix it at the starting point, getRowIndex(., true)
@@ -1206,14 +1189,13 @@ pos_type GuiInputMethod::initializePositions(Cursor * cur)
LYXERR(Debug::KEY, "========== BEGIN: initializePositions ==========");
LYXERR(Debug::KEY, "cur_row_idx = " << cur_row_idx <<
"\treal_boundary = " << real_boundary);
- LYXERR(Debug::KEY, " " <<
+ LYXERR(Debug::KEY, " " <<
"\tvirtual_boundary = " << virtual_boundary);
- LYXERR(Debug::KEY, "max width = " << std::dec <<
- d->buffer_view_->textMetrics(
- cur->innerText()).maxWidth()
- - d->rows_[cur_row_idx].right_margin);
- LYXERR(Debug::KEY, "row width = " << std::dec <<
- d->rows_[cur_row_idx].width());
+ LYXERR(Debug::KEY, "preedit_end_row_idx_ = " << d->preedit_end_row_idx_);
+ LYXERR(Debug::KEY, "max width = " <<
+ d->buffer_view_->textMetrics(cur->innerText()).maxWidth()
+ - d->rows_[cur_row_idx].right_margin);
+ LYXERR(Debug::KEY, "row width = " << d->rows_[cur_row_idx].width());
if (d->preedit_str_.empty())
LYXERR(Debug::KEY, "wchar width = " <<
horizontalAdvance(from_utf8("あ")));
@@ -1266,9 +1248,12 @@ void GuiInputMethod::updateMetrics(Cursor * cur)
ParagraphMetrics * GuiInputMethod::resetParagraphMetrics(Cursor * cur)
{
+ d->buffer_view_->updateMetrics();
+
// paragraph metrics of the par we are in
ParagraphMetrics * pm_ptr =
&cur->bv().textMetrics(cur->innerText()).parMetrics(cur->top().pit());
+
d->rows_ = pm_ptr->rows().begin();
d->rows_size_ = pm_ptr->rows().size();
// ID of the row in which the current real cursor resides
@@ -1286,15 +1271,22 @@ pos_type GuiInputMethod::getCaretPos(size_type preedit_length)
}
-GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo()
+GuiInputMethod::PreeditElement GuiInputMethod::getCaretInfo()
{
- // the virtual boundary case has the real cusor on the second row of
- // the preedit inputs
+ PreeditElement caret_row{};
+
+ // math text doesn't break
+ if (d->cur_->inMathed()) {
+
+ caret_row.index = d->cur_row_idx_;
+ caret_row.pos = d->cur_pos_;
+
+ return caret_row;
+ }
// row index of the preedit's second row in a paragraph (rows)
const pos_type second_row_idx =
d->cur_row_idx_ + 1 + d->real_boundary_ - d->virtual_boundary_;
-
LASSERT(d->cur_row_idx_ < (pos_type)d->rows_size_ &&
d->cur_row_idx_ >= 0, return {});
@@ -1315,8 +1307,6 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo()
eit < d->rows_[visual_row_idx_].end(); ++eit)
second_row_pos += eit->str.length();
- PreeditRow caret_row{};
-
// when d->real_boundary_ is true, cursor position is at the beginning of the
// new line, while the caret on screen stays at the end of one line above
// below is the starting point to calculate caret_row.pos
@@ -1325,8 +1315,9 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo()
LASSERT(caret_row.pos >= 0, return {});
// if the preedit caret is on the second row or later, count the second row
- caret_row.index = d->caret_pos_ > second_row_pos ?
- second_row_idx + d->virtual_boundary_ : d->cur_row_idx_ + d->real_boundary_;
+ caret_row.index = (d->caret_pos_ >= second_row_pos ?
+ second_row_idx + d->virtual_boundary_ :
+ d->cur_row_idx_ + d->real_boundary_);
// the second row exists and begins with the preedit
if (d->cur_row_idx_ + 1 < (pos_type)d->rows_size_ &&
@@ -1334,6 +1325,8 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo()
for (pos_type i = d->cur_row_idx_+1; i < (pos_type)d->rows_size_; i++) {
if (d->rows_[i].front().isPreedit()) {
+ // accumulate element lengths to get the row length except for
+ // the last row; used to judge break time
int row_length = 0;
for (const Row::Element & elm : d->rows_[i])
row_length += elm.str.length();
@@ -1425,7 +1418,7 @@ size_type & GuiInputMethod::segmentLength(size_type seg_id) const
}
-size_type GuiInputMethod::segmentSize() const
+size_type GuiInputMethod::segmentsSize() const
{
return d->style_.segments_.size();
}
diff --git a/src/frontends/qt/GuiInputMethod.h b/src/frontends/qt/GuiInputMethod.h
index 66cdeea033..fe56b8a6c6 100644
--- a/src/frontends/qt/GuiInputMethod.h
+++ b/src/frontends/qt/GuiInputMethod.h
@@ -62,10 +62,12 @@ public:
QString ruby_;
};
- /// pair of row's \p index and its first char \p pos in a paragraph
- struct PreeditRow {
- pos_type pos;
- pos_type index;
+ /// preedit element's \p index and its first char \p pos in a paragraph
+ /// and string in the element
+ struct PreeditElement {
+ pos_type index;
+ pos_type pos;
+ docstring str;
};
struct InputMethodState {
@@ -88,21 +90,22 @@ public:
/// Starting position of preedit segments
pos_type & segmentStart(size_type seg_id) const override;
-
- /// Lengths of each preedit segment
+ /// Length of each preedit segment
size_type & segmentLength(size_type seg_id) const override;
-
- size_type segmentSize() const override;
+ /// Number of segments in preedit
+ size_type segmentsSize() const override;
/// Character format of the given index in the char_formats_ vector
QTextCharFormat & charFormat(pos_type index) const;
/// the index in char_formats_ vector for given pos in preedit string
pos_type charFormatIndex(pos_type pos) const override;
- /// Sets pixel offsets of the caret from real cursor position
- std::array<int,2> setCaretOffset(pos_type caret_pos);
- /// Returns pixel offsets of the caret from real cursor position
- std::array<int,2> preeditCaretOffset() const;
+ /// Sets caret rectangle
+ void setCaretRectangle();
+ /// Returns coordinates of the preedit caret
+ QRectF caretRectangle();
+ /// Returns horizontal pixel offsets of the caret from real cursor position
+ int preeditCaretOffset();
///
void setParagraphMetrics(ParagraphMetrics &) override;
/// Sets surrounding text of the cursor within the paragraph
@@ -119,6 +122,8 @@ public:
int horizontalAdvance(docstring const &);
/// Whether the segment contains language that allows wrapping anywhere
bool canWrapAnywhere(pos_type const) override;
+ void setElemsCoordinates(int x, int y) override;
+ void clearElemsCoordinates() override;
Q_SIGNALS:
void preeditProcessed(QInputMethodEvent* ev);
void inputMethodStateChanged(Qt::InputMethodQueries);
@@ -166,8 +171,10 @@ private:
pos_type focusedSegmentIndex();
/// x shift factor from the caret to the selection segment's head
int shiftFromCaretToSegmentHead();
+ /// get caret index in preedit elements
+ PreeditElement setCaretPreeditElement();
///
- PreeditRow getCaretInfo();
+ PreeditElement getCaretInfo();
/// Pick up next segment from the turnout if there is a match and return
/// the next segment position to be filled
/// If the second argument is given, it is merged before filling the segment
diff --git a/src/frontends/qt/GuiPainter.cpp b/src/frontends/qt/GuiPainter.cpp
index cf00f0257e..0383cf5b45 100644
--- a/src/frontends/qt/GuiPainter.cpp
+++ b/src/frontends/qt/GuiPainter.cpp
@@ -345,7 +345,7 @@ void GuiPainter::text(int x, int y, docstring const & s,
}
-void GuiPainter::text(int x, int y, char_type c, InputMethod const * im,
+void GuiPainter::text(int x, int y, char_type c, InputMethod * im,
pos_type const char_format_index, FontInfo const * f,
Direction const dir)
{
@@ -354,7 +354,7 @@ void GuiPainter::text(int x, int y, char_type c, InputMethod const * im,
void GuiPainter::text(int x, int y, docstring const & s,
- InputMethod const * im, pos_type const char_format_index,
+ InputMethod * im, pos_type const char_format_index,
FontInfo const * f, Direction const dir)
{
if (s.empty())
@@ -379,6 +379,12 @@ void GuiPainter::text(int x, int y, docstring const & s,
qfont.setPointSize(fsize);
}
setFont(qfont);
+ QFontMetrics qfm(qfont);
+
+ // we raise the caret by its height here, not in
+ // GuiInputMethod::caretOffset() since offset is also used for
+ // determination of candidate menu coords
+ im->setElemsCoordinates(x, y - qfm.ascent());
LYXERR(Debug::GUI, "Drawing preedit segment " << char_format_index <<
": fg = " <<
diff --git a/src/frontends/qt/GuiPainter.h b/src/frontends/qt/GuiPainter.h
index b53e4392b5..69ffcf4983 100644
--- a/src/frontends/qt/GuiPainter.h
+++ b/src/frontends/qt/GuiPainter.h
@@ -141,16 +141,15 @@ public:
/// draw a char at position x, y (y is the baseline) using input method
/// char_format_index specifies preedit format
/// f is used to get the font size in the text mode of mathed
- void text(int x, int y, char_type c, InputMethod const * im,
+ void text(int x, int y, char_type c, InputMethod * im,
pos_type const char_format_index, FontInfo const * f = nullptr,
Direction const dir = Auto) override;
/// draw a string at position x, y (y is the baseline) using input method.
/// char_format_index specifies preedit format
/// f is used to get the font size in the text mode of mathed
- void text(int x, int y, docstring const & str, InputMethod const * im,
- pos_type const char_format_index, FontInfo const * f = nullptr,
- Direction const dir = Auto) override;
-
+ void text(int x, int y, docstring const & str,
+ InputMethod * im, pos_type const char_format_index,
+ FontInfo const * f = nullptr, Direction const dir = Auto) override;
///
void textDecoration(FontInfo const & f, int x, int y, int width) override;
diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp
index bd77d0805d..9e1cbb4655 100644
--- a/src/frontends/qt/GuiWorkArea.cpp
+++ b/src/frontends/qt/GuiWorkArea.cpp
@@ -573,11 +573,29 @@ void GuiWorkArea::Private::hideCaret()
*/
void GuiWorkArea::Private::drawCaret(QPainter & painter, int horiz_offset) const
{
- drawCaret(painter, horiz_offset, 0);
+ if (buffer_view_->caretGeometry().shapes.empty())
+ return;
+
+ QColor const color = guiApp->colorCache().get(Color_cursor);
+ painter.setPen(color);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ for (auto const & shape : buffer_view_->caretGeometry().shapes) {
+ bool first = true;
+ QPainterPath path;
+ for (Point const & pt : shape) {
+ if (first) {
+ path.moveTo(pt.x - horiz_offset, pt.y);
+ first = false;
+ } else
+ path.lineTo(pt.x - horiz_offset, pt.y);
+ }
+ painter.fillPath(path, color);
+ }
+ painter.setRenderHint(QPainter::Antialiasing, false);
}
-void GuiWorkArea::Private::drawCaret(QPainter & painter, int horiz_offset,
- int vert_offset) const
+
+void GuiWorkArea::Private::drawPreeditCaret(GuiPainter & painter) const
{
if (buffer_view_->caretGeometry().shapes.empty())
return;
@@ -585,15 +603,22 @@ void GuiWorkArea::Private::drawCaret(QPainter & painter, int horiz_offset,
QColor const color = guiApp->colorCache().get(Color_cursor);
painter.setPen(color);
painter.setRenderHint(QPainter::Antialiasing, true);
+
+ int const & abs_x = im_->caretRectangle().x();
+ int const & abs_y = im_->caretRectangle().y();
+
for (auto const & shape : buffer_view_->caretGeometry().shapes) {
bool first = true;
QPainterPath path;
+ int first_x, first_y;
for (Point const & pt : shape) {
if (first) {
- path.moveTo(pt.x - horiz_offset, pt.y - vert_offset);
+ path.moveTo(abs_x, abs_y);
+ first_x = pt.x;
+ first_y = pt.y;
first = false;
} else
- path.lineTo(pt.x - horiz_offset, pt.y - vert_offset);
+ path.lineTo(pt.x - first_x + abs_x, pt.y - first_y + abs_y);
}
painter.fillPath(path, color);
}
@@ -1356,22 +1381,23 @@ void GuiWorkArea::paintEvent(QPaintEvent * ev)
GuiPainter pain(d->screenDevice(), pixelRatio(), d->lyx_view_->develMode());
+ d->im_->clearElemsCoordinates();
+
d->buffer_view_->draw(pain, d->caret_visible_);
// draw the caret
// FIXME: the code would be a little bit simpler if caret geometry
// was updated unconditionally. Some profiling is required to see
// how expensive this is (especially when idle).
- if ((d->im_->preeditString().empty() && d->caret_visible_) ||
- (!d->im_->preeditString().empty() && d->im_->isCaretVisible())) {
- if (d->needs_caret_geometry_update_)
- d->updateCaretGeometry();
- d->drawCaret(pain,
- d->buffer_view_->horizScrollOffset()
- - d->im_->preeditCaretOffset()[0],
- - d->im_->preeditCaretOffset()[1]);
+ if (d->needs_caret_geometry_update_)
+ d->updateCaretGeometry();
+ if (d->im_->preeditString().empty() && d->caret_visible_) {
+ d->drawCaret(pain, d->buffer_view_->horizScrollOffset());
+ } else if (!d->im_->preeditString().empty()) {
+ d->im_->setCaretRectangle();
+ if (d->im_->isCaretVisible())
+ d->drawPreeditCaret(pain);
}
-
d->updateScreen(ev->rect());
ev->accept();
diff --git a/src/frontends/qt/GuiWorkArea_Private.h b/src/frontends/qt/GuiWorkArea_Private.h
index e82e4d6e8e..c707bd590f 100644
--- a/src/frontends/qt/GuiWorkArea_Private.h
+++ b/src/frontends/qt/GuiWorkArea_Private.h
@@ -91,8 +91,8 @@ struct GuiWorkArea::Private
* has been horizontal scrolling in current row
*/
void drawCaret(QPainter & painter, int horiz_offset) const;
- /// virtual preedits can require vertical offset when contines to next row
- void drawCaret(QPainter & painter, int horiz_offset, int vert_offset) const;
+ ///
+ void drawPreeditCaret(GuiPainter & painter) const;
/// Set the range and value of the scrollbar and connect to its valueChanged
/// signal.
void updateScrollbar();
diff --git a/src/mathed/InsetMathChar.cpp b/src/mathed/InsetMathChar.cpp
index e32c2249ab..84c0be9a09 100644
--- a/src/mathed/InsetMathChar.cpp
+++ b/src/mathed/InsetMathChar.cpp
@@ -192,7 +192,7 @@ void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
void InsetMathChar::draw(PainterInfo & pi, int x, int y,
- frontend::InputMethod const * im,
+ frontend::InputMethod * im,
pos_type const char_format_index) const
{
pi.draw(x, y, char_, im, char_format_index, &pi.base.font);
diff --git a/src/mathed/InsetMathChar.h b/src/mathed/InsetMathChar.h
index bd63436ded..8e424ddfda 100644
--- a/src/mathed/InsetMathChar.h
+++ b/src/mathed/InsetMathChar.h
@@ -28,8 +28,8 @@ public:
///
void draw(PainterInfo & pi, int x, int y) const override;
///
- void draw(PainterInfo & pi, int x, int y, frontend::InputMethod const * im,
- pos_type const char_format_index) const;
+ void draw(PainterInfo & pi, int x, int y,
+ frontend::InputMethod * im, pos_type const char_format_index) const;
///
void metricsT(TextMetricsInfo const & mi, Dimension & dim) const override;
///
More information about the lyx-cvs
mailing list