Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlclassicpreviewhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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// Qt-Security score:significant
4
7
8#include <QtCore/qtimer.h>
9#include <QtGui/qwindow.h>
10#include <QtGui/qguiapplication.h>
11#include <QtQuick/qquickwindow.h>
12#include <QtQuick/qquickitem.h>
13#include <QtQml/qqmlcomponent.h>
14
15#include <private/qqmlmetatype_p.h>
16#include <private/qquickpixmap_p.h>
17#include <private/qquickview_p.h>
18#include <private/qv4compileddata_p.h>
19
21
23{
24 const bool quitLockEnabled;
25
31
33 {
34 QCoreApplication::setQuitLockEnabled(quitLockEnabled);
35 }
36};
37
38static void closeAllWindows()
39{
40 const QWindowList windows = QGuiApplication::allWindows();
41 for (QWindow *window : windows)
42 window->close();
43}
44
45static Qt::WindowFlags fixFlags(Qt::WindowFlags flags)
46{
47 // If only the type flag is given, some other window flags are automatically assumed. When we
48 // add a flag, we need to make those explicit.
49 switch (flags) {
50 case Qt::Window:
51 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint
52 | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
53 case Qt::Dialog:
54 case Qt::Tool:
55 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
56 default:
57 return flags;
58 }
59}
60
61QQmlClassicPreviewHandler::QQmlClassicPreviewHandler(QObject *parent)
62 : QQmlPreviewHandler(parent)
63{
64 m_dummyItem.reset(new QQuickItem);
65 const QString platformName = QGuiApplication::platformName();
66 m_supportsMultipleWindows = (platformName == QStringLiteral("windows")
67 || platformName == QStringLiteral("cocoa")
68 || platformName == QStringLiteral("xcb")
69 || platformName == QStringLiteral("wayland"));
70
71 QCoreApplication::instance()->installEventFilter(this);
72}
73
78
79bool QQmlClassicPreviewHandler::eventFilter(QObject *obj, QEvent *event)
80{
81 QQuickWindow *window = currentWindow();
82 if (window && (event->type() == QEvent::Move) &&
83 qobject_cast<QQuickWindow*>(obj) == window) {
84 m_lastPosition.takePosition(window);
85 }
86
87 return QObject::eventFilter(obj, event);
88}
89
90void QQmlClassicPreviewHandler::removeEngine(QQmlEngine *qmlEngine)
91{
93 for (QObject *obj : m_createdObjects)
94 if (obj && ::qmlEngine(obj) == qmlEngine)
95 delete obj;
96 m_createdObjects.removeAll(nullptr);
97}
98
100{
102 connect(service, &QQmlPreviewServiceImpl::drop, this, &QQmlClassicPreviewHandler::dropCU);
103 connect(service, &QQmlPreviewServiceImpl::rerun, this, &QQmlClassicPreviewHandler::rerun);
104 connect(service, &QQmlPreviewServiceImpl::zoom, this, &QQmlClassicPreviewHandler::zoom);
105}
106
107void QQmlClassicPreviewHandler::load(const QUrl &url)
108{
109 QSharedPointer<QuitLockDisabler> disabler(new QuitLockDisabler);
110
111 clear();
112 m_component.reset(nullptr);
113 QQuickPixmap::purgeCache();
114
115 const QList<QQmlEngine *> seenEngines = engines();
116 const int numEngines = seenEngines.size();
117 if (numEngines > 1) {
118 emit error(QString::fromLatin1("%1 QML engines available. We cannot decide which one "
119 "should load the component.").arg(numEngines));
120 return;
121 } else if (numEngines == 0) {
122 emit error(QLatin1String("No QML engines found."));
123 return;
124 }
125 m_lastPosition.loadWindowPositionSettings(url);
126
127 QQmlEngine *engine = seenEngines.front();
128 engine->clearSingletons();
129 engine->clearComponentCache();
130 m_component.reset(new QQmlComponent(engine, url, this));
131
132 auto onStatusChanged = [disabler, this](QQmlComponent::Status status) {
133 switch (status) {
134 case QQmlComponent::Null:
135 case QQmlComponent::Loading:
136 return true; // try again later
137 case QQmlComponent::Ready:
138 tryCreateObject();
139 break;
140 case QQmlComponent::Error:
141 emit error(m_component->errorString());
142 break;
143 default:
144 Q_UNREACHABLE();
145 break;
146 }
147
148 disconnect(m_component.data(), &QQmlComponent::statusChanged, this, nullptr);
149 return false; // we're done
150 };
151
152 if (onStatusChanged(m_component->status()))
153 connect(m_component.data(), &QQmlComponent::statusChanged, this, onStatusChanged);
154}
155
156void QQmlClassicPreviewHandler::dropCU(const QUrl &url)
157{
158 // Drop any existing compilation units for this URL from the type registry.
159 // There can be multiple, one for each engine.
160 while (const auto cu = QQmlMetaType::obtainCompilationUnit(url))
161 QQmlMetaType::unregisterInternalCompositeType(cu);
162}
163
164void QQmlClassicPreviewHandler::rerun()
165{
166 if (m_component.isNull() || !m_component->isReady()) {
167 emit error(QLatin1String("Component is not ready."));
168 return;
169 }
170
171 QuitLockDisabler disabler;
172 Q_UNUSED(disabler);
173 clear();
174 tryCreateObject();
175}
176
177void QQmlClassicPreviewHandler::clear()
178{
179 qDeleteAll(m_createdObjects);
180 m_createdObjects.clear();
181 setCurrentWindow(nullptr);
182}
183
184void QQmlClassicPreviewHandler::tryCreateObject()
185{
186 if (!m_supportsMultipleWindows)
188 QObject *object = m_component->create();
189 m_createdObjects.append(object);
190 showObject(object);
191}
192
193void QQmlClassicPreviewHandler::showObject(QObject *object)
194{
195 if (QWindow *window = qobject_cast<QWindow *>(object)) {
196 setCurrentWindow(qobject_cast<QQuickWindow *>(window));
197 for (QWindow *otherWindow : QGuiApplication::allWindows()) {
198 if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(otherWindow)) {
199 if (quickWindow == currentWindow())
200 continue;
201 quickWindow->setVisible(false);
202 quickWindow->setFlags(quickWindow->flags() & ~Qt::WindowStaysOnTopHint);
203 }
204 }
205 } else if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) {
206 setCurrentWindow(nullptr);
207 for (QWindow *window : QGuiApplication::allWindows()) {
208 if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window)) {
209 if (currentWindow() != nullptr) {
210 emit error(QLatin1String("Multiple QQuickWindows available. We cannot "
211 "decide which one to use."));
212 return;
213 }
214 setCurrentWindow(quickWindow);
215 } else {
216 window->setVisible(false);
217 window->setFlag(Qt::WindowStaysOnTopHint, false);
218 }
219 }
220
221 if (currentWindow() == nullptr) {
222 setCurrentWindow(new QQuickWindow);
223 m_createdObjects.append(currentWindow());
224 }
225
226 for (QQuickItem *oldItem : currentWindow()->contentItem()->childItems())
227 oldItem->setParentItem(m_dummyItem.data());
228
229 // Special case for QQuickView, as that keeps a "root" pointer around, and uses it to
230 // automatically resize the window or the item.
231 if (QQuickView *view = qobject_cast<QQuickView *>(currentWindow()))
232 QQuickViewPrivate::get(view)->setRootObject(item);
233 else
234 item->setParentItem(currentWindow()->contentItem());
235
236 currentWindow()->resize(item->size().toSize());
237 // used by debug translation service to get the states
238 setCurrentRootItem(item);
239 } else {
240 emit error(QLatin1String("Created object is neither a QWindow nor a QQuickItem."));
241 }
242
243 if (QQuickWindow *window = currentWindow()) {
244 m_lastPosition.initLastSavedWindowPosition(window);
245 window->setFlags(fixFlags(window->flags()) | Qt::WindowStaysOnTopHint);
246 window->setVisible(true);
247 }
248}
249
250void QQmlClassicPreviewHandler::zoom(qreal newFactor)
251{
252 m_zoomFactor = newFactor;
253 QTimer::singleShot(0, this, [this, newFactor]() {
254 zoomWindow(currentWindow(), newFactor, &m_lastPosition);
255 });
256}
257
258QT_END_NAMESPACE
259
260#include "moc_qqmlclassicpreviewhandler.cpp"
void load(const QUrl &url) final
void removeEngine(QQmlEngine *engine) final
void connectToService(QQmlPreviewServiceImpl *service) final
bool eventFilter(QObject *obj, QEvent *event) final
Filters events if this object has been installed as an event filter for the watched object.
void setCurrentRootItem(QQuickItem *item)
void setCurrentWindow(QQuickWindow *window)
virtual void removeEngine(QQmlEngine *engine)
QQuickWindow * currentWindow() const
virtual void connectToService(QQmlPreviewServiceImpl *service)
Combined button and popup list for selecting options.
static Qt::WindowFlags fixFlags(Qt::WindowFlags flags)
static void closeAllWindows()