}
-void Dialog::showData(string const & data)
+void Dialog::showData(string const & data, Qt::FocusReason reason)
{
if (isBufferDependent() && !isBufferAvailable())
return;
return;
}
- showView();
+ showView(reason);
}
}
-void Dialog::showView()
+void Dialog::showView(Qt::FocusReason reason)
{
prepareView();
QWidget * w = asQWidget();
- if (!w->isVisible())
- w->show();
+ if (!w->isVisible()) {
+ w->setFocus(reason);
+ w->show();
+ }
w->raise();
w->activateWindow();
if (wantInitialFocus())
- w->setFocus();
+ w->setFocus(reason);
else {
lyxview_.raise();
lyxview_.activateWindow();
- lyxview_.setFocus();
+ lyxview_.setFocus(reason);
}
}
-
void Dialog::hideView()
{
QWidget * w = asQWidget();
//@{
/// \param data is a string encoding of the data to be displayed.
/// It is passed to the Controller to be translated into a usable form.
- virtual void showData(std::string const & data);
+ virtual void showData(std::string const & data)
+ {
+ return showData(data, Qt::PopupFocusReason);
+ }
+
+ /// \param data is a string encoding of the data to be displayed.
+ /// It is passed to the Controller to be translated into a usable form.
+ /// \param reason provides the focus reason of the dialog.
+ /// E.g. dialog "findreplaceadv" requires special treatment after
+ /// obtaining focus in terms of the input method item transformation,
+ /// so should be marked as reason = Qt::OtherFocusReason.
+ virtual void showData(std::string const & data, Qt::FocusReason reason);
//@}
/// \return inset at current cursor location.
void hideView();
/// Prepare dialog and display it.
- void showView();
+ void showView() { return showView(Qt::PopupFocusReason); }
+
+ /// Prepare dialog and display it.
+ /// \param reason provides the focus reason of the dialog.
+ /// E.g. dialog "findreplaceadv" requires special treatment after
+ /// obtaining focus in terms of the input method item transformation,
+ /// so should be marked as reason = Qt::OtherFocusReason.
+ void showView(Qt::FocusReason reason);
/// Prepare dialog before view.
void prepareView();
FuncRequest func = theTopLevelKeymap().getBinding(seq);
if (!getStatus(func).enabled()) {
LYXERR(Debug::FINDVERBOSE, "Focusing replace WA");
- replace_work_area_->setFocus();
+ replace_work_area_->setFocus(Qt::TabFocusReason);
LYXERR(Debug::FINDVERBOSE, "Selecting entire replace buffer");
dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
FuncRequest func = theTopLevelKeymap().getBinding(seq);
if (!getStatus(func).enabled()) {
LYXERR(Debug::FINDVERBOSE, "Focusing find WA");
- find_work_area_->setFocus();
+ find_work_area_->setFocus(Qt::BacktabFocusReason);
LYXERR(Debug::FINDVERBOSE, "Selecting entire find buffer");
dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
}
+void GuiView::setFocus(Qt::FocusReason reason)
+{
+ LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this << " reason = " << reason);
+ QMainWindow::setFocus(reason);
+}
+
+
bool GuiView::hasFocus() const
{
if (currentWorkArea())
// Make sure guiApp points to the correct view.
guiApp->setCurrentView(this);
if (currentWorkArea())
- currentWorkArea()->setFocus();
+ currentWorkArea()->setFocus(e->reason());
else if (currentMainWorkArea())
- currentMainWorkArea()->setFocus();
+ currentMainWorkArea()->setFocus(e->reason());
else
- d.bg_widget_->setFocus();
+ d.bg_widget_->setFocus(e->reason());
}
connectBufferView(wa->bufferView());
connectBuffer(wa->bufferView().buffer());
d.current_work_area_ = wa;
+ // The below specifies that the input method item transformation will
+ // not reset
+ wa->setFocus(Qt::OtherFocusReason);
QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
this, SLOT(updateWindowTitle(GuiWorkArea *)));
QObject::connect(wa, SIGNAL(busy(bool)),
case QEvent::WindowActivate: {
GuiView * old_view = guiApp->currentView();
if (this == old_view) {
- setFocus();
+ setFocus(Qt::ActiveWindowFocusReason);
return QMainWindow::event(e);
}
if (old_view && old_view->currentBufferView()) {
on_currentWorkAreaChanged(d.current_work_area_);
else
resetWindowTitle();
- setFocus();
+ setFocus(Qt::ActiveWindowFocusReason);
return QMainWindow::event(e);
}
case LFUN_DIALOG_HIDE: {
guiApp->hideDialogs(to_utf8(cmd.argument()), nullptr);
+ setFocus(Qt::PopupFocusReason);
break;
}
Dialog * dialog = findOrBuild(name, false);
if (dialog) {
bool const visible = dialog->isVisibleView();
- dialog->showData(sdata);
+ if (name == "findreplaceadv")
+ dialog->showData(sdata, Qt::OtherFocusReason);
+ else
+ dialog->showData(sdata);
if (currentBufferView())
currentBufferView()->editInset(name, inset);
// We only set the focus to the new dialog if it was not yet
///
void setFocus();
+ void setFocus(Qt::FocusReason reason);
bool hasFocus() const;
///
// 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();
+
+ // Initialize d->im_cursor_rect_
+ Point point;
+ Dimension dim;
+ d->buffer_view_->caretPosAndDim(point, dim);
+ int cur_x = point.x_ - dim.width();
+ int cur_y = point.y_ + dim.height();
+ d->im_cursor_rect_ = QRectF(cur_x, (cur_y - dim.height()) , 1, dim.height() );
}
void GuiWorkArea::setFullScreen(bool full_screen)
{
d->buffer_view_->setFullScreen(full_screen);
+
+ queryInputItemTransform();
+
if (full_screen && lyxrc.full_screen_scrollbar)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
else
void GuiWorkArea::focusInEvent(QFocusEvent * e)
{
- LYXERR(Debug::DEBUG, "GuiWorkArea::focusInEvent(): " << this << endl);
+ LYXERR(Debug::DEBUG, "GuiWorkArea::focusInEvent(): " << this << " reason() = " << e->reason() << endl);
if (d->lyx_view_->currentWorkArea() != this) {
d->lyx_view_->setCurrentWorkArea(this);
d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer();
}
+ // needs to reset IM item coordinates when focus in from dialogs or other apps
+ 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;
+ } else {
+ // Switched from advanced search dialog or else (e.g. mouse event)
+ d->item_trans_needs_reset_ = false;
+ }
+
startBlinkingCaret();
QAbstractScrollArea::focusInEvent(e);
}
}
+void GuiWorkArea::queryInputItemTransform()
+{
+ 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()
+ );
+
+ d->item_trans_ = d->im_->inputItemTransform();
+}
+
+
+void GuiWorkArea::Private::resetInputItemTransform()
+{
+ 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;
+ }
+}
+
+
+//#define DEBUG_PREEDIT
+
void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
{
- if (preedit_string_.empty())
+#ifdef DEBUG_PREEDIT
+ // check the language that current input method uses
+ QLocale::Language lang = im_->locale().language();
+ if (lang != im_lang_) {
+ LYXERR0("QLocale = " << QLocale::languageToString(lang));
+ im_lang_ = lang;
+ }
+#endif
+
+ // 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)
return;
- // FIXME: shall we use real_current_font here? (see #10478)
- FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
- FontMetrics const & fm = theFontMetrics(font);
+ // 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 = 1;
+
Point point;
Dimension dim;
buffer_view_->caretPosAndDim(point, dim);
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_);
+
+ if (preedit_string_.empty()) {
+ // 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);
+ 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();
+
+ // FIXME: shall we use real_current_font here? (see #10478)
+ FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
+ FontMetrics const & fm = theFontMetrics(font);
+
// force fulldraw to remove previous paint remaining on screen
+ // FIXME: This is costly to do
buffer_view_->processUpdateFlags(Update::ForceDraw);
// get attributes of input method cursor.
else
// Switch to the work area.
setCurrentIndex(index);
- work_area->setFocus();
+ work_area->setFocus(Qt::OtherFocusReason);
return true;
}
updateTabTexts();
+ // obtain new input item coordinates in the new and old work areas
+ wa->queryInputItemTransform();
+ if (currentWorkArea())
+ currentWorkArea()->queryInputItemTransform();
+
+ view.setBusy(false);
+
return wa;
}
else
// Show tabbar only if there's more than one tab.
showBar(count() > 1);
+ currentWorkArea()->queryInputItemTransform();
} else
lastWorkAreaRemoved();
/// 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();
bool inDialogMode() const;
void setDialogMode(bool mode);
/// Change the cursor when the mouse hovers over a clickable inset
void updateCursorShape();
+ /// Restore coordinate transformation information
+ void resetInputItemTransform();
+ /// Paint preedit text provided by the platform input method
void paintPreeditText(GuiPainter & pain);
/// Prepare screen for next painting
///
bool need_resize_ = false;
+ /// provides access to the platform input method
QInputMethod * im_ = QGuiApplication::inputMethod();
/// the current preedit text of the input method
docstring preedit_string_;
QList<QInputMethodEvent::Attribute> preedit_attr_;
QRectF im_cursor_rect_;
QRectF im_anchor_rect_;
- QTransform im_item_trans_;
+ QTransform item_trans_;
+ bool item_trans_needs_reset_ = false;
+ /// for debug
+ QLocale::Language im_lang_;
/// Ratio between physical pixels and device-independent pixels
/// We save the last used value to detect changes of the