summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt3
-rw-r--r--src/plugins/platforms/wasm/qwasmbase64iconstore.cpp40
-rw-r--r--src/plugins/platforms/wasm/qwasmbase64iconstore.h36
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp90
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h29
-rw-r--r--src/plugins/platforms/wasm/qwasmcssstyle.cpp70
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmdom.cpp36
-rw-r--r--src/plugins/platforms/wasm/qwasmdom.h29
-rw-r--r--src/plugins/platforms/wasm/qwasmevent.cpp1
-rw-r--r--src/plugins/platforms/wasm/qwasmevent.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp5
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp525
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h55
-rw-r--r--src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp436
-rw-r--r--src/plugins/platforms/wasm/qwasmwindownonclientarea.h212
16 files changed, 915 insertions, 654 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index 962c289640..17acc40289 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -15,10 +15,12 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
SOURCES
main.cpp
qwasmaccessibility.cpp qwasmaccessibility.h
+ qwasmbase64iconstore.cpp qwasmbase64iconstore.h
qwasmclipboard.cpp qwasmclipboard.h
qwasmcompositor.cpp qwasmcompositor.h
qwasmcssstyle.cpp qwasmcssstyle.h
qwasmcursor.cpp qwasmcursor.h
+ qwasmdom.cpp qwasmdom.h
qwasmevent.cpp qwasmevent.h
qwasmeventdispatcher.cpp qwasmeventdispatcher.h
qwasmeventtranslator.cpp qwasmeventtranslator.h
@@ -33,6 +35,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
qwasmstylepixmaps_p.h
qwasmtheme.cpp qwasmtheme.h
qwasmwindow.cpp qwasmwindow.h
+ qwasmwindownonclientarea.cpp qwasmwindownonclientarea.h
qwasminputcontext.cpp qwasminputcontext.h
qwasmdrag.cpp qwasmdrag.h
qwasmwindowstack.cpp qwasmwindowstack.h
diff --git a/src/plugins/platforms/wasm/qwasmbase64iconstore.cpp b/src/plugins/platforms/wasm/qwasmbase64iconstore.cpp
new file mode 100644
index 0000000000..8f05f082ea
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmbase64iconstore.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qwasmbase64iconstore.h"
+
+#include <QtCore/qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(Base64IconStore, globalWasmWindowIconStore);
+
+Base64IconStore::Base64IconStore()
+{
+ QString iconSources[static_cast<size_t>(IconType::Size)] = {
+ QStringLiteral(":/wasm-window/maximize.svg"), QStringLiteral(":/wasm-window/qtlogo.svg"),
+ QStringLiteral(":/wasm-window/restore.svg"), QStringLiteral(":/wasm-window/x.svg")
+ };
+
+ for (size_t iconType = static_cast<size_t>(IconType::First);
+ iconType < static_cast<size_t>(IconType::Size); ++iconType) {
+ QFile svgFile(iconSources[static_cast<size_t>(iconType)]);
+ if (!svgFile.open(QIODevice::ReadOnly))
+ Q_ASSERT(false); // A resource should always be opened.
+ m_storage[static_cast<size_t>(iconType)] = svgFile.readAll().toBase64();
+ }
+}
+
+Base64IconStore::~Base64IconStore() = default;
+
+Base64IconStore *Base64IconStore::get()
+{
+ return globalWasmWindowIconStore();
+}
+
+std::string_view Base64IconStore::getIcon(IconType type) const
+{
+ return m_storage[static_cast<size_t>(type)];
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmbase64iconstore.h b/src/plugins/platforms/wasm/qwasmbase64iconstore.h
new file mode 100644
index 0000000000..6150ea19da
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmbase64iconstore.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMBASE64IMAGESTORE_H
+#define QWASMBASE64IMAGESTORE_H
+
+#include <string_view>
+
+#include <QtCore/qtconfigmacros.h>
+
+QT_BEGIN_NAMESPACE
+class Base64IconStore
+{
+public:
+ enum class IconType {
+ Maximize,
+ First = Maximize,
+ QtLogo,
+ Restore,
+ X,
+ Size,
+ };
+
+ Base64IconStore();
+ ~Base64IconStore();
+
+ static Base64IconStore *get();
+
+ std::string_view getIcon(IconType type) const;
+
+private:
+ std::string m_storage[static_cast<size_t>(IconType::Size)];
+};
+
+QT_END_NAMESPACE
+#endif // QWASMBASE64IMAGESTORE_H
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index f4f2d1b11d..a530cd1f7a 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -44,7 +44,6 @@ EMSCRIPTEN_BINDINGS(qtMouseModule) {
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen),
- m_windowManipulation(screen),
m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)),
m_eventTranslator(std::make_unique<QWasmEventTranslator>())
{
@@ -374,10 +373,9 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
const auto pointInScreen = screen()->mapFromLocal(event.localPoint);
QWindow *const targetWindow = ([this, pointInScreen]() -> QWindow * {
- auto *targetWindow = m_mouseCaptureWindow != nullptr ? m_mouseCaptureWindow.get()
- : m_windowManipulation.operation() == WindowManipulation::Operation::None
- ? screen()->compositor()->windowAt(pointInScreen, 5)
- : nullptr;
+ auto *targetWindow = m_mouseCaptureWindow != nullptr
+ ? m_mouseCaptureWindow.get()
+ : windowAt(pointInScreen, 5);
return targetWindow ? targetWindow : m_lastMouseTargetWindow.get();
})();
@@ -403,20 +401,12 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
if (targetWindow)
targetWindow->requestActivate();
- m_windowManipulation.onPointerDown(event, targetWindow);
break;
}
case EventType::PointerUp:
{
screen()->element().call<void>("releasePointerCapture", event.pointerId);
- m_windowManipulation.onPointerUp(event);
- break;
- }
- case EventType::PointerMove: {
- m_windowManipulation.onPointerMove(event);
- if (m_windowManipulation.operation() != WindowManipulation::Operation::None)
- requestUpdate();
break;
}
case EventType::PointerEnter:
@@ -481,80 +471,6 @@ bool QWasmCompositor::deliverEventToTarget(const PointerEvent &event, QWindow *e
eventType, event.modifiers);
}
-QWasmCompositor::WindowManipulation::WindowManipulation(QWasmScreen *screen)
- : m_screen(screen)
-{
- Q_ASSERT(!!screen);
-}
-
-QWasmCompositor::WindowManipulation::Operation QWasmCompositor::WindowManipulation::operation() const
-{
- if (!m_state)
- return Operation::None;
- return Operation::Move;
-}
-
-void QWasmCompositor::WindowManipulation::onPointerDown(
- const PointerEvent& event, QWindow* windowAtPoint)
-{
- // Only one operation at a time.
- if (operation() != Operation::None)
- return;
-
- if (event.mouseButton != Qt::MouseButton::LeftButton)
- return;
-
- const bool isTargetWindowResizable =
- !windowAtPoint->windowStates().testFlag(Qt::WindowMaximized) &&
- !windowAtPoint->windowStates().testFlag(Qt::WindowFullScreen);
- if (!isTargetWindowResizable)
- return;
-
- const bool isTargetWindowBlocked =
- QGuiApplicationPrivate::instance()->isWindowBlocked(windowAtPoint);
- if (isTargetWindowBlocked)
- return;
-
- if (!asWasmWindow(windowAtPoint)->isPointOnTitle(event.pointInViewport))
- return;
-
- m_state.reset(new OperationState{ .pointerId = event.pointerId,
- .window = windowAtPoint,
- .lastPointInScreenCoords =
- m_screen->mapFromLocal(event.localPoint) });
-}
-
-void QWasmCompositor::WindowManipulation::onPointerMove(
- const PointerEvent& event)
-{
- if (operation() == Operation::None || event.pointerId != m_state->pointerId)
- return;
-
- switch (operation()) {
- case Operation::Move: {
- const QPoint targetPointClippedToScreen =
- m_screen->clipPoint(m_screen->mapFromLocal(event.localPoint));
- const QPoint difference = targetPointClippedToScreen - m_state->lastPointInScreenCoords;
-
- m_state->lastPointInScreenCoords = targetPointClippedToScreen;
-
- m_state->window->setPosition(m_state->window->position() + difference);
- break;
- }
- case Operation::None:
- Q_ASSERT(0);
- break;
- }
-}
-
-void QWasmCompositor::WindowManipulation::onPointerUp(const PointerEvent& event)
-{
- if (operation() == Operation::None || event.mouseButtons != 0 || event.pointerId != m_state->pointerId)
- return;
-
- m_state.reset();
-}
-
bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *emKeyEvent)
{
constexpr bool ProceedToNativeEvent = false;
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index 17fb25bdd0..2c0ab6ae26 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -65,34 +65,6 @@ public:
void handleBackingStoreFlush(QWindow *window);
private:
- class WindowManipulation {
- public:
- enum class Operation {
- None,
- Move,
- };
-
- WindowManipulation(QWasmScreen* screen);
-
- void onPointerDown(const PointerEvent& event, QWindow* windowAtPoint);
- void onPointerMove(const PointerEvent& event);
- void onPointerUp(const PointerEvent &event);
-
- Operation operation() const;
-
- private:
- struct OperationState
- {
- int pointerId;
- QPointer<QWindow> window;
- QPoint lastPointInScreenCoords;
- };
-
- QWasmScreen *m_screen;
-
- std::unique_ptr<OperationState> m_state;
- };
-
void frame(bool all, const QList<QWasmWindow *> &windows);
void onTopWindowChanged();
@@ -124,7 +96,6 @@ private:
void updateEnabledState();
- WindowManipulation m_windowManipulation;
QWasmWindowStack m_windowStack;
bool m_isEnabled = true;
diff --git a/src/plugins/platforms/wasm/qwasmcssstyle.cpp b/src/plugins/platforms/wasm/qwasmcssstyle.cpp
index f37124c834..2fc2527129 100644
--- a/src/plugins/platforms/wasm/qwasmcssstyle.cpp
+++ b/src/plugins/platforms/wasm/qwasmcssstyle.cpp
@@ -3,6 +3,8 @@
#include "qwasmcssstyle.h"
+#include "qwasmbase64iconstore.h"
+
#include <QtCore/qstring.h>
#include <QtCore/qfile.h>
@@ -117,6 +119,7 @@ const char *Style = R"css(
overflow: hidden;
height: 18px;
padding-bottom: 4px;
+ pointer-events: all;
}
.qt-window.has-title-bar .title-bar {
@@ -142,6 +145,10 @@ const char *Style = R"css(
display: flex;
}
+.title-bar div {
+ pointer-events: none;
+}
+
.title-bar .image-button {
width: 18px;
height: 18px;
@@ -151,7 +158,7 @@ const char *Style = R"css(
align-items: center;
}
-.title-bar .image-button span {
+.title-bar .image-button img {
width: 10px;
height: 10px;
user-select: none;
@@ -160,19 +167,19 @@ const char *Style = R"css(
background-size: 10px 10px;
}
-.title-bar .image-button span[qt-builtin-image-type=x] {
+.title-bar .image-button img[qt-builtin-image-type=x] {
background-image: url("data:image/svg+xml;base64,$close_icon");
}
-.title-bar .image-button span[qt-builtin-image-type=qt-logo] {
+.title-bar .image-button img[qt-builtin-image-type=qt-logo] {
background-image: url("qtlogo.svg");
}
-.title-bar .image-button span[qt-builtin-image-type=restore] {
+.title-bar .image-button img[qt-builtin-image-type=restore] {
background-image: url("data:image/svg+xml;base64,$restore_icon");
}
-.title-bar .image-button span[qt-builtin-image-type=maximize] {
+.title-bar .image-button img[qt-builtin-image-type=maximize] {
background-image: url("data:image/svg+xml;base64,$maximize_icon");
}
.title-bar .action-button {
@@ -180,60 +187,24 @@ const char *Style = R"css(
align-self: end;
}
-.qt-window.blocked .title-bar .action-button {
+.qt-window.blocked div {
pointer-events: none;
}
-.title-bar .action-button span {
+.title-bar .action-button img {
transition: filter 0.08s ease-out;
}
-.title-bar .action-button:hover span {
+.title-bar .action-button:hover img {
filter: invert(0.45);
}
-.title-bar .action-button:active span {
+.title-bar .action-button:active img {
filter: invert(0.6);
}
)css";
-class Base64IconStore
-{
-public:
- enum class IconType {
- Maximize,
- First = Maximize,
- QtLogo,
- Restore,
- X,
- Size,
- };
-
- Base64IconStore()
- {
- QString iconSources[static_cast<size_t>(IconType::Size)] = {
- QStringLiteral(":/wasm-window/maximize.svg"),
- QStringLiteral(":/wasm-window/qtlogo.svg"), QStringLiteral(":/wasm-window/restore.svg"),
- QStringLiteral(":/wasm-window/x.svg")
- };
-
- for (size_t iconType = static_cast<size_t>(IconType::First);
- iconType < static_cast<size_t>(IconType::Size); ++iconType) {
- QFile svgFile(iconSources[static_cast<size_t>(iconType)]);
- if (!svgFile.open(QIODevice::ReadOnly))
- Q_ASSERT(false); // A resource should always be opened.
- m_storage[static_cast<size_t>(iconType)] = svgFile.readAll().toBase64();
- }
- }
- ~Base64IconStore() = default;
-
- std::string_view getIcon(IconType type) const { return m_storage[static_cast<size_t>(type)]; }
-
-private:
- std::string m_storage[static_cast<size_t>(IconType::Size)];
-};
-
void replace(std::string &str, const std::string &from, const std::string_view &to)
{
str.replace(str.find(from), from.length(), to);
@@ -242,13 +213,14 @@ void replace(std::string &str, const std::string &from, const std::string_view &
emscripten::val QWasmCSSStyle::createStyleElement(emscripten::val parent)
{
- Base64IconStore store;
auto document = parent["ownerDocument"];
auto screenStyle = document.call<emscripten::val>("createElement", emscripten::val("style"));
auto text = std::string(Style);
- replace(text, "$close_icon", store.getIcon(Base64IconStore::IconType::X));
- replace(text, "$restore_icon", store.getIcon(Base64IconStore::IconType::Restore));
- replace(text, "$maximize_icon", store.getIcon(Base64IconStore::IconType::Maximize));
+
+ using IconType = Base64IconStore::IconType;
+ replace(text, "$close_icon", Base64IconStore::get()->getIcon(IconType::X));
+ replace(text, "$restore_icon", Base64IconStore::get()->getIcon(IconType::Restore));
+ replace(text, "$maximize_icon", Base64IconStore::get()->getIcon(IconType::Maximize));
screenStyle.set("textContent", text);
return screenStyle;
diff --git a/src/plugins/platforms/wasm/qwasmcursor.h b/src/plugins/platforms/wasm/qwasmcursor.h
index 4a5cb23bf4..bb3e2a1346 100644
--- a/src/plugins/platforms/wasm/qwasmcursor.h
+++ b/src/plugins/platforms/wasm/qwasmcursor.h
@@ -5,6 +5,7 @@
#define QWASMCURSOR_H
#include <qpa/qplatformcursor.h>
+
QT_BEGIN_NAMESPACE
class QWasmCursor : public QPlatformCursor
diff --git a/src/plugins/platforms/wasm/qwasmdom.cpp b/src/plugins/platforms/wasm/qwasmdom.cpp
new file mode 100644
index 0000000000..1573b5bf9c
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmdom.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qwasmdom.h"
+
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace dom {
+void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
+{
+ if (flag) {
+ element["classList"].call<void>("add", emscripten::val(std::move(cssClassName)));
+ return;
+ }
+
+ element["classList"].call<void>("remove", emscripten::val(std::move(cssClassName)));
+}
+
+QPoint mapPoint(emscripten::val source, emscripten::val target, const QPoint &point)
+{
+ auto sourceBoundingRect =
+ QRectF::fromDOMRect(source.call<emscripten::val>("getBoundingClientRect"));
+ auto targetBoundingRect =
+ QRectF::fromDOMRect(target.call<emscripten::val>("getBoundingClientRect"));
+
+ auto offset = sourceBoundingRect.topLeft() - targetBoundingRect.topLeft();
+ return (point + offset).toPoint();
+}
+} // namespace dom
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmdom.h b/src/plugins/platforms/wasm/qwasmdom.h
new file mode 100644
index 0000000000..92f710a13d
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmdom.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMDOM_H
+#define QWASMDOM_H
+
+#include <QtCore/qtconfigmacros.h>
+
+#include <emscripten/val.h>
+
+#include <string>
+
+QT_BEGIN_NAMESPACE
+
+class QPoint;
+
+namespace dom {
+inline emscripten::val document()
+{
+ return emscripten::val::global("document");
+}
+
+void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag);
+
+QPoint mapPoint(emscripten::val source, emscripten::val target, const QPoint &point);
+} // namespace dom
+
+QT_END_NAMESPACE
+#endif // QWASMDOM_H
diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp
index 0e43a381bf..7276087033 100644
--- a/src/plugins/platforms/wasm/qwasmevent.cpp
+++ b/src/plugins/platforms/wasm/qwasmevent.cpp
@@ -39,6 +39,7 @@ std::optional<PointerEvent> PointerEvent::fromWeb(emscripten::val event)
return std::nullopt;
ret.type = *eventType;
+ ret.currentTarget = event["currentTarget"];
ret.pointerType = event["pointerType"].as<std::string>() == "mouse" ?
PointerType::Mouse : PointerType::Other;
ret.mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());
diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h
index 26b6b5f22a..7fe68ce6c8 100644
--- a/src/plugins/platforms/wasm/qwasmevent.h
+++ b/src/plugins/platforms/wasm/qwasmevent.h
@@ -110,6 +110,7 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>(
struct Q_CORE_EXPORT Event
{
EventType type;
+ emscripten::val currentTarget = emscripten::val::undefined();
};
struct Q_CORE_EXPORT MouseEvent : public Event
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 366d495089..e50afe652f 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -230,9 +230,8 @@ QPoint QWasmScreen::mapFromLocal(const QPoint &p) const
QPoint QWasmScreen::clipPoint(const QPoint &p) const
{
- return QPoint(
- std::max(screen()->geometry().left(), std::min(screen()->geometry().right(), p.x())),
- std::max(screen()->geometry().top(), std::min(screen()->geometry().bottom(), p.y())));
+ return QPoint(qBound(screen()->geometry().left(), p.x(), screen()->geometry().right()),
+ qBound(screen()->geometry().top(), p.y(), screen()->geometry().bottom()));
}
void QWasmScreen::invalidateSize()
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index e38335f81c..43fc151d72 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -9,6 +9,8 @@
#include <QtGui/qopenglfunctions.h>
#include <QBuffer>
+#include "qwasmbase64iconstore.h"
+#include "qwasmdom.h"
#include "qwasmwindow.h"
#include "qwasmscreen.h"
#include "qwasmstylepixmaps_p.h"
@@ -26,418 +28,24 @@ QT_BEGIN_NAMESPACE
Q_GUI_EXPORT int qt_defaultDpiX();
-namespace {
-enum class IconType {
- Maximize,
- First = Maximize,
- QtLogo,
- Restore,
- X,
- Size,
-};
-
-void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
-{
- if (flag) {
- element["classList"].call<void>("add", emscripten::val(std::move(cssClassName)));
- return;
- }
-
- element["classList"].call<void>("remove", emscripten::val(std::move(cssClassName)));
-}
-
-} // namespace
-
-class QWasmWindow::Resizer
-{
-public:
- class ResizerElement
- {
- public:
- static constexpr const char *cssClassNameForEdges(Qt::Edges edges)
- {
- switch (edges) {
- case Qt::TopEdge | Qt::LeftEdge:;
- return "nw";
- case Qt::TopEdge:
- return "n";
- case Qt::TopEdge | Qt::RightEdge:
- return "ne";
- case Qt::LeftEdge:
- return "w";
- case Qt::RightEdge:
- return "e";
- case Qt::BottomEdge | Qt::LeftEdge:
- return "sw";
- case Qt::BottomEdge:
- return "s";
- case Qt::BottomEdge | Qt::RightEdge:
- return "se";
- default:
- Q_ASSERT(false); // notreached
- return "";
- }
- }
-
- ResizerElement(emscripten::val parentElement, Qt::Edges edges, Resizer *resizer)
- : m_element(emscripten::val::global("document")
- .call<emscripten::val>("createElement", emscripten::val("div"))),
- m_edges(edges),
- m_resizer(resizer)
- {
- Q_ASSERT_X(m_resizer, Q_FUNC_INFO, "Resizer cannot be null");
-
- m_element["classList"].call<void>("add", emscripten::val("resize-outline"));
- m_element["classList"].call<void>("add", emscripten::val(cssClassNameForEdges(edges)));
-
- parentElement.call<void>("appendChild", m_element);
-
- m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
- m_element, "pointerdown", [this](emscripten::val event) {
- if (!onPointerDown(*PointerEvent::fromWeb(event)))
- return;
- m_resizer->onInteraction();
- event.call<void>("preventDefault");
- event.call<void>("stopPropagation");
- });
- m_mouseDragEvent = std::make_unique<qstdweb::EventCallback>(
- m_element, "pointermove", [this](emscripten::val event) {
- if (onPointerMove(*PointerEvent::fromWeb(event))) {
- event.call<void>("preventDefault");
- event.call<void>("stopPropagation");
- }
- });
- m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
- m_element, "pointerup", [this](emscripten::val event) {
- if (onPointerUp(*PointerEvent::fromWeb(event))) {
- event.call<void>("preventDefault");
- event.call<void>("stopPropagation");
- }
- });
- }
-
- ~ResizerElement()
- {
- m_element["parentElement"].call<emscripten::val>("removeChild", m_element);
- }
- ResizerElement(const ResizerElement &other) = delete;
- ResizerElement(ResizerElement &&other) = default;
- ResizerElement &operator=(const ResizerElement &other) = delete;
- ResizerElement &operator=(ResizerElement &&other) = delete;
-
- bool onPointerDown(const PointerEvent &event)
- {
- if (event.pointerType != PointerType::Mouse)
- return false;
-
- m_element.call<void>("setPointerCapture", event.pointerId);
- m_capturedPointerId = event.pointerId;
-
- m_resizer->startResize(m_edges, event.pointInPage);
- return true;
- }
-
- bool onPointerMove(const PointerEvent &event)
- {
- if (m_capturedPointerId != event.pointerId)
- return false;
-
- m_resizer->continueResize(event.pointInPage);
- return true;
- }
-
- bool onPointerUp(const PointerEvent &event)
- {
- if (m_capturedPointerId != event.pointerId)
- return false;
-
- m_resizer->finishResize();
- m_element.call<void>("releasePointerCapture", event.pointerId);
- m_capturedPointerId = -1;
- return true;
- }
-
- private:
- emscripten::val m_element;
-
- int m_capturedPointerId = -1;
-
- const Qt::Edges m_edges;
-
- Resizer *m_resizer;
-
- std::unique_ptr<qstdweb::EventCallback> m_mouseDownEvent;
- std::unique_ptr<qstdweb::EventCallback> m_mouseDragEvent;
- std::unique_ptr<qstdweb::EventCallback> m_mouseUpEvent;
- };
-
- using ClickCallback = std::function<void()>;
-
- Resizer(QWasmWindow *window, emscripten::val parentElement) : m_window(window)
- {
- Q_ASSERT_X(m_window, Q_FUNC_INFO, "Window must not be null");
-
- constexpr std::array<int, 8> ResizeEdges = { Qt::TopEdge | Qt::LeftEdge,
- Qt::TopEdge,
- Qt::TopEdge | Qt::RightEdge,
- Qt::LeftEdge,
- Qt::RightEdge,
- Qt::BottomEdge | Qt::LeftEdge,
- Qt::BottomEdge,
- Qt::BottomEdge | Qt::RightEdge };
- std::transform(std::begin(ResizeEdges), std::end(ResizeEdges),
- std::back_inserter(m_elements), [parentElement, this](int edges) {
- return std::make_unique<ResizerElement>(parentElement,
- Qt::Edges::fromInt(edges), this);
- });
- }
-
- ~Resizer() = default;
-
-private:
- void onInteraction() { m_window->onInteraction(); }
-
- void startResize(Qt::Edges resizeEdges, const QPoint &origin)
- {
- Q_ASSERT_X(!m_currentResizeData, Q_FUNC_INFO, "Another resize in progress");
-
- const QWindow *window = m_window->window();
- // TODO(mikolajboc): Implement system resize
- // .m_originInScreenCoords = m_systemDragInitData.lastMouseMovePoint,
- m_currentResizeData.reset(new ResizeData{
- .edges = resizeEdges,
- .originInScreenCoords = origin,
- .initialWindowBounds = window->geometry(),
- .minShrink = QPoint(window->minimumWidth() - window->geometry().width(),
- window->minimumHeight() - window->geometry().height()),
- .maxGrow = QPoint(window->maximumWidth() - window->geometry().width(),
- window->maximumHeight() - window->geometry().height()) });
- }
-
- void continueResize(const QPoint &point)
- {
- const auto amount = point - m_currentResizeData->originInScreenCoords;
- const QPoint cappedGrowVector(
- std::min(m_currentResizeData->maxGrow.x(),
- std::max(m_currentResizeData->minShrink.x(),
- (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -amount.x()
- : (m_currentResizeData->edges & Qt::Edge::RightEdge)
- ? amount.x()
- : 0)),
- std::min(m_currentResizeData->maxGrow.y(),
- std::max(m_currentResizeData->minShrink.y(),
- (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -amount.y()
- : (m_currentResizeData->edges & Qt::Edge::BottomEdge)
- ? amount.y()
- : 0)));
-
- auto bounds = m_currentResizeData->initialWindowBounds.adjusted(
- (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -cappedGrowVector.x() : 0,
- (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -cappedGrowVector.y() : 0,
- (m_currentResizeData->edges & Qt::Edge::RightEdge) ? cappedGrowVector.x() : 0,
- (m_currentResizeData->edges & Qt::Edge::BottomEdge) ? cappedGrowVector.y() : 0);
- bounds.setY(std::max(m_window->screen()->geometry().y() + m_window->frameMargins().top(),
- bounds.y()));
- m_window->setGeometry(std::move(bounds));
- }
-
- void finishResize()
- {
- Q_ASSERT_X(m_currentResizeData, Q_FUNC_INFO, "No resize in progress");
- m_currentResizeData.reset();
- }
-
- struct ResizeData
- {
- Qt::Edges edges = Qt::Edges::fromInt(0);
- QPoint originInScreenCoords;
- QRect initialWindowBounds;
- QPoint minShrink;
- QPoint maxGrow;
- };
- std::unique_ptr<ResizeData> m_currentResizeData;
-
- QWasmWindow *m_window;
- std::vector<std::unique_ptr<ResizerElement>> m_elements;
-};
-
-class QWasmWindow::WebImageButton
-{
-public:
- class Callbacks
- {
- public:
- Callbacks() = default;
- Callbacks(std::function<void()> onInteraction, std::function<void()> onClick)
- : m_onInteraction(std::move(onInteraction)), m_onClick(std::move(onClick))
- {
- Q_ASSERT_X(!!m_onInteraction == !!m_onClick, Q_FUNC_INFO,
- "Both callbacks need to be either null or non-null");
- }
- ~Callbacks() = default;
-
- Callbacks(const Callbacks &) = delete;
- Callbacks(Callbacks &&) = default;
- Callbacks &operator=(const Callbacks &) = delete;
- Callbacks &operator=(Callbacks &&) = default;
-
- operator bool() const { return !!m_onInteraction; }
-
- void onInteraction() { return m_onInteraction(); }
- void onClick() { return m_onClick(); }
-
- private:
- std::function<void()> m_onInteraction;
- std::function<void()> m_onClick;
- };
-
- WebImageButton()
- : m_containerElement(
- emscripten::val::global("document")
- .call<emscripten::val>("createElement", emscripten::val("div"))),
- m_imageHolderElement(
- emscripten::val::global("document")
- .call<emscripten::val>("createElement", emscripten::val("span")))
- {
- m_imageHolderElement.set("draggable", false);
-
- m_containerElement["classList"].call<void>("add", emscripten::val("image-button"));
- m_containerElement.call<void>("appendChild", m_imageHolderElement);
- }
-
- ~WebImageButton() = default;
-
- void setCallbacks(Callbacks callbacks)
- {
- if (callbacks) {
- if (!m_webClickEventCallback) {
- m_webMouseDownEventCallback = std::make_unique<qstdweb::EventCallback>(
- m_containerElement, "mousedown", [this](emscripten::val event) {
- event.call<void>("preventDefault");
- event.call<void>("stopPropagation");
- m_callbacks.onInteraction();
- });
- m_webClickEventCallback = std::make_unique<qstdweb::EventCallback>(
- m_containerElement, "click",
- [this](emscripten::val) { m_callbacks.onClick(); });
- }
- } else {
- m_webMouseDownEventCallback.reset();
- m_webClickEventCallback.reset();
- }
- syncCSSClassWith(m_containerElement, "action-button", !!callbacks);
- m_callbacks = std::move(callbacks);
- }
-
- void setImage(std::string_view imageData, std::string_view format)
- {
- m_imageHolderElement.call<void>("removeAttribute",
- emscripten::val("qt-builtin-image-type"));
- m_imageHolderElement["style"].set("backgroundImage",
- "url('data:image/" + std::string(format) + ";base64,"
- + std::string(imageData) + "')");
- }
-
- void setImage(IconType type)
- {
- m_imageHolderElement["style"].set("backgroundImage", emscripten::val::undefined());
- const auto imageType = ([type]() {
- switch (type) {
- case IconType::QtLogo:
- return "qt-logo";
- case IconType::X:
- return "x";
- case IconType::Restore:
- return "restore";
- case IconType::Maximize:
- return "maximize";
- default:
- return "err";
- }
- })();
- m_imageHolderElement.call<void>("setAttribute", emscripten::val("qt-builtin-image-type"),
- emscripten::val(imageType));
- }
-
- void setVisible(bool visible)
- {
- m_containerElement["style"].set("display", visible ? "flex" : "none");
- }
-
- emscripten::val htmlElement() const { return m_containerElement; }
- emscripten::val imageElement() const { return m_imageHolderElement; }
-
-private:
- emscripten::val m_containerElement;
- emscripten::val m_imageHolderElement;
- std::unique_ptr<qstdweb::EventCallback> m_webMouseMoveEventCallback;
- std::unique_ptr<qstdweb::EventCallback> m_webMouseDownEventCallback;
- std::unique_ptr<qstdweb::EventCallback> m_webClickEventCallback;
-
- Callbacks m_callbacks;
-};
-
QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore)
: QPlatformWindow(w),
m_window(w),
m_compositor(compositor),
m_backingStore(backingStore),
- m_document(emscripten::val::global("document")),
+ m_document(dom::document()),
m_qtWindow(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
m_windowContents(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
- m_titleBar(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
- m_label(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
m_canvasContainer(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
m_canvas(m_document.call<emscripten::val>("createElement", emscripten::val("canvas")))
{
m_qtWindow.set("className", "qt-window");
m_qtWindow["style"].set("display", std::string("none"));
- m_qtWindow.call<void>("appendChild", m_windowContents);
+ m_nonClientArea = std::make_unique<NonClientArea>(this, m_qtWindow);
+ m_nonClientArea->titleBar()->setTitle(window()->title());
- m_resizer = std::make_unique<Resizer>(this, m_qtWindow);
-
- m_icon = std::make_unique<WebImageButton>();
- m_icon->setImage(IconType::QtLogo);
-
- m_titleBar.call<void>("appendChild", m_icon->htmlElement());
- m_titleBar.set("className", "title-bar");
-
- auto spacer = m_document.call<emscripten::val>("createElement", emscripten::val("div"));
- spacer["style"].set("width", "4px");
- m_titleBar.call<void>("appendChild", spacer);
-
- m_label.set("innerText", emscripten::val(window()->title().toStdString()));
- m_label.set("className", "window-name");
-
- m_titleBar.call<void>("appendChild", m_label);
-
- spacer = m_document.call<emscripten::val>("createElement", emscripten::val("div"));
- spacer.set("className", "spacer");
- m_titleBar.call<void>("appendChild", spacer);
-
- m_restore = std::make_unique<WebImageButton>();
- m_restore->setImage(IconType::Restore);
- m_restore->setCallbacks(WebImageButton::Callbacks([this]() { onInteraction(); },
- [this]() { onRestoreClicked(); }));
-
- m_titleBar.call<void>("appendChild", m_restore->htmlElement());
-
- m_maximize = std::make_unique<WebImageButton>();
- m_maximize->setImage(IconType::Maximize);
- m_maximize->setCallbacks(WebImageButton::Callbacks([this]() { onInteraction(); },
- [this]() { onMaximizeClicked(); }));
-
- m_titleBar.call<void>("appendChild", m_maximize->htmlElement());
-
- m_close = std::make_unique<WebImageButton>();
- m_close->setImage(IconType::X);
- m_close->setCallbacks(WebImageButton::Callbacks([this]() { onInteraction(); },
- [this]() { onCloseClicked(); }));
-
- m_titleBar.call<void>("appendChild", m_close->htmlElement());
-
- m_windowContents.call<void>("appendChild", m_titleBar);
+ m_qtWindow.call<void>("appendChild", m_windowContents);
m_canvas["classList"].call<void>("add", emscripten::val("qt-window-content"));
@@ -468,6 +76,55 @@ QWasmWindow::~QWasmWindow()
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
}
+void QWasmWindow::onRestoreClicked()
+{
+ window()->setWindowState(Qt::WindowNoState);
+}
+
+void QWasmWindow::onMaximizeClicked()
+{
+ window()->setWindowState(Qt::WindowMaximized);
+}
+
+void QWasmWindow::onToggleMaximized()
+{
+ window()->setWindowState(m_state.testFlag(Qt::WindowMaximized) ? Qt::WindowNoState
+ : Qt::WindowMaximized);
+}
+
+void QWasmWindow::onCloseClicked()
+{
+ window()->close();
+}
+
+void QWasmWindow::onNonClientAreaInteraction()
+{
+ if (!isActive())
+ requestActivateWindow();
+}
+
+bool QWasmWindow::onNonClientEvent(const PointerEvent &event)
+{
+ QPoint pointInScreen = platformScreen()->mapFromLocal(
+ dom::mapPoint(event.currentTarget, platformScreen()->element(), event.localPoint));
+ return QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 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;
+ }
+ })(),
+ event.modifiers);
+}
+
void QWasmWindow::destroy()
{
m_qtWindow["parentElement"].call<emscripten::val>("removeChild", m_qtWindow);
@@ -541,6 +198,8 @@ void QWasmWindow::setGeometry(const QRect &rect)
screenGeometry.y() + margins.top()));
return result;
})();
+ m_nonClientArea->onClientAreaWidthChange(clientAreaRect.width());
+
const auto frameRect =
clientAreaRect
.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom())
@@ -591,12 +250,13 @@ bool QWasmWindow::isVisible() const
QMargins QWasmWindow::frameMargins() const
{
- const auto border = borderMargins();
- const auto titleBarBounds =
- QRectF::fromDOMRect(m_titleBar.call<emscripten::val>("getBoundingClientRect"));
-
- return QMarginsF(border.left(), border.top() + titleBarBounds.height(), border.right(),
- border.bottom())
+ const auto frameRect =
+ QRectF::fromDOMRect(m_qtWindow.call<emscripten::val>("getBoundingClientRect"));
+ const auto canvasRect =
+ QRectF::fromDOMRect(m_windowContents.call<emscripten::val>("getBoundingClientRect"));
+ return QMarginsF(canvasRect.left() - frameRect.left(), canvasRect.top() - frameRect.top(),
+ frameRect.right() - canvasRect.right(),
+ frameRect.bottom() - canvasRect.bottom())
.toMargins();
}
@@ -633,44 +293,6 @@ bool QWasmWindow::startSystemResize(Qt::Edges)
return false;
}
-void QWasmWindow::onRestoreClicked()
-{
- window()->setWindowState(Qt::WindowNoState);
-}
-
-void QWasmWindow::onMaximizeClicked()
-{
- window()->setWindowState(Qt::WindowMaximized);
-}
-
-void QWasmWindow::onCloseClicked()
-{
- window()->close();
-}
-
-void QWasmWindow::onInteraction()
-{
- if (!isActive())
- requestActivateWindow();
-}
-
-QMarginsF QWasmWindow::borderMargins() const
-{
- const auto frameRect =
- QRectF::fromDOMRect(m_qtWindow.call<emscripten::val>("getBoundingClientRect"));
- const auto canvasRect =
- QRectF::fromDOMRect(m_windowContents.call<emscripten::val>("getBoundingClientRect"));
- return QMarginsF(canvasRect.left() - frameRect.left(), canvasRect.top() - frameRect.top(),
- frameRect.right() - canvasRect.right(),
- frameRect.bottom() - canvasRect.bottom());
-}
-
-bool QWasmWindow::isPointOnTitle(QPoint globalPoint) const
-{
- return QRectF::fromDOMRect(m_titleBar.call<emscripten::val>("getBoundingClientRect"))
- .contains(globalPoint);
-}
-
void QWasmWindow::invalidate()
{
m_compositor->requestUpdateWindow(this);
@@ -678,13 +300,13 @@ void QWasmWindow::invalidate()
void QWasmWindow::onActivationChanged(bool active)
{
- syncCSSClassWith(m_qtWindow, "inactive", !active);
+ dom::syncCSSClassWith(m_qtWindow, "inactive", !active);
}
void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
{
m_flags = flags;
- syncCSSClassWith(m_qtWindow, "has-title-bar", hasTitleBar());
+ dom::syncCSSClassWith(m_qtWindow, "has-title-bar", hasTitleBar());
}
void QWasmWindow::setWindowState(Qt::WindowStates newState)
@@ -709,7 +331,7 @@ void QWasmWindow::setWindowState(Qt::WindowStates newState)
void QWasmWindow::setWindowTitle(const QString &title)
{
- m_label.set("innerText", emscripten::val(title.toStdString()));
+ m_nonClientArea->titleBar()->setTitle(title);
}
void QWasmWindow::setWindowIcon(const QIcon &icon)
@@ -717,14 +339,15 @@ void QWasmWindow::setWindowIcon(const QIcon &icon)
const auto dpi = screen()->devicePixelRatio();
auto pixmap = icon.pixmap(10 * dpi, 10 * dpi);
if (pixmap.isNull()) {
- m_icon->setImage(IconType::QtLogo);
+ m_nonClientArea->titleBar()->setIcon(
+ Base64IconStore::get()->getIcon(Base64IconStore::IconType::QtLogo), "svg+xml");
return;
}
QByteArray bytes;
QBuffer buffer(&bytes);
pixmap.save(&buffer, "png");
- m_icon->setImage(bytes.toBase64().toStdString(), "png");
+ m_nonClientArea->titleBar()->setIcon(bytes.toBase64().toStdString(), "png");
}
void QWasmWindow::applyWindowState()
@@ -740,11 +363,11 @@ void QWasmWindow::applyWindowState()
else
newGeom = normalGeometry();
- syncCSSClassWith(m_qtWindow, "has-title-bar", hasTitleBar());
- syncCSSClassWith(m_qtWindow, "maximized", isMaximized);
+ dom::syncCSSClassWith(m_qtWindow, "has-title-bar", hasTitleBar());
+ dom::syncCSSClassWith(m_qtWindow, "maximized", isMaximized);
- m_restore->setVisible(isMaximized);
- m_maximize->setVisible(!isMaximized);
+ m_nonClientArea->titleBar()->setRestoreVisible(isMaximized);
+ m_nonClientArea->titleBar()->setMaximizeVisible(!isMaximized);
if (isVisible())
QWindowSystemInterface::handleWindowStateChanged(window(), m_state, m_previousWindowState);
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 8359391825..d5a902e8b0 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -10,6 +10,7 @@
#include "qwasmbackingstore.h"
#include "qwasmscreen.h"
#include "qwasmcompositor.h"
+#include "qwasmwindownonclientarea.h"
#include <QtCore/private/qstdweb_p.h>
#include "QtGui/qopenglcontext.h"
@@ -24,21 +25,26 @@ class QWasmWindow final : public QPlatformWindow
public:
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore);
~QWasmWindow() final;
- void destroy();
-
- void initialize() override;
+ void destroy();
void paint();
void setZOrder(int order);
void onActivationChanged(bool active);
+ bool isVisible() const;
+ void onNonClientAreaInteraction();
+ void onRestoreClicked();
+ void onMaximizeClicked();
+ void onToggleMaximized();
+ void onCloseClicked();
+ bool onNonClientEvent(const PointerEvent &event);
+
+ // QPlatformWindow:
+ void initialize() override;
void setGeometry(const QRect &) override;
void setVisible(bool visible) override;
- bool isVisible() const;
QMargins frameMargins() const override;
-
WId winId() const override;
-
void propagateSizeHints() override;
void raise() override;
void lower() override;
@@ -46,24 +52,19 @@ public:
qreal devicePixelRatio() const override;
void requestUpdate() override;
void requestActivateWindow() override;
-
- QWasmScreen *platformScreen() const;
- void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
- QWasmBackingStore *backingStore() const { return m_backingStore; }
- QWindow *window() const { return m_window; }
-
- bool startSystemResize(Qt::Edges edges) final;
-
- bool isPointOnTitle(QPoint point) const;
-
void setWindowFlags(Qt::WindowFlags flags) override;
void setWindowState(Qt::WindowStates state) override;
void setWindowTitle(const QString &title) override;
void setWindowIcon(const QIcon &icon) override;
- void applyWindowState();
bool setKeyboardGrabEnabled(bool) override { return false; }
bool setMouseGrabEnabled(bool grab) final;
bool windowEvent(QEvent *event) final;
+ bool startSystemResize(Qt::Edges edges) final;
+
+ QWasmScreen *platformScreen() const;
+ void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
+ QWasmBackingStore *backingStore() const { return m_backingStore; }
+ QWindow *window() const { return m_window; }
std::string canvasSelector() const;
emscripten::val context2d() { return m_context2d; }
@@ -71,18 +72,9 @@ public:
private:
friend class QWasmScreen;
- class Resizer;
- class WebImageButton;
-
- QMarginsF borderMargins() const;
-
- void onRestoreClicked();
- void onMaximizeClicked();
- void onCloseClicked();
- void onInteraction();
-
void invalidate();
bool hasTitleBar() const;
+ void applyWindowState();
QWindow *m_window = nullptr;
QWasmCompositor *m_compositor = nullptr;
@@ -92,18 +84,11 @@ private:
emscripten::val m_document;
emscripten::val m_qtWindow;
emscripten::val m_windowContents;
- emscripten::val m_titleBar;
- emscripten::val m_label;
emscripten::val m_canvasContainer;
emscripten::val m_canvas;
emscripten::val m_context2d = emscripten::val::undefined();
- std::unique_ptr<Resizer> m_resizer;
-
- std::unique_ptr<WebImageButton> m_close;
- std::unique_ptr<WebImageButton> m_maximize;
- std::unique_ptr<WebImageButton> m_restore;
- std::unique_ptr<WebImageButton> m_icon;
+ std::unique_ptr<NonClientArea> m_nonClientArea;
Qt::WindowStates m_state = Qt::WindowNoState;
Qt::WindowStates m_previousWindowState = Qt::WindowNoState;
diff --git a/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp b/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp
new file mode 100644
index 0000000000..170cbbfa31
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp
@@ -0,0 +1,436 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qwasmwindownonclientarea.h"
+
+#include "qwasmbase64iconstore.h"
+#include "qwasmdom.h"
+#include "qwasmevent.h"
+#include "qwasmintegration.h"
+
+#include <qpa/qwindowsysteminterface.h>
+
+#include <QtCore/qassert.h>
+
+QT_BEGIN_NAMESPACE
+
+WebImageButton::Callbacks::Callbacks() = default;
+WebImageButton::Callbacks::Callbacks(std::function<void()> onInteraction,
+ std::function<void()> onClick)
+ : m_onInteraction(std::move(onInteraction)), m_onClick(std::move(onClick))
+{
+ Q_ASSERT_X(!!m_onInteraction == !!m_onClick, Q_FUNC_INFO,
+ "Both callbacks need to be either null or non-null");
+}
+WebImageButton::Callbacks::~Callbacks() = default;
+
+WebImageButton::Callbacks::Callbacks(Callbacks &&) = default;
+WebImageButton::Callbacks &WebImageButton::Callbacks::operator=(Callbacks &&) = default;
+
+void WebImageButton::Callbacks::onInteraction()
+{
+ return m_onInteraction();
+}
+
+void WebImageButton::Callbacks::onClick()
+{
+ return m_onClick();
+}
+
+WebImageButton::WebImageButton()
+ : m_containerElement(
+ dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
+ m_imgElement(dom::document().call<emscripten::val>("createElement", emscripten::val("img")))
+{
+ m_imgElement.set("draggable", false);
+
+ m_containerElement["classList"].call<void>("add", emscripten::val("image-button"));
+ m_containerElement.call<void>("appendChild", m_imgElement);
+}
+
+WebImageButton::~WebImageButton() = default;
+
+void WebImageButton::setCallbacks(Callbacks callbacks)
+{
+ if (callbacks) {
+ if (!m_webClickEventCallback) {
+ m_webMouseDownEventCallback = std::make_unique<qstdweb::EventCallback>(
+ m_containerElement, "mousedown", [this](emscripten::val event) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ m_callbacks.onInteraction();
+ });
+ m_webClickEventCallback = std::make_unique<qstdweb::EventCallback>(
+ m_containerElement, "click",
+ [this](emscripten::val) { m_callbacks.onClick(); });
+ }
+ } else {
+ m_webMouseDownEventCallback.reset();
+ m_webClickEventCallback.reset();
+ }
+ dom::syncCSSClassWith(m_containerElement, "action-button", !!callbacks);
+ m_callbacks = std::move(callbacks);
+}
+
+void WebImageButton::setImage(std::string_view imageData, std::string_view format)
+{
+ m_imgElement.set("src",
+ "data:image/" + std::string(format) + ";base64," + std::string(imageData));
+}
+
+void WebImageButton::setVisible(bool visible)
+{
+ m_containerElement["style"].set("display", visible ? "flex" : "none");
+}
+
+Resizer::ResizerElement::ResizerElement(emscripten::val parentElement, Qt::Edges edges,
+ Resizer *resizer)
+ : m_element(dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
+ m_edges(edges),
+ m_resizer(resizer)
+{
+ Q_ASSERT_X(m_resizer, Q_FUNC_INFO, "Resizer cannot be null");
+
+ m_element["classList"].call<void>("add", emscripten::val("resize-outline"));
+ m_element["classList"].call<void>("add", emscripten::val(cssClassNameForEdges(edges)));
+
+ parentElement.call<void>("appendChild", m_element);
+
+ m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointerdown", [this](emscripten::val event) {
+ if (!onPointerDown(*PointerEvent::fromWeb(event)))
+ return;
+ m_resizer->onInteraction();
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ });
+ m_mouseDragEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointermove", [this](emscripten::val event) {
+ if (onPointerMove(*PointerEvent::fromWeb(event))) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ }
+ });
+ m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointerup", [this](emscripten::val event) {
+ if (onPointerUp(*PointerEvent::fromWeb(event))) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ }
+ });
+}
+
+Resizer::ResizerElement::~ResizerElement()
+{
+ m_element["parentElement"].call<emscripten::val>("removeChild", m_element);
+}
+
+Resizer::ResizerElement::ResizerElement(ResizerElement &&other) = default;
+
+bool Resizer::ResizerElement::onPointerDown(const PointerEvent &event)
+{
+ if (event.pointerType != PointerType::Mouse)
+ return false;
+
+ m_element.call<void>("setPointerCapture", event.pointerId);
+ m_capturedPointerId = event.pointerId;
+
+ m_resizer->startResize(m_edges, event);
+ return true;
+}
+
+bool Resizer::ResizerElement::onPointerMove(const PointerEvent &event)
+{
+ if (m_capturedPointerId != event.pointerId)
+ return false;
+
+ m_resizer->continueResize(event);
+ return true;
+}
+
+bool Resizer::ResizerElement::onPointerUp(const PointerEvent &event)
+{
+ if (m_capturedPointerId != event.pointerId)
+ return false;
+
+ m_resizer->finishResize();
+ m_element.call<void>("releasePointerCapture", event.pointerId);
+ m_capturedPointerId = -1;
+ return true;
+}
+
+Resizer::Resizer(QWasmWindow *window, emscripten::val parentElement)
+ : m_window(window), m_windowElement(parentElement)
+{
+ Q_ASSERT_X(m_window, Q_FUNC_INFO, "Window must not be null");
+
+ constexpr std::array<int, 8> ResizeEdges = { Qt::TopEdge | Qt::LeftEdge,
+ Qt::TopEdge,
+ Qt::TopEdge | Qt::RightEdge,
+ Qt::LeftEdge,
+ Qt::RightEdge,
+ Qt::BottomEdge | Qt::LeftEdge,
+ Qt::BottomEdge,
+ Qt::BottomEdge | Qt::RightEdge };
+ std::transform(std::begin(ResizeEdges), std::end(ResizeEdges), std::back_inserter(m_elements),
+ [parentElement, this](int edges) {
+ return std::make_unique<ResizerElement>(parentElement,
+ Qt::Edges::fromInt(edges), this);
+ });
+}
+
+Resizer::~Resizer() = default;
+
+void Resizer::onInteraction()
+{
+ m_window->onNonClientAreaInteraction();
+}
+
+void Resizer::startResize(Qt::Edges resizeEdges, const PointerEvent &event)
+{
+ Q_ASSERT_X(!m_currentResizeData, Q_FUNC_INFO, "Another resize in progress");
+
+ m_currentResizeData.reset(new ResizeData{
+ .edges = resizeEdges,
+ .originInScreenCoords = dom::mapPoint(
+ event.currentTarget, m_window->platformScreen()->element(), event.localPoint),
+ });
+
+ const auto *window = m_window->window();
+ m_currentResizeData->minShrink = QPoint(window->minimumWidth() - window->geometry().width(),
+ window->minimumHeight() - window->geometry().height());
+
+ const auto frameRect =
+ QRectF::fromDOMRect(m_windowElement.call<emscripten::val>("getBoundingClientRect"));
+ const auto screenRect = QRectF::fromDOMRect(
+ m_window->platformScreen()->element().call<emscripten::val>("getBoundingClientRect"));
+
+ const int maxGrowTop = frameRect.top() - screenRect.top();
+
+ m_currentResizeData->maxGrow =
+ QPoint(window->maximumWidth() - window->geometry().width(),
+ std::min(resizeEdges & Qt::Edge::TopEdge ? maxGrowTop : INT_MAX,
+ window->maximumHeight() - window->geometry().height()));
+
+ m_currentResizeData->initialBounds = window->geometry();
+
+ // TODO(mikolajboc): Implement system resize
+ // .m_originInScreenCoords = m_systemDragInitData.lastMouseMovePoint,
+}
+
+void Resizer::continueResize(const PointerEvent &event)
+{
+ const auto pointInScreen = dom::mapPoint(
+ event.currentTarget, m_window->platformScreen()->element(), event.localPoint);
+ const auto amount = pointInScreen - m_currentResizeData->originInScreenCoords;
+ const QPoint cappedGrowVector(
+ std::min(m_currentResizeData->maxGrow.x(),
+ std::max(m_currentResizeData->minShrink.x(),
+ (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -amount.x()
+ : (m_currentResizeData->edges & Qt::Edge::RightEdge)
+ ? amount.x()
+ : 0)),
+ std::min(m_currentResizeData->maxGrow.y(),
+ std::max(m_currentResizeData->minShrink.y(),
+ (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -amount.y()
+ : (m_currentResizeData->edges & Qt::Edge::BottomEdge)
+ ? amount.y()
+ : 0)));
+
+ auto bounds = m_currentResizeData->initialBounds.adjusted(
+ (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -cappedGrowVector.x() : 0,
+ (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -cappedGrowVector.y() : 0,
+ (m_currentResizeData->edges & Qt::Edge::RightEdge) ? cappedGrowVector.x() : 0,
+ (m_currentResizeData->edges & Qt::Edge::BottomEdge) ? cappedGrowVector.y() : 0);
+
+ m_window->window()->setGeometry(bounds);
+}
+
+void Resizer::finishResize()
+{
+ Q_ASSERT_X(m_currentResizeData, Q_FUNC_INFO, "No resize in progress");
+ m_currentResizeData.reset();
+}
+
+TitleBar::TitleBar(QWasmWindow *window, emscripten::val parentElement)
+ : m_window(window),
+ m_element(dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
+ m_label(dom::document().call<emscripten::val>("createElement", emscripten::val("div")))
+{
+ m_icon = std::make_unique<WebImageButton>();
+ m_icon->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::QtLogo), "svg+xml");
+ m_element.call<void>("appendChild", m_icon->htmlElement());
+ m_element.set("className", "title-bar");
+
+ auto spacer = dom::document().call<emscripten::val>("createElement", emscripten::val("div"));
+ spacer["style"].set("width", "4px");
+ m_element.call<void>("appendChild", spacer);
+
+ m_label.set("className", "window-name");
+
+ m_element.call<void>("appendChild", m_label);
+
+ spacer = dom::document().call<emscripten::val>("createElement", emscripten::val("div"));
+ spacer.set("className", "spacer");
+ m_element.call<void>("appendChild", spacer);
+
+ m_restore = std::make_unique<WebImageButton>();
+ m_restore->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Restore),
+ "svg+xml");
+ m_restore->setCallbacks(
+ WebImageButton::Callbacks([this]() { m_window->onNonClientAreaInteraction(); },
+ [this]() { m_window->onRestoreClicked(); }));
+
+ m_element.call<void>("appendChild", m_restore->htmlElement());
+
+ m_maximize = std::make_unique<WebImageButton>();
+ m_maximize->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Maximize),
+ "svg+xml");
+ m_maximize->setCallbacks(
+ WebImageButton::Callbacks([this]() { m_window->onNonClientAreaInteraction(); },
+ [this]() { m_window->onMaximizeClicked(); }));
+
+ m_element.call<void>("appendChild", m_maximize->htmlElement());
+
+ m_close = std::make_unique<WebImageButton>();
+ m_close->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::X), "svg+xml");
+ m_close->setCallbacks(
+ WebImageButton::Callbacks([this]() { m_window->onNonClientAreaInteraction(); },
+ [this]() { m_window->onCloseClicked(); }));
+
+ m_element.call<void>("appendChild", m_close->htmlElement());
+
+ parentElement.call<void>("appendChild", m_element);
+
+ m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointerdown", [this](emscripten::val event) {
+ if (!onPointerDown(*PointerEvent::fromWeb(event)))
+ return;
+ m_window->onNonClientAreaInteraction();
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ });
+ m_mouseDragEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointermove", [this](emscripten::val event) {
+ if (onPointerMove(*PointerEvent::fromWeb(event))) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ }
+ });
+ m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "pointerup", [this](emscripten::val event) {
+ if (onPointerUp(*PointerEvent::fromWeb(event))) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ }
+ });
+ m_doubleClickEvent = std::make_unique<qstdweb::EventCallback>(
+ m_element, "dblclick", [this](emscripten::val event) {
+ if (onDoubleClick()) {
+ event.call<void>("preventDefault");
+ event.call<void>("stopPropagation");
+ }
+ });
+}
+
+TitleBar::~TitleBar()
+{
+ m_element["parentElement"].call<emscripten::val>("removeChild", m_element);
+}
+
+void TitleBar::setTitle(const QString &title)
+{
+ m_label.set("innerText", emscripten::val(title.toStdString()));
+}
+
+void TitleBar::setRestoreVisible(bool visible)
+{
+ m_restore->setVisible(visible);
+}
+
+void TitleBar::setMaximizeVisible(bool visible)
+{
+ m_maximize->setVisible(visible);
+}
+
+void TitleBar::setIcon(std::string_view imageData, std::string_view format)
+{
+ m_icon->setImage(imageData, format);
+}
+
+void TitleBar::setWidth(int width)
+{
+ m_element["style"].set("width", std::to_string(width) + "px");
+}
+
+QRectF TitleBar::geometry() const
+{
+ return QRectF::fromDOMRect(m_element.call<emscripten::val>("getBoundingClientRect"));
+}
+
+bool TitleBar::onPointerDown(const PointerEvent &event)
+{
+ if (event.pointerType != PointerType::Mouse)
+ return false;
+
+ m_element.call<void>("setPointerCapture", event.pointerId);
+ m_capturedPointerId = event.pointerId;
+
+ const QPoint targetPointClippedToScreen = clipPointWithScreen(event.localPoint);
+ m_lastMovePoint = targetPointClippedToScreen;
+ m_window->onNonClientEvent(event);
+ return true;
+}
+
+bool TitleBar::onPointerMove(const PointerEvent &event)
+{
+ if (m_capturedPointerId != event.pointerId)
+ return false;
+
+ const QPoint targetPointClippedToScreen = clipPointWithScreen(event.localPoint);
+ const QPoint delta = targetPointClippedToScreen - m_lastMovePoint;
+ m_lastMovePoint = targetPointClippedToScreen;
+
+ m_window->window()->setPosition(m_window->window()->position() + delta);
+ m_window->onNonClientEvent(event);
+ return true;
+}
+
+bool TitleBar::onPointerUp(const PointerEvent &event)
+{
+ if (m_capturedPointerId != event.pointerId)
+ return false;
+
+ m_element.call<void>("releasePointerCapture", event.pointerId);
+ m_capturedPointerId = -1;
+ m_window->onNonClientEvent(event);
+ return true;
+}
+
+bool TitleBar::onDoubleClick()
+{
+ m_window->onToggleMaximized();
+ return true;
+}
+
+QPoint TitleBar::clipPointWithScreen(const QPoint &pointInTitleBarCoords) const
+{
+ auto *screen = m_window->platformScreen();
+ return screen->clipPoint(screen->mapFromLocal(
+ dom::mapPoint(m_element, screen->element(), pointInTitleBarCoords)));
+}
+
+NonClientArea::NonClientArea(QWasmWindow *window, emscripten::val qtWindowElement)
+{
+ m_titleBar = std::make_unique<TitleBar>(window, qtWindowElement);
+ m_resizer = std::make_unique<Resizer>(window, qtWindowElement);
+}
+
+NonClientArea::~NonClientArea() = default;
+
+void NonClientArea::onClientAreaWidthChange(int width)
+{
+ m_titleBar->setWidth(width);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindownonclientarea.h b/src/plugins/platforms/wasm/qwasmwindownonclientarea.h
new file mode 100644
index 0000000000..b561e8de78
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindownonclientarea.h
@@ -0,0 +1,212 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMWINDOWNONCLIENTAREA_H
+#define QWASMWINDOWNONCLIENTAREA_H
+
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qnamespace.h>
+
+#include <emscripten/val.h>
+
+#include <functional>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+namespace qstdweb {
+class EventCallback;
+}
+
+struct PointerEvent;
+class QWindow;
+class Resizer;
+class TitleBar;
+class QWasmWindow;
+
+class NonClientArea
+{
+public:
+ NonClientArea(QWasmWindow *window, emscripten::val containerElement);
+ ~NonClientArea();
+
+ void onClientAreaWidthChange(int width);
+ TitleBar *titleBar() const { return m_titleBar.get(); }
+
+private:
+ std::unique_ptr<Resizer> m_resizer;
+ std::unique_ptr<TitleBar> m_titleBar;
+};
+
+class WebImageButton
+{
+public:
+ class Callbacks
+ {
+ public:
+ Callbacks();
+ Callbacks(std::function<void()> onInteraction, std::function<void()> onClick);
+ ~Callbacks();
+
+ Callbacks(const Callbacks &) = delete;
+ Callbacks(Callbacks &&);
+ Callbacks &operator=(const Callbacks &) = delete;
+ Callbacks &operator=(Callbacks &&);
+
+ operator bool() const { return !!m_onInteraction; }
+
+ void onInteraction();
+ void onClick();
+
+ private:
+ std::function<void()> m_onInteraction;
+ std::function<void()> m_onClick;
+ };
+
+ WebImageButton();
+ ~WebImageButton();
+
+ void setCallbacks(Callbacks callbacks);
+ void setImage(std::string_view imageData, std::string_view format);
+ void setVisible(bool visible);
+
+ emscripten::val htmlElement() const { return m_containerElement; }
+ emscripten::val imageElement() const { return m_imgElement; }
+
+private:
+ emscripten::val m_containerElement;
+ emscripten::val m_imgElement;
+
+ std::unique_ptr<qstdweb::EventCallback> m_webMouseMoveEventCallback;
+ std::unique_ptr<qstdweb::EventCallback> m_webMouseDownEventCallback;
+ std::unique_ptr<qstdweb::EventCallback> m_webClickEventCallback;
+
+ Callbacks m_callbacks;
+};
+
+class Resizer
+{
+public:
+ class ResizerElement
+ {
+ public:
+ static constexpr const char *cssClassNameForEdges(Qt::Edges edges)
+ {
+ switch (edges) {
+ case Qt::TopEdge | Qt::LeftEdge:;
+ return "nw";
+ case Qt::TopEdge:
+ return "n";
+ case Qt::TopEdge | Qt::RightEdge:
+ return "ne";
+ case Qt::LeftEdge:
+ return "w";
+ case Qt::RightEdge:
+ return "e";
+ case Qt::BottomEdge | Qt::LeftEdge:
+ return "sw";
+ case Qt::BottomEdge:
+ return "s";
+ case Qt::BottomEdge | Qt::RightEdge:
+ return "se";
+ default:
+ return "";
+ }
+ }
+
+ ResizerElement(emscripten::val parentElement, Qt::Edges edges, Resizer *resizer);
+ ~ResizerElement();
+ ResizerElement(const ResizerElement &other) = delete;
+ ResizerElement(ResizerElement &&other);
+ ResizerElement &operator=(const ResizerElement &other) = delete;
+ ResizerElement &operator=(ResizerElement &&other) = delete;
+
+ bool onPointerDown(const PointerEvent &event);
+ bool onPointerMove(const PointerEvent &event);
+ bool onPointerUp(const PointerEvent &event);
+
+ private:
+ emscripten::val m_element;
+
+ int m_capturedPointerId = -1;
+
+ const Qt::Edges m_edges;
+
+ Resizer *m_resizer;
+
+ std::unique_ptr<qstdweb::EventCallback> m_mouseDownEvent;
+ std::unique_ptr<qstdweb::EventCallback> m_mouseDragEvent;
+ std::unique_ptr<qstdweb::EventCallback> m_mouseUpEvent;
+ };
+
+ using ClickCallback = std::function<void()>;
+
+ Resizer(QWasmWindow *window, emscripten::val parentElement);
+ ~Resizer();
+
+private:
+ void onInteraction();
+ void startResize(Qt::Edges resizeEdges, const PointerEvent &event);
+ void continueResize(const PointerEvent &event);
+ void finishResize();
+
+ struct ResizeData
+ {
+ Qt::Edges edges = Qt::Edges::fromInt(0);
+ QPoint originInScreenCoords;
+ QPoint minShrink;
+ QPoint maxGrow;
+ QRect initialBounds;
+ };
+ std::unique_ptr<ResizeData> m_currentResizeData;
+
+ QWasmWindow *m_window;
+ emscripten::val m_windowElement;
+ std::vector<std::unique_ptr<ResizerElement>> m_elements;
+};
+
+class TitleBar
+{
+public:
+ TitleBar(QWasmWindow *window, emscripten::val parentElement);
+ ~TitleBar();
+
+ void setTitle(const QString &title);
+ void setRestoreVisible(bool visible);
+ void setMaximizeVisible(bool visible);
+ void setIcon(std::string_view imageData, std::string_view format);
+ void setWidth(int width);
+
+ QRectF geometry() const;
+
+private:
+ bool onPointerDown(const PointerEvent &event);
+ bool onPointerMove(const PointerEvent &event);
+ bool onPointerUp(const PointerEvent &event);
+ bool onDoubleClick();
+
+ QPoint clipPointWithScreen(const QPoint &pointInTitleBarCoords) const;
+
+ QWasmWindow *m_window;
+
+ emscripten::val m_element;
+ emscripten::val m_label;
+
+ std::unique_ptr<WebImageButton> m_close;
+ std::unique_ptr<WebImageButton> m_maximize;
+ std::unique_ptr<WebImageButton> m_restore;
+ std::unique_ptr<WebImageButton> m_icon;
+
+ int m_capturedPointerId = -1;
+ QPoint m_lastMovePoint;
+
+ std::unique_ptr<qstdweb::EventCallback> m_mouseDownEvent;
+ std::unique_ptr<qstdweb::EventCallback> m_mouseDragEvent;
+ std::unique_ptr<qstdweb::EventCallback> m_mouseUpEvent;
+ std::unique_ptr<qstdweb::EventCallback> m_doubleClickEvent;
+};
+
+QT_END_NAMESPACE
+#endif // QWASMWINDOWNONCLIENTAREA_H