1 | /* |
2 | * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> |
3 | * |
4 | * SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #include "overlayzstackingattached.h" |
8 | |
9 | #include "loggingcategory.h" |
10 | |
11 | #include <QQuickItem> |
12 | |
13 | OverlayZStackingAttached::OverlayZStackingAttached(QObject *parent) |
14 | : QObject(parent) |
15 | , m_layer(defaultLayerForPopupType(popup: parent)) |
16 | , m_parentPopup(nullptr) |
17 | , m_pending(false) |
18 | { |
19 | Q_ASSERT(parent); |
20 | if (!isPopup(object: parent)) { |
21 | qCWarning(KirigamiLog) << "OverlayZStacking must be attached to a Popup" ; |
22 | return; |
23 | } |
24 | |
25 | connect(sender: parent, SIGNAL(parentChanged()), receiver: this, SLOT(updateParentPopup())); |
26 | connect(sender: parent, SIGNAL(closed()), receiver: this, SLOT(dispatchPendingSignal())); |
27 | // Note: aboutToShow is too late, as QQuickPopup has already created modal |
28 | // dimmer based off current z index. |
29 | } |
30 | |
31 | OverlayZStackingAttached::~OverlayZStackingAttached() |
32 | { |
33 | } |
34 | |
35 | qreal OverlayZStackingAttached::z() const |
36 | { |
37 | if (!m_parentPopup) { |
38 | const_cast<OverlayZStackingAttached *>(this)->updateParentPopupSilent(); |
39 | } |
40 | |
41 | qreal layerZ = defaultZForLayer(layer: m_layer); |
42 | qreal parentZ = parentPopupZ() + 1; |
43 | |
44 | return std::max(a: layerZ, b: parentZ); |
45 | } |
46 | |
47 | OverlayZStackingAttached::Layer OverlayZStackingAttached::layer() const |
48 | { |
49 | return m_layer; |
50 | } |
51 | |
52 | void OverlayZStackingAttached::setLayer(Layer layer) |
53 | { |
54 | if (m_layer == layer) { |
55 | return; |
56 | } |
57 | |
58 | m_layer = layer; |
59 | Q_EMIT layerChanged(); |
60 | enqueueSignal(); |
61 | } |
62 | |
63 | OverlayZStackingAttached *OverlayZStackingAttached::qmlAttachedProperties(QObject *object) |
64 | { |
65 | return new OverlayZStackingAttached(object); |
66 | } |
67 | |
68 | void OverlayZStackingAttached::enqueueSignal() |
69 | { |
70 | if (isVisible(popup: parent())) { |
71 | m_pending = true; |
72 | } else { |
73 | Q_EMIT zChanged(); |
74 | } |
75 | } |
76 | |
77 | void OverlayZStackingAttached::dispatchPendingSignal() |
78 | { |
79 | if (m_pending) { |
80 | m_pending = false; |
81 | Q_EMIT zChanged(); |
82 | } |
83 | } |
84 | |
85 | void OverlayZStackingAttached::() |
86 | { |
87 | const qreal oldZ = parentPopupZ(); |
88 | |
89 | updateParentPopupSilent(); |
90 | |
91 | if (oldZ != parentPopupZ()) { |
92 | enqueueSignal(); |
93 | } |
94 | } |
95 | |
96 | void OverlayZStackingAttached::() |
97 | { |
98 | auto = findParentPopup(popup: parent()); |
99 | setParentPopup(popup); |
100 | } |
101 | |
102 | void OverlayZStackingAttached::(QObject *) |
103 | { |
104 | if (m_parentPopup == parentPopup) { |
105 | return; |
106 | } |
107 | |
108 | if (m_parentPopup) { |
109 | disconnect(sender: m_parentPopup.data(), SIGNAL(zChanged()), receiver: this, SLOT(enqueueSignal())); |
110 | } |
111 | |
112 | // Ideally, we would also connect to all the parent items' parentChanged() on the way to parent popup. |
113 | m_parentPopup = parentPopup; |
114 | |
115 | if (m_parentPopup) { |
116 | connect(sender: m_parentPopup.data(), SIGNAL(zChanged()), receiver: this, SLOT(enqueueSignal())); |
117 | } |
118 | } |
119 | |
120 | qreal OverlayZStackingAttached::() const |
121 | { |
122 | if (m_parentPopup) { |
123 | return m_parentPopup->property(name: "z" ).toReal(); |
124 | } |
125 | return -1; |
126 | } |
127 | |
128 | bool OverlayZStackingAttached::isVisible(const QObject *) |
129 | { |
130 | if (!isPopup(object: popup)) { |
131 | return false; |
132 | } |
133 | |
134 | return popup->property(name: "visible" ).toBool(); |
135 | } |
136 | |
137 | bool OverlayZStackingAttached::(const QObject *object) |
138 | { |
139 | return object && object->inherits(classname: "QQuickPopup" ); |
140 | } |
141 | |
142 | QObject *OverlayZStackingAttached::(const QObject *) |
143 | { |
144 | auto item = findParentPopupItem(popup); |
145 | if (!item) { |
146 | return nullptr; |
147 | } |
148 | auto = item->parent(); |
149 | if (!isPopup(object: popup)) { |
150 | return nullptr; |
151 | } |
152 | return parentPopup; |
153 | } |
154 | |
155 | QQuickItem *OverlayZStackingAttached::(const QObject *) |
156 | { |
157 | if (!isPopup(object: popup)) { |
158 | return nullptr; |
159 | } |
160 | |
161 | QQuickItem *parentItem = popup->property(name: "parent" ).value<QQuickItem *>(); |
162 | if (!parentItem) { |
163 | return nullptr; |
164 | } |
165 | |
166 | QQuickItem *item = parentItem; |
167 | do { |
168 | if (item && item->inherits(classname: "QQuickPopupItem" )) { |
169 | return item; |
170 | } |
171 | item = item->parentItem(); |
172 | } while (item); |
173 | |
174 | return nullptr; |
175 | } |
176 | |
177 | OverlayZStackingAttached::Layer OverlayZStackingAttached::(const QObject *) |
178 | { |
179 | if (popup) { |
180 | if (popup->inherits(classname: "QQuickDialog" )) { |
181 | return Layer::Dialog; |
182 | } else if (popup->inherits(classname: "QQuickDrawer" )) { |
183 | return Layer::Drawer; |
184 | } else if (popup->inherits(classname: "QQuickMenu" )) { |
185 | return Layer::Menu; |
186 | } else if (popup->inherits(classname: "QQuickToolTip" )) { |
187 | return Layer::ToolTip; |
188 | } |
189 | } |
190 | return DefaultLowest; |
191 | } |
192 | |
193 | qreal OverlayZStackingAttached::defaultZForLayer(Layer layer) |
194 | { |
195 | switch (layer) { |
196 | case DefaultLowest: |
197 | return 0; |
198 | case Drawer: |
199 | return 100; |
200 | case FullScreen: |
201 | return 200; |
202 | case Dialog: |
203 | return 300; |
204 | case Menu: |
205 | return 400; |
206 | case Notification: |
207 | return 500; |
208 | case ToolTip: |
209 | return 600; |
210 | } |
211 | return 0; |
212 | } |
213 | |
214 | #include "moc_overlayzstackingattached.cpp" |
215 | |