1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qquickviewinspector.h"
44 #include "highlight.h"
45 #include "inspecttool.h"
47 #include <QtQml/private/qqmlengine_p.h>
48 #include <QtQml/private/qqmldebugservice_p.h>
49 #include <QtQuick/private/qquickitem_p.h>
51 #include <QtQuick/QQuickView>
52 #include <QtQuick/QQuickItem>
56 namespace QmlJSDebugger {
60 * Collects all the items at the given position, from top to bottom.
62 static void collectItemsAt(QQuickItem *item, const QPointF &pos,
63 QQuickItem *overlay, QList<QQuickItem *> &resultList)
68 if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
69 if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
73 QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
74 for (int i = children.count() - 1; i >= 0; --i) {
75 QQuickItem *child = children.at(i);
76 collectItemsAt(child, item->mapToItem(child, pos), overlay, resultList);
79 if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
82 resultList.append(item);
86 * Returns the first visible item at the given position, or 0 when no such
89 static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos,
95 if (!item->isVisible() || item->opacity() == 0.0)
98 if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
99 if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
103 QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
104 for (int i = children.count() - 1; i >= 0; --i) {
105 QQuickItem *child = children.at(i);
106 if (QQuickItem *betterCandidate = itemAt(child, item->mapToItem(child, pos),
108 return betterCandidate;
111 if (!(item->flags() & QQuickItem::ItemHasContents))
114 if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
121 QQuickViewInspector::QQuickViewInspector(QQuickView *view, QObject *parent) :
122 AbstractViewInspector(parent),
124 m_overlay(new QQuickItem),
125 m_inspectTool(new InspectTool(this, view)),
126 m_sendQmlReloadedMessage(false)
128 // Try to make sure the overlay is always on top
129 m_overlay->setZ(FLT_MAX);
131 if (QQuickItem *root = view->rootItem())
132 m_overlay->setParentItem(root);
134 view->installEventFilter(this);
135 appendTool(m_inspectTool);
136 connect(view, SIGNAL(statusChanged(QQuickView::Status)),
137 this, SLOT(onViewStatus(QQuickView::Status)));
140 void QQuickViewInspector::changeCurrentObjects(const QList<QObject*> &objects)
142 QList<QQuickItem*> items;
143 foreach (QObject *obj, objects)
144 if (QQuickItem *item = qobject_cast<QQuickItem*>(obj))
147 syncSelectedItems(items);
150 void QQuickViewInspector::reparentQmlObject(QObject *object, QObject *newParent)
155 object->setParent(newParent);
156 QQuickItem *newParentItem = qobject_cast<QQuickItem*>(newParent);
157 QQuickItem *item = qobject_cast<QQuickItem*>(object);
158 if (newParentItem && item)
159 item->setParentItem(newParentItem);
162 QWindow *getMasterWindow(QWindow *w)
164 QWindow *p = w->parent();
172 Qt::WindowFlags QQuickViewInspector::windowFlags() const
174 return getMasterWindow(m_view)->windowFlags();
177 void QQuickViewInspector::setWindowFlags(Qt::WindowFlags flags)
179 QWindow *w = getMasterWindow(m_view);
180 w->setWindowFlags(flags);
181 // make flags are applied
182 w->setVisible(false);
186 QQmlEngine *QQuickViewInspector::declarativeEngine() const
188 return m_view->engine();
191 QQuickItem *QQuickViewInspector::topVisibleItemAt(const QPointF &pos) const
193 QQuickItem *root = m_view->rootItem();
194 return itemAt(root, root->mapFromScene(pos), m_overlay);
197 QList<QQuickItem *> QQuickViewInspector::itemsAt(const QPointF &pos) const
199 QQuickItem *root = m_view->rootItem();
200 QList<QQuickItem *> resultList;
201 collectItemsAt(root, root->mapFromScene(pos), m_overlay,
206 QList<QQuickItem*> QQuickViewInspector::selectedItems() const
208 QList<QQuickItem *> selection;
209 foreach (const QPointer<QQuickItem> &selectedItem, m_selectedItems) {
211 selection << selectedItem;
216 void QQuickViewInspector::setSelectedItems(const QList<QQuickItem *> &items)
218 if (!syncSelectedItems(items))
221 QList<QObject*> objectList;
222 foreach (QQuickItem *item, items)
225 sendCurrentObjects(objectList);
228 bool QQuickViewInspector::syncSelectedItems(const QList<QQuickItem *> &items)
230 bool selectionChanged = false;
232 // Disconnect and remove items that are no longer selected
233 foreach (const QPointer<QQuickItem> &item, m_selectedItems) {
234 if (!item) // Don't see how this can happen due to handling of destroyed()
236 if (items.contains(item))
239 selectionChanged = true;
240 item->disconnect(this);
241 m_selectedItems.removeOne(item);
242 delete m_highlightItems.take(item);
245 // Connect and add newly selected items
246 foreach (QQuickItem *item, items) {
247 if (m_selectedItems.contains(item))
250 selectionChanged = true;
251 connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(removeFromSelectedItems(QObject*)));
252 m_selectedItems.append(item);
253 SelectionHighlight *selectionHighlightItem;
254 selectionHighlightItem = new SelectionHighlight(titleForItem(item), item, m_overlay);
255 m_highlightItems.insert(item, selectionHighlightItem);
258 return selectionChanged;
261 void QQuickViewInspector::showSelectedItemName(QQuickItem *item, const QPointF &point)
263 SelectionHighlight *highlightItem = m_highlightItems.value(item, 0);
265 highlightItem->showName(point);
268 void QQuickViewInspector::removeFromSelectedItems(QObject *object)
270 if (QQuickItem *item = qobject_cast<QQuickItem*>(object)) {
271 if (m_selectedItems.removeOne(item))
272 delete m_highlightItems.take(item);
276 bool QQuickViewInspector::eventFilter(QObject *obj, QEvent *event)
279 return QObject::eventFilter(obj, event);
281 return AbstractViewInspector::eventFilter(obj, event);
284 bool QQuickViewInspector::mouseMoveEvent(QMouseEvent *event)
287 // if (QQuickItem *item = topVisibleItemAt(event->pos()))
288 // m_view->setToolTip(titleForItem(item));
290 // m_view->setToolTip(QString());
292 return AbstractViewInspector::mouseMoveEvent(event);
295 QString QQuickViewInspector::titleForItem(QQuickItem *item) const
297 QString className = QLatin1String(item->metaObject()->className());
298 QString objectStringId = idStringForObject(item);
300 className.remove(QRegExp(QLatin1String("_QMLTYPE_\\d+")));
301 className.remove(QRegExp(QLatin1String("_QML_\\d+")));
302 if (className.startsWith(QLatin1String("QQuick")))
303 className = className.mid(6);
305 QString constructedName;
307 if (!objectStringId.isEmpty()) {
308 constructedName = objectStringId + QLatin1String(" (") + className + QLatin1Char(')');
309 } else if (!item->objectName().isEmpty()) {
310 constructedName = item->objectName() + QLatin1String(" (") + className + QLatin1Char(')');
312 constructedName = className;
315 return constructedName;
318 void QQuickViewInspector::reloadQmlFile(const QHash<QString, QByteArray> &changesHash)
320 clearComponentCache();
322 // Reset the selection since we are reloading the main qml
323 setSelectedItems(QList<QQuickItem *>());
325 QQmlDebugService::clearObjectsFromHash();
327 QHash<QUrl, QByteArray> debugCache;
329 foreach (const QString &str, changesHash.keys())
330 debugCache.insert(QUrl(str), changesHash.value(str, QByteArray()));
332 // Updating the cache in engine private such that the QML Data loader
333 // gets the changes from the cache.
334 QQmlEnginePrivate::get(declarativeEngine())->setDebugChangesCache(debugCache);
336 m_sendQmlReloadedMessage = true;
337 // reloading the view such that the changes done for the files are
339 view()->setSource(view()->source());
342 void QQuickViewInspector::setShowAppOnTop(bool appOnTop)
344 m_appOnTop = appOnTop;
345 // Hack for QTCREATORBUG-6295.
346 // TODO: The root cause to be identified and fixed later.
347 QTimer::singleShot(100, this, SLOT(applyAppOnTop()));
350 void QQuickViewInspector::onViewStatus(QQuickView::Status status)
352 bool success = false;
354 case QQuickView::Loading:
356 case QQuickView::Ready:
357 if (view()->errors().count())
361 case QQuickView::Null:
362 case QQuickView::Error:
367 if (m_sendQmlReloadedMessage) {
368 m_sendQmlReloadedMessage = false;
369 sendQmlFileReloaded(success);
373 void QQuickViewInspector::applyAppOnTop()
375 Qt::WindowFlags flags = windowFlags();
377 flags |= Qt::WindowStaysOnTopHint;
379 flags &= ~Qt::WindowStaysOnTopHint;
381 setWindowFlags(flags);
384 } // namespace QtQuick2
385 } // namespace QmlJSDebugger