Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmwindownonclientarea.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
7#include "qwasmdom.h"
8#include "qwasmevent.h"
9#include "qwasmintegration.h"
10
11#include <qpa/qwindowsysteminterface.h>
12
13#include <QtCore/qassert.h>
14
16
18WebImageButton::Callbacks::Callbacks(std::function<void()> onInteraction,
19 std::function<void()> onClick)
20 : m_onInteraction(std::move(onInteraction)), m_onClick(std::move(onClick))
21{
22 Q_ASSERT_X(!!m_onInteraction == !!m_onClick, Q_FUNC_INFO,
23 "Both callbacks need to be either null or non-null");
24}
26
29
31{
32 return m_onInteraction();
33}
34
36{
37 return m_onClick();
38}
39
41 : m_containerElement(
42 dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
43 m_imgElement(dom::document().call<emscripten::val>("createElement", emscripten::val("img")))
44{
45 m_imgElement.set("draggable", false);
46
47 m_containerElement["classList"].call<void>("add", emscripten::val("image-button"));
48 m_containerElement.call<void>("appendChild", m_imgElement);
49}
50
52
54{
55 if (callbacks) {
56 if (!m_webClickEventCallback) {
57 m_webMouseDownEventCallback = std::make_unique<qstdweb::EventCallback>(
58 m_containerElement, "pointerdown", [this](emscripten::val event) {
59 event.call<void>("preventDefault");
60 event.call<void>("stopPropagation");
61 m_callbacks.onInteraction();
62 });
63 m_webClickEventCallback = std::make_unique<qstdweb::EventCallback>(
64 m_containerElement, "click", [this](emscripten::val event) {
65 m_callbacks.onClick();
66 event.call<void>("stopPropagation");
67 });
68 }
69 } else {
70 m_webMouseDownEventCallback.reset();
71 m_webClickEventCallback.reset();
72 }
73 dom::syncCSSClassWith(m_containerElement, "action-button", !!callbacks);
74 m_callbacks = std::move(callbacks);
75}
76
77void WebImageButton::setImage(std::string_view imageData, std::string_view format)
78{
79 m_imgElement.set("src",
80 "data:image/" + std::string(format) + ";base64," + std::string(imageData));
81}
82
84{
85 m_containerElement["style"].set("display", visible ? "flex" : "none");
86}
87
88Resizer::ResizerElement::ResizerElement(emscripten::val parentElement, Qt::Edges edges,
89 Resizer *resizer)
90 : m_element(dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
91 m_edges(edges),
92 m_resizer(resizer)
93{
94 Q_ASSERT_X(m_resizer, Q_FUNC_INFO, "Resizer cannot be null");
95
96 m_element["classList"].call<void>("add", emscripten::val("resize-outline"));
97 m_element["classList"].call<void>("add", emscripten::val(cssClassNameForEdges(edges)));
98
99 parentElement.call<void>("appendChild", m_element);
100
101 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
102 m_element, "pointerdown", [this](emscripten::val event) {
104 return;
105 m_resizer->onInteraction();
106 event.call<void>("preventDefault");
107 event.call<void>("stopPropagation");
108 });
109 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
110 m_element, "pointermove", [this](emscripten::val event) {
112 event.call<void>("preventDefault");
113 });
114 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
115 m_element, "pointerup", [this](emscripten::val event) {
117 event.call<void>("preventDefault");
118 event.call<void>("stopPropagation");
119 }
120 });
121}
122
124{
125 m_element["parentElement"].call<emscripten::val>("removeChild", m_element);
126}
127
129
131{
132 m_element.call<void>("setPointerCapture", event.pointerId);
133 m_capturedPointerId = event.pointerId;
134
135 m_resizer->startResize(m_edges, event);
136 return true;
137}
138
140{
141 if (m_capturedPointerId != event.pointerId)
142 return false;
143
144 m_resizer->continueResize(event);
145 return true;
146}
147
149{
150 if (m_capturedPointerId != event.pointerId)
151 return false;
152
153 m_resizer->finishResize();
154 m_element.call<void>("releasePointerCapture", event.pointerId);
155 m_capturedPointerId = -1;
156 return true;
157}
158
159Resizer::Resizer(QWasmWindow *window, emscripten::val parentElement)
160 : m_window(window), m_windowElement(parentElement)
161{
162 Q_ASSERT_X(m_window, Q_FUNC_INFO, "Window must not be null");
163
164 constexpr std::array<int, 8> ResizeEdges = { Qt::TopEdge | Qt::LeftEdge,
172 std::transform(std::begin(ResizeEdges), std::end(ResizeEdges), std::back_inserter(m_elements),
173 [parentElement, this](int edges) {
174 return std::make_unique<ResizerElement>(parentElement,
175 Qt::Edges::fromInt(edges), this);
176 });
177}
178
179Resizer::~Resizer() = default;
180
182 const auto *window = m_window->window();
183 const auto minShrink = QPoint(window->minimumWidth() - window->geometry().width(),
184 window->minimumHeight() - window->geometry().height());
185 const auto maxGrow = QPoint(window->maximumWidth() - window->geometry().width(),
186 window->maximumHeight() - window->geometry().height());
187
188 const auto frameRect =
189 QRectF::fromDOMRect(m_windowElement.call<emscripten::val>("getBoundingClientRect"));
190 auto containerGeometry =
191 QRectF::fromDOMRect(m_window->parentNode()->containerElement().call<emscripten::val>(
192 "getBoundingClientRect"));
193
194 const int maxGrowTop = frameRect.top() - containerGeometry.top();
195
196 return ResizeConstraints{minShrink, maxGrow, maxGrowTop};
197}
198
199void Resizer::onInteraction()
200{
201 m_window->onNonClientAreaInteraction();
202}
203
204void Resizer::startResize(Qt::Edges resizeEdges, const PointerEvent &event)
205{
206 Q_ASSERT_X(!m_currentResizeData, Q_FUNC_INFO, "Another resize in progress");
207
208 m_currentResizeData.reset(new ResizeData{
209 .edges = resizeEdges,
210 .originInScreenCoords = dom::mapPoint(
211 event.target(), m_window->platformScreen()->element(), event.localPoint),
212 });
213
214 const auto resizeConstraints = getResizeConstraints();
215 m_currentResizeData->minShrink = resizeConstraints.minShrink;
216
217 m_currentResizeData->maxGrow =
218 QPoint(resizeConstraints.maxGrow.x(),
219 std::min(resizeEdges & Qt::Edge::TopEdge ? resizeConstraints.maxGrowTop : INT_MAX,
220 resizeConstraints.maxGrow.y()));
221
222 m_currentResizeData->initialBounds = m_window->window()->geometry();
223}
224
225void Resizer::continueResize(const PointerEvent &event)
226{
227 const auto pointInScreen =
228 dom::mapPoint(event.target(), m_window->platformScreen()->element(), event.localPoint);
229 const auto amount = (pointInScreen - m_currentResizeData->originInScreenCoords).toPoint();
230 const QPoint cappedGrowVector(
231 std::min(m_currentResizeData->maxGrow.x(),
232 std::max(m_currentResizeData->minShrink.x(),
233 (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -amount.x()
234 : (m_currentResizeData->edges & Qt::Edge::RightEdge)
235 ? amount.x()
236 : 0)),
237 std::min(m_currentResizeData->maxGrow.y(),
238 std::max(m_currentResizeData->minShrink.y(),
239 (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -amount.y()
240 : (m_currentResizeData->edges & Qt::Edge::BottomEdge)
241 ? amount.y()
242 : 0)));
243
244 auto bounds = m_currentResizeData->initialBounds.adjusted(
245 (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -cappedGrowVector.x() : 0,
246 (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -cappedGrowVector.y() : 0,
247 (m_currentResizeData->edges & Qt::Edge::RightEdge) ? cappedGrowVector.x() : 0,
248 (m_currentResizeData->edges & Qt::Edge::BottomEdge) ? cappedGrowVector.y() : 0);
249
250 m_window->window()->setGeometry(bounds);
251}
252
253void Resizer::finishResize()
254{
255 Q_ASSERT_X(m_currentResizeData, Q_FUNC_INFO, "No resize in progress");
256 m_currentResizeData.reset();
257}
258
259TitleBar::TitleBar(QWasmWindow *window, emscripten::val parentElement)
260 : m_window(window),
261 m_element(dom::document().call<emscripten::val>("createElement", emscripten::val("div"))),
262 m_label(dom::document().call<emscripten::val>("createElement", emscripten::val("div")))
263{
264 m_icon = std::make_unique<WebImageButton>();
265 m_icon->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::QtLogo), "svg+xml");
266 m_element.call<void>("appendChild", m_icon->htmlElement());
267 m_element.set("className", "title-bar");
268
269 auto spacer = dom::document().call<emscripten::val>("createElement", emscripten::val("div"));
270 spacer["style"].set("width", "4px");
271 m_element.call<void>("appendChild", spacer);
272
273 m_label.set("className", "window-name");
274
275 m_element.call<void>("appendChild", m_label);
276
277 spacer = dom::document().call<emscripten::val>("createElement", emscripten::val("div"));
278 spacer.set("className", "spacer");
279 m_element.call<void>("appendChild", spacer);
280
281 m_restore = std::make_unique<WebImageButton>();
282 m_restore->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Restore),
283 "svg+xml");
284 m_restore->setCallbacks(
286 [this]() { m_window->onRestoreClicked(); }));
287
288 m_element.call<void>("appendChild", m_restore->htmlElement());
289
290 m_maximize = std::make_unique<WebImageButton>();
291 m_maximize->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Maximize),
292 "svg+xml");
293 m_maximize->setCallbacks(
295 [this]() { m_window->onMaximizeClicked(); }));
296
297 m_element.call<void>("appendChild", m_maximize->htmlElement());
298
299 m_close = std::make_unique<WebImageButton>();
300 m_close->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::X), "svg+xml");
301 m_close->setCallbacks(
303 [this]() { m_window->onCloseClicked(); }));
304
305 m_element.call<void>("appendChild", m_close->htmlElement());
306
307 parentElement.call<void>("appendChild", m_element);
308
309 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
310 m_element, "pointerdown", [this](emscripten::val event) {
311 if (!onPointerDown(*PointerEvent::fromWeb(event)))
312 return;
313 m_window->onNonClientAreaInteraction();
314 event.call<void>("preventDefault");
315 event.call<void>("stopPropagation");
316 });
317 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
318 m_element, "pointermove", [this](emscripten::val event) {
319 if (onPointerMove(*PointerEvent::fromWeb(event))) {
320 event.call<void>("preventDefault");
321 }
322 });
323 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
324 m_element, "pointerup", [this](emscripten::val event) {
325 if (onPointerUp(*PointerEvent::fromWeb(event))) {
326 event.call<void>("preventDefault");
327 event.call<void>("stopPropagation");
328 }
329 });
330 m_doubleClickEvent = std::make_unique<qstdweb::EventCallback>(
331 m_element, "dblclick", [this](emscripten::val event) {
332 if (onDoubleClick()) {
333 event.call<void>("preventDefault");
334 event.call<void>("stopPropagation");
335 }
336 });
337}
338
340{
341 m_element["parentElement"].call<emscripten::val>("removeChild", m_element);
342}
343
345{
346 m_label.set("innerText", emscripten::val(title.toStdString()));
347}
348
350{
351 m_restore->setVisible(visible);
352}
353
355{
356 m_maximize->setVisible(visible);
357}
358
360{
361 m_close->setVisible(visible);
362}
363
364void TitleBar::setIcon(std::string_view imageData, std::string_view format)
365{
366 m_icon->setImage(imageData, format);
367}
368
370{
371 m_element["style"].set("width", std::to_string(width) + "px");
372}
373
375{
376 return QRectF::fromDOMRect(m_element.call<emscripten::val>("getBoundingClientRect"));
377}
378
379bool TitleBar::onPointerDown(const PointerEvent &event)
380{
381 m_element.call<void>("setPointerCapture", event.pointerId);
382 m_capturedPointerId = event.pointerId;
383
384 m_moveStartWindowPosition = m_window->window()->position();
385 m_moveStartPoint = clipPointWithScreen(event.localPoint);
386 m_window->onNonClientEvent(event);
387 return true;
388}
389
390bool TitleBar::onPointerMove(const PointerEvent &event)
391{
392 if (m_capturedPointerId != event.pointerId)
393 return false;
394
395 const QPoint delta = (clipPointWithScreen(event.localPoint) - m_moveStartPoint).toPoint();
396
397 m_window->window()->setPosition(m_moveStartWindowPosition + delta);
398 m_window->onNonClientEvent(event);
399 return true;
400}
401
402bool TitleBar::onPointerUp(const PointerEvent &event)
403{
404 if (m_capturedPointerId != event.pointerId)
405 return false;
406
407 m_element.call<void>("releasePointerCapture", event.pointerId);
408 m_capturedPointerId = -1;
409 m_window->onNonClientEvent(event);
410 return true;
411}
412
413bool TitleBar::onDoubleClick()
414{
415 m_window->onToggleMaximized();
416 return true;
417}
418
419QPointF TitleBar::clipPointWithScreen(const QPointF &pointInTitleBarCoords) const
420{
421 auto containerRect =
422 QRectF::fromDOMRect(m_window->parentNode()->containerElement().call<emscripten::val>(
423 "getBoundingClientRect"));
424 const auto p = dom::mapPoint(m_element, m_window->parentNode()->containerElement(),
425 pointInTitleBarCoords);
426
427 auto result = QPointF(qBound(0., qreal(p.x()), containerRect.width()),
428 qBound(0., qreal(p.y()), containerRect.height()));
429 return m_window->parent() ? result : m_window->platformScreen()->mapFromLocal(result).toPoint();
430}
431
432NonClientArea::NonClientArea(QWasmWindow *window, emscripten::val qtWindowElement)
433 : m_qtWindowElement(qtWindowElement),
434 m_resizer(std::make_unique<Resizer>(window, m_qtWindowElement)),
435 m_titleBar(std::make_unique<TitleBar>(window, m_qtWindowElement))
436{
437 updateResizability();
438}
439
441
443{
444 m_titleBar->setWidth(width);
445}
446
448{
449 updateResizability();
450}
451
452void NonClientArea::updateResizability()
453{
454 const auto resizeConstraints = m_resizer->getResizeConstraints();
455 const bool nonResizable = resizeConstraints.minShrink.isNull()
456 && resizeConstraints.maxGrow.isNull() && resizeConstraints.maxGrowTop == 0;
457 dom::syncCSSClassWith(m_qtWindowElement, "no-resize", nonResizable);
458}
459
static Base64IconStore * get()
void onClientAreaWidthChange(int width)
NonClientArea(QWasmWindow *window, emscripten::val containerElement)
QPlatformWindow * parent() const
Returns the parent platform window (or \nullptr if orphan).
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition qpoint.h:404
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:484
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
std::string toStdString() const
Returns a std::string object with the data contained in this QString.
Definition qstring.h:1444
emscripten::val element() const
QPointF mapFromLocal(const QPointF &p) const
virtual emscripten::val containerElement()=0
void onToggleMaximized()
void onNonClientAreaInteraction()
bool onNonClientEvent(const PointerEvent &event)
QWasmWindowTreeNode * parentNode() final
void onMaximizeClicked()
void onRestoreClicked()
void onCloseClicked()
QWindow * window() const
Definition qwasmwindow.h:90
QWasmScreen * platformScreen() const
void setGeometry(int posx, int posy, int w, int h)
Sets the geometry of the window, excluding its window frame, to a rectangle constructed from posx,...
Definition qwindow.cpp:1802
bool onPointerUp(const PointerEvent &event)
static constexpr const char * cssClassNameForEdges(Qt::Edges edges)
ResizerElement(emscripten::val parentElement, Qt::Edges edges, Resizer *resizer)
bool onPointerDown(const PointerEvent &event)
bool onPointerMove(const PointerEvent &event)
Resizer(QWasmWindow *window, emscripten::val parentElement)
ResizeConstraints getResizeConstraints()
void setTitle(const QString &title)
void setMaximizeVisible(bool visible)
TitleBar(QWasmWindow *window, emscripten::val parentElement)
QRectF geometry() const
void setWidth(int width)
void setIcon(std::string_view imageData, std::string_view format)
void setCloseVisible(bool visible)
void setRestoreVisible(bool visible)
Callbacks & operator=(const Callbacks &)=delete
void setImage(std::string_view imageData, std::string_view format)
void setVisible(bool visible)
void setCallbacks(Callbacks callbacks)
Combined button and popup list for selecting options.
constexpr const T & min(const T &a, const T &b)
Definition qnumeric.h:366
Definition qcompare.h:63
@ RightEdge
@ TopEdge
@ BottomEdge
@ LeftEdge
void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
Definition qwasmdom.cpp:240
QPointF mapPoint(emscripten::val source, emscripten::val target, const QPointF &point)
Definition qwasmdom.cpp:250
emscripten::val document()
Definition qwasmdom.h:49
#define Q_FUNC_INFO
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLint GLint GLint GLint GLint x
[0]
GLint GLsizei width
GLint GLsizei GLsizei GLenum format
GLint y
struct _cl_event * event
GLuint GLfloat * val
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
PromiseCallbacks callbacks
Definition qstdweb.cpp:275
double qreal
Definition qtypes.h:187
QString title
[35]
QSharedPointer< T > other(t)
[5]
QByteArray imageData
[15]
aWidget window() -> setWindowTitle("New Window Title")
[2]
static std::optional< PointerEvent > fromWeb(emscripten::val webEvent)