7#include <QtCore/qtimer.h>
8#include <QtCore/qsettings.h>
9#include <QtCore/qlibraryinfo.h>
11#include <QtGui/qwindow.h>
12#include <QtGui/qguiapplication.h>
13#include <QtQuick/qquickwindow.h>
14#include <QtQuick/qquickitem.h>
15#include <QtQml/qqmlcomponent.h>
17#include <private/qabstractanimation_p.h>
18#include <private/qhighdpiscaling_p.h>
19#include <private/qqmlmetatype_p.h>
20#include <private/qquickpixmap_p.h>
21#include <private/qquickview_p.h>
22#include <private/qv4compileddata_p.h>
26struct QuitLockDisabler
28 const bool quitLockEnabled;
30 Q_NODISCARD_CTOR QuitLockDisabler()
31 : quitLockEnabled(QCoreApplication::isQuitLockEnabled())
33 QCoreApplication::setQuitLockEnabled(
false);
38 QCoreApplication::setQuitLockEnabled(quitLockEnabled);
44 m_dummyItem.reset(
new QQuickItem);
50 const QString platformName = QGuiApplication::platformName();
51 m_supportsMultipleWindows = (platformName == QStringLiteral(
"windows")
52 || platformName == QStringLiteral(
"cocoa")
53 || platformName == QStringLiteral(
"xcb")
54 || platformName == QStringLiteral(
"wayland"));
56 QCoreApplication::instance()->installEventFilter(
this);
58 m_fpsTimer.setInterval(1000);
69 const QWindowList windows = QGuiApplication::allWindows();
70 for (QWindow *window : windows)
76 if (m_currentWindow && (event->type() == QEvent::Move) &&
77 qobject_cast<QQuickWindow*>(obj) == m_currentWindow) {
78 m_lastPosition.takePosition(m_currentWindow);
81 return QObject::eventFilter(obj, event);
86 return m_currentRootItem;
91 m_engines.append(qmlEngine);
96 const bool found = m_engines.removeOne(qmlEngine);
98 for (QObject *obj : m_createdObjects)
99 if (obj && ::qmlEngine(obj) == qmlEngine)
101 m_createdObjects.removeAll(
nullptr);
106 return m_settings.enableInPlaceUpdates ? loadPatch(url) : loadUrl(url);
113 emit hotReloadFailure(QLatin1String(
"In-place reload is not yet implemented"));
118 QSharedPointer<QuitLockDisabler> disabler(
new QuitLockDisabler);
121 m_component.reset(
nullptr);
122 QQuickPixmap::purgeCache();
124 const int numEngines = m_engines.size();
125 if (numEngines > 1) {
126 emit error(QString::fromLatin1(
"%1 QML engines available. We cannot decide which one "
127 "should load the component.").arg(numEngines));
129 }
else if (numEngines == 0) {
130 emit error(QLatin1String(
"No QML engines found."));
133 m_lastPosition.loadWindowPositionSettings(url);
135 QQmlEngine *engine = m_engines.front();
136 engine->clearSingletons();
137 engine->clearComponentCache();
138 m_component.reset(
new QQmlComponent(engine, url,
this));
140 auto onStatusChanged = [disabler,
this](QQmlComponent::Status status) {
142 case QQmlComponent::Null:
143 case QQmlComponent::Loading:
145 case QQmlComponent::Ready:
148 case QQmlComponent::Error:
149 emit error(m_component->errorString());
156 disconnect(m_component.data(), &QQmlComponent::statusChanged,
this,
nullptr);
160 if (onStatusChanged(m_component->status()))
161 connect(m_component.data(), &QQmlComponent::statusChanged,
this, onStatusChanged);
168 while (
const auto cu = QQmlMetaType::obtainCompilationUnit(url))
169 QQmlMetaType::unregisterInternalCompositeType(cu);
174 if (m_component.isNull() || !m_component->isReady())
175 emit error(QLatin1String(
"Component is not ready."));
177 QuitLockDisabler disabler;
185 m_zoomFactor = newFactor;
186 QTimer::singleShot(0,
this, &QQmlPreviewHandler::doZoom);
191 QUnifiedTimer::instance()->setSpeedModifier(newFactor);
196 if (m_settings.enableInPlaceUpdates && !settings.enableInPlaceUpdates) {
197 emit error(QLatin1String(
"Cannot disable in-place updates once enabled"));
200 m_settings = settings;
201 emit confirmation(settings);
206 if (!m_currentWindow)
208 if (qFuzzyIsNull(m_zoomFactor)) {
209 emit error(QString::fromLatin1(
"Zooming with factor: %1 will result in nothing "
210 "so it will be ignored.").arg(m_zoomFactor));
214 bool resetZoom =
false;
215 if (m_zoomFactor < 0) {
220 m_currentWindow->setGeometry(m_currentWindow->geometry());
222 m_lastPosition.takePosition(m_currentWindow, QQmlPreviewPosition::InitializePosition);
223 m_currentWindow->destroy();
225 for (QScreen *screen : QGuiApplication::screens())
226 QHighDpiScaling::setScreenFactor(screen, m_zoomFactor);
228 QHighDpiScaling::updateHighDpiScaling();
230 m_currentWindow->show();
231 m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
236 qDeleteAll(m_createdObjects);
237 m_createdObjects.clear();
238 setCurrentWindow(
nullptr);
247 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint
248 | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
251 return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
259 if (QWindow *window = qobject_cast<QWindow *>(object)) {
260 setCurrentWindow(qobject_cast<QQuickWindow *>(window));
261 for (QWindow *otherWindow : QGuiApplication::allWindows()) {
262 if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(otherWindow)) {
263 if (quickWindow == m_currentWindow)
265 quickWindow->setVisible(
false);
266 quickWindow->setFlags(quickWindow->flags() & ~Qt::WindowStaysOnTopHint);
269 }
else if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) {
270 setCurrentWindow(
nullptr);
271 for (QWindow *window : QGuiApplication::allWindows()) {
272 if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window)) {
273 if (m_currentWindow !=
nullptr) {
274 emit error(QLatin1String(
"Multiple QQuickWindows available. We cannot "
275 "decide which one to use."));
278 setCurrentWindow(quickWindow);
280 window->setVisible(
false);
281 window->setFlag(Qt::WindowStaysOnTopHint,
false);
285 if (m_currentWindow ==
nullptr) {
286 setCurrentWindow(
new QQuickWindow);
287 m_createdObjects.append(m_currentWindow.data());
290 for (QQuickItem *oldItem : m_currentWindow->contentItem()->childItems())
291 oldItem->setParentItem(m_dummyItem.data());
295 if (QQuickView *view = qobject_cast<QQuickView *>(m_currentWindow))
296 QQuickViewPrivate::get(view)->setRootObject(item);
298 item->setParentItem(m_currentWindow->contentItem());
300 m_currentWindow->resize(item->size().toSize());
302 m_currentRootItem = item;
304 emit error(QLatin1String(
"Created object is neither a QWindow nor a QQuickItem."));
307 if (m_currentWindow) {
308 m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
309 m_currentWindow->setFlags(fixFlags(m_currentWindow->flags()) | Qt::WindowStaysOnTopHint);
310 m_currentWindow->setVisible(
true);
316 if (window == m_currentWindow.data())
319 if (m_currentWindow) {
320 disconnect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing,
322 disconnect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing,
324 disconnect(m_currentWindow.data(), &QQuickWindow::beforeRendering,
326 disconnect(m_currentWindow.data(), &QQuickWindow::frameSwapped,
329 m_rendering = FrameTime();
330 m_synchronizing = FrameTime();
333 m_currentWindow = window;
335 if (m_currentWindow) {
336 connect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing,
338 connect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing,
340 connect(m_currentWindow.data(), &QQuickWindow::beforeRendering,
342 connect(m_currentWindow.data(), &QQuickWindow::frameSwapped,
350 m_synchronizing.beginFrame();
356 if (m_rendering.elapsed >= 0)
357 m_rendering.endFrame();
358 m_synchronizing.recordFrame();
359 m_synchronizing.endFrame();
364 m_rendering.beginFrame();
369 m_rendering.recordFrame();
379 elapsed = timer.elapsed();
385 min =
static_cast<quint16>(qMax(0ll, elapsed));
387 max =
static_cast<quint16>(qMin(qint64(std::numeric_limits<quint16>::max()), elapsed));
388 total =
static_cast<quint16>(qBound(0ll, qint64(std::numeric_limits<quint16>::max()),
396 min = std::numeric_limits<quint16>::max();
405 m_synchronizing.number,
408 m_synchronizing.total,
419 m_synchronizing.reset();
424 if (!m_supportsMultipleWindows)
426 QObject *object = m_component->create();
427 m_createdObjects.append(object);
433#include "moc_qqmlpreviewhandler.cpp"
void dropCU(const QUrl &url)
void load(const QUrl &url)
void zoom(qreal newFactor)
QQuickItem * currentRootItem()
bool eventFilter(QObject *obj, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
void setAnimationSpeed(qreal newFactor)
void removeEngine(QQmlEngine *engine)
void configure(const Settings &settings)
void addEngine(QQmlEngine *engine)
void loadPatch(const QUrl &url)
void loadUrl(const QUrl &url)
QQmlPreviewHandler(QObject *parent=nullptr)
static void closeAllWindows()
Qt::WindowFlags fixFlags(Qt::WindowFlags flags)