From 12d45f8f7a2efe46bafd2d2cfd7c38b2e0a18efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20L=C3=B6ffler?= Date: Sun, 29 Oct 2023 08:04:17 +0100 Subject: [PATCH] Keep track of the focusing order of top-level windows Required to trigger actions from external applications, e.g. "insert text into the front-most (source) window" --- src/TWApp.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/TWApp.h | 7 +++++ 2 files changed, 85 insertions(+) diff --git a/src/TWApp.cpp b/src/TWApp.cpp index 5b6320e44..ee9f598f8 100644 --- a/src/TWApp.cpp +++ b/src/TWApp.cpp @@ -222,6 +222,32 @@ void TWApp::init() scriptManager = new TWScriptManager; + connect(this, &QGuiApplication::focusObjectChanged, this, [=](QObject * focusObj) { + QWidget * widget = qobject_cast(focusObj); + if (!widget) { + return; + } + // Get the corresponding top level widget + while (widget && widget->parentWidget()) { + widget = widget->parentWidget(); + } + if (!m_focusStack.empty() && m_focusStack.top() == widget) { + return; + } + // Remove all empty pointers (to widgets that were destroyed already) + // and all mentions of the current widget + // NB: Using one indexed loop instead of two removeAll invocations (the + // latter was only introduced in Qt 5.4) to only traverse the stack once + for (decltype(m_focusStack)::size_type i = 0; i < m_focusStack.size(); ) { + if (!m_focusStack[i] || m_focusStack[i] == widget) { + m_focusStack.remove(i); + continue; + } + ++i; + } + m_focusStack.push(widget); + }); + #if defined(Q_OS_DARWIN) setQuitOnLastWindowClosed(false); setAttribute(Qt::AA_DontShowIconsInMenus); @@ -1211,6 +1237,58 @@ QStringList TWApp::getTranslationList() return translationList; } +QWidget * TWApp::topWindow() const +{ + if (m_focusStack.empty()) { + return nullptr; + } + return m_focusStack.top().data(); +} + +QWidget *TWApp::topTeXWindow() const +{ + // NB: using iterators is safer, in case QStack::size_type is turned into an + // unsigned type at some point +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + for (decltype(m_focusStack)::size_type i = m_focusStack.size() - 1; i >= 0; --i) { + auto * win = qobject_cast(m_focusStack[i]); + if (win) { + return win; + } + } +#else + for (auto it = m_focusStack.crbegin(); it != m_focusStack.crend(); ++it) { + auto * win = qobject_cast(it->data()); + if (win) { + return win; + } + } +#endif + return nullptr; +} + +QWidget *TWApp::topPDFWindow() const +{ + // NB: using iterators is safer, in case QStack::size_type is turned into an + // unsigned type at some point +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + for (decltype(m_focusStack)::size_type i = m_focusStack.size() - 1; i >= 0; --i) { + auto * win = qobject_cast(m_focusStack[i]); + if (win) { + return win; + } + } +#else + for (auto it = m_focusStack.crbegin(); it != m_focusStack.crend(); ++it) { + auto * win = qobject_cast(it->data()); + if (win) { + return win; + } + } +#endif + return nullptr; +} + void TWApp::applyTranslation(const QString& locale) { foreach (QTranslator* t, translators) { diff --git a/src/TWApp.h b/src/TWApp.h index d3baf6df6..cc2bab615 100644 --- a/src/TWApp.h +++ b/src/TWApp.h @@ -35,6 +35,7 @@ #include #endif #include +#include #include #include #include @@ -113,6 +114,10 @@ class TWApp : public QApplication TWScriptManager* getScriptManager() { return scriptManager; } + QWidget * topWindow() const; + QWidget * topTeXWindow() const; + QWidget * topPDFWindow() const; + #if defined(Q_OS_WIN) static QString GetWindowsVersionString(); static unsigned int GetWindowsVersion(); @@ -275,6 +280,8 @@ private slots: static TWApp *theAppInstance; Tw::InterProcessCommunicator m_IPC; + + QStack> m_focusStack; }; inline TWApp *TWApp::instance()