Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwasmwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <qpa/qwindowsysteminterface.h>
6#include <private/qguiapplication_p.h>
7#include <QtCore/qfile.h>
8#include <QtCore/qtimer.h>
9#include <QtCore/qpointer.h>
10#include <QtGui/private/qwindow_p.h>
11#include <QtGui/private/qhighdpiscaling_p.h>
12#include <private/qpixmapcache_p.h>
13#include <QtGui/qopenglfunctions.h>
14#include <QBuffer>
15
17#include "qwasmdom.h"
18#if QT_CONFIG(clipboard)
19#include "qwasmclipboard.h"
20#endif
24#include "qwasmwindow.h"
25#include "qwasmscreen.h"
27#include "qwasmevent.h"
30#if QT_CONFIG(draganddrop)
31#include "qwasmdrag.h"
32#endif
34
35#include <iostream>
36#include <sstream>
37
38#include <emscripten/val.h>
39
40#include <QtCore/private/qstdweb_p.h>
41
43
45
47 QWasmCompositor *compositor, QWasmBackingStore *backingStore,
48 WId nativeHandle)
50 m_compositor(compositor),
51 m_backingStore(backingStore),
53 m_decoratedWindow(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
54 m_window(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
55 m_a11yContainer(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
58 m_canvas(m_document.call<emscripten::val>("createElement", emscripten::val("canvas"))),
59 m_focusHelper(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
60 m_inputElement(m_document.call<emscripten::val>("createElement", emscripten::val("input")))
61
62{
63 m_decoratedWindow.set("className", "qt-decorated-window");
64 m_decoratedWindow["style"].set("display", std::string("none"));
65
66 m_nonClientArea = std::make_unique<NonClientArea>(this, m_decoratedWindow);
67 m_nonClientArea->titleBar()->setTitle(window()->title());
68
69 // If we are wrapping a foregin window, a.k.a. a native html element then that element becomes
70 // the m_window element. In this case setting up event handlers and accessibility etc is not
71 // needed since that is (presumably) handled by the native html element.
72 //
73 // The WId is an emscripten::val *, owned by QWindow user code. We dereference and make
74 // a copy of the val here and don't strictly need it to be kept alive, but that's an
75 // implementation detail. The pointer will be dereferenced again if the window is destroyed
76 // and recreated.
77 if (nativeHandle) {
78 m_window = *(emscripten::val *)(nativeHandle);
79 m_winId = nativeHandle;
80 m_decoratedWindow.set("id", "qt-window-" + std::to_string(m_winId));
81 m_decoratedWindow.call<void>("appendChild", m_window);
82 return;
83 }
84
85 m_window.set("className", "qt-window");
86 m_decoratedWindow.call<void>("appendChild", m_window);
87
88 m_canvas["classList"].call<void>("add", emscripten::val("qt-window-canvas"));
89
90#if QT_CONFIG(clipboard)
91 // The window is the only ancestor of every focusable child element.
92 m_cutCallback = QWasmEventHandler(m_window, "cut", QWasmClipboard::cut);
93 m_copyCallback = QWasmEventHandler(m_window, "copy", QWasmClipboard::copy);
94 m_pasteCallback = QWasmEventHandler(m_window, "paste", QWasmClipboard::paste);
95#endif
96
97 // Set up m_focusHelper, which is an invisible child element of the window which takes
98 // focus on behalf of the window any time the window has focus in general, but none
99 // of the special child elements such as the inputElment or a11y elements have focus.
100 // Set inputMode=none set to prevent the virtual keyboard from popping up.
101 m_focusHelper["classList"].call<void>("add", emscripten::val("qt-window-focus-helper"));
102 m_focusHelper.set("inputMode", std::string("none"));
103 m_focusHelper.call<void>("setAttribute", std::string("contenteditable"), std::string("true"));
104 m_focusHelper["style"].set("position", "absolute");
105 m_focusHelper["style"].set("left", 0);
106 m_focusHelper["style"].set("top", 0);
107 m_focusHelper["style"].set("width", "1px");
108 m_focusHelper["style"].set("height", "1px");
109 m_focusHelper["style"].set("z-index", -2);
110 m_focusHelper["style"].set("opacity", 0);
111 m_window.call<void>("appendChild", m_focusHelper);
112
113 // Set up m_inputElement, which takes focus whenever a Qt text input UI element has
114 // foucus.
115 m_inputElement["classList"].call<void>("add", emscripten::val("qt-window-input-element"));
116 m_inputElement.call<void>("setAttribute", std::string("contenteditable"), std::string("true"));
117 m_inputElement.set("type", "text");
118 m_inputElement["style"].set("position", "absolute");
119 m_inputElement["style"].set("left", 0);
120 m_inputElement["style"].set("top", 0);
121 m_inputElement["style"].set("width", "1px");
122 m_inputElement["style"].set("height", "1px");
123 m_inputElement["style"].set("z-index", -2);
124 m_inputElement["style"].set("opacity", 0);
125 m_inputElement["style"].set("display", "");
126 m_window.call<void>("appendChild", m_inputElement);
127
128 // The canvas displays graphics only, and is not accessible or an event target
129 m_canvas.call<void>("setAttribute", std::string("aria-hidden"), std::string("true"));
130 m_canvas["style"].set("pointerEvents", "none");
131
132 m_window.call<void>("appendChild", m_canvas);
133
134 m_a11yContainer["classList"].call<void>("add", emscripten::val("qt-window-a11y-container"));
135 m_a11yElementContainer["classList"].call<void>(
136 "add", emscripten::val("qt-window-a11y-elements-container"));
137 m_a11yDescribedByContainer["classList"].call<void>(
138 "add", emscripten::val("qt-window-a11y-describedby-container"));
139 m_a11yContainer.call<void>("appendChild", m_a11yElementContainer);
140 m_a11yContainer.call<void>("appendChild", m_a11yDescribedByContainer);
141 m_window.call<void>("appendChild", m_a11yContainer);
142
143 if (QWasmAccessibility::isEnabled())
145
146 const bool rendersTo2dContext = w->surfaceType() != QSurface::OpenGLSurface;
147 if (rendersTo2dContext)
148 m_context2d = m_canvas.call<emscripten::val>("getContext", emscripten::val("2d"));
149
150 m_winId = WId(&m_window);
151 m_decoratedWindow.set("id", "qt-window-" + std::to_string(m_winId));
152 emscripten::val::module_property("specialHTMLTargets").set(canvasSelector(), m_canvas);
153
154 m_flags = window()->flags();
155
157
158 m_transientWindowChangedConnection =
159 QObject::connect(
160 window(), &QWindow::transientParentChanged,
161 window(), [this](QWindow *tp) { onTransientParentChanged(tp); });
162
163 m_modalityChangedConnection =
164 QObject::connect(
165 window(), &QWindow::modalityChanged,
166 window(), [this](Qt::WindowModality) { onModalityChanged(); });
167
168 setParent(parent());
169}
170
172{
173 m_pointerDownCallback = QWasmEventHandler(m_window, "pointerdown",
174 [this](emscripten::val event){ processPointer(PointerEvent(EventType::PointerDown, event)); }
175 );
176 m_pointerMoveCallback = QWasmEventHandler(m_window, "pointermove",
177 [this](emscripten::val event){ processPointer(PointerEvent(EventType::PointerMove, event)); }
178 );
179 m_pointerUpCallback = QWasmEventHandler(m_window, "pointerup",
180 [this](emscripten::val event){ processPointer(PointerEvent(EventType::PointerUp, event)); }
181 );
182 m_pointerCancelCallback = QWasmEventHandler(m_window, "pointercancel",
183 [this](emscripten::val event){ processPointer(PointerEvent(EventType::PointerCancel, event)); }
184 );
185 m_pointerEnterCallback = QWasmEventHandler(m_window, "pointerenter",
186 [this](emscripten::val event) { this->handlePointerEnterLeaveEvent(PointerEvent(EventType::PointerEnter, event)); }
187 );
188 m_pointerLeaveCallback = QWasmEventHandler(m_window, "pointerleave",
189 [this](emscripten::val event) { this->handlePointerEnterLeaveEvent(PointerEvent(EventType::PointerLeave, event)); }
190 );
191
192#if QT_CONFIG(draganddrop)
193 m_window.call<void>("setAttribute", emscripten::val("draggable"), emscripten::val("true"));
194 m_dragStartCallback = QWasmEventHandler(m_window, "dragstart",
195 [this](emscripten::val event) {
196 DragEvent dragEvent(EventType::DragStart, event, window());
197 QWasmDrag::instance()->onNativeDragStarted(&dragEvent);
198 }
199 );
200 m_dragOverCallback = QWasmEventHandler(m_window, "dragover",
201 [this](emscripten::val event) {
202 DragEvent dragEvent(EventType::DragOver, event, window());
203 QWasmDrag::instance()->onNativeDragOver(&dragEvent);
204 }
205 );
206 m_dropCallback = QWasmEventHandler(m_window, "drop",
207 [this](emscripten::val event) {
208 DragEvent dragEvent(EventType::Drop, event, window());
209 QWasmDrag::instance()->onNativeDrop(&dragEvent);
210 }
211 );
212 m_dragEndCallback = QWasmEventHandler(m_window, "dragend",
213 [this](emscripten::val event) {
214 DragEvent dragEvent(EventType::DragEnd, event, window());
215 QWasmDrag::instance()->onNativeDragFinished(&dragEvent, platformScreen());
216 releasePointerGrab(dragEvent);
217 }
218 );
219 m_dragEnterCallback = QWasmEventHandler(m_window, "dragenter",
220 [this](emscripten::val event) {
221 DragEvent dragEvent(EventType::DragEnter, event, window());
222 QWasmDrag::instance()->onNativeDragEnter(&dragEvent);
223 }
224 );
225 m_dragLeaveCallback = QWasmEventHandler(m_window, "dragleave",
226 [this](emscripten::val event) {
227 DragEvent dragEvent(EventType::DragLeave, event, window());
228 QWasmDrag::instance()->onNativeDragLeave(&dragEvent);
229 }
230 );
231#endif // QT_CONFIG(draganddrop)
232
233 m_wheelEventCallback = QWasmEventHandler(m_window, "wheel",
234 [this](emscripten::val event) { this->handleWheelEvent(event); });
235
236 m_keyDownCallback = QWasmEventHandler(m_window, "keydown",
237 [this](emscripten::val event) { this->handleKeyEvent(KeyEvent(EventType::KeyDown, event)); });
238 m_keyUpCallback =QWasmEventHandler(m_window, "keyup",
239 [this](emscripten::val event) {this->handleKeyEvent(KeyEvent(EventType::KeyUp, event)); });
240
241 m_inputCallback = QWasmEventHandler(m_window, "input",
242 [this](emscripten::val event){ handleInputEvent(event); });
243 m_compositionUpdateCallback = QWasmEventHandler(m_window, "compositionupdate",
244 [this](emscripten::val event){ handleCompositionUpdateEvent(event); });
245 m_compositionStartCallback = QWasmEventHandler(m_window, "compositionstart",
246 [this](emscripten::val event){ handleCompositionStartEvent(event); });
247 m_compositionEndCallback = QWasmEventHandler(m_window, "compositionend",
248 [this](emscripten::val event){ handleCompositionEndEvent(event); });
249 m_beforeInputCallback = QWasmEventHandler(m_window, "beforeinput",
250 [this](emscripten::val event){ handleBeforeInputEvent(event); });
251
252 m_focusinCallback = QWasmEventHandler(m_window, "focusin",
253 [this](emscripten::val event) { handleFocusinEvent(event); });
254 m_focusoutCallback = QWasmEventHandler(m_window, "focusout",
255 [this](emscripten::val event) { handleFocusoutEvent(event); });
256 }
257
259{
261
262#if QT_CONFIG(accessibility)
263 QWasmAccessibility::onRemoveWindow(window());
264#endif
265 QObject::disconnect(m_transientWindowChangedConnection);
266 QObject::disconnect(m_modalityChangedConnection);
267
268 transferFocus();
269
270 emscripten::val::module_property("specialHTMLTargets").delete_(canvasSelector());
271 m_window.call<void>("removeChild", m_canvas);
272 m_window.call<void>("removeChild", m_a11yContainer);
273 m_context2d = emscripten::val::undefined();
274 commitParent(nullptr);
275 if (m_requestAnimationFrameId > -1)
276 emscripten_cancel_animation_frame(m_requestAnimationFrameId);
277}
278
279void QWasmWindow::transferFocus()
280{
281 if (!window() ||
282 (QGuiApplication::focusWindow() && // Don't act if we have a focus window different from this
283 QGuiApplication::focusWindow() != window()))
284 return;
285
286 // Make a list of all windows sorted on active index.
287 // Skip windows with active index 0 as they have
288 // never been active.
289 std::map<uint64_t, QWasmWindow *> allWindows;
290 for (const auto &w : platformScreen()->allWindows()) {
291 if (w->getActiveIndex() > 0 && w->isVisible())
292 allWindows.insert({w->getActiveIndex(), w});
293 }
294
295 // window is not in all windows
296 if (getActiveIndex() > 0)
297 allWindows.insert({getActiveIndex(), this});
298
299 if (allWindows.size() >= 2) {
300 const auto lastIt = std::prev(allWindows.end());
301 const auto prevIt = std::prev(lastIt);
302 const auto lastW = lastIt->second;
303 const auto prevW = prevIt->second;
304
305 if (lastW == this) // Only act if window is last to be active
306 prevW->requestActivateWindow();
307 }
308}
309
311{
312 return window()->requestedFormat();
313}
314
315QWasmWindow *QWasmWindow::fromWindow(const QWindow *window)
316{
317 if (!window ||!window->handle())
318 return nullptr;
319 return static_cast<QWasmWindow *>(window->handle());
320}
321
323{
324 return fromWindow(QGuiApplication::focusWindow());
325}
326
328{
329 QWasmWindow *window = focusWindow();
330 return window ? window->inputElement() : emscripten::val::null();
331}
332
334{
335 if (!window())
336 return nullptr;
337
338 return fromWindow(window()->transientParent());
339}
340
342{
343 return window()->flags();
344}
345
346bool QWasmWindow::isModal() const
347{
348 return window()->isModal();
349}
350
352{
353 window()->setWindowState(Qt::WindowNoState);
354}
355
357{
358 window()->setWindowState(Qt::WindowMaximized);
359}
360
362{
363 window()->setWindowState(m_state.testFlag(Qt::WindowMaximized) ? Qt::WindowNoState
364 : Qt::WindowMaximized);
365}
366
368{
369 window()->close();
370}
371
373{
375 QGuiApplicationPrivate::instance()->closeAllPopups();
376}
377
379{
380 QPointF pointInScreen = platformScreen()->mapFromLocal(
381 dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint));
382 return QWindowSystemInterface::handleMouseEvent(
383 window(), QWasmIntegration::getTimestamp(), window()->mapFromGlobal(pointInScreen),
384 pointInScreen, event.mouseButtons, event.mouseButton,
385 MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::NonClient),
386 event.modifiers);
387}
388
390{
391 auto initialGeometry = QPlatformWindow::initialGeometry(window(),
392 windowGeometry(), defaultWindowSize, defaultWindowSize);
393 m_normalGeometry = initialGeometry;
394
395 setWindowState(window()->windowStates());
396 setWindowFlags(window()->flags());
397 setWindowTitle(window()->title());
398 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
399
400 if (window()->isTopLevel())
401 setWindowIcon(window()->icon());
402 QPlatformWindow::setGeometry(m_normalGeometry);
403
404#if QT_CONFIG(accessibility)
405 // Add accessibility-enable button. The user can activate this
406 // button to opt-in to accessibility.
407 if (window()->isTopLevel())
408 QWasmAccessibility::addAccessibilityEnableButton(window());
409#endif
410}
411
413{
414 return static_cast<QWasmScreen *>(window()->screen()->handle());
415}
416
418{
419 if (!m_backingStore || !isVisible() || m_context2d.isUndefined())
420 return;
421
422 auto image = m_backingStore->getUpdatedWebImage(this);
423 if (image.isUndefined())
424 return;
425 m_context2d.call<void>("putImageData", image, emscripten::val(0), emscripten::val(0));
426}
427
429{
430 m_decoratedWindow["style"].set("zIndex", std::to_string(z));
431}
432
433void QWasmWindow::setWindowCursor(QByteArray cssCursorName)
434{
435 m_window["style"].set("cursor", emscripten::val(cssCursorName.constData()));
436}
437
438void QWasmWindow::setGeometry(const QRect &rect)
439{
440 const auto margins = frameMargins();
441
442 const QRect clientAreaRect = ([this, &rect, &margins]() {
443 if (m_state.testFlag(Qt::WindowFullScreen))
444 return platformScreen()->geometry();
445 if (m_state.testFlag(Qt::WindowMaximized))
446 return platformScreen()->availableGeometry().marginsRemoved(frameMargins());
447
448 auto offset = rect.topLeft() - (!parent() ? screen()->geometry().topLeft() : QPoint());
449
450 // In viewport
451 auto containerGeometryInViewport =
452 QRectF::fromDOMRect(parentNode()->containerElement().call<emscripten::val>(
453 "getBoundingClientRect"))
454 .toRect();
455
456 auto rectInViewport = QRect(containerGeometryInViewport.topLeft() + offset, rect.size());
457
458 QRect cappedGeometry(rectInViewport);
459 if (!parent()) {
460 // Clamp top level windows top position to the screen bounds
461 cappedGeometry.moveTop(
462 std::max(std::min(rectInViewport.y(), containerGeometryInViewport.bottom()),
463 containerGeometryInViewport.y() + margins.top()));
464 }
465 cappedGeometry.setSize(
466 cappedGeometry.size().expandedTo(windowMinimumSize()).boundedTo(windowMaximumSize()));
467 return QRect(QPoint(rect.x(), rect.y() + cappedGeometry.y() - rectInViewport.y()),
468 rect.size());
469 })();
470 m_nonClientArea->onClientAreaWidthChange(clientAreaRect.width());
471
472 const auto frameRect =
473 clientAreaRect
474 .adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom())
475 .translated(!parent() ? -screen()->geometry().topLeft() : QPoint());
476
477 m_decoratedWindow["style"].set("left", std::to_string(frameRect.left()) + "px");
478 m_decoratedWindow["style"].set("top", std::to_string(frameRect.top()) + "px");
479 m_canvas["style"].set("width", std::to_string(clientAreaRect.width()) + "px");
480 m_canvas["style"].set("height", std::to_string(clientAreaRect.height()) + "px");
481 m_a11yContainer["style"].set("width", std::to_string(clientAreaRect.width()) + "px");
482 m_a11yContainer["style"].set("height", std::to_string(clientAreaRect.height()) + "px");
483
484 // Important for the title bar and decorated window to size correctly
485 m_window["style"].set("width", std::to_string(clientAreaRect.width()) + "px");
486 m_window["style"].set("height", std::to_string(clientAreaRect.height()) + "px");
487
488 QSizeF canvasSize = clientAreaRect.size() * devicePixelRatio();
489
490 m_canvas.set("width", canvasSize.width());
491 m_canvas.set("height", canvasSize.height());
492
493 bool shouldInvalidate = true;
494 if (!m_state.testFlag(Qt::WindowFullScreen) && !m_state.testFlag(Qt::WindowMaximized)) {
495 shouldInvalidate = m_normalGeometry.size() != clientAreaRect.size();
496 m_normalGeometry = clientAreaRect;
497 }
498
499 QWindowSystemInterface::handleGeometryChange(window(), clientAreaRect);
500 if (shouldInvalidate)
501 invalidate();
502 else
503 m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()));
504}
505
506void QWasmWindow::setVisible(bool visible)
507{
508 // TODO(mikolajboc): isVisible()?
509 const bool nowVisible = m_decoratedWindow["style"]["display"].as<std::string>() == "block";
510 if (visible == nowVisible)
511 return;
512
513 m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()), QWasmCompositor::ExposeEventDelivery);
514 m_decoratedWindow["style"].set("display", visible ? "block" : "none");
515
516 // Handle focus on window visiblity transition; bring focus to this window
517 // or transfer it to a different window.
518 if (visible) {
519
520 // Some window configs should not take focus
521 const auto flags = windowFlags();
522 const bool isTooltip = ((flags & Qt::ToolTip) == Qt::ToolTip);
523 const auto showWithoutActivatingProp = window()->property("_q_showWithoutActivating");
524 const bool showWithoutActivating = showWithoutActivatingProp.isValid() && showWithoutActivatingProp.toBool();
525 const bool dontFocus = isTooltip || showWithoutActivating;
526
527 // Sync focus: make the window take native focus it is the focus window.
528 const bool hasFocus = window() == QGuiApplication::focusWindow();
529
530 // Take focus if this window is a top-level window becoming visible.
531 // This is window manager level behavior: QPlatformWindow subclasses
532 // usually do not implement auto-focus on show; however for wasm we
533 // are the window manager as well.
534 const bool takeFocus = window()->isTopLevel();
535
536 if (!dontFocus && (hasFocus || takeFocus))
537 focus();
538 } else {
539 if (window()->isTopLevel())
540 transferFocus();
541 }
542
543 if (visible) {
544 raise();
545 applyWindowState();
546#if QT_CONFIG(accessibility)
547 QWasmAccessibility::onShowWindow(window());
548#endif
549 }
550}
551
553{
554 return window()->isVisible();
555}
556
558{
559 const auto frameRect =
560 QRectF::fromDOMRect(m_decoratedWindow.call<emscripten::val>("getBoundingClientRect"));
561 const auto canvasRect =
562 QRectF::fromDOMRect(m_window.call<emscripten::val>("getBoundingClientRect"));
563 return QMarginsF(canvasRect.left() - frameRect.left(), canvasRect.top() - frameRect.top(),
564 frameRect.right() - canvasRect.right(),
565 frameRect.bottom() - canvasRect.bottom())
566 .toMargins();
567}
568
570{
571 bringToTop();
572 invalidate();
573}
574
576{
577 sendToBottom();
578 invalidate();
579}
580
582{
583 return m_winId;
584}
585
587{
588 // setGeometry() will take care of minimum and maximum size constraints
589 setGeometry(windowGeometry());
590 m_nonClientArea->propagateSizeHints();
591}
592
593void QWasmWindow::setOpacity(qreal level)
594{
595 m_decoratedWindow["style"].set("opacity", qBound(0.0, level, 1.0));
596}
597
598void QWasmWindow::invalidate()
599{
600 m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()));
601}
602
604{
605 dom::syncCSSClassWith(m_decoratedWindow, "inactive", !active);
606}
607
608void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
609{
610 flags = fixTopLevelWindowFlags(flags);
611
612 // Note: This function is also called from the constructor,
613 // and in this case we should not call onPositionPreferenceChanged
614 // since we haven't inserted the window yet.
615 if (std::find(childStack().begin(), childStack().end(), this) != childStack().end()) {
616 if ((flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint))
617 || (flags.testFlag(Qt::WindowStaysOnBottomHint)
618 != m_flags.testFlag(Qt::WindowStaysOnBottomHint))
619 || shouldBeAboveTransientParentFlags(flags) != shouldBeAboveTransientParentFlags(m_flags)) {
620 onPositionPreferenceChanged(positionPreferenceFromWindowFlags(flags));
621 }
622 }
623 m_flags = flags;
624 dom::syncCSSClassWith(m_decoratedWindow, "frameless", !hasFrame() || !window()->isTopLevel());
625 dom::syncCSSClassWith(m_decoratedWindow, "has-border", hasBorder());
626 dom::syncCSSClassWith(m_decoratedWindow, "has-shadow", hasShadow());
627 dom::syncCSSClassWith(m_decoratedWindow, "has-title", hasTitleBar());
628 dom::syncCSSClassWith(m_decoratedWindow, "transparent-for-input",
629 flags.testFlag(Qt::WindowTransparentForInput));
630
631 m_nonClientArea->titleBar()->setMaximizeVisible(hasMaximizeButton());
632 m_nonClientArea->titleBar()->setCloseVisible(m_flags.testFlag(Qt::WindowCloseButtonHint));
633}
634
635void QWasmWindow::setWindowState(Qt::WindowStates newState)
636{
637 // Child windows can not have window states other than Qt::WindowActive
638 if (parent())
639 newState &= Qt::WindowActive;
640
641 const Qt::WindowStates oldState = m_state;
642
643 if (newState.testFlag(Qt::WindowMinimized)) {
644 newState.setFlag(Qt::WindowMinimized, false);
645 qWarning("Qt::WindowMinimized is not implemented in wasm");
646 window()->setWindowStates(newState);
647 return;
648 }
649
650 if (newState == oldState)
651 return;
652
653 m_state = newState;
654 m_previousWindowState = oldState;
655
656 applyWindowState();
657}
658
659void QWasmWindow::setWindowTitle(const QString &title)
660{
661 m_nonClientArea->titleBar()->setTitle(title);
662}
663
664void QWasmWindow::setWindowIcon(const QIcon &icon)
665{
666 const auto dpi = screen()->devicePixelRatio();
667 auto pixmap = icon.pixmap(10 * dpi, 10 * dpi);
668 if (pixmap.isNull()) {
669 m_nonClientArea->titleBar()->setIcon(
670 Base64IconStore::get()->getIcon(Base64IconStore::IconType::QtLogo), "svg+xml");
671 return;
672 }
673
674 QByteArray bytes;
675 QBuffer buffer(&bytes);
676 pixmap.save(&buffer, "png");
677 m_nonClientArea->titleBar()->setIcon(bytes.toBase64().toStdString(), "png");
678}
679
680void QWasmWindow::applyWindowState()
681{
682 QRect newGeom;
683
684 const bool isFullscreen = m_state.testFlag(Qt::WindowFullScreen);
685 const bool isMaximized = m_state.testFlag(Qt::WindowMaximized);
686
687 // The screen ResizeObserver updates the geometry asynchronously, so a container
688 // just revealed may still read as empty here. Re-read it for fullscreen and maximized.
689 if ((isFullscreen || isMaximized) && platformScreen()->geometry().size().isEmpty())
691
692 if (isFullscreen)
693 newGeom = platformScreen()->geometry();
694 else if (isMaximized)
695 newGeom = platformScreen()->availableGeometry().marginsRemoved(frameMargins());
696 else
697 newGeom = normalGeometry();
698
699 dom::syncCSSClassWith(m_decoratedWindow, "has-border", hasBorder());
700 dom::syncCSSClassWith(m_decoratedWindow, "maximized", isMaximized);
701
702 m_nonClientArea->titleBar()->setRestoreVisible(isMaximized);
703 m_nonClientArea->titleBar()->setMaximizeVisible(hasMaximizeButton());
704
705 if (isVisible())
706 QWindowSystemInterface::handleWindowStateChanged(window(), m_state, m_previousWindowState);
707 setGeometry(newGeom);
708}
709
710void QWasmWindow::commitParent(QWasmWindowTreeNode *parent)
711{
712 onParentChanged(m_commitedParent, parent, positionPreferenceFromWindowFlags(window()->flags()));
713 m_commitedParent = parent;
714}
715
716void QWasmWindow::handleKeyEvent(const KeyEvent &event)
717{
718 qCDebug(qLcQpaWasmInputContext) << "handleKeyEvent";
719
720 if (QWasmInputContext *inputContext = activeWasmInputContext()) {
721 // Don't send Qt key events that are part of input composition; let the
722 // input event handler deal with those (keyCode 229 / isComposing).
723 if (event.isComposing || event.keyCode == 229)
724 return;
725 if (processKey(event)) {
726 // The key is already handled in Qt; flag the following input event
727 // so it is ignored by inputCallback (it still reaches it, to keep
728 // word suggestions working) instead of inserting the text twice.
729 inputContext->m_ignoreNextInput = true;
730 }
731 event.webEvent.call<void>("stopImmediatePropagation");
732 } else {
733 if (processKey(event)) {
734 event.webEvent.call<void>("preventDefault");
735 event.webEvent.call<void>("stopPropagation");
736 }
737 }
738}
739
740bool QWasmWindow::processKey(const KeyEvent &event)
741{
742 constexpr bool ProceedToNativeEvent = false;
743 Q_ASSERT(event.type == EventType::KeyDown || event.type == EventType::KeyUp);
744
745 // Copy/cut/paste shortcuts are partly serviced by the browser's native
746 // clipboard, which decides whether the event should be handed to the
747 // browser instead of (or in addition to) sending the Qt key event.
748#if QT_CONFIG(clipboard)
749 const auto clipboardResult =
750 QWasmIntegration::get()->getWasmClipboard()->processKeyboard(event);
751
752 using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult;
753 if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded)
754 return ProceedToNativeEvent;
755#endif
756
757 const auto result = QWindowSystemInterface::handleKeyEvent(
758 0, event.type == EventType::KeyDown ? QEvent::KeyPress : QEvent::KeyRelease, event.key,
759 event.modifiers, event.text, event.autoRepeat);
760
761#if QT_CONFIG(clipboard)
762 if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded)
763 return ProceedToNativeEvent;
764#endif
765
766 return result;
767}
768
769void QWasmWindow::handleInputEvent(emscripten::val event)
770{
771 if (QWasmInputContext *inputContext = activeWasmInputContext())
772 inputContext->inputCallback(event);
773 else
774 m_focusHelper.set("innerHTML", std::string());
775}
776
777void QWasmWindow::handleCompositionStartEvent(emscripten::val event)
778{
779 if (QWasmInputContext *inputContext = activeWasmInputContext())
780 inputContext->compositionStartCallback(event);
781 else
782 m_focusHelper.set("innerHTML", std::string());
783}
784
785void QWasmWindow::handleCompositionUpdateEvent(emscripten::val event)
786{
787 if (QWasmInputContext *inputContext = activeWasmInputContext())
788 inputContext->compositionUpdateCallback(event);
789 else
790 m_focusHelper.set("innerHTML", std::string());
791}
792
793void QWasmWindow::handleCompositionEndEvent(emscripten::val event)
794{
795 if (QWasmInputContext *inputContext = activeWasmInputContext())
796 inputContext->compositionEndCallback(event);
797 else
798 m_focusHelper.set("innerHTML", std::string());
799}
800
801void QWasmWindow::handleBeforeInputEvent(emscripten::val event)
802{
803 if (QWasmInputContext *inputContext = activeWasmInputContext())
804 inputContext->beforeInputCallback(event);
805 else
806 m_focusHelper.set("innerHTML", std::string());
807}
808
809void QWasmWindow::handleFocusinEvent(emscripten::val event)
810{
811 // Determine if the focus moved from outside the html tree for the window
812 emscripten::val relatedTarget = event["relatedTarget"];
813 bool enteringWindow = relatedTarget.isNull() || relatedTarget.isUndefined()
814 || !m_window.call<bool>("contains", relatedTarget);
815
816 if (enteringWindow) {
817 setAsActiveNode();
818 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
819 }
820}
821
822void QWasmWindow::handleFocusoutEvent(emscripten::val event)
823{
824 // Determine if foucs now leaves the html tree for the window
825 emscripten::val relatedTarget = event["relatedTarget"];
826 bool leavingWindow = relatedTarget.isNull() || relatedTarget.isUndefined()
827 || !m_window.call<bool>("contains", relatedTarget);
828
829 if (leavingWindow)
830 QWindowSystemInterface::handleFocusWindowChanged(nullptr, Qt::ActiveWindowFocusReason);
831}
832
833void QWasmWindow::handlePointerEnterLeaveEvent(const PointerEvent &event)
834{
835 if (processPointerEnterLeave(event))
836 event.webEvent.call<void>("preventDefault");
837}
838
839bool QWasmWindow::processPointerEnterLeave(const PointerEvent &event)
840{
842 return false;
843
844 switch (event.type) {
846 const auto pointInScreen = platformScreen()->mapFromLocal(
847 dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint));
848 QWindowSystemInterface::handleEnterEvent(
849 window(), mapFromGlobal(pointInScreen.toPoint()), pointInScreen);
850 break;
851 }
854 break;
855 default:
856 break;
857 }
858
859 return false;
860}
861
862void QWasmWindow::releasePointerGrab(const MouseEvent &event)
863{
864 // We check hasPointerCapture due to the implicit release
865 // browsers do.
866 if (m_capturedPointerId && event.isTargetedForElement(m_window) &&
867 m_window.call<bool>("hasPointerCapture", *m_capturedPointerId)) {
868 m_window.call<void>("releasePointerCapture", *m_capturedPointerId);
869 m_capturedPointerId = std::nullopt;
870 }
871}
872
873void QWasmWindow::processPointer(const PointerEvent &event)
874{
875 // Process pointer events targeted at the window only, and not
876 // for instance events for the accessibility elements.
877 if (!event.isTargetedForElement(m_window))
878 return;
879
880 switch (event.type) {
881 case EventType::PointerDown:
882 m_capturedPointerId = event.pointerId;
883 m_window.call<void>("setPointerCapture", event.pointerId);
884
885 // Activate (focus) the top-level window for this window on click
886 if (!window()->flags().testFlag(Qt::WindowDoesNotAcceptFocus)
887 && QGuiApplication::focusWindow() != window()) {
888 QWindow *win = window();
889 while (!win->isTopLevel())
890 win = win->parent(QWindow::ExcludeTransients);
891 win->requestActivate();
892 }
893
894 // Prevent the browser's default focus handling from
895 // moving focus away from the focus helper element.
896 event.webEvent.call<void>("preventDefault");
897
898 break;
900 releasePointerGrab(event);
901 break;
902 default:
903 break;
904 };
905
906 const bool eventAccepted = deliverPointerEvent(event);
907 if (!eventAccepted && event.type == EventType::PointerDown)
908 QGuiApplicationPrivate::instance()->closeAllPopups();
909
910 if (eventAccepted) {
911 event.webEvent.call<void>("preventDefault");
912 event.webEvent.call<void>("stopPropagation");
913 }
914}
915
916bool QWasmWindow::deliverPointerEvent(const PointerEvent &event)
917{
918 const auto pointInScreen = platformScreen()->mapFromLocal(
919 dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint));
920
921 const auto geometryF = platformScreen()->geometry().toRectF();
922 const QPointF targetPointClippedToScreen(
923 qBound(geometryF.left(), pointInScreen.x(), geometryF.right()),
924 qBound(geometryF.top(), pointInScreen.y(), geometryF.bottom()));
925
926 if (event.pointerType == PointerType::Mouse) {
927 const QEvent::Type eventType =
928 MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::Client);
929
930 return eventType != QEvent::None
931 && QWindowSystemInterface::handleMouseEvent(
932 window(), QWasmIntegration::getTimestamp(),
933 window()->mapFromGlobal(targetPointClippedToScreen),
934 targetPointClippedToScreen, event.mouseButtons, event.mouseButton,
935 eventType, event.modifiers);
936 }
937
938 if (event.pointerType == PointerType::Pen) {
939 qreal pressure;
940 switch (event.type) {
943 pressure = event.pressure;
944 break;
946 pressure = 0.0;
947 break;
948 default:
949 return false;
950 }
951 // Tilt in the browser is in the range +-90, but QTabletEvent only goes to +-60.
952 qreal xTilt = qBound(-60.0, event.tiltX, 60.0);
953 qreal yTilt = qBound(-60.0, event.tiltY, 60.0);
954 // Barrel rotation is reported as 0 to 359, but QTabletEvent wants a signed value.
955 qreal rotation = event.twist > 180.0 ? 360.0 - event.twist : event.twist;
956 return QWindowSystemInterface::handleTabletEvent(
957 window(), QWasmIntegration::getTimestamp(), platformScreen()->tabletDevice(),
958 window()->mapFromGlobal(targetPointClippedToScreen),
959 targetPointClippedToScreen, event.mouseButtons, pressure, xTilt, yTilt,
960 event.tangentialPressure, rotation, event.modifiers);
961 }
962
963 QWindowSystemInterface::TouchPoint *touchPoint;
964
965 QPointF pointInTargetWindowCoords =
966 QPointF(window()->mapFromGlobal(targetPointClippedToScreen));
967 QPointF normalPosition(pointInTargetWindowCoords.x() / window()->width(),
968 pointInTargetWindowCoords.y() / window()->height());
969
970 const auto tp = m_pointerIdToTouchPoints.find(event.pointerId);
971 if (event.pointerType != PointerType::Pen && tp != m_pointerIdToTouchPoints.end()) {
972 touchPoint = &tp.value();
973 } else {
974 touchPoint = &m_pointerIdToTouchPoints
975 .insert(event.pointerId, QWindowSystemInterface::TouchPoint())
976 .value();
977
978 // Assign touch point id. TouchPoint::id is int, but QGuiApplicationPrivate::processTouchEvent()
979 // will not synthesize mouse events for touch points with negative id; use the absolute value for
980 // the touch point id.
981 touchPoint->id = qAbs(event.pointerId);
982
983 touchPoint->state = QEventPoint::State::Pressed;
984 }
985
986 const bool stationaryTouchPoint = (normalPosition == touchPoint->normalPosition);
987 touchPoint->normalPosition = normalPosition;
988 touchPoint->area = QRectF(targetPointClippedToScreen, QSizeF(event.width, event.height))
989 .translated(-event.width / 2, -event.height / 2);
990 touchPoint->pressure = event.pressure;
991
992 switch (event.type) {
993 case EventType::PointerUp:
994 touchPoint->state = QEventPoint::State::Released;
995 break;
996 case EventType::PointerMove:
997 touchPoint->state = (stationaryTouchPoint ? QEventPoint::State::Stationary
998 : QEventPoint::State::Updated);
999 break;
1000 default:
1001 break;
1002 }
1003
1004 QList<QWindowSystemInterface::TouchPoint> touchPointList;
1005 touchPointList.reserve(m_pointerIdToTouchPoints.size());
1006 std::transform(m_pointerIdToTouchPoints.begin(), m_pointerIdToTouchPoints.end(),
1007 std::back_inserter(touchPointList),
1008 [](const QWindowSystemInterface::TouchPoint &val) { return val; });
1009
1010 if (event.type == EventType::PointerUp || event.type == EventType::PointerCancel)
1011 m_pointerIdToTouchPoints.remove(event.pointerId);
1012
1013 return event.type == EventType::PointerCancel
1014 ? QWindowSystemInterface::handleTouchCancelEvent(
1015 window(), QWasmIntegration::getTimestamp(), platformScreen()->touchDevice(),
1016 event.modifiers)
1017 : QWindowSystemInterface::handleTouchEvent(
1018 window(), QWasmIntegration::getTimestamp(), platformScreen()->touchDevice(),
1019 touchPointList, event.modifiers);
1020}
1021
1022void QWasmWindow::handleWheelEvent(const emscripten::val &event)
1023{
1024 if (processWheel(WheelEvent(EventType::Wheel, event)))
1025 event.call<void>("preventDefault");
1026}
1027
1028bool QWasmWindow::processWheel(const WheelEvent &event)
1029{
1030 // Web scroll deltas are inverted from Qt deltas - negate.
1031 const int scrollFactor = -([&event]() {
1032 switch (event.deltaMode) {
1033 case DeltaMode::Pixel:
1034 return 1;
1035 case DeltaMode::Line:
1036 return 12;
1037 case DeltaMode::Page:
1038 return 20;
1039 };
1040 })();
1041
1042 const auto pointInScreen = platformScreen()->mapFromLocal(
1043 dom::mapPoint(event.target(), platformScreen()->element(), event.localPoint));
1044
1045 return QWindowSystemInterface::handleWheelEvent(
1046 window(), QWasmIntegration::getTimestamp(), window()->mapFromGlobal(pointInScreen),
1047 pointInScreen, (event.delta * scrollFactor).toPoint(),
1048 (event.delta * scrollFactor).toPoint(), event.modifiers, Qt::NoScrollPhase,
1049 Qt::MouseEventNotSynthesized, event.webkitDirectionInvertedFromDevice);
1050}
1051
1052// Fix top level window flags in case only the type flags are passed.
1053Qt::WindowFlags QWasmWindow::fixTopLevelWindowFlags(Qt::WindowFlags flags) const
1054{
1055 if (!(flags.testFlag(Qt::CustomizeWindowHint))) {
1056 if (flags.testFlag(Qt::Window)) {
1057 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
1058 |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
1059 }
1060 if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Tool))
1061 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
1062
1063 if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
1064 flags |= Qt::FramelessWindowHint;
1065 }
1066 return flags;
1067}
1068
1069bool QWasmWindow::shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const
1070{
1071 if (!transientParent())
1072 return false;
1073
1074 if (isModal())
1075 return true;
1076
1077 if (flags.testFlag(Qt::Tool) ||
1078 flags.testFlag(Qt::SplashScreen) ||
1079 flags.testFlag(Qt::ToolTip) ||
1080 flags.testFlag(Qt::Popup))
1081 {
1082 return true;
1083 }
1084
1085 return false;
1086}
1087
1088QWasmWindowStack<>::PositionPreference QWasmWindow::positionPreferenceFromWindowFlags(Qt::WindowFlags flags) const
1089{
1090 flags = fixTopLevelWindowFlags(flags);
1091
1092 if (flags.testFlag(Qt::WindowStaysOnTopHint))
1093 return QWasmWindowStack<>::PositionPreference::StayOnTop;
1094 if (flags.testFlag(Qt::WindowStaysOnBottomHint))
1095 return QWasmWindowStack<>::PositionPreference::StayOnBottom;
1096 if (shouldBeAboveTransientParentFlags(flags))
1097 return QWasmWindowStack<>::PositionPreference::StayAboveTransientParent;
1098 return QWasmWindowStack<>::PositionPreference::Regular;
1099}
1100
1101// Returns the wasm input context if it exists and is active, otherwise nullptr.
1102QWasmInputContext *QWasmWindow::activeWasmInputContext() const
1103{
1104 // The input context is switched off while accessibility is enabled. Its key
1105 // path does not prevent the browser default, so Tab in a text field moved
1106 // focus in the browser on top of the move Qt had just made.
1107 if (QWasmAccessibility::isEnabled())
1108 return nullptr;
1109 QWasmInputContext *inputContext = QWasmIntegration::get()->wasmInputContext();
1110 return inputContext && inputContext->isActive() ? inputContext : nullptr;
1111}
1112
1114{
1115 return m_normalGeometry;
1116}
1117
1119{
1120 return screen()->devicePixelRatio();
1121}
1122
1124{
1125 m_compositor->requestUpdateWindow(this, QRect(QPoint(0, 0), geometry().size()), QWasmCompositor::UpdateRequestDelivery);
1126}
1127
1128bool QWasmWindow::hasFrame() const
1129{
1130 return !m_flags.testFlag(Qt::FramelessWindowHint);
1131}
1132
1133bool QWasmWindow::hasBorder() const
1134{
1135 return hasFrame() && !m_state.testFlag(Qt::WindowFullScreen) && !m_flags.testFlag(Qt::SubWindow)
1136 && !windowIsPopupType(m_flags) && !parent();
1137}
1138
1139bool QWasmWindow::hasTitleBar() const
1140{
1141 return hasBorder() && m_flags.testFlag(Qt::WindowTitleHint);
1142}
1143
1144bool QWasmWindow::hasShadow() const
1145{
1146 return hasBorder() && !m_flags.testFlag(Qt::NoDropShadowWindowHint);
1147}
1148
1149bool QWasmWindow::hasMaximizeButton() const
1150{
1151 return !m_state.testFlag(Qt::WindowMaximized) && m_flags.testFlag(Qt::WindowMaximizeButtonHint);
1152}
1153
1154bool QWasmWindow::windowIsPopupType(Qt::WindowFlags flags) const
1155{
1156 if (flags.testFlag(Qt::Tool))
1157 return false; // Qt::Tool has the Popup bit set but isn't an actual Popup window
1158
1159 return (flags.testFlag(Qt::Popup));
1160}
1161
1163{
1164 QWindow *modalWindow;
1165 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
1166 static_cast<QWasmWindow *>(modalWindow->handle())->requestActivateWindow();
1167 return;
1168 }
1169
1170 raise();
1171 focus();
1172
1173 // Don't call base class requestActivateWindow() or send as focus
1174 // changed event here; that is done in the focusin event handler
1175 // when native foucs actually moves.
1176}
1177
1178// Moves native focus to this window, via the foucus helper or the input element.
1180{
1181 if (QWasmAccessibility::isEnabled())
1182 return;
1183
1184 emscripten::val &element = target == InputFocus ? m_inputElement : m_focusHelper;
1185 element.call<void>("focus");
1186}
1187
1189{
1190 m_focusHelper.call<void>("setAttribute", std::string("aria-hidden"), std::string("true"));
1191 m_inputElement.call<void>("setAttribute", std::string("aria-hidden"), std::string("true"));
1192}
1193
1195{
1196 Q_UNUSED(grab);
1197 return false;
1198}
1199
1200bool QWasmWindow::windowEvent(QEvent *event)
1201{
1202 switch (event->type()) {
1203 case QEvent::WindowBlocked:
1204 m_decoratedWindow["classList"].call<void>("add", emscripten::val("blocked"));
1205 return false; // Propagate further
1206 case QEvent::WindowUnblocked:;
1207 m_decoratedWindow["classList"].call<void>("remove", emscripten::val("blocked"));
1208 return false; // Propagate further
1209 default:
1210 return QPlatformWindow::windowEvent(event);
1211 }
1212}
1213
1214void QWasmWindow::setMask(const QRegion &region)
1215{
1216 if (region.isEmpty()) {
1217 m_decoratedWindow["style"].set("clipPath", emscripten::val(""));
1218 return;
1219 }
1220
1221 std::ostringstream cssClipPath;
1222 cssClipPath << "path('";
1223 for (const auto &rect : region) {
1224 const auto cssRect = rect.adjusted(0, 0, 1, 1);
1225 cssClipPath << "M " << cssRect.left() << " " << cssRect.top() << " ";
1226 cssClipPath << "L " << cssRect.right() << " " << cssRect.top() << " ";
1227 cssClipPath << "L " << cssRect.right() << " " << cssRect.bottom() << " ";
1228 cssClipPath << "L " << cssRect.left() << " " << cssRect.bottom() << " z ";
1229 }
1230 cssClipPath << "')";
1231 m_decoratedWindow["style"].set("clipPath", emscripten::val(cssClipPath.str()));
1232}
1233
1234void QWasmWindow::onTransientParentChanged(QWindow *newTransientParent)
1235{
1236 Q_UNUSED(newTransientParent);
1237
1238 const auto positionPreference = positionPreferenceFromWindowFlags(window()->flags());
1239 QWasmWindowTreeNode::onParentChanged(parentNode(), nullptr, positionPreference);
1240 QWasmWindowTreeNode::onParentChanged(nullptr, parentNode(), positionPreference);
1241}
1242
1244{
1245 const auto positionPreference = positionPreferenceFromWindowFlags(window()->flags());
1246 QWasmWindowTreeNode::onParentChanged(parentNode(), nullptr, positionPreference);
1247 QWasmWindowTreeNode::onParentChanged(nullptr, parentNode(), positionPreference);
1248}
1249
1250void QWasmWindow::setParent(const QPlatformWindow *)
1251{
1252 // The window flags depend on whether we are a
1253 // child window or not, so update them here.
1254 setWindowFlags(window()->flags());
1255
1256 commitParent(parentNode());
1257}
1258
1260{
1261 return "!qtwindow" + std::to_string(m_winId);
1262}
1263
1265{
1266 return m_window;
1267}
1268
1270{
1271 if (parent())
1272 return static_cast<QWasmWindow *>(parent());
1273 return platformScreen();
1274}
1275
1277{
1278 return this;
1279}
1280
1281void QWasmWindow::onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
1282 QWasmWindowStack<>::PositionPreference positionPreference)
1283{
1284 if (previous)
1285 previous->containerElement().call<void>("removeChild", m_decoratedWindow);
1286 if (current)
1287 current->containerElement().call<void>("appendChild", m_decoratedWindow);
1288 QWasmWindowTreeNode::onParentChanged(previous, current, positionPreference);
1289
1290 QWasmScreen *screen = platformScreen();
1291 if (previous)
1292 m_compositor->onWindowRemoved(this);
1293 if (current) {
1294 m_compositor->onWindowAdded(this);
1295
1296 // The first top-level window inserted as maximized/fullscreen stays on bottom.
1297 const auto state = window()->windowState();
1298 const bool isMaxFull = state & (Qt::WindowMaximized | Qt::WindowFullScreen);
1299 if (current == screen && screen->topLevelWindowCount() == 1 && isMaxFull)
1300 window()->setFlag(Qt::WindowStaysOnBottomHint);
1301 }
1302}
1303
1304QT_END_NAMESPACE
QRect window() const
Returns the window rectangle.
void onWindowAdded(QWasmWindow *window)
void onWindowRemoved(QWasmWindow *window)
QWasmCompositor(QWasmScreen *screen)
static QWasmIntegration * get()
QWasmInputContext * wasmInputContext() const
static void destroyWebGLContext(QPlatformSurface *surface)
emscripten::val element() const
void updateQScreenSize()
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
qreal devicePixelRatio() const override
Reimplement this function in subclass to return the device pixel ratio for the window.
QRect normalGeometry() const override
Returns the geometry of a window in 'normal' state (neither maximized, fullscreen nor minimized) for ...
void focus(FocusTarget target=WindowFocus)
QSurfaceFormat format() const override
Returns the actual surface format of the window.
void setParent(const QPlatformWindow *window) final
This function is called to enable native child window in QPA.
static QWasmWindow * fromWindow(const QWindow *window)
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
void setWindowTitle(const QString &title) override
Reimplement to set the window title to title.
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
WId winId() const override
Reimplement in subclasses to return a handle to the native window.
static emscripten::val focusedWindowInputElement()
void onToggleMaximized()
std::string canvasSelector() const
static QWasmWindow * focusWindow()
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore, WId nativeHandle)
friend class QWasmCompositor
void onNonClientAreaInteraction()
void setGeometry(const QRect &) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
bool setMouseGrabEnabled(bool grab) final
bool onNonClientEvent(const PointerEvent &event)
void setWindowCursor(QByteArray cssCursorName)
void setZOrder(int order)
void setMask(const QRegion &region) final
Reimplement to be able to let Qt set the mask of a window.
QWasmWindow * transientParent() const
QWasmWindowTreeNode * parentNode() final
void onModalityChanged()
void initialize() override
Called as part of QWindow::create(), after constructing the window.
bool isModal() const
Qt::WindowFlags windowFlags() const
bool isVisible() const
void registerEventHandlers()
~QWasmWindow() final
void onMaximizeClicked()
void requestUpdate() override
Requests an QEvent::UpdateRequest event.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
void onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current, QWasmWindowStack<>::PositionPreference positionPreference) final
void onRestoreClicked()
void setOpacity(qreal level) override
Reimplement to be able to let Qt set the opacity level of a window.
void onActivationChanged(bool active)
void onAccessibilityEnable()
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
void setWindowIcon(const QIcon &icon) override
Reimplement to set the window icon to icon.
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
QMargins frameMargins() const override
void onCloseClicked()
QWasmWindow * asWasmWindow() final
bool windowEvent(QEvent *event) final
Reimplement this method to be able to do any platform specific event handling.
QWasmScreen * platformScreen() const
emscripten::val containerElement() final
void setWindowFlags(Qt::WindowFlags flags) override
Requests setting the window flags of this surface to flags.
Definition qwasmdom.h:30
Q_GUI_EXPORT int qt_defaultDpiX()
Definition qfont.cpp:134
QDebug Q_GUI_EXPORT & operator<<(QDebug &s, const QVectorPath &path)
DeltaMode
Definition qwasmevent.h:53
PointerType
Definition qwasmevent.h:41
EventType
Definition qwasmevent.h:23
WindowArea
Definition qwasmevent.h:48
bool isTargetedForElement(emscripten::val element) const
int keyCode
Definition qwasmevent.h:78
bool isComposing
Definition qwasmevent.h:77
PointerType pointerType
Definition qwasmevent.h:132
DeltaMode deltaMode
Definition qwasmevent.h:161