12#include <qpa/qwindowsysteminterface.h>
14#include <QtCore/qassert.h>
18WebImageButton::Callbacks::Callbacks() =
default;
20 std::function<
void()> onClick)
23 Q_ASSERT_X(!!m_onInteraction == !!m_onClick, Q_FUNC_INFO,
24 "Both callbacks need to be either null or non-null");
33 return m_onInteraction();
46 m_imgElement.set(
"draggable",
false);
48 m_containerElement[
"classList"].call<
void>(
"add", emscripten::val(
"image-button"));
49 m_containerElement.call<
void>(
"appendChild", m_imgElement);
57 if (!m_webClickEventCallback) {
58 m_webMouseDownEventCallback = std::make_unique<qstdweb::EventCallback>(
59 m_containerElement,
"pointerdown", [
this](emscripten::val event) {
60 event.call<
void>(
"preventDefault");
61 event.call<
void>(
"stopPropagation");
62 m_callbacks.onInteraction();
64 m_webClickEventCallback = std::make_unique<qstdweb::EventCallback>(
65 m_containerElement,
"click", [
this](emscripten::val event) {
66 m_callbacks.onClick();
67 event.call<
void>(
"stopPropagation");
71 m_webMouseDownEventCallback.reset();
72 m_webClickEventCallback.reset();
74 dom::syncCSSClassWith(m_containerElement,
"action-button", !!callbacks);
75 m_callbacks = std::move(callbacks);
80 m_imgElement.set(
"src",
81 "data:image/" + std::string(format) +
";base64," + std::string(imageData));
86 m_containerElement[
"style"].set(
"display", visible ?
"flex" :
"none");
95 Q_ASSERT_X(m_resizer, Q_FUNC_INFO,
"Resizer cannot be null");
97 m_element[
"classList"].call<
void>(
"add", emscripten::val(
"resize-outline"));
98 m_element[
"classList"].call<
void>(
"add", emscripten::val(cssClassNameForEdges(edges)));
100 parentElement.call<
void>(
"appendChild", m_element);
102 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
103 m_element,
"pointerdown", [
this](emscripten::val event) {
104 if (!onPointerDown(PointerEvent(EventType::PointerDown, event)))
106 m_resizer->onInteraction();
107 event.call<
void>(
"preventDefault");
108 event.call<
void>(
"stopPropagation");
110 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
111 m_element,
"pointermove", [
this](emscripten::val event) {
112 if (onPointerMove(PointerEvent(EventType::PointerMove, event)))
113 event.call<
void>(
"preventDefault");
115 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
116 m_element,
"pointerup", [
this](emscripten::val event) {
117 if (onPointerUp(PointerEvent(EventType::PointerUp, event))) {
118 event.call<
void>(
"preventDefault");
119 event.call<
void>(
"stopPropagation");
126 m_element[
"parentElement"].call<emscripten::val>(
"removeChild", m_element);
133 m_element.call<
void>(
"setPointerCapture", event.pointerId);
136 m_resizer->startResize(m_edges, event);
145 m_resizer->continueResize(event);
154 m_resizer->finishResize();
155 m_element.call<
void>(
"releasePointerCapture", event.pointerId);
156 m_capturedPointerId = -1;
163 Q_ASSERT_X(m_window, Q_FUNC_INFO,
"Window must not be null");
165 constexpr std::array<
int, 8> ResizeEdges = { Qt::TopEdge | Qt::LeftEdge,
167 Qt::TopEdge | Qt::RightEdge,
170 Qt::BottomEdge | Qt::LeftEdge,
172 Qt::BottomEdge | Qt::RightEdge };
173 std::transform(std::begin(ResizeEdges), std::end(ResizeEdges), std::back_inserter(m_elements),
174 [parentElement,
this](
int edges) {
175 return std::make_unique<ResizerElement>(parentElement,
176 Qt::Edges::fromInt(edges),
this);
183 const auto *window = m_window->window();
184 const auto minShrink =
QPoint(window->minimumWidth() - window->geometry().width(),
185 window->minimumHeight() - window->geometry().height());
186 const auto maxGrow =
QPoint(window->maximumWidth() - window->geometry().width(),
187 window->maximumHeight() - window->geometry().height());
189 const auto frameRect =
190 QRectF::fromDOMRect(m_windowElement.call<emscripten::val>(
"getBoundingClientRect"));
191 auto containerGeometry =
192 QRectF::fromDOMRect(m_window->parentNode()->containerElement().call<emscripten::val>(
193 "getBoundingClientRect"));
195 const int maxGrowTop = frameRect.top() - containerGeometry.top();
207 Q_ASSERT_X(!m_currentResizeData, Q_FUNC_INFO,
"Another resize in progress");
209 m_currentResizeData.reset(
new ResizeData{
210 .edges = resizeEdges,
211 .originInScreenCoords = dom::mapPoint(
212 event.target(), m_window->platformScreen()->element(), event.localPoint),
216 m_currentResizeData->minShrink = resizeConstraints.minShrink;
218 m_currentResizeData->maxGrow =
219 QPoint(resizeConstraints.maxGrow.x(),
220 std::min(resizeEdges & Qt::Edge::TopEdge ? resizeConstraints.maxGrowTop : INT_MAX,
221 resizeConstraints.maxGrow.y()));
223 m_currentResizeData->initialBounds = m_window->window()->geometry();
228 const auto pointInScreen =
230 const auto amount = (pointInScreen - m_currentResizeData->originInScreenCoords).toPoint();
231 const QPoint cappedGrowVector(
232 std::min(m_currentResizeData->maxGrow.x(),
233 std::max(m_currentResizeData->minShrink.x(),
234 (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -amount.x()
235 : (m_currentResizeData->edges & Qt::Edge::RightEdge)
238 std::min(m_currentResizeData->maxGrow.y(),
239 std::max(m_currentResizeData->minShrink.y(),
240 (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -amount.y()
241 : (m_currentResizeData->edges & Qt::Edge::BottomEdge)
245 auto bounds = m_currentResizeData->initialBounds.adjusted(
246 (m_currentResizeData->edges & Qt::Edge::LeftEdge) ? -cappedGrowVector.x() : 0,
247 (m_currentResizeData->edges & Qt::Edge::TopEdge) ? -cappedGrowVector.y() : 0,
248 (m_currentResizeData->edges & Qt::Edge::RightEdge) ? cappedGrowVector.x() : 0,
249 (m_currentResizeData->edges & Qt::Edge::BottomEdge) ? cappedGrowVector.y() : 0);
251 m_window->window()->setGeometry(bounds);
256 Q_ASSERT_X(m_currentResizeData, Q_FUNC_INFO,
"No resize in progress");
257 m_currentResizeData.reset();
265 m_icon = std::make_unique<WebImageButton>();
266 m_icon->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::QtLogo),
"svg+xml");
267 m_element.call<
void>(
"appendChild", m_icon->htmlElement());
268 m_element.set(
"className",
"title-bar");
270 auto spacer = dom::document().call<
emscripten::val>(
"createElement", emscripten::val(
"div"));
271 spacer[
"style"].set(
"width",
"4px");
272 m_element.call<
void>(
"appendChild", spacer);
274 m_label.set(
"className",
"window-name");
276 m_element.call<
void>(
"appendChild", m_label);
278 spacer = dom::document().call<
emscripten::val>(
"createElement", emscripten::val(
"div"));
279 spacer.set(
"className",
"spacer");
280 m_element.call<
void>(
"appendChild", spacer);
282 m_restore = std::make_unique<WebImageButton>();
283 m_restore->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Restore),
285 m_restore->setCallbacks(
286 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
287 [
this]() { m_window->onRestoreClicked(); }));
289 m_element.call<
void>(
"appendChild", m_restore->htmlElement());
291 m_maximize = std::make_unique<WebImageButton>();
292 m_maximize->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Maximize),
294 m_maximize->setCallbacks(
295 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
296 [
this]() { m_window->onMaximizeClicked(); }));
298 m_element.call<
void>(
"appendChild", m_maximize->htmlElement());
300 m_close = std::make_unique<WebImageButton>();
301 m_close->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::X),
"svg+xml");
302 m_close->setCallbacks(
303 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
304 [
this]() { m_window->onCloseClicked(); }));
306 m_element.call<
void>(
"appendChild", m_close->htmlElement());
308 parentElement.call<
void>(
"appendChild", m_element);
310 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
311 m_element,
"pointerdown", [
this](emscripten::val event) {
312 if (!onPointerDown(PointerEvent(EventType::PointerDown, event)))
314 m_window->onNonClientAreaInteraction();
315 event.call<
void>(
"preventDefault");
316 event.call<
void>(
"stopPropagation");
318 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
319 m_element,
"pointermove", [
this](emscripten::val event) {
320 if (onPointerMove(PointerEvent(EventType::PointerMove, event))) {
321 event.call<
void>(
"preventDefault");
324 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
325 m_element,
"pointerup", [
this](emscripten::val event) {
326 if (onPointerUp(PointerEvent(EventType::PointerUp, event))) {
327 event.call<
void>(
"preventDefault");
328 event.call<
void>(
"stopPropagation");
331 m_doubleClickEvent = std::make_unique<qstdweb::EventCallback>(
332 m_element,
"dblclick", [
this](emscripten::val event) {
333 if (onDoubleClick()) {
334 event.call<
void>(
"preventDefault");
335 event.call<
void>(
"stopPropagation");
342 m_element[
"parentElement"].call<emscripten::val>(
"removeChild", m_element);
347 m_label.set(
"innerText", emscripten::val(title.toStdString()));
352 m_restore->setVisible(visible);
357 m_maximize->setVisible(visible);
362 m_close->setVisible(visible);
367 m_icon->setImage(imageData, format);
372 m_element[
"style"].set(
"width", std::to_string(width) +
"px");
377 return QRectF::fromDOMRect(m_element.call<emscripten::val>(
"getBoundingClientRect"));
382 m_element.call<
void>(
"setPointerCapture", event.pointerId);
385 m_moveStartWindowPosition = m_window->window()->position();
386 m_moveStartPoint = clipPointWithScreen(event.localPoint);
396 const QPoint delta = (clipPointWithScreen(event.localPoint) - m_moveStartPoint).toPoint();
398 m_window->window()->setPosition(m_moveStartWindowPosition + delta);
408 m_element.call<
void>(
"releasePointerCapture", event.pointerId);
409 m_capturedPointerId = -1;
420QPointF
TitleBar::clipPointWithScreen(
const QPointF &pointInTitleBarCoords)
const
423 QRectF::fromDOMRect(m_window->parentNode()->containerElement().call<emscripten::val>(
424 "getBoundingClientRect"));
425 const auto p = dom::mapPoint(m_element, m_window->parentNode()->containerElement(),
426 pointInTitleBarCoords);
428 auto result = QPointF(qBound(0., qreal(p.x()), containerRect.width()),
429 qBound(0., qreal(p.y()), containerRect.height()));
430 return m_window->parent() ? result : m_window
->platformScreen()->mapFromLocal(result).toPoint();
438 updateResizability();
445 m_titleBar->setWidth(width);
450 updateResizability();
455 const auto resizeConstraints = m_resizer->getResizeConstraints();
456 const bool nonResizable = resizeConstraints.minShrink.isNull()
457 && resizeConstraints.maxGrow.isNull() && resizeConstraints.maxGrowTop == 0;
458 dom::syncCSSClassWith(m_qtWindowElement,
"no-resize", nonResizable);
void onClientAreaWidthChange(int width)
void propagateSizeHints()
NonClientArea(QWasmWindow *window, emscripten::val containerElement)
\inmodule QtCore\reentrant
emscripten::val element() const
friend class QWasmCompositor
void onNonClientAreaInteraction()
bool onNonClientEvent(const PointerEvent &event)
QWasmScreen * platformScreen() const
bool onPointerUp(const PointerEvent &event)
ResizerElement(ResizerElement &&other)
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)
void setIcon(std::string_view imageData, std::string_view format)
void setCloseVisible(bool visible)
void setRestoreVisible(bool visible)
Combined button and popup list for selecting options.