From 0737fca6b2f4b29b7e4eda221147187cf72f96f3 Mon Sep 17 00:00:00 2001 From: Even Oscar Andersen Date: Mon, 26 Feb 2024 12:46:35 +0800 Subject: wasm: Qt::WA_ShowWithoutActivating was not respected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Qt::WA_ShowWithoutActivating flag was not respected Added test in the part of the code that calls requestActivateWindow Added selenium focus test Fixes: QTBUG-122776 Change-Id: I1a248ed4352f86376d615a4cb7022e7ea095d4e7 Reviewed-by: Piotr WierciƄski --- src/plugins/platforms/wasm/qwasmwindowtreenode.cpp | 8 +- tests/auto/wasm/selenium/CMakeLists.txt | 1 + tests/auto/wasm/selenium/qwasmwindow.py | 85 +++++++++++++ .../auto/wasm/selenium/tst_qwasmwindow_harness.cpp | 138 ++++++++++++++++++++- 4 files changed, 224 insertions(+), 8 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp index e16410dcde..ea8d8dbcfa 100644 --- a/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp +++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp @@ -39,8 +39,12 @@ void QWasmWindowTreeNode::onSubtreeChanged(QWasmWindowTreeNodeChangeType changeT QWasmWindowTreeNode *parent, QWasmWindow *child) { if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this - && m_childStack.topWindow()) { - m_childStack.topWindow()->requestActivateWindow(); + && m_childStack.topWindow() + && m_childStack.topWindow()->window()) { + + const QVariant showWithoutActivating = m_childStack.topWindow()->window()->property("_q_showWithoutActivating"); + if (!showWithoutActivating.isValid() || !showWithoutActivating.toBool()) + m_childStack.topWindow()->requestActivateWindow(); } if (parentNode()) diff --git a/tests/auto/wasm/selenium/CMakeLists.txt b/tests/auto/wasm/selenium/CMakeLists.txt index a4963be579..b544c2a0d6 100644 --- a/tests/auto/wasm/selenium/CMakeLists.txt +++ b/tests/auto/wasm/selenium/CMakeLists.txt @@ -12,6 +12,7 @@ qt_internal_add_test(tst_qwasmwindow_harness LIBRARIES Qt::Core Qt::Gui + Qt::Widgets ) if(CMAKE_HOST_WIN32) diff --git a/tests/auto/wasm/selenium/qwasmwindow.py b/tests/auto/wasm/selenium/qwasmwindow.py index 39147b7a0f..1932feb4bb 100644 --- a/tests/auto/wasm/selenium/qwasmwindow.py +++ b/tests/auto/wasm/selenium/qwasmwindow.py @@ -27,9 +27,45 @@ class WidgetTestCase(unittest.TestCase): self.addTypeEqualityFunc(Color, assert_colors_equal) self.addTypeEqualityFunc(Rect, assert_rects_equal) + def test_hasFocus_returnsFalse_whenSetNoFocusShowWasCalled(self): + screen = Screen(self._driver, ScreenPosition.FIXED, + x=0, y=0, width=600, height=1200) + + w0 = Widget(self._driver, "w0") + w0.show() + self.assertEqual(w0.hasFocus(), True) + + w1 = Widget(self._driver, "w1") + w1.setNoFocusShow() + w1.show() + self.assertEqual(w0.hasFocus(), True) + self.assertEqual(w1.hasFocus(), False) + + w2 = Widget(self._driver, "w2") + w2.show() + self.assertEqual(w0.hasFocus(), False) + self.assertEqual(w1.hasFocus(), False) + self.assertEqual(w2.hasFocus(), True) + + w3 = Widget(self._driver, "w3") + w3.setNoFocusShow() + w3.show() + self.assertEqual(w0.hasFocus(), False) + self.assertEqual(w1.hasFocus(), False) + self.assertEqual(w2.hasFocus(), True) + self.assertEqual(w3.hasFocus(), False) + w3.activate(); + self.assertEqual(w0.hasFocus(), False) + self.assertEqual(w1.hasFocus(), False) + self.assertEqual(w2.hasFocus(), False) + self.assertEqual(w3.hasFocus(), True) + + clearWidgets(self._driver) + def test_window_resizing(self): screen = Screen(self._driver, ScreenPosition.FIXED, x=0, y=0, width=600, height=600) + window = Window(parent=screen, rect=Rect(x=100, y=100, width=200, height=200)) self.assertEqual(window.rect, Rect(x=100, y=100, width=200, height=200)) @@ -527,6 +563,48 @@ class Screen: shadow_container = self.element.find_element(By.CSS_SELECTOR, f'#qt-shadow-container') return shadow_container.shadow_root.find_element(method, query) +def clearWidgets(driver): + driver.execute_script( + f''' + instance.clearWidgets(); + ''' + ) + +class Widget: + def __init__(self, driver, name): + self.name=name + self.driver=driver + + self.driver.execute_script( + f''' + instance.createWidget('{self.name}'); + ''' + ) + + def setNoFocusShow(self): + self.driver.execute_script( + f''' + instance.setWidgetNoFocusShow('{self.name}'); + ''' + ) + + def show(self): + self.driver.execute_script( + f''' + instance.showWidget('{self.name}'); + ''' + ) + def hasFocus(self): + focus = call_instance_function_arg(self.driver, 'hasWidgetFocus', self.name) + return focus + + def activate(self): + self.driver.execute_script( + f''' + instance.activateWidget('{self.name}'); + ''' + ) + class Window: def __init__(self, parent=None, rect=None, title=None, element=None, visible=True): @@ -815,6 +893,13 @@ def call_instance_function(driver, name): instance.{name}(); return eval(result);''') +def call_instance_function_arg(driver, name, arg): + return driver.execute_script( + f'''let result; + window.{name}Callback = data => result = data; + instance.{name}('{arg}'); + return eval(result);''') + def wait_for_animation_frame(driver): driver.execute_script( ''' diff --git a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp index f69cb9775f..a54173e058 100644 --- a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp +++ b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include +#include #include #include @@ -11,6 +12,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -20,6 +25,12 @@ #include #include +class TestWidget : public QDialog +{ + Q_OBJECT +}; + + class TestWindow : public QRasterWindow { Q_OBJECT @@ -76,6 +87,71 @@ TestWindow *findWindowByTitle(const std::string &title) }); return window_it == windows.end() ? nullptr : static_cast(*window_it); } + +class WidgetStorage +{ +private: + ~WidgetStorage() + { + } +public: + static WidgetStorage *getInstance() + { + if (!s_instance) + { + s_instance = new WidgetStorage(); + } + return s_instance; + } + static void clearInstance() + { + delete s_instance; + s_instance = nullptr; + } + + TestWidget *findWidget(const std::string &name) + { + auto it = m_widgets.find(name); + if (it != m_widgets.end()) + return it->second.get(); + return nullptr; + } + + QLineEdit *findEdit(const std::string &name) + { + auto it = m_lineEdits.find(name); + if (it != m_lineEdits.end()) + return it->second; + return nullptr; + } + + void make(const std::string &name) + { + auto widget = std::make_shared(); + auto *lineEdit = new QLineEdit(widget.get()); + + widget->setMinimumSize(200, 200); + widget->setMaximumSize(200, 200); + widget->setGeometry(0, m_widgetY, 200, 200); + m_widgetY += 200; + + lineEdit->setText("Hello world"); + + m_widgets[name] = widget; + m_lineEdits[name] = lineEdit; + } + +private: + using TestWidgetPtr = std::shared_ptr; + + static WidgetStorage * s_instance; + std::map m_widgets; + std::map m_lineEdits; + int m_widgetY = 0; +}; + +WidgetStorage *WidgetStorage::s_instance = nullptr; + } // namespace using namespace emscripten; @@ -161,8 +237,50 @@ void screenInformation() emscripten::val(toJSArray(screensAsJsObjects))); } -void createWindow(int x, int y, int w, int h, std::string parentType, std::string parentId, - std::string title) +void createWidget(const std::string &name) +{ + WidgetStorage::getInstance()->make(name); +} + +void setWidgetNoFocusShow(const std::string &name) +{ + auto w = WidgetStorage::getInstance()->findWidget(name); + if (w) + w->setAttribute(Qt::WA_ShowWithoutActivating); +} + +void showWidget(const std::string &name) +{ + auto w = WidgetStorage::getInstance()->findWidget(name); + if (w) + w->show(); +} + +void hasWidgetFocus(const std::string &name) +{ + bool focus = false; + auto le = WidgetStorage::getInstance()->findEdit(name); + if (le) + focus = le->hasFocus(); + + emscripten::val::global("window").call("hasWidgetFocusCallback", + emscripten::val(focus)); +} + +void activateWidget(const std::string &name) +{ + auto w = WidgetStorage::getInstance()->findWidget(name); + if (w) + w->activateWindow(); +} + +void clearWidgets() +{ + WidgetStorage::clearInstance(); +} + +void createWindow(int x, int y, int w, int h, const std::string &parentType, const std::string &parentId, + const std::string &title) { QScreen *parentScreen = nullptr; QWindow *parentWindow = nullptr; @@ -202,7 +320,7 @@ void createWindow(int x, int y, int w, int h, std::string parentType, std::strin window->setParent(parentWindow); } -void setWindowBackgroundColor(std::string title, int r, int g, int b) +void setWindowBackgroundColor(const std::string &title, int r, int g, int b) { auto *window = findWindowByTitle(title); if (!window) { @@ -225,7 +343,7 @@ void setWindowVisible(int windowId, bool visible) { (*window_it)->setVisible(visible); } -void setWindowParent(std::string windowTitle, std::string parentTitle) +void setWindowParent(const std::string &windowTitle, const std::string &parentTitle) { QWindow *window = findWindowByTitle(windowTitle); if (!window) { @@ -242,7 +360,7 @@ void setWindowParent(std::string windowTitle, std::string parentTitle) window->setParent(parent); } -bool closeWindow(std::string title) +bool closeWindow(const std::string &title) { QWindow *window = findWindowByTitle(title); return window ? window->close() : false; @@ -252,16 +370,24 @@ EMSCRIPTEN_BINDINGS(qwasmwindow) { emscripten::function("screenInformation", &screenInformation); emscripten::function("windowInformation", &windowInformation); + emscripten::function("createWindow", &createWindow); emscripten::function("setWindowVisible", &setWindowVisible); emscripten::function("setWindowParent", &setWindowParent); emscripten::function("closeWindow", &closeWindow); emscripten::function("setWindowBackgroundColor", &setWindowBackgroundColor); + + emscripten::function("createWidget", &createWidget); + emscripten::function("setWidgetNoFocusShow", &setWidgetNoFocusShow); + emscripten::function("showWidget", &showWidget); + emscripten::function("activateWidget", &activateWidget); + emscripten::function("hasWidgetFocus", &hasWidgetFocus); + emscripten::function("clearWidgets", &clearWidgets); } int main(int argc, char **argv) { - QGuiApplication app(argc, argv); + QApplication app(argc, argv); app.exec(); return 0; -- cgit v1.2.3