#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "region.hpp" #include "reload.hpp" // Proxy to an actual window exposing a limited property set with the ability to // transfer it to a new window. ///! Base class for reloadable windows /// Base class for reloadable windows. See [ShellWindow] and [FloatingWindow] /// /// [ShellWindow]: ../shellwindow /// [FloatingWindow]: ../floatingwindow class ProxyWindowBase: public Reloadable { Q_OBJECT; /// The QtQuick window backing this window. /// /// > [!WARNING] Do not expect values set via this property to work correctly. /// > Values set this way will almost certainly misbehave across a reload, possibly /// > even without one. /// > /// > Use **only** if you know what you are doing. Q_PROPERTY(QQuickWindow* _backingWindow READ backingWindow); /// The visibility of the window. /// /// > [!INFO] Windows are not visible by default so you will need to set this to make the window /// > appear. Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); /// The background color of the window. Defaults to white. /// /// > [!WARNING] This seems to behave weirdly when using transparent colors on some systems. /// > Using a colored content item over a transparent window is the recommended way to work around this: /// > ```qml /// > ProxyWindow { /// > Rectangle { /// > anchors.fill: parent /// > color: "#20ffffff" /// > /// > // your content here /// > } /// > } /// > ``` Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged); /// The clickthrough mask. Defaults to null. /// /// If non null then the clickable areas of the window will be determined by the provided region. /// /// ```qml /// ShellWindow { /// // The mask region is set to `rect`, meaning only `rect` is clickable. /// // All other clicks pass through the window to ones behind it. /// mask: Region { item: rect } /// /// Rectangle { /// id: rect /// /// anchors.centerIn: parent /// width: 100 /// height: 100 /// } /// } /// ``` /// /// If the provided region's intersection mode is `Combine` (the default), /// then the region will be used as is. Otherwise it will be applied on top of the window region. /// /// For example, setting the intersection mode to `Xor` will invert the mask and make everything in /// the mask region not clickable and pass through clicks inside it through the window. /// /// ```qml /// ShellWindow { /// // The mask region is set to `rect`, but the intersection mode is set to `Xor`. /// // This inverts the mask causing all clicks inside `rect` to be passed to the window /// // behind this one. /// mask: Region { item: rect; intersection: Intersection.Xor } /// /// Rectangle { /// id: rect /// /// anchors.centerIn: parent /// width: 100 /// height: 100 /// } /// } /// ``` Q_PROPERTY(PendingRegion* mask READ mask WRITE setMask NOTIFY maskChanged); Q_PROPERTY(QQmlListProperty data READ data); Q_CLASSINFO("DefaultProperty", "data"); QML_ELEMENT; QML_UNCREATABLE("use ShellWindow or FloatingWindow"); public: explicit ProxyWindowBase(QObject* parent = nullptr); ~ProxyWindowBase() override; ProxyWindowBase(ProxyWindowBase&) = delete; ProxyWindowBase(ProxyWindowBase&&) = delete; void operator=(ProxyWindowBase&) = delete; void operator=(ProxyWindowBase&&) = delete; void onReload(QObject* oldInstance) override; virtual void setupWindow(); // Disown the backing window and delete all its children. virtual QQuickWindow* disownWindow(); [[nodiscard]] QQuickWindow* backingWindow() const; [[nodiscard]] virtual bool isVisible() const; virtual void setVisible(bool visible); [[nodiscard]] virtual qint32 width() const; virtual void setWidth(qint32 width); [[nodiscard]] virtual qint32 height() const; virtual void setHeight(qint32 height); [[nodiscard]] QColor color() const; void setColor(QColor color); [[nodiscard]] PendingRegion* mask() const; void setMask(PendingRegion* mask); QQmlListProperty data(); signals: void windowConnected(); void visibleChanged(); void widthChanged(); void heightChanged(); void colorChanged(); void maskChanged(); private slots: void onMaskChanged(); void onWidthChanged(); void onHeightChanged(); protected: bool mVisible = false; qint32 mWidth = 100; qint32 mHeight = 100; QColor mColor = Qt::white; PendingRegion* mMask = nullptr; QQuickWindow* window = nullptr; QQuickItem* contentItem = nullptr; private: void updateMask(); }; // qt attempts to resize the window but fails because wayland // and only resizes the graphics context which looks terrible. ///! Standard floating window. class ProxyFloatingWindow: public ProxyWindowBase { Q_OBJECT; QML_NAMED_ELEMENT(FloatingWindow); public: // Setting geometry while the window is visible makes the content item shrink but not the window // which is awful so we disable it for floating windows. void setWidth(qint32 width) override; void setHeight(qint32 height) override; };