diff options
-rw-r--r-- | src/plugins/platforms/wasm/qwasmbackingstore.cpp | 9 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.cpp | 31 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.h | 9 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 10 | ||||
-rw-r--r-- | tests/auto/wasm/selenium/qwasmwindow.py | 57 | ||||
-rw-r--r-- | tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp | 43 |
6 files changed, 130 insertions, 29 deletions
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp index a3c1ae8a502..c14d1f59e4e 100644 --- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp +++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp @@ -39,11 +39,12 @@ QPaintDevice *QWasmBackingStore::paintDevice() void QWasmBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { Q_UNUSED(window); - Q_UNUSED(region); - Q_UNUSED(offset); - m_dirty |= region; - m_compositor->handleBackingStoreFlush(window); + + QRect updateRect = region.boundingRect(); + updateRect.translate(offset); + + m_compositor->handleBackingStoreFlush(this->window(), updateRect); } void QWasmBackingStore::updateTexture(QWasmWindow *window) diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index ef460f666ff..c534cce9ef5 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -55,16 +55,23 @@ bool QWasmCompositor::releaseRequestUpdateHold() return wasEnabled; } -void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType) +void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, const QRect &updateRect, UpdateRequestDeliveryType updateType) { auto it = m_requestUpdateWindows.find(window); if (it == m_requestUpdateWindows.end()) { - m_requestUpdateWindows.insert(window, updateType); + m_requestUpdateWindows.insert(window, std::make_tuple(updateRect, updateType)); } else { // Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType. // if needed, to make sure QWindow::updateRequest's are matched. - if (it.value() == ExposeEventDelivery && updateType == UpdateRequestDelivery) - it.value() = UpdateRequestDelivery; + if (std::get<0>(it.value()) != updateRect) { + QRegion region; + region |= std::get<0>(it.value()); + region |= updateRect; + std::get<0>(it.value()) = region.boundingRect(); + } + if (std::get<1>(it.value()) == ExposeEventDelivery && + updateType == UpdateRequestDelivery) + std::get<1>(it.value()) = UpdateRequestDelivery; } requestUpdate(); @@ -106,15 +113,20 @@ void QWasmCompositor::deliverUpdateRequests() m_inDeliverUpdateRequest = true; for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) { auto *window = it.key(); - UpdateRequestDeliveryType updateType = it.value(); - deliverUpdateRequest(window, updateType); + + const QRect updateRect = std::get<0>(it.value()); + const UpdateRequestDeliveryType updateType = std::get<1>(it.value()); + deliverUpdateRequest(window, updateRect, updateType); } m_inDeliverUpdateRequest = false; frame(requestUpdateWindows.keys()); } -void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType) +void QWasmCompositor::deliverUpdateRequest( + QWasmWindow *window, + const QRect &updateRect, + UpdateRequestDeliveryType updateType) { QWindow *qwindow = window->window(); @@ -126,7 +138,6 @@ void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDel // type. If the window has not yet been exposed then we must expose it first regardless // of update type. The deliverUpdateRequest must still be sent in this case in order // to maintain correct window update state. - QRect updateRect(QPoint(0, 0), qwindow->geometry().size()); if (updateType == UpdateRequestDelivery) { if (qwindow->isExposed() == false) QWindowSystemInterface::handleExposeEvent(qwindow, updateRect); @@ -136,12 +147,12 @@ void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDel } } -void QWasmCompositor::handleBackingStoreFlush(QWindow *window) +void QWasmCompositor::handleBackingStoreFlush(QWindow *window, const QRect &updateRect) { // Request update to flush the updated backing store content, unless we are currently // processing an update, in which case the new content will flushed as a part of that update. if (!m_inDeliverUpdateRequest) - requestUpdateWindow(static_cast<QWasmWindow *>(window->handle())); + requestUpdateWindow(static_cast<QWasmWindow *>(window->handle()), updateRect); } void QWasmCompositor::frame(const QList<QWasmWindow *> &windows) diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 4953d652338..0f24a6690e7 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -9,6 +9,7 @@ #include <qpa/qplatformwindow.h> #include <QMap> +#include <tuple> QT_BEGIN_NAMESPACE @@ -35,9 +36,9 @@ public: void requestUpdate(); enum UpdateRequestDeliveryType { ExposeEventDelivery, UpdateRequestDelivery }; - void requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType = ExposeEventDelivery); + void requestUpdateWindow(QWasmWindow *window, const QRect &updateRect, UpdateRequestDeliveryType updateType = ExposeEventDelivery); - void handleBackingStoreFlush(QWindow *window); + void handleBackingStoreFlush(QWindow *window, const QRect &updateRect); void onWindowTreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindow *window); private: @@ -46,10 +47,10 @@ private: void deregisterEventHandlers(); void deliverUpdateRequests(); - void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType); + void deliverUpdateRequest(QWasmWindow *window, const QRect &updateRect, UpdateRequestDeliveryType updateType); bool m_isEnabled = true; - QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows; + QMap<QWasmWindow *, std::tuple<QRect, UpdateRequestDeliveryType>> m_requestUpdateWindows; int m_requestAnimationFrameId = -1; bool m_inDeliverUpdateRequest = false; static bool m_requestUpdateHoldEnabled; diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 99e9bb22f1b..6fa56f1d064 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -317,7 +317,7 @@ void QWasmWindow::setGeometry(const QRect &rect) if (shouldInvalidate) invalidate(); else - m_compositor->requestUpdateWindow(this); + m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size())); } void QWasmWindow::setVisible(bool visible) @@ -327,7 +327,7 @@ void QWasmWindow::setVisible(bool visible) if (visible == nowVisible) return; - m_compositor->requestUpdateWindow(this, QWasmCompositor::ExposeEventDelivery); + m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()), QWasmCompositor::ExposeEventDelivery); m_qtWindow["style"].set("display", visible ? "block" : "none"); if (window()->isActive()) m_canvas.call<void>("focus"); @@ -385,7 +385,7 @@ void QWasmWindow::setOpacity(qreal level) void QWasmWindow::invalidate() { - m_compositor->requestUpdateWindow(this); + m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size())); } void QWasmWindow::onActivationChanged(bool active) @@ -401,7 +401,7 @@ void QWasmWindow::setWindowFlags(Qt::WindowFlags flags) onPositionPreferenceChanged(positionPreferenceFromWindowFlags(flags)); } m_flags = flags; - dom::syncCSSClassWith(m_qtWindow, "frameless", !hasFrame()); + dom::syncCSSClassWith(m_qtWindow, "frameless", !hasFrame() || !window()->isTopLevel()); dom::syncCSSClassWith(m_qtWindow, "has-border", hasBorder()); dom::syncCSSClassWith(m_qtWindow, "has-shadow", hasShadow()); dom::syncCSSClassWith(m_qtWindow, "has-title", hasTitleBar()); @@ -566,7 +566,7 @@ qreal QWasmWindow::devicePixelRatio() const void QWasmWindow::requestUpdate() { - m_compositor->requestUpdateWindow(this, QWasmCompositor::UpdateRequestDelivery); + m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()), QWasmCompositor::UpdateRequestDelivery); } bool QWasmWindow::hasFrame() const diff --git a/tests/auto/wasm/selenium/qwasmwindow.py b/tests/auto/wasm/selenium/qwasmwindow.py index 260e9d2d24a..df2d39f5167 100644 --- a/tests/auto/wasm/selenium/qwasmwindow.py +++ b/tests/auto/wasm/selenium/qwasmwindow.py @@ -28,6 +28,24 @@ class WidgetTestCase(unittest.TestCase): self.addTypeEqualityFunc(Color, assert_colors_equal) self.addTypeEqualityFunc(Rect, assert_rects_equal) + # + # This is a manual test + # The reason is that the color readback works + # even if the display is incorrect + # + def test_native_widgets(self): + screen = Screen(self._driver, ScreenPosition.FIXED, + x=0, y=0, width=600, height=1200) + + w0 = Widget(self._driver, "w0", 1) + w0.show() + #time.sleep(3600) + color = w0.color_at(100, 150) + self.assertEqual(color.r, 255) + self.assertEqual(color.g, 255) + self.assertEqual(color.b, 255) + self.assertEqual(w0.hasFocus(), True) + def test_hasFocus_returnsFalse_whenSetNoFocusShowWasCalled(self): screen = Screen(self._driver, ScreenPosition.FIXED, x=0, y=0, width=600, height=1200) @@ -607,15 +625,30 @@ def clearWidgets(driver): ) class Widget: - def __init__(self, driver, name): + def __init__(self, driver, name, isNative=0): self.name=name self.driver=driver - self.driver.execute_script( - f''' - instance.createWidget('{self.name}'); - ''' - ) + if isNative == 0: + self.driver.execute_script( + f''' + instance.createWidget('{self.name}'); + ''' + ) + if isNative == 1: + self.driver.execute_script( + f''' + instance.createNativeWidget('{self.name}'); + ''' + ) + + if isNative == 1: + information = self.__window_information() + self.screen = Screen(self.driver, screen_name=information['screen']['name']) + + self._window_id = self.__window_information()['id'] + self.element = self.screen.find_element( + By.CSS_SELECTOR, f'#qt-window-{self._window_id}') def setNoFocusShow(self): self.driver.execute_script( @@ -641,6 +674,18 @@ class Widget: ''' ) + def color_at(self, x, y): + raw = self.driver.execute_script( + f''' + return arguments[0].querySelector('canvas') + .getContext('2d').getImageData({x}, {y}, 1, 1).data; + ''', self.element) + return Color(r=raw[0], g=raw[1], b=raw[2]) + + def __window_information(self): + information = call_instance_function(self.driver, 'windowInformation') + return next(filter(lambda e: e['title'] == "Dialog", information)) + class Window: def __init__(self, parent=None, rect=None, title=None, element=None, visible=True, opengl=0): diff --git a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp index 365fc74a34e..75e23cecdaf 100644 --- a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp +++ b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp @@ -16,6 +16,10 @@ #include <QApplication> #include <QDialog> #include <QSysInfo> +#include <QTreeView> +#include <QFileSystemModel> +#include <QScrollArea> +#include <QVBoxLayout> #include <QOpenGLWindow> #include <QOpenGLFunctions> @@ -389,6 +393,7 @@ public: void make(const std::string &name) { auto widget = std::make_shared<TestWidget>(); + widget->setWindowTitle("Dialog"); auto *lineEdit = new QLineEdit(widget.get()); widget->setMinimumSize(200, 200); @@ -401,6 +406,38 @@ public: m_widgets[name] = widget; m_lineEdits[name] = lineEdit; } + void makeNative(const std::string &name) + { + auto widget = std::make_shared<TestWidget>(); + widget->setWindowTitle("Dialog"); + auto *lineEdit = new QLineEdit(); + + 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; + + QFileSystemModel *model = new QFileSystemModel; + model->setRootPath(QDir::currentPath()); + + auto *scrollArea = new QScrollArea(); + auto *layout = new QVBoxLayout(widget.get()); + auto *treeView = new QTreeView(scrollArea); + treeView->setModel(model); + + layout->addWidget(lineEdit); + layout->addWidget(scrollArea); + + treeView->setAttribute(Qt::WA_NativeWindow); + scrollArea->setAttribute(Qt::WA_NativeWindow); + lineEdit->setAttribute(Qt::WA_NativeWindow); + widget->setAttribute(Qt::WA_NativeWindow); + } private: using TestWidgetPtr = std::shared_ptr<TestWidget>; @@ -503,6 +540,11 @@ void createWidget(const std::string &name) WidgetStorage::getInstance()->make(name); } +void createNativeWidget(const std::string &name) +{ + WidgetStorage::getInstance()->makeNative(name); +} + void setWidgetNoFocusShow(const std::string &name) { auto w = WidgetStorage::getInstance()->findWidget(name); @@ -678,6 +720,7 @@ EMSCRIPTEN_BINDINGS(qwasmwindow) emscripten::function("getOpenGLColorAt_0_0", &getOpenGLColorAt_0_0); emscripten::function("createWidget", &createWidget); + emscripten::function("createNativeWidget", &createNativeWidget); emscripten::function("setWidgetNoFocusShow", &setWidgetNoFocusShow); emscripten::function("showWidget", &showWidget); emscripten::function("activateWidget", &activateWidget); |