Resize wasm windows using a div outline
Introducing a div outline which handles the resize events by itself. Manual computations in wasm compositor are no longer needed. The outline reacts to setting css variables (border-width, resize-outline-width), it sets the correct cursors using css and always keeps the correct size. Fixes: QTBUG-107498 Change-Id: I6b0564632af5e17e464fe93a3dfa20820c624292 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
f546f3700b
commit
e50bc60e87
@ -149,11 +149,6 @@ void QWasmCompositor::initEventHandlers()
|
||||
emscripten::val(quintptr(reinterpret_cast<void *>(screen()))));
|
||||
}
|
||||
|
||||
void QWasmCompositor::startResize(Qt::Edges edges)
|
||||
{
|
||||
m_windowManipulation.startResize(edges);
|
||||
}
|
||||
|
||||
void QWasmCompositor::addWindow(QWasmWindow *window)
|
||||
{
|
||||
m_windowStack.pushWindow(window);
|
||||
@ -317,33 +312,6 @@ void QWasmCompositor::frame(bool all, const QList<QWasmWindow *> &windows)
|
||||
}
|
||||
}
|
||||
|
||||
void QWasmCompositor::WindowManipulation::resizeWindow(const QPoint& amount)
|
||||
{
|
||||
const auto& minShrink = std::get<ResizeState>(m_state->operationSpecific).m_minShrink;
|
||||
const auto& maxGrow = std::get<ResizeState>(m_state->operationSpecific).m_maxGrow;
|
||||
const auto &resizeEdges = std::get<ResizeState>(m_state->operationSpecific).m_resizeEdges;
|
||||
|
||||
const QPoint cappedGrowVector(
|
||||
std::min(maxGrow.x(),
|
||||
std::max(minShrink.x(),
|
||||
(resizeEdges & Qt::Edge::LeftEdge) ? -amount.x()
|
||||
: (resizeEdges & Qt::Edge::RightEdge) ? amount.x()
|
||||
: 0)),
|
||||
std::min(maxGrow.y(),
|
||||
std::max(minShrink.y(),
|
||||
(resizeEdges & Qt::Edge::TopEdge) ? -amount.y()
|
||||
: (resizeEdges & Qt::Edge::BottomEdge) ? amount.y()
|
||||
: 0)));
|
||||
|
||||
const auto& initialBounds =
|
||||
std::get<ResizeState>(m_state->operationSpecific).m_initialWindowBounds;
|
||||
m_state->window->setGeometry(initialBounds.adjusted(
|
||||
(resizeEdges & Qt::Edge::LeftEdge) ? -cappedGrowVector.x() : 0,
|
||||
(resizeEdges & Qt::Edge::TopEdge) ? -cappedGrowVector.y() : 0,
|
||||
(resizeEdges & Qt::Edge::RightEdge) ? cappedGrowVector.x() : 0,
|
||||
(resizeEdges & Qt::Edge::BottomEdge) ? cappedGrowVector.y() : 0));
|
||||
}
|
||||
|
||||
void QWasmCompositor::onTopWindowChanged()
|
||||
{
|
||||
constexpr int zOrderForElementInFrontOfScreen = 3;
|
||||
@ -413,7 +381,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
|
||||
|
||||
const QPoint pointInTargetWindowCoords = targetWindow->mapFromGlobal(event.point);
|
||||
const bool pointerIsWithinTargetWindowBounds = targetWindow->geometry().contains(event.point);
|
||||
const bool isTargetWindowBlocked = QGuiApplicationPrivate::instance()->isWindowBlocked(targetWindow);
|
||||
|
||||
if (m_mouseInScreen && m_windowUnderMouse != targetWindow
|
||||
&& pointerIsWithinTargetWindowBounds) {
|
||||
@ -422,10 +389,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
|
||||
m_windowUnderMouse = targetWindow;
|
||||
}
|
||||
|
||||
QWasmWindow *wasmTargetWindow = asWasmWindow(targetWindow);
|
||||
Qt::WindowStates windowState = targetWindow->windowState();
|
||||
const bool isTargetWindowResizable = !windowState.testFlag(Qt::WindowMaximized) && !windowState.testFlag(Qt::WindowFullScreen);
|
||||
|
||||
switch (event.type) {
|
||||
case EventType::PointerDown:
|
||||
{
|
||||
@ -444,25 +407,7 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
|
||||
m_windowManipulation.onPointerUp(event);
|
||||
break;
|
||||
}
|
||||
case EventType::PointerMove:
|
||||
{
|
||||
if (wasmTargetWindow && event.mouseButtons.testFlag(Qt::NoButton)) {
|
||||
const bool isOnResizeRegion = wasmTargetWindow->isPointOnResizeRegion(event.point);
|
||||
|
||||
if (isTargetWindowResizable && isOnResizeRegion && !isTargetWindowBlocked) {
|
||||
const QCursor resizingCursor = QWasmEventTranslator::cursorForEdges(
|
||||
wasmTargetWindow->resizeEdgesAtPoint(event.point));
|
||||
|
||||
if (resizingCursor != targetWindow->cursor()) {
|
||||
m_isResizeCursorDisplayed = true;
|
||||
QWasmCursor::setOverrideWasmCursor(resizingCursor, targetWindow->screen());
|
||||
}
|
||||
} else if (m_isResizeCursorDisplayed) { // off resizing area
|
||||
m_isResizeCursorDisplayed = false;
|
||||
QWasmCursor::clearOverrideWasmCursor(targetWindow->screen());
|
||||
}
|
||||
}
|
||||
|
||||
case EventType::PointerMove: {
|
||||
m_windowManipulation.onPointerMove(event);
|
||||
if (m_windowManipulation.operation() != WindowManipulation::Operation::None)
|
||||
requestUpdate();
|
||||
@ -538,9 +483,7 @@ QWasmCompositor::WindowManipulation::Operation QWasmCompositor::WindowManipulati
|
||||
{
|
||||
if (!m_state)
|
||||
return Operation::None;
|
||||
|
||||
return std::holds_alternative<MoveState>(m_state->operationSpecific)
|
||||
? Operation::Move : Operation::Resize;
|
||||
return Operation::Move;
|
||||
}
|
||||
|
||||
void QWasmCompositor::WindowManipulation::onPointerDown(
|
||||
@ -564,61 +507,30 @@ void QWasmCompositor::WindowManipulation::onPointerDown(
|
||||
if (isTargetWindowBlocked)
|
||||
return;
|
||||
|
||||
std::unique_ptr<std::variant<ResizeState, MoveState>> operationSpecific;
|
||||
if (asWasmWindow(windowAtPoint)->isPointOnTitle(event.point)) {
|
||||
operationSpecific = std::make_unique<std::variant<ResizeState, MoveState>>(
|
||||
MoveState{ .m_lastPointInScreenCoords = event.point });
|
||||
} else if (asWasmWindow(windowAtPoint)->isPointOnResizeRegion(event.point)) {
|
||||
operationSpecific = std::make_unique<std::variant<ResizeState, MoveState>>(ResizeState{
|
||||
.m_resizeEdges = asWasmWindow(windowAtPoint)->resizeEdgesAtPoint(event.point),
|
||||
.m_originInScreenCoords = event.point,
|
||||
.m_initialWindowBounds = windowAtPoint->geometry(),
|
||||
.m_minShrink =
|
||||
QPoint(windowAtPoint->minimumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->minimumHeight() - windowAtPoint->geometry().height()),
|
||||
.m_maxGrow =
|
||||
QPoint(windowAtPoint->maximumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->maximumHeight() - windowAtPoint->geometry().height()),
|
||||
});
|
||||
} else {
|
||||
if (!asWasmWindow(windowAtPoint)->isPointOnTitle(event.point))
|
||||
return;
|
||||
}
|
||||
|
||||
m_state.reset(new OperationState{
|
||||
.pointerId = event.pointerId,
|
||||
.window = windowAtPoint,
|
||||
.operationSpecific = std::move(*operationSpecific),
|
||||
});
|
||||
m_state.reset(new OperationState{ .pointerId = event.pointerId,
|
||||
.window = windowAtPoint,
|
||||
.lastPointInScreenCoords = event.point });
|
||||
}
|
||||
|
||||
void QWasmCompositor::WindowManipulation::onPointerMove(
|
||||
const PointerEvent& event)
|
||||
{
|
||||
m_systemDragInitData = {
|
||||
.lastMouseMovePoint = m_screen->clipPoint(event.point),
|
||||
.lastMousePointerId = event.pointerId,
|
||||
};
|
||||
|
||||
if (operation() == Operation::None || event.pointerId != m_state->pointerId)
|
||||
return;
|
||||
|
||||
switch (operation()) {
|
||||
case Operation::Move: {
|
||||
const QPoint targetPointClippedToScreen = m_screen->clipPoint(event.point);
|
||||
const QPoint difference = targetPointClippedToScreen -
|
||||
std::get<MoveState>(m_state->operationSpecific).m_lastPointInScreenCoords;
|
||||
const QPoint difference = targetPointClippedToScreen - m_state->lastPointInScreenCoords;
|
||||
|
||||
std::get<MoveState>(m_state->operationSpecific).m_lastPointInScreenCoords = targetPointClippedToScreen;
|
||||
m_state->lastPointInScreenCoords = targetPointClippedToScreen;
|
||||
|
||||
m_state->window->setPosition(m_state->window->position() + difference);
|
||||
break;
|
||||
}
|
||||
case Operation::Resize: {
|
||||
const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point;
|
||||
resizeWindow(pointInScreenCoords -
|
||||
std::get<ResizeState>(m_state->operationSpecific).m_originInScreenCoords);
|
||||
break;
|
||||
}
|
||||
case Operation::None:
|
||||
Q_ASSERT(0);
|
||||
break;
|
||||
@ -633,34 +545,6 @@ void QWasmCompositor::WindowManipulation::onPointerUp(const PointerEvent& event)
|
||||
m_state.reset();
|
||||
}
|
||||
|
||||
void QWasmCompositor::WindowManipulation::startResize(Qt::Edges edges)
|
||||
{
|
||||
Q_ASSERT_X(operation() == Operation::None, Q_FUNC_INFO,
|
||||
"Resize must not start anew when one is in progress");
|
||||
|
||||
auto *window = m_screen->compositor()->windowAt(m_systemDragInitData.lastMouseMovePoint);
|
||||
if (Q_UNLIKELY(!window))
|
||||
return;
|
||||
|
||||
m_state.reset(new OperationState{
|
||||
.pointerId = m_systemDragInitData.lastMousePointerId,
|
||||
.window = window,
|
||||
.operationSpecific =
|
||||
ResizeState{
|
||||
.m_resizeEdges = edges,
|
||||
.m_originInScreenCoords = m_systemDragInitData.lastMouseMovePoint,
|
||||
.m_initialWindowBounds = window->geometry(),
|
||||
.m_minShrink =
|
||||
QPoint(window->minimumWidth() - window->geometry().width(),
|
||||
window->minimumHeight() - window->geometry().height()),
|
||||
.m_maxGrow =
|
||||
QPoint(window->maximumWidth() - window->geometry().width(),
|
||||
window->maximumHeight() - window->geometry().height()),
|
||||
},
|
||||
});
|
||||
m_screen->element().call<void>("setPointerCapture", m_systemDragInitData.lastMousePointerId);
|
||||
}
|
||||
|
||||
bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *emKeyEvent)
|
||||
{
|
||||
constexpr bool ProceedToNativeEvent = false;
|
||||
|
@ -43,8 +43,6 @@ public:
|
||||
QPalette palette;
|
||||
};
|
||||
|
||||
void startResize(Qt::Edges edges);
|
||||
|
||||
void addWindow(QWasmWindow *window);
|
||||
void removeWindow(QWasmWindow *window);
|
||||
|
||||
@ -72,47 +70,26 @@ private:
|
||||
enum class Operation {
|
||||
None,
|
||||
Move,
|
||||
Resize,
|
||||
};
|
||||
|
||||
WindowManipulation(QWasmScreen* screen);
|
||||
|
||||
void onPointerDown(const PointerEvent& event, QWindow* windowAtPoint);
|
||||
void onPointerMove(const PointerEvent& event);
|
||||
void onPointerUp(const PointerEvent& event);
|
||||
void startResize(Qt::Edges edges);
|
||||
void onPointerUp(const PointerEvent &event);
|
||||
|
||||
Operation operation() const;
|
||||
|
||||
private:
|
||||
struct ResizeState {
|
||||
Qt::Edges m_resizeEdges;
|
||||
QPoint m_originInScreenCoords;
|
||||
QRect m_initialWindowBounds;
|
||||
const QPoint m_minShrink;
|
||||
const QPoint m_maxGrow;
|
||||
};
|
||||
struct MoveState {
|
||||
QPoint m_lastPointInScreenCoords;
|
||||
};
|
||||
struct OperationState
|
||||
{
|
||||
int pointerId;
|
||||
QPointer<QWindow> window;
|
||||
std::variant<ResizeState, MoveState> operationSpecific;
|
||||
QPoint lastPointInScreenCoords;
|
||||
};
|
||||
struct SystemDragInitData
|
||||
{
|
||||
QPoint lastMouseMovePoint;
|
||||
int lastMousePointerId = -1;
|
||||
};
|
||||
|
||||
void resizeWindow(const QPoint& amount);
|
||||
ResizeState makeResizeState(Qt::Edges edges, const QPoint &startPoint, QWindow *window);
|
||||
|
||||
QWasmScreen *m_screen;
|
||||
|
||||
SystemDragInitData m_systemDragInitData;
|
||||
std::unique_ptr<OperationState> m_state;
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,8 @@ namespace {
|
||||
const char *Style = R"css(
|
||||
.qt-screen {
|
||||
--border-width: 4px;
|
||||
--resize-outline-width: 8px;
|
||||
--resize-outline-half-width: var(--resize-outline-width) / 2;
|
||||
|
||||
position: relative;
|
||||
border: none;
|
||||
@ -35,6 +37,80 @@ const char *Style = R"css(
|
||||
caret-color: transparent;
|
||||
}
|
||||
|
||||
.resize-outline {
|
||||
position: absolute;
|
||||
pointer-events: all;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.qt-window.has-title-bar:not(.maximized) .resize-outline {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.resize-outline.nw {
|
||||
left: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
top: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
width: var(--resize-outline-width);
|
||||
height: var(--resize-outline-width);
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
.resize-outline.n {
|
||||
left: var(--resize-outline-half-width);
|
||||
top: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
height: var(--resize-outline-width);
|
||||
width: calc(100% + 2 * var(--border-width) - var(--resize-outline-width));
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.resize-outline.ne {
|
||||
left: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
top: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
width: var(--resize-outline-width);
|
||||
height: var(--resize-outline-width);
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.resize-outline.w {
|
||||
left: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
top: 0;
|
||||
height: calc(100% + 2 * var(--border-width) - var(--resize-outline-width));
|
||||
width: var(--resize-outline-width);
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.resize-outline.e {
|
||||
left: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
top: 0;
|
||||
height: calc(100% + 2 * var(--border-width) - var(--resize-outline-width));
|
||||
width: var(--resize-outline-width);
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.resize-outline.sw {
|
||||
left: calc(-1 * var(--resize-outline-half-width) - var(--border-width));
|
||||
top: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
width: var(--resize-outline-width);
|
||||
height: var(--resize-outline-width);
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.resize-outline.s {
|
||||
left: var(--resize-outline-half-width);
|
||||
top: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
height: var(--resize-outline-width);
|
||||
width: calc(100% + 2 * var(--border-width) - var(--resize-outline-width));
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.resize-outline.se {
|
||||
left: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
top: calc(100% + var(--border-width) - var(--resize-outline-half-width));
|
||||
width: var(--resize-outline-width);
|
||||
height: var(--resize-outline-width);
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
display: none;
|
||||
align-items: center;
|
||||
|
@ -44,6 +44,7 @@ std::optional<PointerEvent> PointerEvent::fromWeb(emscripten::val event)
|
||||
ret.mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());
|
||||
ret.mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as<unsigned short>());
|
||||
ret.point = QPoint(event["offsetX"].as<int>(), event["offsetY"].as<int>());
|
||||
ret.pointInViewport = QPoint(event["x"].as<int>(), event["y"].as<int>());
|
||||
ret.pointerId = event["pointerId"].as<int>();
|
||||
ret.modifiers = KeyboardModifier::getForEvent(event);
|
||||
|
||||
|
@ -115,6 +115,7 @@ struct Q_CORE_EXPORT Event
|
||||
struct Q_CORE_EXPORT MouseEvent : public Event
|
||||
{
|
||||
QPoint point;
|
||||
QPoint pointInViewport;
|
||||
Qt::MouseButton mouseButton;
|
||||
Qt::MouseButtons mouseButtons;
|
||||
QFlags<Qt::KeyboardModifier> modifiers;
|
||||
|
@ -283,29 +283,6 @@ QWasmEventTranslator::QWasmEventTranslator() = default;
|
||||
|
||||
QWasmEventTranslator::~QWasmEventTranslator() = default;
|
||||
|
||||
QCursor QWasmEventTranslator::cursorForEdges(Qt::Edges edges)
|
||||
{
|
||||
switch (edges) {
|
||||
case Qt::Edge::LeftEdge | Qt::Edge::TopEdge:
|
||||
case Qt::Edge::RightEdge | Qt::Edge::BottomEdge:
|
||||
return Qt::SizeFDiagCursor;
|
||||
case Qt::Edge::LeftEdge | Qt::Edge::BottomEdge:
|
||||
case Qt::Edge::RightEdge | Qt::Edge::TopEdge:
|
||||
return Qt::SizeBDiagCursor;
|
||||
case Qt::Edge::TopEdge:
|
||||
case Qt::Edge::BottomEdge:
|
||||
return Qt::SizeVerCursor;
|
||||
case Qt::Edge::LeftEdge:
|
||||
case Qt::Edge::RightEdge:
|
||||
return Qt::SizeHorCursor;
|
||||
case Qt::Edge(0):
|
||||
return Qt::ArrowCursor;
|
||||
default:
|
||||
Q_ASSERT(false); // Bad edges
|
||||
}
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
|
||||
QWasmEventTranslator::TranslatedEvent
|
||||
QWasmEventTranslator::translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent)
|
||||
{
|
||||
|
@ -33,8 +33,6 @@ public:
|
||||
explicit QWasmEventTranslator();
|
||||
~QWasmEventTranslator();
|
||||
|
||||
static QCursor cursorForEdges(Qt::Edges edges);
|
||||
|
||||
TranslatedEvent translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent);
|
||||
|
||||
private:
|
||||
|
@ -45,8 +45,222 @@ void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool fl
|
||||
|
||||
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.pointInViewport);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onPointerMove(const PointerEvent &event)
|
||||
{
|
||||
if (m_capturedPointerId != event.pointerId)
|
||||
return false;
|
||||
|
||||
m_resizer->continueResize(event.pointInViewport);
|
||||
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:
|
||||
@ -181,6 +395,8 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
|
||||
|
||||
m_qtWindow.call<void>("appendChild", m_windowContents);
|
||||
|
||||
m_resizer = std::make_unique<Resizer>(this, m_qtWindow);
|
||||
|
||||
m_icon = std::make_unique<WebImageButton>();
|
||||
m_icon->setImage(IconType::QtLogo);
|
||||
|
||||
@ -411,11 +627,10 @@ void QWasmWindow::propagateSizeHints()
|
||||
}
|
||||
}
|
||||
|
||||
bool QWasmWindow::startSystemResize(Qt::Edges edges)
|
||||
bool QWasmWindow::startSystemResize(Qt::Edges)
|
||||
{
|
||||
m_compositor->startResize(edges);
|
||||
|
||||
return true;
|
||||
// TODO(mikolajboc): This can only be implemented if per-window events are up and running
|
||||
return false;
|
||||
}
|
||||
|
||||
void QWasmWindow::onRestoreClicked()
|
||||
@ -450,51 +665,12 @@ QMarginsF QWasmWindow::borderMargins() const
|
||||
frameRect.bottom() - canvasRect.bottom());
|
||||
}
|
||||
|
||||
QRegion QWasmWindow::resizeRegion() const
|
||||
{
|
||||
QMargins margins = borderMargins().toMargins();
|
||||
QRegion result(window()->frameGeometry().marginsAdded(margins));
|
||||
result -= window()->frameGeometry().marginsRemoved(margins);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QWasmWindow::isPointOnTitle(QPoint globalPoint) const
|
||||
{
|
||||
return QRectF::fromDOMRect(m_titleBar.call<emscripten::val>("getBoundingClientRect"))
|
||||
.contains(globalPoint);
|
||||
}
|
||||
|
||||
bool QWasmWindow::isPointOnResizeRegion(QPoint point) const
|
||||
{
|
||||
// Certain windows, like undocked dock widgets, are both popups and dialogs. Those should be
|
||||
// resizable.
|
||||
if (windowIsPopupType(window()->flags()))
|
||||
return false;
|
||||
return (window()->maximumSize().isEmpty() || window()->minimumSize() != window()->maximumSize())
|
||||
&& resizeRegion().contains(point);
|
||||
}
|
||||
|
||||
Qt::Edges QWasmWindow::resizeEdgesAtPoint(QPoint point) const
|
||||
{
|
||||
const QPoint topLeft = window()->frameGeometry().topLeft() - QPoint(5, 5);
|
||||
const QPoint bottomRight = window()->frameGeometry().bottomRight() + QPoint(5, 5);
|
||||
const int gripAreaWidth = std::min(20, (bottomRight.y() - topLeft.y()) / 2);
|
||||
|
||||
const QRect top(topLeft, QPoint(bottomRight.x(), topLeft.y() + gripAreaWidth));
|
||||
const QRect bottom(QPoint(topLeft.x(), bottomRight.y() - gripAreaWidth), bottomRight);
|
||||
const QRect left(topLeft, QPoint(topLeft.x() + gripAreaWidth, bottomRight.y()));
|
||||
const QRect right(QPoint(bottomRight.x() - gripAreaWidth, topLeft.y()), bottomRight);
|
||||
|
||||
Q_ASSERT(!top.intersects(bottom));
|
||||
Q_ASSERT(!left.intersects(right));
|
||||
|
||||
Qt::Edges edges(top.contains(point) ? Qt::Edge::TopEdge : Qt::Edge(0));
|
||||
edges |= bottom.contains(point) ? Qt::Edge::BottomEdge : Qt::Edge(0);
|
||||
edges |= left.contains(point) ? Qt::Edge::LeftEdge : Qt::Edge(0);
|
||||
return edges | (right.contains(point) ? Qt::Edge::RightEdge : Qt::Edge(0));
|
||||
}
|
||||
|
||||
void QWasmWindow::invalidate()
|
||||
{
|
||||
m_compositor->requestUpdateWindow(this);
|
||||
@ -565,6 +741,7 @@ void QWasmWindow::applyWindowState()
|
||||
newGeom = normalGeometry();
|
||||
|
||||
syncCSSClassWith(m_qtWindow, "has-title-bar", hasTitleBar());
|
||||
syncCSSClassWith(m_qtWindow, "maximized", isMaximized);
|
||||
|
||||
m_restore->setVisible(isMaximized);
|
||||
m_maximize->setVisible(!isMaximized);
|
||||
|
@ -55,9 +55,6 @@ public:
|
||||
bool startSystemResize(Qt::Edges edges) final;
|
||||
|
||||
bool isPointOnTitle(QPoint point) const;
|
||||
bool isPointOnResizeRegion(QPoint point) const;
|
||||
|
||||
Qt::Edges resizeEdgesAtPoint(QPoint point) const;
|
||||
|
||||
void setWindowFlags(Qt::WindowFlags flags) override;
|
||||
void setWindowState(Qt::WindowStates state) override;
|
||||
@ -74,10 +71,10 @@ public:
|
||||
private:
|
||||
friend class QWasmScreen;
|
||||
|
||||
class Resizer;
|
||||
class WebImageButton;
|
||||
|
||||
QMarginsF borderMargins() const;
|
||||
QRegion resizeRegion() const;
|
||||
|
||||
void onRestoreClicked();
|
||||
void onMaximizeClicked();
|
||||
@ -101,6 +98,8 @@ private:
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user