// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include "qwaylandqtshellchrome.h"
#include "qwaylandqtshellchrome_p.h"
#include "qwaylandqtshell.h"

#include <QtWaylandCompositor/qwaylandquickshellsurfaceitem.h>

QT_BEGIN_NAMESPACE

QPointF QWaylandQtShellChromePrivate::constrainPoint(const QPointF &point) const
{
    float x0 = maximizedRect.left();
    float y0 = maximizedRect.top();
    float x1 = maximizedRect.right();
    float y1 = maximizedRect.bottom();
    return QPoint(qBound(x0, point.x(), x1),
                  qBound(y0, point.y(), y1));
}

void QWaylandQtShellChromePrivate::updateDecorationInteraction(quint8 flags,
                                                               const QQuickHandlerPoint &centroid)
{
    if (shellSurface == nullptr)
        return;

    if (decorationInteraction == quint8(DecorationInteraction::None)) {
        decorationInteraction = flags;
        decorationInteractionPosition = centroid.scenePressPosition();
        decorationInteractionGeometry = shellSurface->windowGeometry();
    }

    if (decorationInteraction != flags)
        return;

    QPointF position = constrainPoint(centroid.scenePosition());
    float dx = position.x() - decorationInteractionPosition.x();
    float dy = position.y() - decorationInteractionPosition.y();

    float minWidth = qMax(0, shellSurface->minimumSize().width());
    float minHeight = qMax(0, shellSurface->minimumSize().height());

    float maxWidth = shellSurface->maximumSize().width();
    float maxHeight = shellSurface->maximumSize().height();

    float minX = maxWidth >= 0.0f
            ? decorationInteractionGeometry.right() - maxWidth
            : -FLT_MAX;
    float minY = maxHeight >= 0.0f
            ? decorationInteractionGeometry.bottom() - maxHeight
            : -FLT_MAX;
    float maxX = maxWidth >= 0
            ? decorationInteractionGeometry.left() + maxWidth
            : FLT_MAX;
    float maxY = maxHeight >= 0.0f
            ? decorationInteractionGeometry.top() + maxHeight
            : FLT_MAX;

    float newLeft = decorationInteractionGeometry.left();
    if (flags & quint8(DecorationInteraction::WestBound)) {
        newLeft = qBound(minX,
                         newLeft + dx,
                         float(decorationInteractionGeometry.right() - minWidth));
    }

    float newTop = decorationInteractionGeometry.top();
    if (flags & quint8(DecorationInteraction::NorthBound)) {
        newTop = qBound(minY,
                        newTop + dy,
                        decorationInteractionGeometry.bottom() + minHeight);
    }

    float newRight = decorationInteractionGeometry.right();
    if (flags & quint8(DecorationInteraction::EastBound)) {
        newRight = qBound(decorationInteractionGeometry.left() + minWidth,
                          newRight + dx,
                          maxX);
    }

    float newBottom = decorationInteractionGeometry.bottom();
    if (flags & quint8(DecorationInteraction::SouthBound)) {
        newBottom = qBound(decorationInteractionGeometry.top() + minHeight,
                           newBottom + dy,
                           maxY);
    }

    shellSurface->requestWindowGeometry(shellSurface->windowState(),
                                        QRect(int(newLeft), int(newTop),
                                              int(newRight - newLeft), int(newBottom - newTop)));
}

/*!
 * \qmltype QtShellChrome
 * \nativetype QWaylandQtShellChrome
 * \inqmlmodule QtWayland.Compositor.QtShell
 * \since 6.3
 * \brief Provides default window manager functionality for use with the \c qt-shell extension.
 *
 * The QtShellChrome is a convenience type that can be used to provide window manager functionality
 * to the interaction with clients over the \c qt-shell
 * \l{Shell Extensions - Qt Wayland Compositor}{shell extension protocol}.
 *
 * Given a ShellSurfaceItem with an associated QtShellSurface, the item will automatically adapt
 * its size to match the surface. It will also provide automatic handling of:
 * \list
 *   \li Window states, such as maximized, minimized and fullscreen.
 *   \li Window activation.
 *   \li Window resizing using with resize handles (if the appropriate properties are set.)
 *   \li Window repositioning using title bar interaction (if the \l titleBar property is set.)
 * \endlist
 *
 * The QtShellChrome is intended to be used together with QtShell and QtShellSurface.
 *
 * \sa {QtShell Compositor}
 */
QWaylandQtShellChrome::QWaylandQtShellChrome(QQuickItem *parent)
    : QQuickItem(*new QWaylandQtShellChromePrivate{}, parent)
{
    init();
}

QWaylandQtShellChrome::QWaylandQtShellChrome(QWaylandQtShellChromePrivate &dd,
                                             QQuickItem *parent)
    : QQuickItem(dd, parent)
{
    init();
}

QWaylandQtShellChrome::~QWaylandQtShellChrome()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shell != nullptr)
        d->shell->unregisterChrome(this);
}

void QWaylandQtShellChrome::init()
{
    connect(this, &QWaylandQtShellChrome::currentWindowStateChanged,
            this, &QWaylandQtShellChrome::windowMetaInfoChanged);

    connect(this, &QWaylandQtShellChrome::currentWindowFlagsChanged,
            this, &QWaylandQtShellChrome::windowMetaInfoChanged);

    connect(this, &QWaylandQtShellChrome::windowMetaInfoChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::leftResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::rightResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::topResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::bottomResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::topLeftResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::bottomLeftResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::topRightResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);

    connect(this, &QWaylandQtShellChrome::bottomRightResizeHandleChanged,
            this, &QWaylandQtShellChrome::updateDecorations);
}

/*!
 * \qmlmethod void QtShellChrome::toggleFullScreen()
 *
 * Toggles between fullscreen and normal window states. This method also clears the minimized
 * or maximized window states if either is set.
 */
void QWaylandQtShellChrome::toggleFullScreen()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    uint newState;
    if ((d->shellSurface->windowState() & Qt::WindowFullScreen) == Qt::WindowFullScreen)
        newState = d->currentState & ~Qt::WindowFullScreen;
    else
        newState = d->currentState | Qt::WindowFullScreen;

    if ((newState & (Qt::WindowMinimized | Qt::WindowMaximized)) != 0)
        newState &= ~(Qt::WindowMinimized | Qt::WindowMaximized);

    setWindowState(newState);
}

/*!
 * \qmlmethod void QtShellChrome::toggleMaximized()
 *
 * Toggles between maximized and normal states. This method also clears the minimized
 * window state if it is set.
 */
void QWaylandQtShellChrome::toggleMaximized()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    uint newState;
    if ((d->shellSurface->windowState() & Qt::WindowMaximized) == Qt::WindowMaximized)
        newState = d->currentState & ~Qt::WindowMaximized;
    else
        newState = d->currentState | Qt::WindowMaximized;

    if ((newState & Qt::WindowMinimized) == Qt::WindowMinimized)
        newState &= ~Qt::WindowMinimized;

    setWindowState(newState);
}

/*!
 * \qmlmethod void QtShellChrome::toggleMinimized()
 *
 * Toggles between minimized and normal states. This method also clears the maximized
 * window state if it is set.
 */
void QWaylandQtShellChrome::toggleMinimized()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    uint newState;
    if ((d->shellSurface->windowState() & Qt::WindowMinimized) == Qt::WindowMinimized)
        newState = d->currentState & ~Qt::WindowMinimized;
    else
        newState = d->currentState | Qt::WindowMinimized;

    if ((newState & Qt::WindowMaximized) == Qt::WindowMaximized)
        newState &= ~Qt::WindowMaximized;

    setWindowState(newState);
}

/*!
 * \qmlproperty ShellSurfaceItem QtShellChrome::shellSurfaceItem
 *
 * This property holds the shell surface item associated with this QtShellChrome. It will
 * in turn manage the \c shellSurface of this item. The \c shellSurface of the item is expected to
 * be of the type QtShellSurface.
 *
 * \qml
 * QtShellChrome {
 *    id: chrome
 *    ShellSurfaceItem {
 *        id: sfi
 *        anchors.fill: parent
 *        moveItem: chrome
 *    }
 *    shellSurfaceItem: sfi
 * }
 * \endqml
 */

/*!
 * \property QWaylandQtShellChrome::shellSurfaceItem
 *
 * This property holds the shell surface item associated with this
 * QWaylandQtShellChrome. It will in turn manage the shell surface of this item.
 * The shell surface of the item is expected to be of the type
 * QWaylandQtShellSurface.
 */
void QWaylandQtShellChrome::setShellSurfaceItem(QWaylandQuickShellSurfaceItem *shellSurfaceItem)
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurfaceItem == shellSurfaceItem)
        return;

    if (d->shellSurfaceItem != nullptr)
        d->shellSurfaceItem->disconnect(this);

    d->shellSurfaceItem = shellSurfaceItem;

    if (d->shellSurfaceItem != nullptr) {
        connect(d->shellSurfaceItem, &QWaylandQuickShellSurfaceItem::shellSurfaceChanged,
                this, &QWaylandQtShellChrome::updateShellSurface);
        connect(d->shellSurfaceItem, &QWaylandQuickShellSurfaceItem::surfaceDestroyed,
                this, &QWaylandQtShellChrome::clientDestroyed);
    }

    updateShellSurface();
    emit shellSurfaceItemChanged();
}

QWaylandQuickShellSurfaceItem *QWaylandQtShellChrome::shellSurfaceItem() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->shellSurfaceItem;
}

void QWaylandQtShellChrome::stopGrab()
{
    Q_D(QWaylandQtShellChrome);
    d->decorationInteraction = quint8(QWaylandQtShellChromePrivate::DecorationInteraction::None);
}

void QWaylandQtShellChrome::leftResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->leftResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound),
                                   d->leftResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::rightResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->rightResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound),
                                   d->rightResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::topResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->topResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
                                   d->topResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::bottomResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->bottomResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
                                   d->bottomResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::topLeftResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->topLeftResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound)
                                   | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
                                   d->topLeftResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::topRightResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->topRightResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound)
                                   | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
                                   d->topRightResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::bottomLeftResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->bottomLeftResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound)
                                   | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
                                   d->bottomLeftResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::bottomRightResize()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->bottomRightResizeHandleHandler->active())
        return;

    d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound)
                                   | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
                                   d->bottomRightResizeHandleHandler->centroid());
}

void QWaylandQtShellChrome::titleBarMove()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->titleBarHandler->active())
        return;

    quint8 flags = quint8(QWaylandQtShellChromePrivate::DecorationInteraction::TitleBar);
    QQuickHandlerPoint centroid = d->titleBarHandler->centroid();
    if (d->decorationInteraction == quint8(QWaylandQtShellChromePrivate::DecorationInteraction::None)) {
        d->decorationInteraction = flags;
        d->decorationInteractionPosition = d->shellSurface->windowPosition() - centroid.scenePressPosition();

        activate();
    }

    if (d->decorationInteraction != flags)
        return;

    QPointF position = d->constrainPoint(centroid.scenePosition());
    d->shellSurface->setWindowPosition((position + d->decorationInteractionPosition).toPoint());
}

/*!
 * \qmlproperty Item QtShellChrome::titleBar
 *
 * This property holds the default title bar item of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the title bar which moves the window around on user interaction. In
 * addition, the window will automatically be activated if the title bar is clicked.
 *
 * The title bar will automatically hide and show, depending on the window flags and the
 * window's full screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: tb
 *        anchors.top: parent.top
 *        anchors.right: parent.right
 *        anchors.left: parent.left
 *        height: 50
 *        color: "black"
 *
 *        Text {
 *            color: "white"
 *            anchors.centerIn: parent
 *            text: shellSurfaceItem.shellSurface.windowTitle
 *            font.pixelSize: 25
 *        }
 *    }
 *    titleBar: tb
 * }
 * \endqml
 *
 * \note Unless explicit frame margins are set, the title bar's height will be included in the
 * window's top frame margin.
 */

/*!
 * \property QWaylandQtShellChrome::titleBar
 *
 * This property holds the default title bar item of the QWaylandQtShellChrome.
 * If set, a QQuickDragHandler will be installed on the title bar which moves
 * the window around on user interaction.
 *
 * \note Unless explicit frame margins are set, the title bar's height will be
 * included in the window's top frame margin.
 */
QQuickItem *QWaylandQtShellChrome::titleBar() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->titleBar;
}

void QWaylandQtShellChrome::setTitleBar(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->titleBar == item)
        return;

    if (d->titleBar != nullptr) {
        d->titleBar->disconnect(this);

        delete d->titleBarHandler;
        d->titleBarHandler = nullptr;
    }

    d->titleBar = item;

    if (d->titleBar != nullptr) {
        connect(d->titleBar, &QQuickItem::heightChanged,
                this, &QWaylandQtShellChrome::updateDecorations);

        d->titleBarHandler = new QQuickDragHandler(d->titleBar);
        d->titleBarHandler->setTarget(nullptr);

        connect(d->titleBarHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->titleBarHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::activateOnGrab);
        connect(d->titleBarHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::titleBarMove);
    }

    emit titleBarChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::leftResizeHandle
 *
 * This property holds the default left resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its left edge.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: lrh
 *        anchors.left: parent.left
 *        anchors.top: parent.top
 *        anchors.bottom: parent.bottom
 *        width: 5
 *        color: "white"
 *    }
 *    leftResizeHandle: lrh
 * }
 * \endqml
 *
 * \note Unless explicit frame margins are set, the handle's width will be included in the
 * window's left frame margin.
 */

/*!
 * \property QWaylandQtShellChrome::leftResizeHandle
 *
 * This property holds the default left resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its left edge.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 *
 * \note Unless explicit frame margins are set, the handle's width will be
 *       included in the window's left frame margin.
 */
QQuickItem *QWaylandQtShellChrome::leftResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->leftResizeHandle;
}

void QWaylandQtShellChrome::setLeftResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->leftResizeHandle == item)
        return;

    if (d->leftResizeHandle != nullptr) {
        d->leftResizeHandle->disconnect(this);

        delete d->leftResizeHandleHandler;
        d->leftResizeHandleHandler = nullptr;
    }

    d->leftResizeHandle = item;

    if (d->leftResizeHandle != nullptr) {
        connect(d->leftResizeHandle, &QQuickItem::widthChanged,
                this, &QWaylandQtShellChrome::updateDecorations);

        d->leftResizeHandleHandler = new QQuickDragHandler(d->leftResizeHandle);
        d->leftResizeHandleHandler->setCursorShape(Qt::SizeHorCursor);
        d->leftResizeHandleHandler->setTarget(nullptr);

        connect(d->leftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->leftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::leftResize);
    }

    emit leftResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::rightResizeHandle
 *
 * This property holds the default right resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its right edge.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: rrh
 *        anchors.right: parent.right
 *        anchors.top: parent.top
 *        anchors.bottom: parent.bottom
 *        width: 5
 *        color: "white"
 *    }
 *    rightResizeHandle: rrh
 * }
 * \endqml
 *
 * \note Unless explicit frame margins are set, the handle's width will be included in the
 * window's right frame margin.
 */

/*!
 * \property QWaylandQtShellChrome::rightResizeHandle
 *
 * This property holds the default right resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its right edge.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 *
 * \note Unless explicit frame margins are set, the handle's width will be
 * included in the window's right frame margin.
 */
QQuickItem *QWaylandQtShellChrome::rightResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->rightResizeHandle;
}

void QWaylandQtShellChrome::setRightResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->rightResizeHandle == item)
        return;

    if (d->rightResizeHandle != nullptr) {
        d->rightResizeHandle->disconnect(this);

        delete d->rightResizeHandleHandler;
        d->rightResizeHandleHandler = nullptr;
    }

    d->rightResizeHandle = item;

    if (d->rightResizeHandle != nullptr) {
        connect(d->rightResizeHandle, &QQuickItem::widthChanged,
                this, &QWaylandQtShellChrome::updateDecorations);

        d->rightResizeHandleHandler = new QQuickDragHandler(d->rightResizeHandle);
        d->rightResizeHandleHandler->setCursorShape(Qt::SizeHorCursor);
        d->rightResizeHandleHandler->setTarget(nullptr);

        connect(d->rightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->rightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::rightResize);
    }

    emit rightResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::topResizeHandle
 *
 * This property holds the default top resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its top edge.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: trh
 *        anchors.top: parent.top
 *        anchors.left: parent.left
 *        anchors.right: parent.right
 *        height: 5
 *        color: "white"
 *    }
 *    topResizeHandle: trh
 * }
 * \endqml
 *
 * \note Unless explicit frame margins are set, the handle's height will be included in the
 * window's top frame margin.
 */

/*!
 * \property QWaylandQtShellChrome::topResizeHandle
 *
 * This property holds the default top resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its top edge.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 *
 * \note Unless explicit frame margins are set, the handle's height will be
 * included in the window's top frame margin.
 */
QQuickItem *QWaylandQtShellChrome::topResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->topResizeHandle;
}

void QWaylandQtShellChrome::setTopResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->topResizeHandle == item)
        return;

    if (d->topResizeHandle != nullptr) {
        d->topResizeHandle->disconnect(this);

        delete d->topResizeHandleHandler;
        d->topResizeHandleHandler = nullptr;
    }

    d->topResizeHandle = item;

    if (d->topResizeHandle != nullptr) {
        connect(d->topResizeHandle, &QQuickItem::heightChanged,
                this, &QWaylandQtShellChrome::updateDecorations);

        d->topResizeHandleHandler = new QQuickDragHandler(d->topResizeHandle);
        d->topResizeHandleHandler->setCursorShape(Qt::SizeVerCursor);
        d->topResizeHandleHandler->setTarget(nullptr);

        connect(d->topResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->topResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::topResize);
    }

    emit topResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::bottomResizeHandle
 *
 * This property holds the default bottom resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its bottom edge.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: brh
 *        anchors.bottom: parent.bottom
 *        anchors.left: parent.left
 *        anchors.right: parent.right
 *        height: 5
 *        color: "white"
 *    }
 *    bottomResizeHandle: brh
 * }
 * \endqml
 *
 * \note Unless explicit frame margins are set, the handle's height will be included in the
 * window's bottom frame margin.
 */

/*!
 * \property QWaylandQtShellChrome::bottomResizeHandle
 *
 * This property holds the default bottom resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its bottom edge.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 *
 * \note Unless explicit frame margins are set, the handle's height will be
 *       included in the window's bottom frame margin.
 */
QQuickItem *QWaylandQtShellChrome::bottomResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->bottomResizeHandle;
}

void QWaylandQtShellChrome::setBottomResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->bottomResizeHandle == item)
        return;

    if (d->bottomResizeHandle != nullptr) {
        d->bottomResizeHandle->disconnect(this);

        delete d->bottomResizeHandleHandler;
        d->bottomResizeHandleHandler = nullptr;
    }

    d->bottomResizeHandle = item;

    if (d->bottomResizeHandle != nullptr) {
        connect(d->bottomResizeHandle, &QQuickItem::heightChanged,
                this, &QWaylandQtShellChrome::updateDecorations);

        d->bottomResizeHandleHandler = new QQuickDragHandler(d->bottomResizeHandle);
        d->bottomResizeHandleHandler->setCursorShape(Qt::SizeVerCursor);
        d->bottomResizeHandleHandler->setTarget(nullptr);

        connect(d->bottomResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->bottomResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::bottomResize);

    }

    emit bottomResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::topLeftResizeHandle
 *
 * This property holds the default top-left resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its top and left edges
 * in equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: tlrh
 *        anchors.top: parent.top
 *        anchors.left: parent.left
 *        height: 5
 *        width: 5
 *        color: "white"
 *    }
 *    topLeftResizeHandle: tlrh
 * }
 * \endqml
 */

/*!
 * \property QWaylandQtShellChrome::topLeftResizeHandle
 *
 * This property holds the default top-left resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its top and left edges in
 * equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 */
QQuickItem *QWaylandQtShellChrome::topLeftResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->topLeftResizeHandle;
}

void QWaylandQtShellChrome::setTopLeftResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->topLeftResizeHandle == item)
        return;

    if (d->topLeftResizeHandle != nullptr) {
        delete d->topLeftResizeHandleHandler;
        d->topLeftResizeHandleHandler = nullptr;
    }

    d->topLeftResizeHandle = item;

    if (d->topLeftResizeHandle != nullptr) {
        d->topLeftResizeHandleHandler = new QQuickDragHandler(d->topLeftResizeHandle);
        d->topLeftResizeHandleHandler->setCursorShape(Qt::SizeFDiagCursor);
        d->topLeftResizeHandleHandler->setTarget(nullptr);

        connect(d->topLeftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->topLeftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::topLeftResize);
    }

    emit topLeftResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::bottomLeftResizeHandle
 *
 * This property holds the default bottom-left resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its bottom and left edges
 * in equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: blrh
 *        anchors.bottom: parent.bottom
 *        anchors.left: parent.left
 *        height: 5
 *        width: 5
 *        color: "white"
 *    }
 *    bottomLeftResizeHandle: blrh
 * }
 * \endqml
 */

/*!
 * \property QWaylandQtShellChrome::bottomLeftResizeHandle
 *
 * This property holds the default bottom-left resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its bottom and left edges in
 * equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 */
QQuickItem *QWaylandQtShellChrome::bottomLeftResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->bottomLeftResizeHandle;
}

void QWaylandQtShellChrome::setBottomLeftResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->bottomLeftResizeHandle == item)
        return;

    if (d->bottomLeftResizeHandle != nullptr) {
        delete d->bottomLeftResizeHandleHandler;
        d->bottomLeftResizeHandleHandler = nullptr;
    }

    d->bottomLeftResizeHandle = item;

    if (d->bottomLeftResizeHandle != nullptr) {
        d->bottomLeftResizeHandleHandler = new QQuickDragHandler(d->bottomLeftResizeHandle);
        d->bottomLeftResizeHandleHandler->setCursorShape(Qt::SizeBDiagCursor);
        d->bottomLeftResizeHandleHandler->setTarget(nullptr);

        connect(d->bottomLeftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->bottomLeftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::bottomLeftResize);
    }

    emit bottomLeftResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::topRightResizeHandle
 *
 * This property holds the default top-right resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its top and right edges
 * in equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: trrh
 *        anchors.top: parent.top
 *        anchors.right: parent.right
 *        height: 5
 *        width: 5
 *        color: "white"
 *    }
 *    topRightResizeHandle: trrh
 * }
 * \endqml
 */

/*!
 * \property QWaylandQtShellChrome::topRightResizeHandle
 *
 * This property holds the default top-right resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its top and right edges in
 * equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 */
QQuickItem *QWaylandQtShellChrome::topRightResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->topRightResizeHandle;
}

void QWaylandQtShellChrome::setTopRightResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->topRightResizeHandle == item)
        return;

    if (d->topRightResizeHandle != nullptr) {
        delete d->topRightResizeHandleHandler;
        d->topRightResizeHandleHandler = nullptr;
    }

    d->topRightResizeHandle = item;

    if (d->topRightResizeHandle != nullptr) {
        d->topRightResizeHandleHandler = new QQuickDragHandler(d->topRightResizeHandle);
        d->topRightResizeHandleHandler->setCursorShape(Qt::SizeBDiagCursor);
        d->topRightResizeHandleHandler->setTarget(nullptr);

        connect(d->topRightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->topRightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::topRightResize);
    }

    emit topRightResizeHandleChanged();
}

/*!
 * \qmlproperty Item QtShellChrome::bottomRightResizeHandle
 *
 * This property holds the default bottom-right resize handle of the QtShellChrome. If set, a \l DragHandler
 * will be installed on the resize handle which resizes the window by moving its bottom and right edges
 * in equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags and the window's full
 * screen state.
 *
 * \qml
 * QtShellChrome {
 *    Rectangle {
 *        id: brrh
 *        anchors.bottom: parent.bottom
 *        anchors.right: parent.right
 *        height: 5
 *        width: 5
 *        color: "white"
 *    }
 *    bottomRightResizeHandle: brrh
 * }
 * \endqml
 */

/*!
 * \property QWaylandQtShellChrome::bottomRightResizeHandle
 *
 * This property holds the default bottom-right resize handle of the
 * QWaylandQtShellChrome. If set, a QQuickDragHandler will be installed on the
 * resize handle which resizes the window by moving its bottom and right edges
 * in equal amounts.
 *
 * The handle will automatically hide and show, depending on the window flags
 * and the window's full screen state.
 */
QQuickItem *QWaylandQtShellChrome::bottomRightResizeHandle() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->bottomRightResizeHandle;
}

void QWaylandQtShellChrome::setBottomRightResizeHandle(QQuickItem *item)
{
    Q_D(QWaylandQtShellChrome);
    if (d->bottomRightResizeHandle == item)
        return;

    if (d->bottomRightResizeHandle != nullptr) {
        delete d->bottomRightResizeHandleHandler;
        d->bottomRightResizeHandleHandler = nullptr;
    }

    d->bottomRightResizeHandle = item;

    if (d->bottomRightResizeHandle != nullptr) {
        d->bottomRightResizeHandleHandler = new QQuickDragHandler(d->bottomRightResizeHandle);
        d->bottomRightResizeHandleHandler->setCursorShape(Qt::SizeFDiagCursor);
        d->bottomRightResizeHandleHandler->setTarget(nullptr);

        connect(d->bottomRightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
                this, &QWaylandQtShellChrome::stopGrab);
        connect(d->bottomRightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
                this, &QWaylandQtShellChrome::bottomRightResize);
    }

    emit bottomRightResizeHandleChanged();
}

/*!
 * \qmlproperty rect QtShellChrome::maximizedRect
 *
 * This property holds the are of the WaylandOutput which is available to be filled by the
 * window when it is in maximized state. By default, the window will fill the entire geometry
 * of the WaylandOutput when it is maximized. Changing it can be useful for example when the
 * compositor has other system UI which should not be obscured by maximized applications, such as
 * a task bar.
 */

/*!
 * \property QWaylandQtShellChrome::maximizedRect
 *
 * This property holds the area of the QWaylandOutput which is available to be
 * filled by the window when it is in maximized state. By default, the window
 * will fill the entire geometry of the QWaylandOutput when it is maximized.
 * Changing it can be useful such as when the compositor has other system UI
 * which should not be obscured by maximized applications, such as a task bar.
 */
void QWaylandQtShellChrome::setMaximizedRect(const QRect &rect)
{
    Q_D(QWaylandQtShellChrome);
    if (d->maximizedRect == rect)
        return;

    d->maximizedRect = rect;
    emit maximizedRectChanged();
}

QRect QWaylandQtShellChrome::maximizedRect() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->maximizedRect.isValid())
        return d->maximizedRect;
    else if (d->shellSurfaceItem != nullptr && d->shellSurfaceItem->output() != nullptr)
        return d->shellSurfaceItem->output()->geometry();

    return QRect{};
}

void QWaylandQtShellChrome::updateDecorations()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    bool decorations = hasDecorations();
    bool titleBarShowing = hasTitleBar();

    QMargins margins;
    if (d->automaticFrameMargins) {
        if (d->leftResizeHandle != nullptr && decorations)
            margins.setLeft(d->leftResizeHandle->width());
        if (d->rightResizeHandle != nullptr && decorations)
            margins.setRight(d->rightResizeHandle->width());
        if (d->bottomResizeHandle != nullptr && decorations)
            margins.setBottom(d->bottomResizeHandle->height());

        margins.setTop((decorations && d->topResizeHandle != nullptr ? d->topResizeHandle->height() : 0)
                    + (titleBarShowing && d->titleBar != nullptr ? d->titleBar->height() : 0));
    } else {
        margins = d->explicitFrameMargins;
    }
    d->shellSurface->setFrameMargins(margins);

    if (d->titleBar != nullptr)
        d->titleBar->setVisible(titleBarShowing);
    if (d->leftResizeHandle != nullptr)
        d->leftResizeHandle->setVisible(decorations);
    if (d->rightResizeHandle != nullptr)
        d->rightResizeHandle->setVisible(decorations);
    if (d->topResizeHandle != nullptr)
        d->topResizeHandle->setVisible(decorations);
    if (d->bottomResizeHandle != nullptr)
        d->bottomResizeHandle->setVisible(decorations);
    if (d->bottomLeftResizeHandle != nullptr)
        d->bottomLeftResizeHandle->setVisible(decorations);
    if (d->topLeftResizeHandle != nullptr)
        d->topLeftResizeHandle->setVisible(decorations);
    if (d->bottomRightResizeHandle != nullptr)
        d->bottomRightResizeHandle->setVisible(decorations);
    if (d->topRightResizeHandle != nullptr)
        d->topRightResizeHandle->setVisible(decorations);

    bool minimizedOrMaximized = (d->currentState & (Qt::WindowMaximized|Qt::WindowMinimized)) != 0;
    if (d->leftResizeHandleHandler != nullptr)
        d->leftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->rightResizeHandleHandler != nullptr)
        d->rightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->bottomResizeHandleHandler != nullptr)
        d->bottomResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->topResizeHandleHandler != nullptr)
        d->topResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->bottomLeftResizeHandleHandler != nullptr)
        d->bottomLeftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->bottomRightResizeHandleHandler != nullptr)
        d->bottomRightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->topLeftResizeHandleHandler != nullptr)
        d->topLeftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->topRightResizeHandleHandler != nullptr)
        d->topRightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
    if (d->titleBarHandler != nullptr)
        d->titleBarHandler->setEnabled(titleBarShowing && !minimizedOrMaximized);
}

void QWaylandQtShellChrome::updateGeometry()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    QRect windowGeometry = d->shellSurface->windowGeometry();

    QPointF position = windowGeometry.topLeft();
    position.rx() -= d->shellSurface->frameMarginLeft();
    position.ry() -= d->shellSurface->frameMarginTop();

    QSizeF size = windowGeometry.size();
    size.rwidth() += d->shellSurface->frameMarginLeft() + d->shellSurface->frameMarginRight();
    size.rheight() += d->shellSurface->frameMarginTop() + d->shellSurface->frameMarginBottom();

    setPosition(position);
    setSize(size);
}

void QWaylandQtShellChrome::updateSurface()
{
    Q_D(QWaylandQtShellChrome);
    QWaylandSurface *surface = d->shellSurface != nullptr ? d->shellSurface->surface() : nullptr;
    if (d->surface == surface)
        return;

    if (d->surface != nullptr)
        d->surface->disconnect(this);

    d->surface = surface;

    if (d->surface != nullptr) {
        connect(d->surface, &QWaylandSurface::hasContentChanged,
                this, &QWaylandQtShellChrome::updateAutomaticPosition);
    }
}

void QWaylandQtShellChrome::updateShellSurface()
{
    Q_D(QWaylandQtShellChrome);
    QWaylandQtShellSurface *sf = d->shellSurfaceItem != nullptr
            ? qobject_cast<QWaylandQtShellSurface *>(d->shellSurfaceItem->shellSurface())
            : nullptr;
    if (d->shellSurface == sf)
        return;

    if (d->shellSurface != nullptr) {
        d->shellSurface->disconnect(this);
        if (d->shell != nullptr)
            d->shell->unregisterChrome(this);
        d->shell = nullptr;
    }

    d->shellSurface = sf;
    if (d->shellSurface != nullptr) {
        d->shell = d->shellSurface->shell();
        if (d->shell != nullptr)
            d->shell->registerChrome(this);

        updateWindowFlags();
        connect(d->shellSurface, &QWaylandQtShellSurface::windowFlagsChanged,
                this, &QWaylandQtShellChrome::updateWindowFlags);
        connect(d->shellSurface, &QWaylandQtShellSurface::windowStateChanged,
                this, &QWaylandQtShellChrome::updateWindowState);
        connect(d->shellSurface, &QWaylandQtShellSurface::frameMarginChanged,
                this, &QWaylandQtShellChrome::updateGeometry);
        connect(d->shellSurface, &QWaylandQtShellSurface::windowGeometryChanged,
                this, &QWaylandQtShellChrome::updateGeometry);
        connect(d->shellSurface, &QWaylandQtShellSurface::raiseRequested,
                this, &QWaylandQtShellChrome::raise);
        connect(d->shellSurface, &QWaylandQtShellSurface::lowerRequested,
                this, &QWaylandQtShellChrome::lower);
        connect(d->shellSurface, &QWaylandQtShellSurface::activeChanged,
                this, &QWaylandQtShellChrome::updateActiveState);
        connect(d->shellSurface, &QWaylandQtShellSurface::surfaceChanged,
                this, &QWaylandQtShellChrome::updateSurface);
    }

    updateDecorations();
    updateSurface();
}

void QWaylandQtShellChrome::updateWindowState()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    setWindowState(d->shellSurface->windowState());
}

void QWaylandQtShellChrome::updateWindowFlags()
{
    Q_D(QWaylandQtShellChrome);

    uint nextFlags = d->shellSurface == nullptr || d->shellSurface->windowFlags() == Qt::Window
            ? d->defaultFlags
            : d->shellSurface->windowFlags();

    if (d->currentFlags != nextFlags) {
        d->currentFlags = nextFlags;
        emit currentWindowFlagsChanged();
    }
}

/*!
 * \qmlproperty int QtShellChrome::windowFlags
 *
 * This property holds the window flags of the QtShellChrome. They will match the \c windowFlags
 * property of the associated QtShellSurface, except when this is equal to Qt.Window. In this case,
 * a set of default window flags will be used instead. The default window flags are Qt.Window,
 * Qt.WindowMaximizeButtonHint, Qt.WindowMinimizeButtonHint and Qt.WindowCloseButtonHint.
 */

/*!
 * \property QWaylandQtShellChrome::windowFlags
 *
 * This property holds the window flags of the QWaylandQtShellChrome. They will
 * match the windowFlags property of the associated QWaylandQtShellSurface,
 * except when this is equal to Qt::Window. In this case, a set of default
 * window flags will be used instead. The default window flags are Qt::Window,
 * Qt::WindowMaximizeButtonHint, Qt::WindowMinimizeButtonHint and
 * Qt::WindowCloseButtonHint.
 */
uint QWaylandQtShellChrome::currentWindowFlags() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->currentFlags;
}

/*!
 * \qmlproperty int QtShellChrome::windowState
 *
 * This property holds the window state of the shell surface. It will be updated immediately when
 * the window state is requested on the compositor-side, before this has been acknowledged by the
 * client. Therefore, it may in brief periods differ from the shell surface's \c windowState
 * property, which will be updated when the client has acknowledged the request.
 */

/*!
 * \property QWaylandQtShellChrome::windowState
 *
 * This property holds the window state of the shell surface. It will be updated
 * immediately when the window state is requested on the compositor-side, before
 * this has been acknowledged by the client. Therefore, it may in brief periods
 * differ from the shell surface's windowState property, which will be updated
 * when the client has acknowledged the request.
 */
uint QWaylandQtShellChrome::currentWindowState() const
{
    Q_D(const QWaylandQtShellChrome);
    return d->currentState;
}

bool QWaylandQtShellChrome::hasTitleBar() const
{
    Q_D(const QWaylandQtShellChrome);

    bool frameless = (d->currentFlags & Qt::FramelessWindowHint) == Qt::FramelessWindowHint
            || ((d->currentFlags & Qt::Popup) == Qt::Popup
                && (d->currentFlags & Qt::Tool) != Qt::Tool)
            || (d->currentState & Qt::WindowFullScreen) == Qt::WindowFullScreen;
    return !frameless;
}

/*!
 * \qmlproperty bool QtShellChrome::hasDecorations
 *
 * This property is true if the QtShellChrome's decorations should be visible, based on its window
 * state and window flags.
 */

/*!
 * \property QWaylandQtShellChrome::hasDecorations
 *
 * This property is \c true if the QWaylandQtShellChrome's decorations should be
 * visible, based on its window state and window flags.
 */
bool QWaylandQtShellChrome::hasDecorations() const
{
    Q_D(const QWaylandQtShellChrome);

    return hasTitleBar() && (d->currentFlags & Qt::Window) == Qt::Window;
}

QRect QWaylandQtShellChrome::maxContentRect() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return QRect{};

    int x0 = d->maximizedRect.x() + d->shellSurface->frameMarginLeft();
    int x1 = d->maximizedRect.x() + d->maximizedRect.width() - d->shellSurface->frameMarginRight();
    int y0 = d->maximizedRect.y() + d->shellSurface->frameMarginTop();
    int y1 = d->maximizedRect.y() + d->maximizedRect.height() - d->shellSurface->frameMarginBottom();

    return QRect(x0, y0, x1 - x0, y1 - y0);
}

static int randomPos(int windowSize, int screenSize)
{
    return (windowSize >= screenSize) ? 0 : rand() % (screenSize - windowSize);
}

void QWaylandQtShellChrome::setWindowState(uint nextState)
{
    Q_D(QWaylandQtShellChrome);

    if (d->currentState == nextState)
        return;

    if (d->shellSurface == nullptr || d->shellSurfaceItem == nullptr)
        return;

    QWaylandOutput *output = d->shellSurfaceItem->output();
    if (output == nullptr)
        return;

    if ((d->currentState & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) == 0) {
        d->restoreGeometry = d->shellSurface->windowGeometry();
    }

    d->currentState = nextState;
    emit currentWindowStateChanged();

    if ((nextState & Qt::WindowMinimized) != 0) {
        d->shellSurface->requestWindowGeometry(nextState, QRect(0, 0, 1, 1));
        d->shellSurfaceItem->setVisible(false);
        deactivate();
    } else if ((nextState & Qt::WindowFullScreen) != 0) {
        d->shellSurfaceItem->setVisible(true);
        d->shellSurface->requestWindowGeometry(nextState, QRect(QPoint(0, 0), output->window()->size()));
        activate();
    } else if ((nextState & Qt::WindowMaximized) != 0) {
        d->shellSurfaceItem->setVisible(true);
        d->shellSurface->requestWindowGeometry(nextState, maxContentRect());
        activate();
    } else {
        d->shellSurfaceItem->setVisible(true);
        d->shellSurface->requestWindowGeometry(nextState, d->restoreGeometry);
        activate();
    }
}

void QWaylandQtShellChrome::updateAutomaticPosition()
{
    Q_D(QWaylandQtShellChrome);
    if (!d->positionSet && d->shellSurface != nullptr) {
        bool randomize = d->shellSurface->positionAutomatic();
        QRect rect = d->shellSurface->windowGeometry();
        QRect space = maxContentRect();

        int xpos = randomize ? randomPos(rect.width(), space.width()) + space.x()
                             : qMax(rect.x(), space.x());
        int ypos = randomize ? randomPos(rect.height(), space.height()) + space.y()
                             : qMax(rect.y(), space.y());

        d->shellSurface->setWindowPosition(QPoint(xpos, ypos));
        d->positionSet = true;
    }
}

/*!
 * \qmlmethod void QtShellChrome::deactivate()
 *
 * Manually deactivates this window. If the window was active, this will activate the next window in
 * the stack instead.
 */
void QWaylandQtShellChrome::deactivate()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface != nullptr)
        d->shellSurface->setActive(false);
}

void QWaylandQtShellChrome::activateOnGrab(QPointingDevice::GrabTransition transition)
{
    Q_D(QWaylandQtShellChrome);
    if (d->titleBarHandler != nullptr) {
        switch (transition) {
        case QPointingDevice::GrabPassive:
        case QPointingDevice::OverrideGrabPassive:
        case QPointingDevice::GrabExclusive:
            activate();
            break;
        default:
            break;
        }
    }
}

/*!
 * \qmlmethod void QtShellChrome::activate()
 *
 * Manually activate this window. This will also raise the window.
 *
 * \sa raise()
 */
void QWaylandQtShellChrome::activate()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface != nullptr)
        d->shellSurface->setActive(true);
    raise();
}

/*!
 * \qmlmethod void QtShellChrome::raise()
 *
 * Raise this window, so that it stacks on top of other windows (except if the other window's
 * flags prohibit this.)
 */
void QWaylandQtShellChrome::raise()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurfaceItem != nullptr)
        d->shellSurfaceItem->raise();
}

/*!
 * \qmlmethod void QtShellChrome::lower()
 *
 * Lower this window, so that it stacks underneath other windows (except if the other window's
 * window flags prohibit this.)
 */
void QWaylandQtShellChrome::lower()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurfaceItem != nullptr)
        d->shellSurfaceItem->lower();
}

void QWaylandQtShellChrome::updateActiveState()
{
    Q_D(QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return;

    if (d->shellSurface->active()) {
        raise();
        emit activated();
    } else {
        emit deactivated();
    }
}

/*!
 * \qmlproperty int QtShellChrome::frameMarginLeft
 *
 * Sets the size of the left margin of the QtShellChrome which is reserved for window decorations.
 * By default, this will equal the width of the \l leftResizeHandle if it is set. Otherwise it will
 * be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be overridden with
 * their corresponding properties.
 */

/*!
 * \property QWaylandQtShellChrome::frameMarginLeft
 *
 * Sets the size of the left margin of the QWaylandQtShellChrome which is
 * reserved for window decorations. By default, this will equal the width of the
 * leftResizeHandle if it is set. Otherwise it will be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be
 *       overridden with their corresponding properties.
 */
void QWaylandQtShellChrome::setFrameMarginLeft(int left)
{
    Q_D(QWaylandQtShellChrome);
    if (d->explicitFrameMargins.left() == left)
        return;

    d->explicitFrameMargins.setLeft(left);
    d->automaticFrameMargins = false;
    updateDecorations();

    emit frameMarginChanged();
}

int QWaylandQtShellChrome::frameMarginLeft() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return 0;
    return d->shellSurface->frameMarginLeft();
}

/*!
 * \qmlproperty int QtShellChrome::frameMarginRight
 *
 * Sets the size of the right margin of the QtShellChrome which is reserved for window decorations.
 * By default, this will equal the width of the \l rightResizeHandle if it is set. Otherwise it will
 * be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be overridden with
 * their corresponding properties.
 */

/*!
 * \property QWaylandQtShellChrome::frameMarginRight
 *
 * Sets the size of the right margin of the QWaylandQtShellChrome which is
 * reserved for window decorations. By default, this will equal the width of the
 * rightResizeHandle if it is set. Otherwise it will be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be
 *       overridden with their corresponding properties.
 */
void QWaylandQtShellChrome::setFrameMarginRight(int right)
{
    Q_D(QWaylandQtShellChrome);
    if (d->explicitFrameMargins.right() == right)
        return;

    d->explicitFrameMargins.setRight(right);
    d->automaticFrameMargins = false;
    updateDecorations();

    emit frameMarginChanged();
}

int QWaylandQtShellChrome::frameMarginRight() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return 0;
    return d->shellSurface->frameMarginRight();
}

/*!
 * \qmlproperty int QtShellChrome::frameMarginTop
 *
 * Sets the size of the top margin of the QtShellChrome which is reserved for window decorations.
 * By default, this will equal the sum of the \l leftResizeHandle and the \l{titleBar}'s heights,
 * if they are set. Otherwise it will be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be overridden with
 * their corresponding properties.
 */

/*!
 * \property QWaylandQtShellChrome::frameMarginTop
 *
 * Sets the size of the top margin of the QWaylandQtShellChrome which is
 * reserved for window decorations. By default, this will equal the sum of the
 * topResizeHandle and the titleBar's heights, if they are set. Otherwise it
 * will be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be
 *       overridden with their corresponding properties.
 */
void QWaylandQtShellChrome::setFrameMarginTop(int top)
{
    Q_D(QWaylandQtShellChrome);
    if (d->explicitFrameMargins.top() == top)
        return;
    d->explicitFrameMargins.setTop(top);
    d->automaticFrameMargins = false;
    updateDecorations();

    emit frameMarginChanged();
}

int QWaylandQtShellChrome::frameMarginTop() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return 0;
    return d->shellSurface->frameMarginTop();
}

/*!
 * \qmlproperty int QtShellChrome::frameMarginBottom
 *
 * Sets the size of the bottom margin of the QtShellChrome which is reserved for window decorations.
 * By default, this will equal the height of the \l bottomResizeHandle if it is set. Otherwise it will
 * be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be overridden with
 * their corresponding properties.
 */

/*!
 * \property QWaylandQtShellChrome::frameMarginBottom
 *
 * Sets the size of the bottom margin of the QWaylandQtShellChrome which is
 * reserved for window decorations. By default, this will equal the height of
 * the bottomResizeHandle if it is set. Otherwise it will be 0.
 *
 * \note By setting this property explicitly, all default frame margins will be
 *       overridden with their corresponding properties.
 */
void QWaylandQtShellChrome::setFrameMarginBottom(int bottom)
{
    Q_D(QWaylandQtShellChrome);
    if (d->explicitFrameMargins.bottom() == bottom)
        return;
    d->explicitFrameMargins.setBottom(bottom);
    d->automaticFrameMargins = false;
    updateDecorations();

    emit frameMarginChanged();
}

int QWaylandQtShellChrome::frameMarginBottom() const
{
    Q_D(const QWaylandQtShellChrome);
    if (d->shellSurface == nullptr)
        return 0;
    return d->shellSurface->frameMarginBottom();
}

QT_END_NAMESPACE

#include "moc_qwaylandqtshellchrome.cpp"
