#pragma once #include #include #include #include #include #include class EngineGeneration; ///! The base class of all types that can be reloaded. /// Reloadables will attempt to take specific state from previous config revisions if possible. /// Some examples are @@ProxyWindowBase and @@PersistentProperties class Reloadable : public QObject , public QQmlParserStatus { Q_OBJECT; Q_INTERFACES(QQmlParserStatus); /// An additional identifier that can be used to try to match a reloadable object to its /// previous state. /// /// Simply keeping a stable identifier across config versions (saves) is /// enough to help the reloader figure out which object in the old revision corrosponds to /// this object in the current revision, and facilitate smoother reloading. /// /// Note that identifiers are scoped, and will try to do the right thing in context. /// For example if you have a @@Variants wrapping an object with an identified element inside, /// a scope is created at the variant level. /// /// ```qml /// Variants { /// // multiple variants of the same object tree /// variants: [ { foo: 1 }, { foo: 2 } ] /// /// // any non `Reloadable` object /// QtObject { /// FloatingWindow { /// // this FloatingWindow will now be matched to the same one in the previous /// // widget tree for its variant. "myFloatingWindow" refers to both the variant in /// // `foo: 1` and `foo: 2` for each tree. /// reloadableId: "myFloatingWindow" /// /// // ... /// } /// } /// } /// ``` Q_PROPERTY(QString reloadableId MEMBER mReloadableId); QML_ELEMENT; QML_UNCREATABLE( "Reloadable is the base class of reloadable types and cannot be created on its own." ); public: explicit Reloadable(QObject* parent = nullptr): QObject(parent) {} void reload(QObject* oldInstance = nullptr); void classBegin() override {}; void componentComplete() override; // Reload objects in the parent->child graph recursively. static void reloadRecursive(QObject* newObj, QObject* oldRoot); // Same as above but does not reload the passed object, only its children. static void reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot); QString mReloadableId; bool reloadComplete = false; EngineGeneration* engineGeneration = nullptr; private slots: void onReloadFinished(); protected: // Called unconditionally in the reload phase, with nullptr if no source could be determined. // If non null the old instance may or may not be of the same type, and should be checked // by `onReload`. virtual void onReload(QObject* oldInstance) = 0; private: static QObject* getChildByReloadId(QObject* parent, const QString& reloadId); }; ///! Scope that propagates reloads to child items in order. /// Convenience type equivalent to setting @@Reloadable.reloadableId for all children. /// /// Note that this does not work for visible @@QtQuick.Item$s (all widgets). /// /// ```qml /// ShellRoot { /// Variants { /// variants: ... /// /// Scope { /// // everything in here behaves the same as if it was defined /// // directly in `Variants` reload-wise. /// } /// } /// } class ReloadPropagator: public Reloadable { Q_OBJECT; Q_PROPERTY(QQmlListProperty children READ data); Q_CLASSINFO("DefaultProperty", "children"); QML_NAMED_ELEMENT(Scope); public: explicit ReloadPropagator(QObject* parent = nullptr): Reloadable(parent) {} void onReload(QObject* oldInstance) override; QQmlListProperty data(); private: static void appendComponent(QQmlListProperty* list, QObject* obj); QList mChildren; }; /// Hook that runs after the old widget tree is dropped during a reload. class PostReloadHook { public: PostReloadHook() = default; virtual ~PostReloadHook() = default; PostReloadHook(PostReloadHook&&) = default; PostReloadHook(const PostReloadHook&) = default; PostReloadHook& operator=(PostReloadHook&&) = default; PostReloadHook& operator=(const PostReloadHook&) = default; virtual void onPostReload() = 0; static void postReloadTree(QObject* root); };