summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikolaj Boc <mikolaj.boc@qt.io>2022-10-13 18:24:51 +0200
committerMikolaj Boc <mikolaj.boc@qt.io>2022-12-21 00:20:01 +0100
commit32666691c21cc91a3d7c7585dad711dc9743fdce (patch)
treefc981b05aa3fff66fd6a40929bb2ca8a2fbce5af
parent09141ebad7109b90e5dfdbf3b065543b8be14641 (diff)
Move the window through the title bar element itself
The compositor is redundant in the process of moving the window. Have the title bar react to move all by itself. Additionally, a clearer structure in the window was introduced. The non-client area has been extracted into a separate class, as was the icon store and free DOM functions used across files. Since it was now easy, made the window maximize/restore on double click on the title element. Fixes: QTBUG-107626 Pick-to: 6.5 Change-Id: Iba7f207e46806ae7162656965892ae5a48ac5ebe Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
-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