commit 8064dbeb66f85a59e75e6b06e3d734f74a3b5f15 Author: Koji Yokota <yok...@lyx.org> Date: Tue Aug 19 12:44:41 2025 +0900
Respond correct pos and surrounding texts in QInputMethodQueryEvent This fix makes it work the re-conversion feature of IM after string commitment. --- src/frontends/qt/GuiInputMethod.cpp | 77 ++++++++++++++++++++++--------------- src/frontends/qt/GuiInputMethod.h | 4 ++ 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/frontends/qt/GuiInputMethod.cpp b/src/frontends/qt/GuiInputMethod.cpp index 4b0c4cb34d..8e91f1bdb9 100644 --- a/src/frontends/qt/GuiInputMethod.cpp +++ b/src/frontends/qt/GuiInputMethod.cpp @@ -74,11 +74,11 @@ struct GuiInputMethod::Private Rows::iterator rows_; size_type rows_size_; - pos_type * cur_pos_ptr_ = nullptr; + pos_type cur_pos_ = 0; pos_type cur_row_idx_; pos_type caret_pos_; - pos_type anchor_pos_; + pos_type anchor_pos_ = 0; pos_type abs_pos_; bool real_boundary_ = false; @@ -109,6 +109,8 @@ GuiInputMethod::GuiInputMethod(GuiWorkArea *parent) d->sys_im_, &QInputMethod::update); connect(d->sys_im_, &QInputMethod::localeChanged, this, &GuiInputMethod::onLocaleChanged); + connect(this, &GuiInputMethod::cursorPositionChanged, + this, &GuiInputMethod::onCursorPositionChanged); } GuiInputMethod::~GuiInputMethod() @@ -256,14 +258,6 @@ void GuiInputMethod::processPreedit(QInputMethodEvent* ev) d->init_point_.y + d->caret_offset_[1] + d->cur_dim_.height()); d->im_state_.anchor_rect_ = d->im_state_.cursor_rect_; - /* - * Get surrounding text - */ - // take this opportunity to obtain the surrounding text to report back to - // the input method - // FIXME: is there a benefit to cache this? - setSurroundingText(*d->cur_); - // if preedit string is not empty, we are still working on it d->im_state_.preediting_ = d->preedit_str_.empty() ? false : true; @@ -273,6 +267,14 @@ void GuiInputMethod::processPreedit(QInputMethodEvent* ev) } +void GuiInputMethod::onCursorPositionChanged() +{ + d->cur_pos_ = d->cur_->top().pos(); + d->anchor_pos_ = d->cur_->realAnchor().pos(); + setSurroundingText(*d->cur_); +} + + void GuiInputMethod::setPreeditStyle( const QList<QInputMethodEvent::Attribute> & attr) { @@ -675,15 +677,15 @@ std::array<int,2> GuiInputMethod::setCaretOffset(pos_type caret_pos){ #if defined(Q_OS_MACOS) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (d->im_state_.composing_mode_) str_before_caret = - toqstr(d->preedit_str_.substr(0, caret_pos - *d->cur_pos_ptr_)); + toqstr(d->preedit_str_.substr(0, caret_pos - d->cur_pos_)); 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_ptr_ - + 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_ptr_)); + toqstr(d->preedit_str_.substr(0, caret_pos - d->cur_pos_)); #endif // process line wrapping @@ -707,12 +709,12 @@ std::array<int,2> GuiInputMethod::setCaretOffset(pos_type caret_pos){ else #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) lastline_str = str_before_caret.sliced( - caret_row.pos - *d->cur_pos_ptr_, - *d->cur_pos_ptr_ + str_before_caret.length() - caret_row.pos); + 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_ptr_, - *d->cur_pos_ptr_ + str_before_caret.length() - caret_row.pos); + caret_row.pos - d->cur_pos_, + d->cur_pos_ + str_before_caret.length() - caret_row.pos); #endif // // calculate left margin @@ -904,6 +906,7 @@ void GuiInputMethod::processQuery(Qt::InputMethodQuery query) } // plain text before the cursor case Qt::ImTextBeforeCursor: { + updatePosAndSurroundingText(); if (d->im_state_.text_before_.empty()) LYXERR(Debug::DEBUG, msg << "\"\""); else @@ -914,6 +917,7 @@ void GuiInputMethod::processQuery(Qt::InputMethodQuery query) } // plain text after the cursor case Qt::ImTextAfterCursor: { + updatePosAndSurroundingText(); if (d->im_state_.text_after_.empty()) LYXERR(Debug::DEBUG, msg << "\"\""); else @@ -924,12 +928,14 @@ void GuiInputMethod::processQuery(Qt::InputMethodQuery query) } // logical position of the cursor within the text surrounding the input area case Qt::ImCursorPosition: { + updatePosAndSurroundingText(); LYXERR(Debug::DEBUG, msg << std::dec << d->cur_->pos()); Q_EMIT queryProcessed((qlonglong)d->cur_->pos()); break; } // position of the selection anchor case Qt::ImAnchorPosition: { + updatePosAndSurroundingText(); LYXERR(Debug::DEBUG, msg << std::dec << (unsigned int)d->anchor_pos_); Q_EMIT queryProcessed(QVariant((unsigned int)d->anchor_pos_)); break; @@ -1016,21 +1022,22 @@ docstring GuiInputMethod::inputMethodQueryFlagsAsString(unsigned long int query) pos_type GuiInputMethod::initializePositions(Cursor * cur) { // the function sets the following variables: - // d->cur_pos_ptr_ = pointer to the starting pos of preedits + // d->cur_pos_ = the starting pos of preedits // d->real_boundary_ = if the starting point is boundary // d->virtual_boundary_ = if the preedits hit the boundary // and returns the row index of the starting point of preedits // position of the real cursor (also the start of the preedit) - d->cur_pos_ptr_ = &cur->top().pos(); + if (cur->top().pos() != d->cur_pos_) + Q_EMIT cursorPositionChanged(); + d->pm_ptr_ = resetParagraphMetrics(cur); - d->anchor_pos_ = *d->cur_pos_ptr_; // 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) // is used here. As a result, cur_row_idx points the row before the boundary // in the case it is true. - pos_type cur_row_idx = d->pm_ptr_->getRowIndex(*d->cur_pos_ptr_, true); + pos_type cur_row_idx = d->pm_ptr_->getRowIndex(d->cur_pos_, true); // // boundary check @@ -1086,7 +1093,7 @@ pos_type GuiInputMethod::initializePositions(Cursor * cur) { // cursor is at the head of the next row after boundary post_real_boundary = cur_row_idx + 1 < (pos_type)d->rows_size_ ? - *d->cur_pos_ptr_ == d->rows_[cur_row_idx+1].pos() && + d->cur_pos_ == d->rows_[cur_row_idx+1].pos() && !has_room_to_insert : false; // Preedit string and the caret starts from the new line when either @@ -1187,8 +1194,8 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo( // the length of str is used since preedits has zero widths (pos == endpos) // second_row_pos is only useful when preedit string goes over two rows Row::const_iterator begin = - d->rows_[d->cur_row_idx_].findElement(*d->cur_pos_ptr_, false); - pos_type second_row_pos = *d->cur_pos_ptr_; + d->rows_[d->cur_row_idx_].findElement(d->cur_pos_, false); + pos_type second_row_pos = d->cur_pos_; for (Row::const_iterator eit = begin; eit < d->rows_[d->cur_row_idx_].end(); ++eit) second_row_pos += eit->str.length(); @@ -1199,7 +1206,7 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo( // 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 caret_row.pos = (real_boundary && !d->im_state_.composing_mode_) ? - *d->cur_pos_ptr_ : second_row_pos; + d->cur_pos_ : second_row_pos; // if the preedit caret is on the second row or later, count the second row caret_row.index = d->caret_pos_ > second_row_pos ? @@ -1223,10 +1230,10 @@ GuiInputMethod::PreeditRow GuiInputMethod::getCaretInfo( } } } else - caret_row.pos = *d->cur_pos_ptr_; + caret_row.pos = d->cur_pos_; LYXERR(Debug::DEBUG, "============= BEGIN: getCaretInfo ==============="); - LYXERR(Debug::DEBUG, "*d->cur_pos_ptr = " << *d->cur_pos_ptr_); + LYXERR(Debug::DEBUG, "*d->cur_pos_ptr = " << d->cur_pos_); LYXERR(Debug::DEBUG, "second_row_pos = " << second_row_pos); LYXERR(Debug::DEBUG, "d->caret_pos_ = " << std::dec << d->caret_pos_ << "\tcaret_row.index = " << std::dec << caret_row.index); @@ -1251,15 +1258,15 @@ void GuiInputMethod::setSurroundingText(const Cursor & cur) { InsetList const & inset_list = cur.paragraph().insetList(); pos_type insets_before_cur = 0; for (const auto & inset : inset_list) { - if (inset.pos < *d->cur_pos_ptr_) + if (inset.pos < d->cur_pos_) ++insets_before_cur; } - if (*d->cur_pos_ptr_ >= insets_before_cur) { + if (d->cur_pos_ >= insets_before_cur) { d->im_state_.text_before_ = - partext.substr(0, *d->cur_pos_ptr_ - insets_before_cur); + partext.substr(0, d->cur_pos_ - insets_before_cur); d->im_state_.text_after_ = - partext.substr(*d->cur_pos_ptr_ - insets_before_cur); + partext.substr(d->cur_pos_ - insets_before_cur); d->im_state_.surrounding_text_ = d->im_state_.text_before_ + d->im_state_.text_after_; } else { @@ -1285,6 +1292,14 @@ void GuiInputMethod::setSurroundingText(const Cursor & cur) { return; } +void GuiInputMethod::updatePosAndSurroundingText() +{ + if (d->cur_->top().pos() == d->cur_pos_) + return; + Q_EMIT cursorPositionChanged(); +} + + docstring & GuiInputMethod::preeditString() const { return d->preedit_str_; diff --git a/src/frontends/qt/GuiInputMethod.h b/src/frontends/qt/GuiInputMethod.h index 9baa2ae8b2..637e8adf0a 100644 --- a/src/frontends/qt/GuiInputMethod.h +++ b/src/frontends/qt/GuiInputMethod.h @@ -118,6 +118,7 @@ Q_SIGNALS: void preeditProcessed(QInputMethodEvent* ev); void queryProcessed(QVariant response); void inputMethodStateChanged(Qt::InputMethodQueries); + void cursorPositionChanged(); public Q_SLOT: /// Process incoming preedit string @@ -127,6 +128,7 @@ public Q_SLOT: /// Turn off IM in math mode and command phase and turn it on otherwise void toggleInputMethodAcceptance() override; void onLocaleChanged(); + void onCursorPositionChanged(); #ifdef Q_DEBUG /// void setHint(InputMethod::Hint hint) override; @@ -166,6 +168,8 @@ private: pos_type registerSegment(pos_type start, size_type length, QTextCharFormat char_format); /// Returns enum Qt::InputMethodQuery constant from its value docstring inputMethodQueryFlagsAsString(unsigned long int query) const; + /// update cursor position and surrounding text + void updatePosAndSurroundingText(); struct Private; Private * const d; -- lyx-cvs mailing list lyx-cvs@lists.lyx.org https://lists.lyx.org/mailman/listinfo/lyx-cvs