From 2e4cc60eecb8a2f16feb8f194c1ce25ef20a5e36 Mon Sep 17 00:00:00 2001 From: Koji Yokota Date: Mon, 30 Sep 2024 08:22:38 +0900 Subject: [PATCH] Fix #13094 Wrong IM window after opening a new window --- src/frontends/qt/GuiApplication.cpp | 48 +++++++++ src/frontends/qt/GuiApplication.h | 13 +++ src/frontends/qt/GuiWorkArea.cpp | 140 +++++++++++++++++++------ src/frontends/qt/GuiWorkArea.h | 10 +- src/frontends/qt/GuiWorkArea_Private.h | 8 +- 5 files changed, 184 insertions(+), 35 deletions(-) diff --git a/src/frontends/qt/GuiApplication.cpp b/src/frontends/qt/GuiApplication.cpp index de955e4e7c..5308455214 100644 --- a/src/frontends/qt/GuiApplication.cpp +++ b/src/frontends/qt/GuiApplication.cpp @@ -1083,6 +1083,13 @@ struct GuiApplication::Private /// KeyModifier meta_fake_bit; + /// input method uses this to preserve initial input item transform + bool first_work_area = true; + /// geometry of the input item of the first working area + QRectF item_rect_base_; + /// input item transformation of the first working area + QTransform item_trans_base_; + /// The result of last dispatch action DispatchResult dispatch_result_; @@ -2300,6 +2307,42 @@ docstring GuiApplication::viewStatusMessage() } +bool GuiApplication::isFirstWorkArea() const +{ + return d->first_work_area; +} + + +void GuiApplication::firstWorkAreaDone() +{ + d->first_work_area = false; +} + + +QRectF GuiApplication::baseInputItemRectangle() +{ + return d->item_rect_base_; +} + + +void GuiApplication::setBaseInputItemRectangle(QRectF rect) +{ + d->item_rect_base_ = rect; +} + + +QTransform GuiApplication::baseInputItemTransform() +{ + return d->item_trans_base_; +} + + +void GuiApplication::setBaseInputItemTransform(QTransform trans) +{ + d->item_trans_base_ = trans; +} + + string GuiApplication::inputLanguageCode() const { QLocale loc = inputMethod()->locale(); @@ -2586,6 +2629,11 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow, if (d->global_menubar_) d->global_menubar_->releaseKeyboard(); + // need to reset system input method coords with the preserved one + // when the new view is the second one or later + if (d->views_.size() > 0) + current_view_->currentWorkArea()->resetInputItemGeometry(true); + // create new view int id = view_id; while (d->views_.find(id) != d->views_.end()) diff --git a/src/frontends/qt/GuiApplication.h b/src/frontends/qt/GuiApplication.h index b31face5b7..11bb670886 100644 --- a/src/frontends/qt/GuiApplication.h +++ b/src/frontends/qt/GuiApplication.h @@ -185,6 +185,19 @@ public: /// return the status bar state string docstring viewStatusMessage(); + /// if current work area is the first one in the lyx application + bool isFirstWorkArea() const; + /// mark first work area is already set up + void firstWorkAreaDone(); + /// input item rectangle of the base view + QRectF baseInputItemRectangle(); + /// set input item rectangle of the base view + void setBaseInputItemRectangle(QRectF rect); + /// input item transform of the base view + QTransform baseInputItemTransform(); + /// set input item transform of the base view + void setBaseInputItemTransform(QTransform trans); + /// \name Methods to process FuncRequests //@{ /// process the func request diff --git a/src/frontends/qt/GuiWorkArea.cpp b/src/frontends/qt/GuiWorkArea.cpp index 7c1832b6e9..bbd857d79e 100644 --- a/src/frontends/qt/GuiWorkArea.cpp +++ b/src/frontends/qt/GuiWorkArea.cpp @@ -290,7 +290,7 @@ void GuiWorkArea::setFullScreen(bool full_screen) { d->buffer_view_->setFullScreen(full_screen); - queryInputItemTransform(); + queryInputItemGeometry(); if (full_screen && lyxrc.full_screen_scrollbar) setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -706,10 +706,10 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e) if ((e->reason() == Qt::PopupFocusReason || e->reason() == Qt::ActiveWindowFocusReason) && !(this->inDialogMode())) { // Switched from most of dialogs or other apps, and not on a dialog (e.g. findreplaceadv) - d->item_trans_needs_reset_ = true; + d->item_geom_needs_reset_ = true; } else { // Switched from advanced search dialog or else (e.g. mouse event) - d->item_trans_needs_reset_ = false; + d->item_geom_needs_reset_ = false; } startBlinkingCaret(); @@ -1143,33 +1143,111 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev) } -void GuiWorkArea::queryInputItemTransform() +void GuiWorkArea::queryInputItemGeometry() { LYXERR( - Debug::DEBUG, - "item_trans_ is aquired: dx() = " << d->item_trans_.dx() << - " -> " << d->im_->inputItemTransform().dx() << - ", dy() = " << d->item_trans_.dy() << - " -> " << d->im_->inputItemTransform().dy() - ); + Debug::DEBUG, + "item_rect_ is aquired: x() = " << d->item_rect_.x() << + " -> " << d->sys_im_->inputItemRectangle().x() << + ", y() = " << d->item_rect_.y() << + " -> " << d->sys_im_->inputItemRectangle().y() << + ", width() = " << d->item_rect_.width() << + " -> " << d->sys_im_->inputItemRectangle().width() << + ", height() = " << d->item_rect_.height() << + " -> " << d->sys_im_->inputItemRectangle().height() + ); + LYXERR( + Debug::DEBUG, + "item_trans_ is aquired: dx() = " << d->item_trans_.dx() << + " -> " << d->sys_im_->inputItemTransform().dx() << + ", dy() = " << d->item_trans_.dy() << + " -> " << d->sys_im_->inputItemTransform().dy() + ); - d->item_trans_ = d->im_->inputItemTransform(); + d->item_rect_ = d->sys_im_->inputItemRectangle(); + d->item_trans_ = d->sys_im_->inputItemTransform(); + + // save coordinates of the base working area for later use necessary to + // creat new GuiViews + if (guiApp->isFirstWorkArea()) { + guiApp->setBaseInputItemRectangle(d->item_rect_); + guiApp->setBaseInputItemTransform(d->item_trans_); + guiApp->firstWorkAreaDone(); + + LYXERR( + Debug::DEBUG, + "base inputItemRectangle x = " << + guiApp->baseInputItemRectangle().x() << + ", y = " << guiApp->baseInputItemRectangle().y() << + ", width = " << guiApp->baseInputItemRectangle().width() << + ", height = " << guiApp->baseInputItemRectangle().height() + ); + LYXERR( + Debug::DEBUG, + "base inputItemTransform dx = " << + guiApp->baseInputItemTransform().dx() << + ", dy = " << guiApp->baseInputItemTransform().dy() + ); + } } -void GuiWorkArea::Private::resetInputItemTransform() +void GuiWorkArea::resetInputItemGeometry() { - if (item_trans_needs_reset_) { - LYXERR( - Debug::DEBUG, - "(" << this << - ") item_trans_ is reset: dx() = " << im_->inputItemTransform().dx() << - " -> " << item_trans_.dx() << - ", dy() = " << im_->inputItemTransform().dy() << - " -> " << item_trans_.dy() - ); - im_->setInputItemTransform(item_trans_); - item_trans_needs_reset_ = false; + resetInputItemGeometry(false); +} + +void GuiWorkArea::resetInputItemGeometry(bool is_new_view) +{ + if (d->item_geom_needs_reset_) { + if (is_new_view) { + LYXERR( + Debug::DEBUG, + "QInputMethod::inputItemRectangle is reset: x = " << + d->sys_im_->inputItemRectangle().x() << + " -> " << guiApp->baseInputItemRectangle().x() << + ", y = " << d->sys_im_->inputItemRectangle().y() << + " -> " << guiApp->baseInputItemRectangle().y() << + ", width = " << d->sys_im_->inputItemRectangle().width() << + " -> " << guiApp->baseInputItemRectangle().width() << + ", height = " << d->sys_im_->inputItemRectangle().height() << + " -> " << guiApp->baseInputItemRectangle().height() + ); + LYXERR( + Debug::DEBUG, + "QInputMethod::inputItemTransform is reset: dx = " << + d->sys_im_->inputItemTransform().dx() << + " -> " << guiApp->baseInputItemTransform().dx() << + ", dy = " << d->sys_im_->inputItemTransform().dy() << + " -> " << guiApp->baseInputItemTransform().dy() + ); + d->sys_im_->setInputItemRectangle(guiApp->baseInputItemRectangle()); + d->sys_im_->setInputItemTransform(guiApp->baseInputItemTransform()); + } else { + LYXERR( + Debug::DEBUG, + "QInputMethod::inputItemRectangle is reset: x = " << + d->sys_im_->inputItemRectangle().x() << + " -> " << d->item_rect_.x() << + ", y = " << d->sys_im_->inputItemRectangle().y() << + " -> " << d->item_rect_.y() << + ", width = " << d->sys_im_->inputItemRectangle().width() << + " -> " << d->item_rect_.width() << + ", height = " << d->sys_im_->inputItemRectangle().height() << + " -> " << d->item_rect_.height() + ); + LYXERR( + Debug::DEBUG, + "QInputMethod::inputItemTransform is reset: dx = " << + d->sys_im_->inputItemTransform().dx() << + " -> " << d->item_trans_.dx() << + ", dy = " << d->sys_im_->inputItemTransform().dy() << + " -> " << d->item_trans_.dy() + ); + d->sys_im_->setInputItemRectangle(d->item_rect_); + d->sys_im_->setInputItemTransform(d->item_trans_); + } + d->item_geom_needs_reset_ = false; } } @@ -1180,7 +1258,7 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain) { #ifdef DEBUG_PREEDIT // check the language that current input method uses - QLocale::Language lang = im_->locale().language(); + QLocale::Language lang = sys_im_->locale().language(); if (lang != im_lang_) { LYXERR0("QLocale = " << QLocale::languageToString(lang)); im_lang_ = lang; @@ -1189,7 +1267,7 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain) // Chinese IM may want cursor position even when preedit string is empty // such a case is handled below - if (preedit_string_.empty() && im_->locale().language() != QLocale::Chinese) + if (preedit_string_.empty() && sys_im_->locale().language() != QLocale::Chinese) return; // lower margin of the preedit area to separate the candidate window @@ -1208,13 +1286,13 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain) // Chinese input methods may exit here just obtaining im_cursor_rect im_cursor_rect_ = QRectF(cur_x, cur_y - dim.height(), 1, dim.height() + preedit_lower_margin); - im_->update(Qt::ImCursorRectangle); + sys_im_->update(Qt::ImCursorRectangle); return; } // reset item transformation since it can go wrong after the item gets // lost and regains focus or after a new tab (dis)appears etc. - resetInputItemTransform(); + p->resetInputItemGeometry(); // FIXME: shall we use real_current_font here? (see #10478) FontInfo const font = buffer_view_->cursor().getFont().fontInfo(); @@ -1361,7 +1439,7 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain) } // Urge platform input method to make inputMethodQuery to check the values // set above - im_->update(Qt::ImQueryInput); + sys_im_->update(Qt::ImQueryInput); } @@ -1912,9 +1990,9 @@ GuiWorkArea * TabWorkArea::addWorkArea(Buffer & buffer, GuiView & view) updateTabTexts(); // obtain new input item coordinates in the new and old work areas - wa->queryInputItemTransform(); + wa->queryInputItemGeometry(); if (currentWorkArea()) - currentWorkArea()->queryInputItemTransform(); + currentWorkArea()->queryInputItemGeometry(); return wa; } @@ -1940,7 +2018,7 @@ bool TabWorkArea::removeWorkArea(GuiWorkArea * work_area) else // Show tabbar only if there's more than one tab. showBar(count() > 1); - currentWorkArea()->queryInputItemTransform(); + currentWorkArea()->queryInputItemGeometry(); } else lastWorkAreaRemoved(); diff --git a/src/frontends/qt/GuiWorkArea.h b/src/frontends/qt/GuiWorkArea.h index 8846a4d6ef..10802cda31 100644 --- a/src/frontends/qt/GuiWorkArea.h +++ b/src/frontends/qt/GuiWorkArea.h @@ -67,8 +67,14 @@ public: /// return true if the key is part of a shortcut bool queryKeySym(KeySymbol const & key, KeyModifier mod) const; - /// Ask relative position of input item coordinates against the main coordinates - void queryInputItemTransform(); + + /// Ask relative position of the input item coordinates against the main + /// coordinates to the system input method + void queryInputItemGeometry(); + /// Restore coordinate transformation information + void resetInputItemGeometry(); + /// Restore coordinate transformation information + void resetInputItemGeometry(bool is_new_view); bool inDialogMode() const; void setDialogMode(bool mode); diff --git a/src/frontends/qt/GuiWorkArea_Private.h b/src/frontends/qt/GuiWorkArea_Private.h index 42dcef836c..9e736f162d 100644 --- a/src/frontends/qt/GuiWorkArea_Private.h +++ b/src/frontends/qt/GuiWorkArea_Private.h @@ -140,7 +140,7 @@ struct GuiWorkArea::Private bool need_resize_ = false; /// provides access to the platform input method - QInputMethod * im_ = QGuiApplication::inputMethod(); + QInputMethod * sys_im_ = QGuiApplication::inputMethod(); /// the current preedit text of the input method docstring preedit_string_; /// Number of lines used by preedit text @@ -149,8 +149,12 @@ struct GuiWorkArea::Private QList preedit_attr_; QRectF im_cursor_rect_; QRectF im_anchor_rect_; + /// geometry of the input item + QRectF item_rect_; + /// transformation from input item coordinates to the working area QTransform item_trans_; - bool item_trans_needs_reset_ = false; + /// whether item_rect_ and item_trans need to be reset + bool item_geom_needs_reset_ = false; /// for debug QLocale::Language im_lang_;