1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickwindowinspector.h" |
5 | #include "inspecttool.h" |
6 | |
7 | #include <private/qquickitem_p.h> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | namespace QmlJSDebugger { |
12 | |
13 | /* |
14 | * Returns the first visible item at the given position, or 0 when no such |
15 | * child exists. |
16 | */ |
17 | static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos, |
18 | QQuickItem *overlay) |
19 | { |
20 | if (item == overlay) |
21 | return nullptr; |
22 | |
23 | if (!item->isVisible() || item->opacity() == 0.0) |
24 | return nullptr; |
25 | |
26 | if (item->flags() & QQuickItem::ItemClipsChildrenToShape) { |
27 | if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos)) |
28 | return nullptr; |
29 | } |
30 | |
31 | QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems(); |
32 | for (int i = children.size() - 1; i >= 0; --i) { |
33 | QQuickItem *child = children.at(i); |
34 | if (QQuickItem *betterCandidate = itemAt(item: child, pos: item->mapToItem(item: child, point: pos), |
35 | overlay)) |
36 | return betterCandidate; |
37 | } |
38 | |
39 | if (!(item->flags() & QQuickItem::ItemHasContents)) |
40 | return nullptr; |
41 | |
42 | if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos)) |
43 | return nullptr; |
44 | |
45 | return item; |
46 | } |
47 | |
48 | /* |
49 | * Collects all the items at the given position, from top to bottom. |
50 | */ |
51 | static void collectItemsAt(QQuickItem *item, const QPointF &pos, |
52 | QQuickItem *overlay, QList<QQuickItem *> &resultList) |
53 | { |
54 | if (item == overlay) |
55 | return; |
56 | |
57 | if (item->flags() & QQuickItem::ItemClipsChildrenToShape) { |
58 | if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos)) |
59 | return; |
60 | } |
61 | |
62 | QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems(); |
63 | for (int i = children.size() - 1; i >= 0; --i) { |
64 | QQuickItem *child = children.at(i); |
65 | collectItemsAt(item: child, pos: item->mapToItem(item: child, point: pos), overlay, resultList); |
66 | } |
67 | |
68 | if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos)) |
69 | return; |
70 | |
71 | resultList.append(t: item); |
72 | } |
73 | |
74 | QQuickWindowInspector::QQuickWindowInspector(QQuickWindow *quickWindow, QObject *parent) : |
75 | QObject(parent), |
76 | m_overlay(new QQuickItem), |
77 | m_window(quickWindow), |
78 | m_parentWindow(nullptr), |
79 | m_tool(nullptr) |
80 | { |
81 | setParentWindow(quickWindow); |
82 | |
83 | // Try to make sure the overlay is always on top |
84 | m_overlay->setZ(FLT_MAX); |
85 | |
86 | if (QQuickItem *root = m_window->contentItem()) |
87 | m_overlay->setParentItem(root); |
88 | |
89 | m_window->installEventFilter(filterObj: this); |
90 | } |
91 | |
92 | bool QQuickWindowInspector::eventFilter(QObject *obj, QEvent *event) |
93 | { |
94 | if (!m_tool || obj != m_window) |
95 | return QObject::eventFilter(watched: obj, event); |
96 | |
97 | switch (event->type()) { |
98 | case QEvent::Enter: |
99 | m_tool->enterEvent(static_cast<QEnterEvent*>(event)); |
100 | return true; |
101 | case QEvent::Leave: |
102 | m_tool->leaveEvent(event); |
103 | return true; |
104 | case QEvent::MouseButtonPress: |
105 | m_tool->mousePressEvent(static_cast<QMouseEvent*>(event)); |
106 | return true; |
107 | case QEvent::MouseMove: |
108 | m_tool->mouseMoveEvent(static_cast<QMouseEvent*>(event)); |
109 | return true; |
110 | case QEvent::MouseButtonRelease: |
111 | return true; |
112 | case QEvent::KeyPress: |
113 | m_tool->keyPressEvent(static_cast<QKeyEvent*>(event)); |
114 | return true; |
115 | case QEvent::KeyRelease: |
116 | return true; |
117 | case QEvent::MouseButtonDblClick: |
118 | m_tool->mouseDoubleClickEvent(static_cast<QMouseEvent*>(event)); |
119 | return true; |
120 | #if QT_CONFIG(wheelevent) |
121 | case QEvent::Wheel: |
122 | return true; |
123 | #endif |
124 | case QEvent::TouchBegin: |
125 | case QEvent::TouchUpdate: |
126 | case QEvent::TouchEnd: |
127 | m_tool->touchEvent(event: static_cast<QTouchEvent*>(event)); |
128 | return true; |
129 | default: |
130 | break; |
131 | } |
132 | |
133 | return QObject::eventFilter(watched: obj, event); |
134 | } |
135 | |
136 | static Qt::WindowFlags fixFlags(Qt::WindowFlags flags) |
137 | { |
138 | // If only the type flag is given, some other window flags are automatically assumed. When we |
139 | // add a flag, we need to make those explicit. |
140 | switch (flags) { |
141 | case Qt::Window: |
142 | return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint |
143 | | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint; |
144 | case Qt::Dialog: |
145 | case Qt::Tool: |
146 | return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; |
147 | default: |
148 | return flags; |
149 | } |
150 | } |
151 | |
152 | void QQuickWindowInspector::setShowAppOnTop(bool appOnTop) |
153 | { |
154 | if (!m_parentWindow) |
155 | return; |
156 | |
157 | Qt::WindowFlags flags = m_parentWindow->flags(); |
158 | Qt::WindowFlags newFlags = appOnTop ? (fixFlags(flags) | Qt::WindowStaysOnTopHint) : |
159 | (flags & ~Qt::WindowStaysOnTopHint); |
160 | if (newFlags != flags) |
161 | m_parentWindow->setFlags(newFlags); |
162 | } |
163 | |
164 | bool QQuickWindowInspector::isEnabled() const |
165 | { |
166 | return m_tool != nullptr; |
167 | } |
168 | |
169 | void QQuickWindowInspector::setEnabled(bool enabled) |
170 | { |
171 | if (enabled) { |
172 | m_tool = new InspectTool(this, m_window); |
173 | } else { |
174 | delete m_tool; |
175 | m_tool = nullptr; |
176 | } |
177 | } |
178 | |
179 | QQuickWindow *QQuickWindowInspector::quickWindow() const |
180 | { |
181 | return m_window; |
182 | } |
183 | |
184 | void QQuickWindowInspector::setParentWindow(QWindow *parentWindow) |
185 | { |
186 | if (parentWindow) { |
187 | while (QWindow *w = parentWindow->parent()) |
188 | parentWindow = w; |
189 | } |
190 | |
191 | m_parentWindow = parentWindow; |
192 | } |
193 | |
194 | QList<QQuickItem *> QQuickWindowInspector::itemsAt(const QPointF &pos) const |
195 | { |
196 | QList<QQuickItem *> resultList; |
197 | QQuickItem *root = m_window->contentItem(); |
198 | collectItemsAt(item: root, pos: root->mapFromScene(point: pos), overlay: m_overlay, |
199 | resultList); |
200 | return resultList; |
201 | } |
202 | |
203 | QQuickItem *QQuickWindowInspector::topVisibleItemAt(const QPointF &pos) const |
204 | { |
205 | QQuickItem *root = m_window->contentItem(); |
206 | return itemAt(item: root, pos: root->mapFromScene(point: pos), overlay: m_overlay); |
207 | } |
208 | |
209 | |
210 | } // namespace QmlJSDebugger |
211 | |
212 | QT_END_NAMESPACE |
213 | |
214 | #include "moc_qquickwindowinspector.cpp" |
215 | |