11#include <qpa/qwindowsysteminterface.h>
13#include <QtCore/qassert.h>
17WebImageButton::Callbacks::Callbacks() =
default;
19 std::function<
void()> onClick)
22 Q_ASSERT_X(!!m_onInteraction == !!m_onClick, Q_FUNC_INFO,
23 "Both callbacks need to be either null or non-null");
32 return m_onInteraction();
45 m_imgElement.set(
"draggable",
false);
47 m_containerElement[
"classList"].call<
void>(
"add", emscripten::val(
"image-button"));
48 m_containerElement.call<
void>(
"appendChild", m_imgElement);
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();
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");
70 m_webMouseDownEventCallback.reset();
71 m_webClickEventCallback.reset();
73 dom::syncCSSClassWith(m_containerElement,
"action-button", !!callbacks);
74 m_callbacks = std::move(callbacks);
79 m_imgElement.set(
"src",
80 "data:image/" + std::string(format) +
";base64," + std::string(imageData));
85 m_containerElement[
"style"].set(
"display", visible ?
"flex" :
"none");
94 Q_ASSERT_X(m_resizer, Q_FUNC_INFO,
"Resizer cannot be null");
96 m_element[
"classList"].call<
void>(
"add", emscripten::val(
"resize-outline"));
97 m_element[
"classList"].call<
void>(
"add", emscripten::val(cssClassNameForEdges(edges)));
99 parentElement.call<
void>(
"appendChild", m_element);
101 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
102 m_element,
"pointerdown", [
this](emscripten::val event) {
103 if (!onPointerDown(PointerEvent(EventType::PointerDown, event)))
105 m_resizer->onInteraction();
106 event.call<
void>(
"preventDefault");
107 event.call<
void>(
"stopPropagation");
109 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
110 m_element,
"pointermove", [
this](emscripten::val event) {
111 if (onPointerMove(PointerEvent(EventType::PointerMove, event)))
112 event.call<
void>(
"preventDefault");
114 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
115 m_element,
"pointerup", [
this](emscripten::val event) {
116 if (onPointerUp(PointerEvent(EventType::PointerUp, event))) {
117 event.call<
void>(
"preventDefault");
118 event.call<
void>(
"stopPropagation");
125 m_element[
"parentElement"].call<emscripten::val>(
"removeChild", m_element);
132 m_element.call<
void>(
"setPointerCapture", event.pointerId);
135 m_resizer->startResize(m_edges, event);
144 m_resizer->continueResize(event);
153 m_resizer->finishResize();
154 m_element.call<
void>(
"releasePointerCapture", event.pointerId);
155 m_capturedPointerId = -1;
162 Q_ASSERT_X(m_window, Q_FUNC_INFO,
"Window must not be null");
164 constexpr std::array<
int, 8> ResizeEdges = { Qt::TopEdge | Qt::LeftEdge,
166 Qt::TopEdge | Qt::RightEdge,
169 Qt::BottomEdge | Qt::LeftEdge,
171 Qt::BottomEdge | Qt::RightEdge };
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);
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());
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"));
194 const int maxGrowTop = frameRect.top() - containerGeometry.top();
206 Q_ASSERT_X(!m_currentResizeData, Q_FUNC_INFO,
"Another resize in progress");
208 m_currentResizeData.reset(
new ResizeData{
209 .edges = resizeEdges,
210 .originInScreenCoords = dom::mapPoint(
211 event.target(), m_window->platformScreen()->element(), event.localPoint),
215 m_currentResizeData->minShrink = resizeConstraints.minShrink;
217 m_currentResizeData->maxGrow =
218 QPoint(resizeConstraints.maxGrow.x(),
219 std::min(resizeEdges & Qt::Edge::TopEdge ? resizeConstraints.maxGrowTop : INT_MAX,
220 resizeConstraints.maxGrow.y()));
222 m_currentResizeData->initialBounds = m_window->window()->geometry();
227 const auto pointInScreen =
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)
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)
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);
250 m_window->window()->setGeometry(bounds);
255 Q_ASSERT_X(m_currentResizeData, Q_FUNC_INFO,
"No resize in progress");
256 m_currentResizeData.reset();
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");
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);
273 m_label.set(
"className",
"window-name");
275 m_element.call<
void>(
"appendChild", m_label);
277 spacer = dom::document().call<
emscripten::val>(
"createElement", emscripten::val(
"div"));
278 spacer.set(
"className",
"spacer");
279 m_element.call<
void>(
"appendChild", spacer);
281 m_restore = std::make_unique<WebImageButton>();
282 m_restore->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Restore),
284 m_restore->setCallbacks(
285 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
286 [
this]() { m_window->onRestoreClicked(); }));
288 m_element.call<
void>(
"appendChild", m_restore->htmlElement());
290 m_maximize = std::make_unique<WebImageButton>();
291 m_maximize->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::Maximize),
293 m_maximize->setCallbacks(
294 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
295 [
this]() { m_window->onMaximizeClicked(); }));
297 m_element.call<
void>(
"appendChild", m_maximize->htmlElement());
299 m_close = std::make_unique<WebImageButton>();
300 m_close->setImage(Base64IconStore::get()->getIcon(Base64IconStore::IconType::X),
"svg+xml");
301 m_close->setCallbacks(
302 WebImageButton::Callbacks([
this]() { m_window->onNonClientAreaInteraction(); },
303 [
this]() { m_window->onCloseClicked(); }));
305 m_element.call<
void>(
"appendChild", m_close->htmlElement());
307 parentElement.call<
void>(
"appendChild", m_element);
309 m_mouseDownEvent = std::make_unique<qstdweb::EventCallback>(
310 m_element,
"pointerdown", [
this](emscripten::val event) {
311 if (!onPointerDown(PointerEvent(EventType::PointerDown, event)))
313 m_window->onNonClientAreaInteraction();
314 event.call<
void>(
"preventDefault");
315 event.call<
void>(
"stopPropagation");
317 m_mouseMoveEvent = std::make_unique<qstdweb::EventCallback>(
318 m_element,
"pointermove", [
this](emscripten::val event) {
319 if (onPointerMove(PointerEvent(EventType::PointerMove, event))) {
320 event.call<
void>(
"preventDefault");
323 m_mouseUpEvent = std::make_unique<qstdweb::EventCallback>(
324 m_element,
"pointerup", [
this](emscripten::val event) {
325 if (onPointerUp(PointerEvent(EventType::PointerUp, event))) {
326 event.call<
void>(
"preventDefault");
327 event.call<
void>(
"stopPropagation");
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");
341 m_element[
"parentElement"].call<emscripten::val>(
"removeChild", m_element);
346 m_label.set(
"innerText", emscripten::val(title.toStdString()));
351 m_restore->setVisible(visible);
356 m_maximize->setVisible(visible);
361 m_close->setVisible(visible);
366 m_icon->setImage(imageData, format);
371 m_element[
"style"].set(
"width", std::to_string(width) +
"px");
376 return QRectF::fromDOMRect(m_element.call<emscripten::val>(
"getBoundingClientRect"));
381 m_element.call<
void>(
"setPointerCapture", event.pointerId);
384 m_moveStartWindowPosition = m_window->window()->position();
385 m_moveStartPoint = clipPointWithScreen(event.localPoint);
395 const QPoint delta = (clipPointWithScreen(event.localPoint) - m_moveStartPoint).toPoint();
397 m_window->window()->setPosition(m_moveStartWindowPosition + delta);
407 m_element.call<
void>(
"releasePointerCapture", event.pointerId);
408 m_capturedPointerId = -1;
419QPointF
TitleBar::clipPointWithScreen(
const QPointF &pointInTitleBarCoords)
const
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);
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();
437 updateResizability();
444 m_titleBar->setWidth(width);
449 updateResizability();
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);
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.