[LyX/master] Change the reference point of the preedit caret
Koji Yokota <[email protected]> Tue, 12 May 2026 10:17:44 +0000
| Newsgroups | gmane.editors.lyx.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit c2d2c2ca14fb89f976abfa6e9f2ea1fa413d7b67 Author: Koji Yokota <[email protected]> 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; /// -- lyx-cvs mailing list [email protected] https://lists.lyx.org/mailman/listinfo/lyx-cvs