Refactor the implementation to use focusProxy directly.

The reason of this is that: focusWidgetChanged arrives after
setFocusObject. This makes QApplication::focusWidget value out of date
at the point of setFocusObject. The qwidget logic is actually simple
enough (see QWidgetPrivate::deepestFocusProxy), just use the same logic
here.
This commit is contained in:
Weng Xuetian 2022-11-25 11:08:45 -08:00 committed by liulinsong
parent a5a444f513
commit fe938a7717
2 changed files with 60 additions and 49 deletions

View File

@ -5,7 +5,6 @@
*
*/
#include <QApplication>
#include <QDBusConnection>
#include <QDebug>
#include <QInputMethod>
@ -143,6 +142,18 @@ struct xkb_context *_xkb_context_new_helper() {
return context;
}
QObject *deepestFocusProxy(QObject *object) {
auto *widget = qobject_cast<QWidget *>(object);
if (!widget) {
return object;
}
while (auto *proxy = widget->focusProxy()) {
widget = proxy;
}
return widget;
}
FcitxQtICData::FcitxQtICData(QFcitxPlatformInputContext *context,
QWindow *window)
: proxy(new FcitxQtInputContextProxy(context->watcher(), context)),
@ -207,13 +218,6 @@ QFcitxPlatformInputContext::QFcitxPlatformInputContext()
registerFcitxQtDBusTypes();
watcher_->setWatchPortal(true);
watcher_->watch();
// Monitor the focus widget from QApplication, because
// QGuiApplication::focusObject does not respect the QWidget's focusProxy.
if (qobject_cast<QApplication *>(qGuiApp)) {
connect(qApp, &QApplication::focusChanged, this,
&QFcitxPlatformInputContext::focusWidgetChanged);
}
}
QFcitxPlatformInputContext::~QFcitxPlatformInputContext() {
@ -232,9 +236,11 @@ bool QFcitxPlatformInputContext::objectAcceptsInputMethod() const {
enabled = query.value(Qt::ImEnabled).toBool();
}
if (focusWidget_ && focusWidget_ != object && !enabled) {
QObject *realFocusObject = focusObjectWrapper();
// Make sure we don't query same object twice.
if (realFocusObject && realFocusObject != object && !enabled) {
QInputMethodQueryEvent query(Qt::ImEnabled);
QGuiApplication::sendEvent(focusWidget_, &query);
QGuiApplication::sendEvent(realFocusObject, &query);
enabled = query.value(Qt::ImEnabled).toBool();
}
@ -304,7 +310,7 @@ void QFcitxPlatformInputContext::reset() {
}
void QFcitxPlatformInputContext::update(Qt::InputMethodQueries queries) {
QWindow *window = this->focusWindowWrapper();
QWindow *window = focusWindowWrapper();
FcitxQtInputContextProxy *proxy = validICByWindow(window);
if (!proxy)
return;
@ -418,7 +424,13 @@ void QFcitxPlatformInputContext::update(Qt::InputMethodQueries queries) {
void QFcitxPlatformInputContext::commit() { QPlatformInputContext::commit(); }
void QFcitxPlatformInputContext::setFocusObject(QObject *object) {
Q_UNUSED(object);
// Since we have a wrapper, it's possible that real focus object is not
// changed.
QObject *realFocusObject = focusObjectWrapper();
if (realFocusObject != object && lastObject_ == object) {
return;
}
FcitxQtInputContextProxy *proxy = validICByWindow(lastWindow_);
commitPreedit(lastObject_);
if (proxy) {
@ -428,7 +440,7 @@ void QFcitxPlatformInputContext::setFocusObject(QObject *object) {
data.resetCandidateWindow();
}
QWindow *window = this->focusWindowWrapper();
QWindow *window = focusWindowWrapper();
lastWindow_ = window;
lastObject_ = object;
// Always create IC Data for window.
@ -454,13 +466,6 @@ void QFcitxPlatformInputContext::setFocusObject(QObject *object) {
}
}
void QFcitxPlatformInputContext::focusWidgetChanged(QWidget *, QWidget *now) {
focusWidget_ = now;
if (focusWidget_ != qGuiApp->focusObject()) {
update(Qt::ImEnabled);
}
}
void QFcitxPlatformInputContext::updateCursorRect(QPointer<QWindow> window) {
if (window != lastWindow_) {
return;
@ -476,7 +481,7 @@ void QFcitxPlatformInputContext::windowDestroyed(QObject *object) {
}
void QFcitxPlatformInputContext::cursorRectChanged() {
QWindow *inputWindow = this->focusWindowWrapper();
QWindow *inputWindow = focusWindowWrapper();
if (!inputWindow)
return;
FcitxQtInputContextProxy *proxy = validICByWindow(inputWindow);
@ -486,7 +491,7 @@ void QFcitxPlatformInputContext::cursorRectChanged() {
FcitxQtICData &data = *static_cast<FcitxQtICData *>(
proxy->property("icData").value<void *>());
QRect r = this->cursorRectangleWrapper();
QRect r = cursorRectangleWrapper();
if (!r.isValid())
return;
@ -531,7 +536,7 @@ void QFcitxPlatformInputContext::createInputContextFinished(
data->rect = QRect();
if (proxy->isValid()) {
QWindow *window = this->focusWindowWrapper();
QWindow *window = focusWindowWrapper();
setFocusGroupForX11(uuid);
if (window && window == w) {
cursorRectChanged();
@ -663,7 +668,7 @@ void QFcitxPlatformInputContext::updateClientSideUI(
FcitxQtICData *data =
static_cast<FcitxQtICData *>(proxy->property("icData").value<void *>());
auto w = data->window();
auto window = this->focusWindowWrapper();
auto window = focusWindowWrapper();
if (window && w == window) {
data->candidateWindow()->updateClientSideUI(
preedit, cursorpos, auxUp, auxDown, candidates, candidateIndex,
@ -740,7 +745,7 @@ void QFcitxPlatformInputContext::forwardKey(unsigned int keyval,
proxy->property("icData").value<void *>());
auto *w = data.window();
QObject *input = qGuiApp->focusObject();
auto window = this->focusWindowWrapper();
auto window = focusWindowWrapper();
if (input && window && w == window) {
std::unique_ptr<QKeyEvent> keyevent{
createKeyEvent(keyval, state, type, data.event.get())};
@ -936,8 +941,7 @@ bool QFcitxPlatformInputContext::filterEvent(const QEvent *event) {
break;
}
FcitxQtInputContextProxy *proxy =
validICByWindow(this->focusWindowWrapper());
FcitxQtInputContextProxy *proxy = validICByWindow(focusWindowWrapper());
if (!proxy) {
if (filterEventFallback(keyval, keycode, state, isRelease)) {
@ -973,7 +977,7 @@ bool QFcitxPlatformInputContext::filterEvent(const QEvent *event) {
}
} else {
ProcessKeyWatcher *watcher = new ProcessKeyWatcher(
*keyEvent, this->focusWindowWrapper(), reply, proxy);
*keyEvent, focusWindowWrapper(), reply, proxy);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
&QFcitxPlatformInputContext::processKeyEventFinished);
return true;
@ -1053,7 +1057,7 @@ FcitxQtInputContextProxy *QFcitxPlatformInputContext::validIC() {
if (icMap_.empty()) {
return nullptr;
}
QWindow *window = this->focusWindowWrapper();
QWindow *window = focusWindowWrapper();
return validICByWindow(window);
}
@ -1112,39 +1116,49 @@ bool QFcitxPlatformInputContext::processCompose(unsigned int keyval,
}
QWindow *QFcitxPlatformInputContext::focusWindowWrapper() const {
QWindow *focusWindow = nullptr;
if (focusWidget_ && qGuiApp->focusObject() &&
qGuiApp->focusObject() != focusWidget_) {
focusWindow = focusWidget_->topLevelWidget()->windowHandle();
}
if (!focusWindow) {
focusWindow = qGuiApp->focusWindow();
}
QWindow *focusWindow = qGuiApp->focusWindow();
do {
if (!focusWindow) {
break;
}
QObject *focusObject = qGuiApp->focusObject();
QObject *realFocusObject = deepestFocusProxy(qGuiApp->focusObject());
if (focusObject == realFocusObject) {
break;
}
auto *widget = qobject_cast<QWidget *>(realFocusObject);
if (!widget) {
break;
}
auto *window = widget->topLevelWidget()->windowHandle();
if (!window) {
break;
}
focusWindow = window;
} while (0);
return focusWindow;
}
QObject *QFcitxPlatformInputContext::focusObjectWrapper() const {
if (focusWidget_ && qGuiApp->focusObject() &&
qGuiApp->focusObject() != focusWidget_) {
return focusWidget_;
}
return qGuiApp->focusObject();
return deepestFocusProxy(qGuiApp->focusObject());
}
QRect QFcitxPlatformInputContext::cursorRectangleWrapper() const {
QObject *object = focusObjectWrapper();
QRect r;
if (focusWidget_ && focusWidget_ != qGuiApp->focusObject()) {
if (object && object != qGuiApp->focusObject() && object->isWidgetType()) {
// Logic is borrowed from QWidgetPrivate::updateWidgetTransform.
// If focusObject mismatches, the inputItemTransform will also mismatch,
// so we need to do our own calculation.
auto *widget = qobject_cast<QWidget *>(object);
QTransform t;
const QPoint p =
focusWidget_->mapTo(focusWidget_->topLevelWidget(), QPoint(0, 0));
const QPoint p = widget->mapTo(widget->topLevelWidget(), QPoint(0, 0));
t.translate(p.x(), p.y());
r = focusWidget_->inputMethodQuery(Qt::ImCursorRectangle).toRect();
r = widget->inputMethodQuery(Qt::ImCursorRectangle).toRect();
if (r.isValid()) {
r = t.mapRect(r);
}
return r;
} else {
r = qGuiApp->inputMethod()->cursorRectangle().toRect();
}

View File

@ -23,7 +23,6 @@
#include <unordered_map>
#include <xkbcommon/xkbcommon-compose.h>
class QWidget;
namespace fcitx {
class FcitxQtConnection;
@ -150,7 +149,6 @@ public Q_SLOTS:
bool hasNext);
private Q_SLOTS:
void processKeyEventFinished(QDBusPendingCallWatcher *);
void focusWidgetChanged(QWidget *old, QWidget *now);
private:
bool processCompose(unsigned int keyval, unsigned int state,
@ -206,7 +204,6 @@ private:
xkbComposeState_;
QLocale locale_;
FcitxTheme *theme_ = nullptr;
QPointer<QWidget> focusWidget_;
};
} // namespace fcitx