File indexing completed on 2024-04-21 03:56:00
0001 /* 0002 * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "overlayzstackingattached.h" 0008 0009 #include "loggingcategory.h" 0010 0011 #include <QQuickItem> 0012 0013 OverlayZStackingAttached::OverlayZStackingAttached(QObject *parent) 0014 : QObject(parent) 0015 , m_layer(defaultLayerForPopupType(parent)) 0016 , m_parentPopup(nullptr) 0017 , m_pending(false) 0018 { 0019 Q_ASSERT(parent); 0020 if (!isPopup(parent)) { 0021 qCWarning(KirigamiLog) << "OverlayZStacking must be attached to a Popup"; 0022 return; 0023 } 0024 0025 connect(parent, SIGNAL(parentChanged()), this, SLOT(updateParentPopup())); 0026 connect(parent, SIGNAL(closed()), this, SLOT(dispatchPendingSignal())); 0027 // Note: aboutToShow is too late, as QQuickPopup has already created modal 0028 // dimmer based off current z index. 0029 } 0030 0031 OverlayZStackingAttached::~OverlayZStackingAttached() 0032 { 0033 } 0034 0035 qreal OverlayZStackingAttached::z() const 0036 { 0037 if (!m_parentPopup) { 0038 const_cast<OverlayZStackingAttached *>(this)->updateParentPopupSilent(); 0039 } 0040 0041 qreal layerZ = defaultZForLayer(m_layer); 0042 qreal parentZ = parentPopupZ() + 1; 0043 0044 return std::max(layerZ, parentZ); 0045 } 0046 0047 OverlayZStackingAttached::Layer OverlayZStackingAttached::layer() const 0048 { 0049 return m_layer; 0050 } 0051 0052 void OverlayZStackingAttached::setLayer(Layer layer) 0053 { 0054 if (m_layer == layer) { 0055 return; 0056 } 0057 0058 m_layer = layer; 0059 Q_EMIT layerChanged(); 0060 enqueueSignal(); 0061 } 0062 0063 OverlayZStackingAttached *OverlayZStackingAttached::qmlAttachedProperties(QObject *object) 0064 { 0065 return new OverlayZStackingAttached(object); 0066 } 0067 0068 void OverlayZStackingAttached::enqueueSignal() 0069 { 0070 if (isVisible(parent())) { 0071 m_pending = true; 0072 } else { 0073 Q_EMIT zChanged(); 0074 } 0075 } 0076 0077 void OverlayZStackingAttached::dispatchPendingSignal() 0078 { 0079 if (m_pending) { 0080 m_pending = false; 0081 Q_EMIT zChanged(); 0082 } 0083 } 0084 0085 void OverlayZStackingAttached::updateParentPopup() 0086 { 0087 const qreal oldZ = parentPopupZ(); 0088 0089 updateParentPopupSilent(); 0090 0091 if (oldZ != parentPopupZ()) { 0092 enqueueSignal(); 0093 } 0094 } 0095 0096 void OverlayZStackingAttached::updateParentPopupSilent() 0097 { 0098 auto popup = findParentPopup(parent()); 0099 setParentPopup(popup); 0100 } 0101 0102 void OverlayZStackingAttached::setParentPopup(QObject *parentPopup) 0103 { 0104 if (m_parentPopup == parentPopup) { 0105 return; 0106 } 0107 0108 if (m_parentPopup) { 0109 disconnect(m_parentPopup.data(), SIGNAL(zChanged()), this, SLOT(enqueueSignal())); 0110 } 0111 0112 // Ideally, we would also connect to all the parent items' parentChanged() on the way to parent popup. 0113 m_parentPopup = parentPopup; 0114 0115 if (m_parentPopup) { 0116 connect(m_parentPopup.data(), SIGNAL(zChanged()), this, SLOT(enqueueSignal())); 0117 } 0118 } 0119 0120 qreal OverlayZStackingAttached::parentPopupZ() const 0121 { 0122 if (m_parentPopup) { 0123 return m_parentPopup->property("z").toReal(); 0124 } 0125 return -1; 0126 } 0127 0128 bool OverlayZStackingAttached::isVisible(const QObject *popup) 0129 { 0130 if (!isPopup(popup)) { 0131 return false; 0132 } 0133 0134 return popup->property("visible").toBool(); 0135 } 0136 0137 bool OverlayZStackingAttached::isPopup(const QObject *object) 0138 { 0139 return object && object->inherits("QQuickPopup"); 0140 } 0141 0142 QObject *OverlayZStackingAttached::findParentPopup(const QObject *popup) 0143 { 0144 auto item = findParentPopupItem(popup); 0145 if (!item) { 0146 return nullptr; 0147 } 0148 auto parentPopup = item->parent(); 0149 if (!isPopup(popup)) { 0150 return nullptr; 0151 } 0152 return parentPopup; 0153 } 0154 0155 QQuickItem *OverlayZStackingAttached::findParentPopupItem(const QObject *popup) 0156 { 0157 if (!isPopup(popup)) { 0158 return nullptr; 0159 } 0160 0161 QQuickItem *parentItem = popup->property("parent").value<QQuickItem *>(); 0162 if (!parentItem) { 0163 return nullptr; 0164 } 0165 0166 QQuickItem *item = parentItem; 0167 do { 0168 if (item && item->inherits("QQuickPopupItem")) { 0169 return item; 0170 } 0171 item = item->parentItem(); 0172 } while (item); 0173 0174 return nullptr; 0175 } 0176 0177 OverlayZStackingAttached::Layer OverlayZStackingAttached::defaultLayerForPopupType(const QObject *popup) 0178 { 0179 if (popup) { 0180 if (popup->inherits("QQuickDialog")) { 0181 return Layer::Dialog; 0182 } else if (popup->inherits("QQuickDrawer")) { 0183 return Layer::Drawer; 0184 } else if (popup->inherits("QQuickMenu")) { 0185 return Layer::Menu; 0186 } else if (popup->inherits("QQuickToolTip")) { 0187 return Layer::ToolTip; 0188 } 0189 } 0190 return DefaultLowest; 0191 } 0192 0193 qreal OverlayZStackingAttached::defaultZForLayer(Layer layer) 0194 { 0195 switch (layer) { 0196 case DefaultLowest: 0197 return 0; 0198 case Drawer: 0199 return 100; 0200 case FullScreen: 0201 return 200; 0202 case Dialog: 0203 return 300; 0204 case Menu: 0205 return 400; 0206 case Notification: 0207 return 500; 0208 case ToolTip: 0209 return 600; 0210 } 0211 return 0; 0212 } 0213 0214 #include "moc_overlayzstackingattached.cpp"