summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/wasm/qwasmwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmwindow.cpp')
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp248
1 files changed, 148 insertions, 100 deletions
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 3772217cdb..b8197c5113 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,
@@ -56,6 +68,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
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,6 @@ 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) {
@@ -105,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)))
@@ -120,21 +124,48 @@ 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 (QWasmIntegration::get()->inputContext()) {
+ QWasmInputContext *wasmContext =
+ static_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()
@@ -160,15 +191,14 @@ void QWasmWindow::onCloseClicked()
void QWasmWindow::onNonClientAreaInteraction()
{
- if (!isActive())
- requestActivateWindow();
+ requestActivateWindow();
QGuiApplicationPrivate::instance()->closeAllPopups();
}
bool QWasmWindow::onNonClientEvent(const PointerEvent &event)
{
QPointF pointInScreen = platformScreen()->mapFromLocal(
- dom::mapPoint(event.target, platformScreen()->element(), event.localPoint));
+ dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint));
return QWindowSystemInterface::handleMouseEvent(
window(), QWasmIntegration::getTimestamp(), window()->mapFromGlobal(pointInScreen),
pointInScreen, event.mouseButtons, event.mouseButton,
@@ -176,46 +206,27 @@ bool QWasmWindow::onNonClientEvent(const PointerEvent &event)
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();
-
- const auto windowFlags = window()->flags();
- const bool shouldRestrictMinSize =
- !windowFlags.testFlag(Qt::FramelessWindowHint) && !windowIsPopupType(windowFlags);
- const bool isMinSizeUninitialized = window()->minimumSize() == QSize(0, 0);
-
- if (shouldRestrictMinSize && isMinSizeUninitialized)
- window()->setMinimumSize(QSize(minSizeForRegularWindows, minSizeForRegularWindows));
-
-
- const QSize minimumSize = windowMinimumSize();
- 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
@@ -254,21 +265,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()));
- result.setSize(
- result.size().expandedTo(windowMinimumSize()).boundedTo(windowMaximumSize()));
- 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");
@@ -306,6 +330,8 @@ void QWasmWindow::setVisible(bool visible)
m_compositor->requestUpdateWindow(this, QWasmCompositor::ExposeEventDelivery);
m_qtWindow["style"].set("display", visible ? "block" : "none");
+ if (window()->isActive())
+ m_canvas.call<void>("focus");
if (visible)
applyWindowState();
}
@@ -329,13 +355,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();
}
@@ -346,12 +374,8 @@ 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();
}
@@ -372,13 +396,16 @@ void QWasmWindow::onActivationChanged(bool active)
void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
{
- if (flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint))
- m_compositor->windowPositionPreferenceChanged(this, 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, "frameless", !hasFrame());
dom::syncCSSClassWith(m_qtWindow, "has-border", hasBorder());
dom::syncCSSClassWith(m_qtWindow, "has-shadow", hasShadow());
- dom::syncCSSClassWith(m_qtWindow, "has-title", flags.testFlag(Qt::WindowTitleHint));
- dom::syncCSSClassWith(m_qtWindow, "frameless", flags.testFlag(Qt::FramelessWindowHint));
+ dom::syncCSSClassWith(m_qtWindow, "has-title", hasTitleBar());
dom::syncCSSClassWith(m_qtWindow, "transparent-for-input",
flags.testFlag(Qt::WindowTransparentForInput));
@@ -455,6 +482,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;
@@ -477,13 +510,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;
@@ -498,30 +531,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.toPoint(), event.dropAction,
- event.mouseButton, event.modifiers);
-
- QWindowSystemInterface::handleDrop(window(), data.get(),
- event.pointInPage.toPoint(), 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.
@@ -537,7 +546,7 @@ 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(), window()->mapFromGlobal(pointInScreen),
@@ -561,16 +570,25 @@ void QWasmWindow::requestUpdate()
m_compositor->requestUpdateWindow(this, QWasmCompositor::UpdateRequestDelivery);
}
+bool QWasmWindow::hasFrame() const
+{
+ return !m_flags.testFlag(Qt::FramelessWindowHint);
+}
+
bool QWasmWindow::hasBorder() const
{
- return !m_state.testFlag(Qt::WindowFullScreen) && !m_flags.testFlag(Qt::FramelessWindowHint)
- && !windowIsPopupType(m_flags);
+ 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 !m_flags.testFlag(Qt::NoDropShadowWindowHint)
- && !m_flags.testFlag(Qt::FramelessWindowHint);
+ return hasBorder() && !m_flags.testFlag(Qt::NoDropShadowWindowHint);
}
bool QWasmWindow::hasMaximizeButton() const
@@ -594,10 +612,8 @@ void QWasmWindow::requestActivateWindow()
return;
}
- if (window()->isTopLevel()) {
- raise();
- m_compositor->setActive(this);
- }
+ raise();
+ setAsActiveNode();
if (!QWasmIntegration::get()->inputContext())
m_canvas.call<void>("focus");
@@ -645,9 +661,41 @@ void QWasmWindow::setMask(const QRegion &region)
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