1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickwindowinspector.h"
41#include "inspecttool.h"
42
43#include <private/qquickitem_p.h>
44
45QT_BEGIN_NAMESPACE
46
47namespace QmlJSDebugger {
48
49/*
50 * Returns the first visible item at the given position, or 0 when no such
51 * child exists.
52 */
53static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos,
54 QQuickItem *overlay)
55{
56 if (item == overlay)
57 return nullptr;
58
59 if (!item->isVisible() || item->opacity() == 0.0)
60 return nullptr;
61
62 if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
63 if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos))
64 return nullptr;
65 }
66
67 QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
68 for (int i = children.count() - 1; i >= 0; --i) {
69 QQuickItem *child = children.at(i);
70 if (QQuickItem *betterCandidate = itemAt(item: child, pos: item->mapToItem(item: child, point: pos),
71 overlay))
72 return betterCandidate;
73 }
74
75 if (!(item->flags() & QQuickItem::ItemHasContents))
76 return nullptr;
77
78 if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos))
79 return nullptr;
80
81 return item;
82}
83
84/*
85 * Collects all the items at the given position, from top to bottom.
86 */
87static void collectItemsAt(QQuickItem *item, const QPointF &pos,
88 QQuickItem *overlay, QList<QQuickItem *> &resultList)
89{
90 if (item == overlay)
91 return;
92
93 if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
94 if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos))
95 return;
96 }
97
98 QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
99 for (int i = children.count() - 1; i >= 0; --i) {
100 QQuickItem *child = children.at(i);
101 collectItemsAt(item: child, pos: item->mapToItem(item: child, point: pos), overlay, resultList);
102 }
103
104 if (!QRectF(0, 0, item->width(), item->height()).contains(p: pos))
105 return;
106
107 resultList.append(t: item);
108}
109
110QQuickWindowInspector::QQuickWindowInspector(QQuickWindow *quickWindow, QObject *parent) :
111 QObject(parent),
112 m_overlay(new QQuickItem),
113 m_window(quickWindow),
114 m_parentWindow(nullptr),
115 m_tool(nullptr)
116{
117 setParentWindow(quickWindow);
118
119 // Try to make sure the overlay is always on top
120 m_overlay->setZ(FLT_MAX);
121
122 if (QQuickItem *root = m_window->contentItem())
123 m_overlay->setParentItem(root);
124
125 m_window->installEventFilter(filterObj: this);
126}
127
128bool QQuickWindowInspector::eventFilter(QObject *obj, QEvent *event)
129{
130 if (!m_tool || obj != m_window)
131 return QObject::eventFilter(watched: obj, event);
132
133 switch (event->type()) {
134 case QEvent::Enter:
135 m_tool->enterEvent(event);
136 return true;
137 case QEvent::Leave:
138 m_tool->leaveEvent(event);
139 return true;
140 case QEvent::MouseButtonPress:
141 m_tool->mousePressEvent(static_cast<QMouseEvent*>(event));
142 return true;
143 case QEvent::MouseMove:
144 m_tool->mouseMoveEvent(static_cast<QMouseEvent*>(event));
145 return true;
146 case QEvent::MouseButtonRelease:
147 return true;
148 case QEvent::KeyPress:
149 m_tool->keyPressEvent(static_cast<QKeyEvent*>(event));
150 return true;
151 case QEvent::KeyRelease:
152 return true;
153 case QEvent::MouseButtonDblClick:
154 m_tool->mouseDoubleClickEvent(static_cast<QMouseEvent*>(event));
155 return true;
156#if QT_CONFIG(wheelevent)
157 case QEvent::Wheel:
158 return true;
159#endif
160 case QEvent::TouchBegin:
161 case QEvent::TouchUpdate:
162 case QEvent::TouchEnd:
163 m_tool->touchEvent(event: static_cast<QTouchEvent*>(event));
164 return true;
165 default:
166 break;
167 }
168
169 return QObject::eventFilter(watched: obj, event);
170}
171
172static Qt::WindowFlags fixFlags(Qt::WindowFlags flags)
173{
174 // If only the type flag is given, some other window flags are automatically assumed. When we
175 // add a flag, we need to make those explicit.
176 switch (flags) {
177 case Qt::Window:
178 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint
179 | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
180 case Qt::Dialog:
181 case Qt::Tool:
182 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
183 default:
184 return flags;
185 }
186}
187
188void QQuickWindowInspector::setShowAppOnTop(bool appOnTop)
189{
190 if (!m_parentWindow)
191 return;
192
193 Qt::WindowFlags flags = m_parentWindow->flags();
194 Qt::WindowFlags newFlags = appOnTop ? (fixFlags(flags) | Qt::WindowStaysOnTopHint) :
195 (flags & ~Qt::WindowStaysOnTopHint);
196 if (newFlags != flags)
197 m_parentWindow->setFlags(newFlags);
198}
199
200bool QQuickWindowInspector::isEnabled() const
201{
202 return m_tool != nullptr;
203}
204
205void QQuickWindowInspector::setEnabled(bool enabled)
206{
207 if (enabled) {
208 m_tool = new InspectTool(this, m_window);
209 } else {
210 delete m_tool;
211 m_tool = nullptr;
212 }
213}
214
215QQuickWindow *QQuickWindowInspector::quickWindow() const
216{
217 return m_window;
218}
219
220void QQuickWindowInspector::setParentWindow(QWindow *parentWindow)
221{
222 if (parentWindow) {
223 while (QWindow *w = parentWindow->parent())
224 parentWindow = w;
225 }
226
227 m_parentWindow = parentWindow;
228}
229
230QList<QQuickItem *> QQuickWindowInspector::itemsAt(const QPointF &pos) const
231{
232 QList<QQuickItem *> resultList;
233 QQuickItem *root = m_window->contentItem();
234 collectItemsAt(item: root, pos: root->mapFromScene(point: pos), overlay: m_overlay,
235 resultList);
236 return resultList;
237}
238
239QQuickItem *QQuickWindowInspector::topVisibleItemAt(const QPointF &pos) const
240{
241 QQuickItem *root = m_window->contentItem();
242 return itemAt(item: root, pos: root->mapFromScene(point: pos), overlay: m_overlay);
243}
244
245
246} // namespace QmlJSDebugger
247
248QT_END_NAMESPACE
249
250#include "moc_qquickwindowinspector.cpp"
251

source code of qtdeclarative/src/plugins/qmltooling/qmldbg_inspector/qquickwindowinspector.cpp