diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmwindow.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 300 |
1 files changed, 182 insertions, 118 deletions
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 2e75c39cee..6fa56f1d06 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -5,6 +5,7 @@ #include <private/qguiapplication_p.h> #include <QtCore/qfile.h> #include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <private/qpixmapcache_p.h> #include <QtGui/qopenglfunctions.h> #include <QBuffer> @@ -32,6 +33,17 @@ QT_BEGIN_NAMESPACE +namespace { +QWasmWindowStack::PositionPreference positionPreferenceFromWindowFlags(Qt::WindowFlags flags) +{ + if (flags.testFlag(Qt::WindowStaysOnTopHint)) + return QWasmWindowStack::PositionPreference::StayOnTop; + if (flags.testFlag(Qt::WindowStaysOnBottomHint)) + return QWasmWindowStack::PositionPreference::StayOnBottom; + return QWasmWindowStack::PositionPreference::Regular; +} +} // namespace + Q_GUI_EXPORT int qt_defaultDpiX(); QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, @@ -54,8 +66,9 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, m_nonClientArea = std::make_unique<NonClientArea>(this, m_qtWindow); m_nonClientArea->titleBar()->setTitle(window()->title()); - m_clientArea = std::make_unique<ClientArea>(this, compositor->screen(), m_canvas); + m_clientArea = std::make_unique<ClientArea>(this, compositor->screen(), m_windowContents); + m_windowContents.set("className", "qt-window-contents"); m_qtWindow.call<void>("appendChild", m_windowContents); m_canvas["classList"].call<void>("add", emscripten::val("qt-window-content")); @@ -67,9 +80,9 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, QWasmClipboard::installEventHandlers(m_canvas); - // set inputmode to none to stop mobile keyboard opening + // set inputMode to none to stop mobile keyboard opening // when user clicks anywhere on the canvas. - m_canvas.set("inputmode", std::string("none")); + m_canvas.set("inputMode", std::string("none")); // Hide the canvas from screen readers. m_canvas.call<void>("setAttribute", std::string("aria-hidden"), std::string("true")); @@ -82,8 +95,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, m_canvasContainer.call<void>("appendChild", m_a11yContainer); m_a11yContainer["classList"].call<void>("add", emscripten::val("qt-window-a11y-container")); - compositor->screen()->element().call<void>("appendChild", m_qtWindow); - const bool rendersTo2dContext = w->surfaceType() != QSurface::OpenGLSurface; if (rendersTo2dContext) m_context2d = m_canvas.call<emscripten::val>("getContext", emscripten::val("2d")); @@ -92,7 +103,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, m_qtWindow.set("id", "qt-window-" + std::to_string(m_winId)); emscripten::val::module_property("specialHTMLTargets").set(canvasSelector(), m_canvas); - m_compositor->addWindow(this); + m_flags = window()->flags(); const auto pointerCallback = std::function([this](emscripten::val event) { if (processPointer(*PointerEvent::fromWeb(event))) @@ -104,12 +115,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, m_pointerLeaveCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", pointerCallback); - m_dropCallback = std::make_unique<qstdweb::EventCallback>( - m_qtWindow, "drop", [this](emscripten::val event) { - if (processDrop(*DragEvent::fromWeb(event))) - event.call<void>("preventDefault"); - }); - m_wheelEventCallback = std::make_unique<qstdweb::EventCallback>( m_qtWindow, "wheel", [this](emscripten::val event) { if (processWheel(*WheelEvent::fromWeb(event))) @@ -119,21 +124,47 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, const auto keyCallback = std::function([this](emscripten::val event) { if (processKey(*KeyEvent::fromWebWithDeadKeyTranslation(event, m_deadKeySupport))) event.call<void>("preventDefault"); + event.call<void>("stopPropagation"); }); + emscripten::val keyFocusWindow; + if (QWasmInputContext *wasmContext = + qobject_cast<QWasmInputContext *>(QWasmIntegration::get()->inputContext())) { + // if there is an touchscreen input context, + // use that window for key input + keyFocusWindow = wasmContext->m_inputElement; + } else { + keyFocusWindow = m_qtWindow; + } + m_keyDownCallback = - std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keydown", keyCallback); - m_keyUpCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keyup", keyCallback); + std::make_unique<qstdweb::EventCallback>(keyFocusWindow, "keydown", keyCallback); + m_keyUpCallback = std::make_unique<qstdweb::EventCallback>(keyFocusWindow, "keyup", keyCallback); + + setParent(parent()); } QWasmWindow::~QWasmWindow() { emscripten::val::module_property("specialHTMLTargets").delete_(canvasSelector()); - destroy(); - m_compositor->removeWindow(this); + m_canvasContainer.call<void>("removeChild", m_canvas); + m_context2d = emscripten::val::undefined(); + commitParent(nullptr); if (m_requestAnimationFrameId > -1) emscripten_cancel_animation_frame(m_requestAnimationFrameId); +#if QT_CONFIG(accessibility) QWasmAccessibility::removeAccessibilityEnableButton(window()); +#endif +} + +QSurfaceFormat QWasmWindow::format() const +{ + return window()->requestedFormat(); +} + +QWasmWindow *QWasmWindow::fromWindow(QWindow *window) +{ + return static_cast<QWasmWindow *>(window->handle()); } void QWasmWindow::onRestoreClicked() @@ -159,70 +190,42 @@ void QWasmWindow::onCloseClicked() void QWasmWindow::onNonClientAreaInteraction() { - if (!isActive()) - requestActivateWindow(); + requestActivateWindow(); + QGuiApplicationPrivate::instance()->closeAllPopups(); } bool QWasmWindow::onNonClientEvent(const PointerEvent &event) { - QPoint pointInScreen = platformScreen()->mapFromLocal( - dom::mapPoint(event.target, platformScreen()->element(), event.localPoint)); + QPointF pointInScreen = platformScreen()->mapFromLocal( + dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint)); return QWindowSystemInterface::handleMouseEvent( window(), QWasmIntegration::getTimestamp(), window()->mapFromGlobal(pointInScreen), - pointInScreen, event.mouseButtons, event.mouseButton, ([event]() { - switch (event.type) { - case EventType::PointerDown: - return QEvent::NonClientAreaMouseButtonPress; - case EventType::PointerUp: - return QEvent::NonClientAreaMouseButtonRelease; - case EventType::PointerMove: - return QEvent::NonClientAreaMouseMove; - default: - Q_ASSERT(false); // notreached - return QEvent::None; - } - })(), + pointInScreen, event.mouseButtons, event.mouseButton, + MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::NonClient), event.modifiers); } -void QWasmWindow::destroy() -{ - m_qtWindow["parentElement"].call<emscripten::val>("removeChild", m_qtWindow); - - m_canvasContainer.call<void>("removeChild", m_canvas); - m_context2d = emscripten::val::undefined(); -} - void QWasmWindow::initialize() { - QRect rect = windowGeometry(); - - constexpr int minSizeBoundForDialogsAndRegularWindows = 100; - const int windowType = window()->flags() & Qt::WindowType_Mask; - const int systemMinSizeLowerBound = windowType == Qt::Window || windowType == Qt::Dialog - ? minSizeBoundForDialogsAndRegularWindows - : 0; - - const QSize minimumSize(std::max(windowMinimumSize().width(), systemMinSizeLowerBound), - std::max(windowMinimumSize().height(), systemMinSizeLowerBound)); - const QSize maximumSize = windowMaximumSize(); - const QSize targetSize = !rect.isEmpty() ? rect.size() : minimumSize; - - rect.setWidth(qBound(minimumSize.width(), targetSize.width(), maximumSize.width())); - rect.setHeight(qBound(minimumSize.width(), targetSize.height(), maximumSize.height())); + auto initialGeometry = QPlatformWindow::initialGeometry(window(), + windowGeometry(), defaultWindowSize, defaultWindowSize); + m_normalGeometry = initialGeometry; setWindowState(window()->windowStates()); setWindowFlags(window()->flags()); setWindowTitle(window()->title()); + setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); + if (window()->isTopLevel()) setWindowIcon(window()->icon()); - m_normalGeometry = rect; QPlatformWindow::setGeometry(m_normalGeometry); +#if QT_CONFIG(accessibility) // Add accessibility-enable button. The user can activate this // button to opt-in to accessibility. if (window()->isTopLevel()) QWasmAccessibility::addAccessibilityEnableButton(window()); +#endif } QWasmScreen *QWasmWindow::platformScreen() const @@ -248,7 +251,7 @@ void QWasmWindow::setZOrder(int z) void QWasmWindow::setWindowCursor(QByteArray cssCursorName) { - m_canvas["style"].set("cursor", emscripten::val(cssCursorName.constData())); + m_windowContents["style"].set("cursor", emscripten::val(cssCursorName.constData())); } void QWasmWindow::setGeometry(const QRect &rect) @@ -261,19 +264,34 @@ void QWasmWindow::setGeometry(const QRect &rect) if (m_state.testFlag(Qt::WindowMaximized)) return platformScreen()->availableGeometry().marginsRemoved(frameMargins()); - const auto screenGeometry = screen()->geometry(); - - QRect result(rect); - result.moveTop(std::max(std::min(rect.y(), screenGeometry.bottom()), - screenGeometry.y() + margins.top())); - return result; + auto offset = rect.topLeft() - (!parent() ? screen()->geometry().topLeft() : QPoint()); + + // In viewport + auto containerGeometryInViewport = + QRectF::fromDOMRect(parentNode()->containerElement().call<emscripten::val>( + "getBoundingClientRect")) + .toRect(); + + auto rectInViewport = QRect(containerGeometryInViewport.topLeft() + offset, rect.size()); + + QRect cappedGeometry(rectInViewport); + if (!parent()) { + // Clamp top level windows top position to the screen bounds + cappedGeometry.moveTop( + std::max(std::min(rectInViewport.y(), containerGeometryInViewport.bottom()), + containerGeometryInViewport.y() + margins.top())); + } + cappedGeometry.setSize( + cappedGeometry.size().expandedTo(windowMinimumSize()).boundedTo(windowMaximumSize())); + return QRect(QPoint(rect.x(), rect.y() + cappedGeometry.y() - rectInViewport.y()), + rect.size()); })(); m_nonClientArea->onClientAreaWidthChange(clientAreaRect.width()); const auto frameRect = clientAreaRect .adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()) - .translated(-screen()->geometry().topLeft()); + .translated(!parent() ? -screen()->geometry().topLeft() : QPoint()); m_qtWindow["style"].set("left", std::to_string(frameRect.left()) + "px"); m_qtWindow["style"].set("top", std::to_string(frameRect.top()) + "px"); @@ -299,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) @@ -309,8 +327,10 @@ 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"); if (visible) applyWindowState(); } @@ -334,13 +354,15 @@ QMargins QWasmWindow::frameMargins() const void QWasmWindow::raise() { - m_compositor->raise(this); + bringToTop(); invalidate(); + if (QWasmIntegration::get()->inputContext()) + m_canvas.call<void>("focus"); } void QWasmWindow::lower() { - m_compositor->lower(this); + sendToBottom(); invalidate(); } @@ -351,17 +373,19 @@ WId QWasmWindow::winId() const void QWasmWindow::propagateSizeHints() { - QRect rect = windowGeometry(); - if (rect.size().width() < windowMinimumSize().width() - && rect.size().height() < windowMinimumSize().height()) { - rect.setSize(windowMinimumSize()); - setGeometry(rect); - } + // setGeometry() will take care of minimum and maximum size constraints + setGeometry(windowGeometry()); + m_nonClientArea->propagateSizeHints(); +} + +void QWasmWindow::setOpacity(qreal level) +{ + m_qtWindow["style"].set("opacity", qBound(0.0, level, 1.0)); } void QWasmWindow::invalidate() { - m_compositor->requestUpdateWindow(this); + m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size())); } void QWasmWindow::onActivationChanged(bool active) @@ -371,10 +395,16 @@ void QWasmWindow::onActivationChanged(bool active) void QWasmWindow::setWindowFlags(Qt::WindowFlags flags) { + if (flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint) + || flags.testFlag(Qt::WindowStaysOnBottomHint) + != m_flags.testFlag(Qt::WindowStaysOnBottomHint)) { + onPositionPreferenceChanged(positionPreferenceFromWindowFlags(flags)); + } m_flags = flags; - dom::syncCSSClassWith(m_qtWindow, "has-frame", hasFrame()); - dom::syncCSSClassWith(m_qtWindow, "has-shadow", !flags.testFlag(Qt::NoDropShadowWindowHint)); - dom::syncCSSClassWith(m_qtWindow, "has-title", flags.testFlag(Qt::WindowTitleHint)); + 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()); dom::syncCSSClassWith(m_qtWindow, "transparent-for-input", flags.testFlag(Qt::WindowTransparentForInput)); @@ -384,6 +414,10 @@ void QWasmWindow::setWindowFlags(Qt::WindowFlags flags) void QWasmWindow::setWindowState(Qt::WindowStates newState) { + // Child windows can not have window states other than Qt::WindowActive + if (parent()) + newState &= Qt::WindowActive; + const Qt::WindowStates oldState = m_state; if (newState.testFlag(Qt::WindowMinimized)) { @@ -436,7 +470,7 @@ void QWasmWindow::applyWindowState() else newGeom = normalGeometry(); - dom::syncCSSClassWith(m_qtWindow, "has-frame", hasFrame()); + dom::syncCSSClassWith(m_qtWindow, "has-border", hasBorder()); dom::syncCSSClassWith(m_qtWindow, "maximized", isMaximized); m_nonClientArea->titleBar()->setRestoreVisible(isMaximized); @@ -447,6 +481,12 @@ void QWasmWindow::applyWindowState() setGeometry(newGeom); } +void QWasmWindow::commitParent(QWasmWindowTreeNode *parent) +{ + onParentChanged(m_commitedParent, parent, positionPreferenceFromWindowFlags(window()->flags())); + m_commitedParent = parent; +} + bool QWasmWindow::processKey(const KeyEvent &event) { constexpr bool ProceedToNativeEvent = false; @@ -461,7 +501,7 @@ bool QWasmWindow::processKey(const KeyEvent &event) const auto result = QWindowSystemInterface::handleKeyEvent( 0, event.type == EventType::KeyDown ? QEvent::KeyPress : QEvent::KeyRelease, event.key, - event.modifiers, event.text); + event.modifiers, event.text, event.autoRepeat); return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded ? ProceedToNativeEvent : result; @@ -469,13 +509,13 @@ bool QWasmWindow::processKey(const KeyEvent &event) bool QWasmWindow::processPointer(const PointerEvent &event) { - if (event.pointerType != PointerType::Mouse) + if (event.pointerType != PointerType::Mouse && event.pointerType != PointerType::Pen) return false; switch (event.type) { case EventType::PointerEnter: { const auto pointInScreen = platformScreen()->mapFromLocal( - dom::mapPoint(event.target, platformScreen()->element(), event.localPoint)); + dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint)); QWindowSystemInterface::handleEnterEvent( window(), m_window->mapFromGlobal(pointInScreen), pointInScreen); break; @@ -490,30 +530,6 @@ bool QWasmWindow::processPointer(const PointerEvent &event) return false; } -bool QWasmWindow::processDrop(const DragEvent &event) -{ - m_dropDataReadCancellationFlag = qstdweb::readDataTransfer( - event.dataTransfer, - [](QByteArray fileContent) { - QImage image; - image.loadFromData(fileContent, nullptr); - return image; - }, - [this, event](std::unique_ptr<QMimeData> data) { - QWindowSystemInterface::handleDrag(window(), data.get(), event.pointInPage, - event.dropAction, event.mouseButton, - event.modifiers); - - QWindowSystemInterface::handleDrop(window(), data.get(), event.pointInPage, - event.dropAction, event.mouseButton, - event.modifiers); - - QWindowSystemInterface::handleDrag(window(), nullptr, QPoint(), Qt::IgnoreAction, - {}, {}); - }); - return true; -} - bool QWasmWindow::processWheel(const WheelEvent &event) { // Web scroll deltas are inverted from Qt deltas - negate. @@ -529,13 +545,13 @@ bool QWasmWindow::processWheel(const WheelEvent &event) })(); const auto pointInScreen = platformScreen()->mapFromLocal( - dom::mapPoint(event.target, platformScreen()->element(), event.localPoint)); + dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint)); return QWindowSystemInterface::handleWheelEvent( - window(), QWasmIntegration::getTimestamp(), mapFromGlobal(pointInScreen), pointInScreen, - event.delta * scrollFactor, event.delta * scrollFactor, event.modifiers, - Qt::NoScrollPhase, Qt::MouseEventNotSynthesized, - event.webkitDirectionInvertedFromDevice); + window(), QWasmIntegration::getTimestamp(), window()->mapFromGlobal(pointInScreen), + pointInScreen, (event.delta * scrollFactor).toPoint(), + (event.delta * scrollFactor).toPoint(), event.modifiers, Qt::NoScrollPhase, + Qt::MouseEventNotSynthesized, event.webkitDirectionInvertedFromDevice); } QRect QWasmWindow::normalGeometry() const @@ -550,13 +566,28 @@ 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 { - return !m_state.testFlag(Qt::WindowFullScreen) && !m_flags.testFlag(Qt::FramelessWindowHint) - && !windowIsPopupType(m_flags); + return !m_flags.testFlag(Qt::FramelessWindowHint); +} + +bool QWasmWindow::hasBorder() const +{ + return hasFrame() && !m_state.testFlag(Qt::WindowFullScreen) && !m_flags.testFlag(Qt::SubWindow) + && !windowIsPopupType(m_flags) && !parent(); +} + +bool QWasmWindow::hasTitleBar() const +{ + return hasBorder() && m_flags.testFlag(Qt::WindowTitleHint); +} + +bool QWasmWindow::hasShadow() const +{ + return hasBorder() && !m_flags.testFlag(Qt::NoDropShadowWindowHint); } bool QWasmWindow::hasMaximizeButton() const @@ -580,10 +611,11 @@ void QWasmWindow::requestActivateWindow() return; } - if (window()->isTopLevel()) - raise(); + raise(); + setAsActiveNode(); - m_canvas.call<void>("focus"); + if (!QWasmIntegration::get()->inputContext()) + m_canvas.call<void>("focus"); QPlatformWindow::requestActivateWindow(); } @@ -628,9 +660,41 @@ void QWasmWindow::setMask(const QRegion ®ion) m_qtWindow["style"].set("clipPath", emscripten::val(cssClipPath.str())); } +void QWasmWindow::setParent(const QPlatformWindow *) +{ + commitParent(parentNode()); +} + std::string QWasmWindow::canvasSelector() const { return "!qtwindow" + std::to_string(m_winId); } +emscripten::val QWasmWindow::containerElement() +{ + return m_windowContents; +} + +QWasmWindowTreeNode *QWasmWindow::parentNode() +{ + if (parent()) + return static_cast<QWasmWindow *>(parent()); + return platformScreen(); +} + +QWasmWindow *QWasmWindow::asWasmWindow() +{ + return this; +} + +void QWasmWindow::onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current, + QWasmWindowStack::PositionPreference positionPreference) +{ + if (previous) + previous->containerElement().call<void>("removeChild", m_qtWindow); + if (current) + current->containerElement().call<void>("appendChild", m_qtWindow); + QWasmWindowTreeNode::onParentChanged(previous, current, positionPreference); +} + QT_END_NAMESPACE |