57void QQmlInPlacePreviewHandler::connectToService(QQmlPreviewServiceImpl *service)
59 QQmlPreviewHandler::connectToService(service);
60 connect(
this, &QQmlInPlacePreviewHandler::hotReloadFailure, service,
61 &QQmlPreviewServiceImpl::forwardHotReloadFailure, Qt::DirectConnection);
62 connect(service, &QQmlPreviewServiceImpl::drop,
this, [
this](
const QUrl &url) {
63 if (!m_droppedUrls.contains(url))
64 m_droppedUrls.push_back(url);
66 connect(service, &QQmlPreviewServiceImpl::rerun,
this, [
this]() {
67 emit error(QLatin1String(
"You cannot rerun if in-place updates are enabled."));
69 connect(service, &QQmlPreviewServiceImpl::zoom,
this, [
this](qreal zoomFactor) {
70 QQmlPreviewPosition position;
71 zoomWindow(currentWindow(), zoomFactor, &position);
91 QMetaObject::invokeMethod(engine, [engine, url, receiver]() {
92 QV4::ExecutionEngine *v4 = engine->handle();
93 const QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = v4->compilationUnitForUrl(url);
97 const std::vector<QObject *> objects =
98 v4->memoryManager->findObjectsForCompilationUnits({ unit->baseCompilationUnit() });
99 QPointer<QObject> found;
100 for (QObject *object : objects) {
101 QQmlData *ddata = QQmlData::get(object);
102 if (ddata && ddata->compilationUnit == unit && ddata->cuObjectIndex == 0) {
103 if (std::exchange(found, object))
109 QMetaObject::invokeMethod(receiver, [receiver, currentRoot = std::move(found)]() {
110 if (QQuickItem *rootItem = qobject_cast<QQuickItem *>(currentRoot)) {
111 receiver->setCurrentRootItem(rootItem);
112 receiver->setCurrentWindow(findCurrentWindow());
116 if (QQuickWindow *window = qobject_cast<QQuickWindow *>(currentRoot)) {
117 receiver->setCurrentWindow(window);
118 receiver->setCurrentRootItem(
nullptr);
122 receiver->setCurrentRootItem(
nullptr);
123 receiver->setCurrentWindow(findCurrentWindow());
133 QV4::ExecutionEngine *v4,
134 const QList<QQmlRefPointer<QV4::CompiledData::CompilationUnit>> &droppedUnits)
136 const auto allCUs = v4->compilationUnits();
137 for (
const auto &ecu : allCUs) {
138 const auto &baseCU = ecu->baseCompilationUnit();
139 for (
auto *typeRef : std::as_const(baseCU->resolvedTypes)) {
140 if (typeRef->isSelfReference())
142 const auto &refCU = typeRef->compilationUnit();
145 for (
const auto &oldCU : droppedUnits) {
146 if (refCU == oldCU) {
147 const auto newCU = QQmlMetaType::obtainCompilationUnit(oldCU->finalUrl());
149 typeRef->setCompilationUnit(newCU);
160 const auto componentUpdateIt =
161 std::find_if(inplaceUpdate->pendingComponentUpdates.begin(),
162 inplaceUpdate->pendingComponentUpdates.end(),
164 return engineUpdate.component.get() == component;
167 if (componentUpdateIt == inplaceUpdate->pendingComponentUpdates.end())
170 componentUpdate =
std::move(*componentUpdateIt);
171 inplaceUpdate->pendingComponentUpdates.erase(componentUpdateIt);
173 switch (component->status()) {
174 case QQmlComponent::Null:
175 case QQmlComponent::Loading:
176 Q_UNREACHABLE_RETURN();
177 case QQmlComponent::Ready:
179 case QQmlComponent::Error:
180 inplaceUpdate->emitError(component->errorString());
184 QV4::ExecutionEngine *v4 = component->engine()->handle();
186 if (
const auto oldExecCU = componentUpdate.oldUnit) {
187 const auto newExecCU = QQmlComponentPrivate::get(component)->compilationUnit();
188 std::vector<QObject *> objects = v4->memoryManager->findObjectsForCompilationUnits(
189 { oldExecCU->baseCompilationUnit() });
191 const QQmlPreview::PatchResult result =
192 QQmlPreview::applyDiff(objects, oldExecCU, newExecCU);
193 if (result == QQmlPreview::PatchResult::Failed)
194 inplaceUpdate->emitReloadFailure(
"Could not apply diff");
196 componentUpdate.newUnit = newExecCU;
197 componentUpdate.patchResult = result;
198 inplaceUpdate->processedComponentUpdates.push_back(
std::move(componentUpdate));
203 if (inplaceUpdate->pendingComponentUpdates.empty()) {
204 updateResolvedTypeReferences(v4, inplaceUpdate->droppedUnits);
205 for (
const ComponentUpdate &update : inplaceUpdate->processedComponentUpdates) {
208 QQmlPreview::refreshBindings(
210 update.patchResult == QQmlPreview::PatchResult::PatchedInPlace ? update.newUnit
218 QV4::ExecutionEngine *v4 = inplaceUpdate->engine->handle();
219 std::vector<QQmlComponent *> components;
220 for (
const QUrl &url : urls) {
223 if (!v4->typeLoader()->removeFromCache(url))
227 QQmlRefPointer<QV4::ExecutableCompilationUnit> oldUnit = v4->compilationUnitForUrl(url);
231 inplaceUpdate->pendingComponentUpdates.push_back({
232 std::make_unique<QQmlComponent>(inplaceUpdate->engine, url),
235 QQmlPreview::PatchResult::Failed,
239 components.push_back(inplaceUpdate->pendingComponentUpdates.back().component.get());
242 for (QQmlComponent *component : components) {
243 switch (component->status()) {
244 case QQmlComponent::Null:
245 case QQmlComponent::Loading:
246 QObject::connect(component, &QQmlComponent::statusChanged, component,
247 [component, inplaceUpdate]() {
248 switch (component->status()) {
249 case QQmlComponent::Ready:
250 case QQmlComponent::Error:
251 updateInplace(component, inplaceUpdate);
258 case QQmlComponent::Ready:
259 case QQmlComponent::Error:
260 updateInplace(component, inplaceUpdate);
266void QQmlInPlacePreviewHandler::load(
const QUrl &url)
271 const QList<QQmlEngine *> seenEngines = engines();
272 const QList<QUrl> urls = std::exchange(m_droppedUrls, {});
274 QList<QQmlRefPointer<QV4::CompiledData::CompilationUnit>> droppedUnits;
275 for (
const QUrl &droppedUrl : urls) {
276 while (
const auto cu = QQmlMetaType::obtainCompilationUnit(droppedUrl)) {
277 droppedUnits.append(cu);
278 QQmlMetaType::deepClearCompositeType(cu);
281 for (QQmlEngine *engine : seenEngines) {
282 std::shared_ptr<InplaceUpdate> inplaceUpdate =
283 std::make_shared<InplaceUpdate>(
this, engine);
284 inplaceUpdate->droppedUnits = droppedUnits;
288 QMetaObject::invokeMethod(engine, [update = std::move(inplaceUpdate), urls]() {
289 updateEngine(update, urls);
297 if (seenEngines.size() == 1) {
298 findCurrentRootObject(seenEngines[0], url,
this);
300 setCurrentRootItem(
nullptr);
301 setCurrentWindow(findCurrentWindow());