/* * SPDX-FileCopyrightText: 2023 ivan tkachenko * * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef OVERLAYZSTACKINGATTACHED_H #define OVERLAYZSTACKINGATTACHED_H #include #include #include class QQuickItem; /** * This attached property manages z-index for stacking overlays relative to each other. * * When a popup is about to show, OverlayZStacking object kicks in, searches for the * next nearest popup in the QtQuick hierarchy of items, and sets its z value to the * biggest of two: current stacking value for its layer, or parent's z index + 1. * This way OverlayZStacking algorithm ensures that a popup is always stacked higher * than its logical parent popup, but also no lower than its siblings on the same * logical layer. * * @code * import QtQuick.Controls as QQC2 * import org.kde.kirigami as Kirigami * * QQC2.Popup { * Kirigami.OverlayZStacking.layer: Kirigami.OverlayZStacking.ToolTip * z: Kirigami.OverlayZStacking.z * } * @endcode * * @since 6.0 */ class OverlayZStackingAttached : public QObject { Q_OBJECT QML_ELEMENT QML_NAMED_ELEMENT(OverlayZStacking) QML_UNCREATABLE("Cannot create objects of type OverlayZStacking, use it as an attached property") QML_ATTACHED(OverlayZStackingAttached) /** * An optimal z-index that attachee popup should bind to. */ Q_PROPERTY(qreal z READ z NOTIFY zChanged FINAL) /** * The logical stacking layer of attachee popup, akin to window manager's layers. */ Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged FINAL) public: enum Layer { DefaultLowest = 0, Drawer, FullScreen, Dialog, Menu, Notification, ToolTip, }; Q_ENUM(Layer) explicit OverlayZStackingAttached(QObject *parent = nullptr); ~OverlayZStackingAttached() override; qreal z() const; Layer layer() const; void setLayer(Layer layer); // QML attached property static OverlayZStackingAttached *qmlAttachedProperties(QObject *object); Q_SIGNALS: void zChanged(); void layerChanged(); private Q_SLOTS: // Popup shall not change z index while being open, so if changes arrive, we defer it until closed. void enqueueSignal(); void dispatchPendingSignal(); void updateParentPopup(); private: void updateParentPopupSilent(); void setParentPopup(QObject *popup); qreal parentPopupZ() const; static bool isVisible(const QObject *popup); static bool isPopup(const QObject *object); static QObject *findParentPopup(const QObject *popup); static QQuickItem *findParentPopupItem(const QObject *popup); static Layer defaultLayerForPopupType(const QObject *popup); static qreal defaultZForLayer(Layer layer); Layer m_layer = Layer::DefaultLowest; QPointer m_parentPopup; bool m_pending; }; QML_DECLARE_TYPEINFO(OverlayZStackingAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // OVERLAYZSTACKINGATTACHED_H