| #ifndef _RIVE_SCRIPTED_TRANSITION_HPP_ | |
| #define _RIVE_SCRIPTED_TRANSITION_HPP_ | |
| #include "rive/generated/scripted/scripted_transition_base.hpp" | |
| #include "rive/data_bind/data_bind_list_item_consumer.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include <memory> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <vector> | |
| namespace rive | |
| { | |
| class Component; | |
| class NestedArtboard; | |
| class ArtboardInstance; | |
| class StateMachineInstance; | |
| class File; | |
| class ViewModelInstanceListItem; | |
| /// A ScriptedTransition renders exactly one active child (Solo-style), chosen | |
| /// by activeComponentId, but when the active child changes it hands the | |
| /// outgoing (from) and incoming (to) children to a Transition-protocol script | |
| /// which composites them over time (crossfade / slide / wipe / ...). | |
| /// | |
| /// The switchable set is a COMBINED solo set: the design-time NestedArtboard | |
| /// children (indices 0..D-1), followed by artboards instanced from a bound | |
| /// ViewModel list via `listSource` (indices D..D+N-1). List artboards are | |
| /// host-owned (not core objects, so they have no id) and are instanced lazily | |
| /// on first selection. `activeComponentId` data-bound to a number selects by | |
| /// this combined index. | |
| /// | |
| /// While a transition is in flight both sources are composited by the script; | |
| /// design children are excluded from the normal draw loop via the Hidden flag, | |
| /// and list instances (which are never in the loop) are drawn by this | |
| /// component. | |
| class ScriptedTransition : public ScriptedTransitionBase, | |
| public DataBindListItemConsumer | |
| { | |
| public: | |
| ~ScriptedTransition() override; | |
| ScriptProtocol scriptProtocol() override | |
| { | |
| return ScriptProtocol::transition; | |
| } | |
| void activeComponentIdChanged() override; | |
| void listSourceChanged() override; | |
| StatusCode onAddedClean(CoreContext* context) override; | |
| bool collapse(bool value) override; | |
| bool advanceComponent(float elapsedSeconds, | |
| AdvanceFlags flags = AdvanceFlags::Animate | | |
| AdvanceFlags::NewFrame) override; | |
| void draw(Renderer* renderer) override; | |
| bool willDraw() override; | |
| void update(ComponentDirt value) override; | |
| Core* clone() const override; | |
| // File is threaded in by File import (see file.cpp) so list artboards can | |
| // be resolved from the file's artboard table. | |
| void file(File* value) { m_file = value; } | |
| File* file() const { return m_file; } | |
| // DataBindListItemConsumer: receives the bound ViewModel list (via | |
| // listSource) and (lazily) instances an artboard per item. | |
| void updateList(std::vector<rcp<ViewModelInstanceListItem>>* list) override; | |
| /// The design-time child resolved from activeComponentId (or null). | |
| Component* activeComponent(); | |
| // Combined-index selectors (design children then list instances). | |
| void updateByIndex(size_t index); | |
| void updateByName(const std::string& name); | |
| int getActiveChildIndex(); | |
| std::string getActiveChildName(); | |
| #ifdef WITH_RIVE_LAYOUT | |
| void recollectOwningLayout(); | |
| #endif | |
| private: | |
| // A renderable source in the combined solo set: either a design-time | |
| // NestedArtboard child, or a list-instanced ArtboardInstance. Exactly one | |
| // of the two pointers is set (both null == empty/no selection). | |
| struct TransitionSource | |
| { | |
| NestedArtboard* design = nullptr; | |
| ArtboardInstance* instance = nullptr; | |
| bool empty() const { return design == nullptr && instance == nullptr; } | |
| bool operator==(const TransitionSource& o) const | |
| { | |
| return design == o.design && instance == o.instance; | |
| } | |
| bool operator!=(const TransitionSource& o) const | |
| { | |
| return !(*this == o); | |
| } | |
| }; | |
| void propagateCollapse(bool collapse); | |
| void startTransition(const TransitionSource& from, | |
| const TransitionSource& to); | |
| void completeTransition(); | |
| void callChanged(const TransitionSource& from, const TransitionSource& to); | |
| bool scriptReady() const; | |
| bool scriptManagesTo() const; | |
| #ifdef WITH_RIVE_SCRIPTING | |
| // Resolve a source into the artboard the script draws and the world | |
| // transform that places it. Design children carry their own transform; | |
| // list instances are placed at this container's. | |
| ScriptBackend::TransitionChildRef childRef( | |
| const TransitionSource& source) const; | |
| #endif | |
| // Combined solo set + unified selection. | |
| int designChildCount(); | |
| NestedArtboard* designChildAt(int index); | |
| // Combined index (design children 0..D-1, then list instances D..) of a | |
| // source, or -1 if not found. Used to derive the transition direction. | |
| int sourceIndex(const TransitionSource& source); | |
| TransitionSource intendedSource(); | |
| void applySelection(); | |
| void selectActiveSource(const TransitionSource& next); | |
| // list instancing (lazy) | |
| ArtboardInstance* ensureListInstance(int index); | |
| Artboard* findListArtboard(const rcp<ViewModelInstanceListItem>& item); | |
| void disposeListItem(const rcp<ViewModelInstanceListItem>& item); | |
| void clearListInstances(); | |
| // Drop any selection/transition reference to a list instance that is about | |
| // to be destroyed, so m_current/m_from/m_to never dangle. | |
| void forgetInstance(ArtboardInstance* instance); | |
| void advanceLiveInstance(const TransitionSource& source, | |
| float elapsedSeconds, | |
| AdvanceFlags flags, | |
| bool& keepGoing); | |
| // The child currently exposed as active (design or list). | |
| TransitionSource m_current; | |
| // The in-flight transition pair. | |
| TransitionSource m_from; | |
| TransitionSource m_to; | |
| bool m_transitioning = false; | |
| // Whether the script composites the incoming (to) source this transition. | |
| bool m_manageTo = true; | |
| // >= 0 => active is that list instance; -1 => active is a design child. | |
| int m_activeListIndex = -1; | |
| File* m_file = nullptr; | |
| std::vector<rcp<ViewModelInstanceListItem>> m_listItems; | |
| std::unordered_map<rcp<ViewModelInstanceListItem>, | |
| std::unique_ptr<ArtboardInstance>> | |
| m_listInstances; | |
| // Keyed by the instance pointer for O(1) advance lookup. Destroyed before | |
| // the owning ArtboardInstance (see disposeListItem / clearListInstances). | |
| std::unordered_map<ArtboardInstance*, std::unique_ptr<StateMachineInstance>> | |
| m_listStateMachines; | |
| // Lazily-filled fast index -> instance (nullptr until ensureListInstance). | |
| std::vector<ArtboardInstance*> m_listInstancesByIndex; | |
| // Cache: viewModelId -> source artboard in the file. | |
| std::unordered_map<uint32_t, Artboard*> m_artboardsMap; | |
| }; | |
| } // namespace rive | |
| #endif |