From 6362192a61d295c68d273ddfbd7f2f147cd2b960 Mon Sep 17 00:00:00 2001 From: bodymovin Date: Tue, 30 Sep 2025 00:01:40 +0000 Subject: [PATCH] data bind artboards rcp file (#10214) c542b9b7ac * make file ref counted * migrate File to refcnt * add bindable artboard class to keep file reference * feat(unity): support rcp file and BindableArtboard class (#10228) * refactor(apple): use updated file bindable artboard apis (#10229) * update command queue to support bindable artboards * use bindable artboards on command queue * self manage view model instances in js runtime * change remaining viewModelInstanceRuntimes to rcp * refactor(apple): update view model instances to use rcp (#10298) * refactor(unity): support rcp ViewModelInstanceRuntime (#10309) * rebase fix * deprecate getArtboard in favor of getBindableArtboard * fix merge * remove unused lambda capture * fix rive binding * feat(Android): RCP File, VMI, and add bindable artboards (#10456) * refactor: C++ refactors - Import from long (incorrect) -> jlong - Header clang-tidy fix - Use reinterpret_cast instead of C-style cast - Break out some variables instead of long one liners - Use auto - Remove unused env and thisObj # Conflicts: # packages/runtime_android/kotlin/src/main/cpp/src/helpers/general.cpp * docs: Improve documentation on the File class * feat: Support bindable artboard type and RCP VMIs # Conflicts: # packages/runtime_android/kotlin/src/androidTest/java/app/rive/runtime/kotlin/core/RiveDataBindingTest.kt * feat: Support RCP files * refactor: Change from +1/-1 ref to just release * fix: Moved to the more appropriate null pointer for GetStringUTFChars * replace unref with release Co-authored-by: Adam <67035612+damzobridge@users.noreply.github.com> Co-authored-by: David Skuza Co-authored-by: Erik Co-authored-by: hernan --- .rive_head | 2 +- .../rive/animation/state_machine_instance.hpp | 1 + include/rive/artboard.hpp | 3 +- include/rive/artboard_component_list.hpp | 2 +- include/rive/artboard_host.hpp | 2 +- include/rive/bindable_artboard.hpp | 23 ++++++ include/rive/command_server.hpp | 4 +- .../data_converter_number_to_list.hpp | 2 +- include/rive/data_bind/data_bind.hpp | 6 +- include/rive/file.hpp | 6 ++ include/rive/nested_artboard.hpp | 1 + .../viewmodel_instance_artboard_runtime.hpp | 5 +- .../viewmodel_instance_list_runtime.hpp | 2 +- .../runtime/viewmodel_instance_runtime.hpp | 3 +- .../viewmodel/runtime/viewmodel_runtime.hpp | 12 ++- .../viewmodel/viewmodel_instance_artboard.hpp | 7 +- renderer/path_fiddle/path_fiddle.cpp | 1 + rivinfo/main.cpp | 1 + src/animation/state_machine.cpp | 1 + src/animation/state_machine_instance.cpp | 2 + src/artboard.cpp | 3 +- src/bindable_artboard.cpp | 8 ++ src/command_server.cpp | 41 +++++----- src/data_bind/data_bind.cpp | 6 +- src/file.cpp | 23 ++++++ src/importers/backboard_importer.cpp | 1 + src/nested_artboard.cpp | 5 +- .../viewmodel_instance_artboard_runtime.cpp | 8 +- .../viewmodel_instance_list_runtime.cpp | 7 +- .../runtime/viewmodel_instance_runtime.cpp | 4 +- src/viewmodel/runtime/viewmodel_runtime.cpp | 13 ++-- src/viewmodel/viewmodel_instance_artboard.cpp | 6 +- tests/common/rive_mgr.cpp | 1 + tests/goldens/goldens.cpp | 1 + tests/player/player.cpp | 1 + .../unit_tests/runtime/command_queue_test.cpp | 47 ++++++------ .../runtime/data_binding_artboards_test.cpp | 72 ++++++++++++++++-- .../data_binding_artboards_default_test.sriv | Bin 0 -> 30016 bytes .../silvers/data_binding_artboards_test.sriv | Bin 62373 -> 62472 bytes ...data_binding_artboards_test_recursive.sriv | Bin 1893 -> 1974 bytes .../silvers/scripted_animated_oval.sriv | Bin 147022 -> 147022 bytes 41 files changed, 236 insertions(+), 97 deletions(-) create mode 100644 include/rive/bindable_artboard.hpp create mode 100644 src/bindable_artboard.cpp create mode 100644 tests/unit_tests/silvers/data_binding_artboards_default_test.sriv diff --git a/.rive_head b/.rive_head index fc570544..601243e0 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -f7613dbf3542cf1d666612856728d70ba1715521 +c542b9b7ac7ac5cd96f798f3fbe7dd5411beecd3 diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index 908521ba..f9742011 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp @@ -38,6 +38,7 @@ class DataBind; class BindableProperty; class HitDrawable; class ListenerViewModel; +typedef void (*DataBindChanged)(); #ifdef WITH_RIVE_TOOLS class StateMachineInstance; diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index f1b46a66..e3bbe028 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp @@ -6,9 +6,7 @@ #include "rive/animation/linear_animation.hpp" #include "rive/animation/state_machine.hpp" #include "rive/core_context.hpp" -#include "rive/data_bind/data_bind.hpp" #include "rive/data_bind/data_context.hpp" -#include "rive/data_bind/data_bind_context.hpp" #include "rive/viewmodel/viewmodel_instance_value.hpp" #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" #include "rive/generated/artboard_base.hpp" @@ -48,6 +46,7 @@ class SMIBool; class SMIInput; class SMINumber; class SMITrigger; +class DataBind; #ifdef WITH_RIVE_TOOLS typedef void (*ArtboardCallback)(void*); diff --git a/include/rive/artboard_component_list.hpp b/include/rive/artboard_component_list.hpp index eacc74e2..6b1967d7 100644 --- a/include/rive/artboard_component_list.hpp +++ b/include/rive/artboard_component_list.hpp @@ -8,6 +8,7 @@ #include "rive/artboard.hpp" #include "rive/constraints/constrainable_list.hpp" #include "rive/property_recorder.hpp" +#include "rive/file.hpp" #include "rive/artboard_host.hpp" #include "rive/data_bind/data_bind_list_item_consumer.hpp" #include "rive/layout/layout_node_provider.hpp" @@ -17,7 +18,6 @@ #include namespace rive { -class File; class LayoutComponent; class ScrollConstraint; diff --git a/include/rive/artboard_host.hpp b/include/rive/artboard_host.hpp index 74f1d49f..93cb158f 100644 --- a/include/rive/artboard_host.hpp +++ b/include/rive/artboard_host.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_ARTBOARD_HOST_HPP_ #define _RIVE_ARTBOARD_HOST_HPP_ #include "rive/refcnt.hpp" +#include "rive/file.hpp" #include namespace rive { @@ -8,7 +9,6 @@ class ArtboardInstance; class DataBind; class DataContext; class ViewModelInstance; -class File; class ArtboardHost { diff --git a/include/rive/bindable_artboard.hpp b/include/rive/bindable_artboard.hpp new file mode 100644 index 00000000..fac7888f --- /dev/null +++ b/include/rive/bindable_artboard.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_BINDABLE_ARTBOARD_HPP_ +#define _RIVE_BINDABLE_ARTBOARD_HPP_ +#include "rive/refcnt.hpp" +#include "rive/file.hpp" +#include "rive/artboard.hpp" + +namespace rive +{ + +class BindableArtboard : public RefCnt +{ +public: + BindableArtboard(rcp file, + std::unique_ptr artboard); + ArtboardInstance* artboard() { return m_artboard.get(); } + +private: + rcp m_file; + std::unique_ptr m_artboard; +}; +} // namespace rive + +#endif diff --git a/include/rive/command_server.hpp b/include/rive/command_server.hpp index b9f968c3..4a478c8e 100644 --- a/include/rive/command_server.hpp +++ b/include/rive/command_server.hpp @@ -29,6 +29,7 @@ public: AudioSource* getAudioSource(AudioSourceHandle) const; Font* getFont(FontHandle) const; ArtboardInstance* getArtboardInstance(ArtboardHandle) const; + rcp getBindableArtboard(ArtboardHandle) const; StateMachineInstance* getStateMachineInstance(StateMachineHandle) const; ViewModelInstanceRuntime* getViewModelInstance( ViewModelInstanceHandle) const; @@ -187,8 +188,7 @@ private: std::unordered_map> m_fonts; std::unordered_map> m_images; std::unordered_map> m_audioSources; - std::unordered_map> - m_artboards; + std::unordered_map> m_artboards; std::unordered_map> m_viewModels; std::unordered_map namespace rive { -class File; class DataConverterNumberToList : public DataConverterNumberToListBase { diff --git a/include/rive/data_bind/data_bind.hpp b/include/rive/data_bind/data_bind.hpp index 35d4ecfd..dd32b2ba 100644 --- a/include/rive/data_bind/data_bind.hpp +++ b/include/rive/data_bind/data_bind.hpp @@ -7,10 +7,10 @@ #include "rive/data_bind/converters/data_converter.hpp" #include "rive/data_bind/data_values/data_type.hpp" #include "rive/dirtyable.hpp" +#include "rive/file.hpp" #include namespace rive { -class File; class DataBindContextValue; #ifdef WITH_RIVE_TOOLS class DataBind; @@ -43,8 +43,8 @@ public: bool sourceToTargetRunsFirst(); bool advance(float elapsedTime); void suppressDirt(bool value) { m_suppressDirt = value; }; - void file(File* value) { m_file = value; }; - File* file() const { return m_file; }; + void file(File* value); + File* file() const; DataType outputType(); DataType sourceOutputType(); diff --git a/include/rive/file.hpp b/include/rive/file.hpp index 6a40305e..cc789267 100644 --- a/include/rive/file.hpp +++ b/include/rive/file.hpp @@ -12,6 +12,7 @@ #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" #include "rive/viewmodel/viewmodel_instance_list_item.hpp" #include "rive/animation/keyframe_interpolator.hpp" +#include "rive/data_bind/converters/data_converter.hpp" #include "rive/refcnt.hpp" #include #include @@ -23,10 +24,12 @@ namespace rive { class BinaryReader; +class DataBind; class RuntimeHeader; class Factory; class ScrollPhysics; class ViewModelRuntime; +class BindableArtboard; /// /// Tracks the success/failure result when importing a Rive file. @@ -92,6 +95,9 @@ public: std::unique_ptr artboardDefault() const; std::unique_ptr artboardAt(size_t index) const; std::unique_ptr artboardNamed(std::string name) const; + rcp bindableArtboardNamed(std::string name) const; + rcp bindableArtboardDefault() const; + rcp internalBindableArtboardFromArtboard(Artboard*) const; Artboard* artboard() const; diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index abe7c4cd..6af39905 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp @@ -12,6 +12,7 @@ #include "rive/viewmodel/viewmodel_instance_artboard.hpp" #include "rive/refcnt.hpp" #include "rive/file.hpp" +#include namespace rive { diff --git a/include/rive/viewmodel/runtime/viewmodel_instance_artboard_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_instance_artboard_runtime.hpp index 4f337f7c..fb512fc7 100644 --- a/include/rive/viewmodel/runtime/viewmodel_instance_artboard_runtime.hpp +++ b/include/rive/viewmodel/runtime/viewmodel_instance_artboard_runtime.hpp @@ -5,6 +5,7 @@ #include #include "rive/viewmodel/runtime/viewmodel_instance_value_runtime.hpp" #include "rive/viewmodel/viewmodel_instance_artboard.hpp" +#include "rive/bindable_artboard.hpp" namespace rive { @@ -17,11 +18,11 @@ public: ViewModelInstanceArtboard* viewModelInstance) : ViewModelInstanceValueRuntime(viewModelInstance) {} - void value(Artboard* artboard); + void value(rcp bindableArtboard); const DataType dataType() override { return DataType::artboard; } #ifdef TESTING - Artboard* testing_value(); + rcp testing_value(); #endif }; } // namespace rive diff --git a/include/rive/viewmodel/runtime/viewmodel_instance_list_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_instance_list_runtime.hpp index 96a5f3be..7fb82ea0 100644 --- a/include/rive/viewmodel/runtime/viewmodel_instance_list_runtime.hpp +++ b/include/rive/viewmodel/runtime/viewmodel_instance_list_runtime.hpp @@ -19,7 +19,7 @@ public: ViewModelInstanceListRuntime(ViewModelInstanceList* viewModelInstance) : ViewModelInstanceValueRuntime(viewModelInstance) {} - ViewModelInstanceRuntime* instanceAt(int index); + rcp instanceAt(int index); void addInstance(ViewModelInstanceRuntime*); bool addInstanceAt(ViewModelInstanceRuntime*, int); void removeInstance(ViewModelInstanceRuntime*); diff --git a/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp index b8f8752a..6da29210 100644 --- a/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp +++ b/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp @@ -43,7 +43,8 @@ public: ViewModelInstanceTriggerRuntime* propertyTrigger( const std::string& path) const; ViewModelInstanceListRuntime* propertyList(const std::string& path) const; - ViewModelInstanceRuntime* propertyViewModel(const std::string& path) const; + rcp propertyViewModel( + const std::string& path) const; ViewModelInstanceAssetImageRuntime* propertyImage( const std::string& path) const; ViewModelInstanceArtboardRuntime* propertyArtboard( diff --git a/include/rive/viewmodel/runtime/viewmodel_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_runtime.hpp index 3546c127..77f6f0e2 100644 --- a/include/rive/viewmodel/runtime/viewmodel_runtime.hpp +++ b/include/rive/viewmodel/runtime/viewmodel_runtime.hpp @@ -27,11 +27,11 @@ public: const std::string& name() const; size_t instanceCount() const; size_t propertyCount() const; - ViewModelInstanceRuntime* createInstanceFromIndex(size_t index) const; - ViewModelInstanceRuntime* createInstanceFromName( + rcp createInstanceFromIndex(size_t index) const; + rcp createInstanceFromName( const std::string& name) const; - ViewModelInstanceRuntime* createDefaultInstance() const; - ViewModelInstanceRuntime* createInstance() const; + rcp createDefaultInstance() const; + rcp createInstance() const; std::vector properties(); static std::vector buildPropertiesData( std::vector& properties); @@ -40,9 +40,7 @@ public: private: ViewModel* m_viewModel; const File* m_file; - mutable std::vector> - m_viewModelInstanceRuntimes; - ViewModelInstanceRuntime* createRuntimeInstance( + rcp createRuntimeInstance( rcp instance) const; }; } // namespace rive diff --git a/include/rive/viewmodel/viewmodel_instance_artboard.hpp b/include/rive/viewmodel/viewmodel_instance_artboard.hpp index ca0176c4..060076ca 100644 --- a/include/rive/viewmodel/viewmodel_instance_artboard.hpp +++ b/include/rive/viewmodel/viewmodel_instance_artboard.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_VIEW_MODEL_INSTANCE_ARTBOARD_HPP_ #define _RIVE_VIEW_MODEL_INSTANCE_ARTBOARD_HPP_ #include "rive/generated/viewmodel/viewmodel_instance_artboard_base.hpp" +#include "rive/bindable_artboard.hpp" #include namespace rive { @@ -15,11 +16,11 @@ protected: void propertyValueChanged() override; public: - void asset(Artboard* value); - Artboard* asset() { return m_artboard; } + void asset(rcp value); + rcp asset() { return m_bindableArtboard; } private: - Artboard* m_artboard = nullptr; + rcp m_bindableArtboard = nullptr; #ifdef WITH_RIVE_TOOLS public: void onChanged(ViewModelArtboardChanged callback) diff --git a/renderer/path_fiddle/path_fiddle.cpp b/renderer/path_fiddle/path_fiddle.cpp index 50136660..dd465649 100644 --- a/renderer/path_fiddle/path_fiddle.cpp +++ b/renderer/path_fiddle/path_fiddle.cpp @@ -3,6 +3,7 @@ #include "rive/math/simd.hpp" #include "rive/artboard.hpp" #include "rive/file.hpp" +#include "rive/refcnt.hpp" #include "rive/layout.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/static_scene.hpp" diff --git a/rivinfo/main.cpp b/rivinfo/main.cpp index a0fdbd91..d7d7d0ba 100644 --- a/rivinfo/main.cpp +++ b/rivinfo/main.cpp @@ -4,6 +4,7 @@ #include "rive/artboard.hpp" #include "rive/file.hpp" +#include "rive/refcnt.hpp" #include "rive/animation/linear_animation_instance.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/animation/state_machine_input_instance.hpp" diff --git a/src/animation/state_machine.cpp b/src/animation/state_machine.cpp index d2c822b5..dbea78ea 100644 --- a/src/animation/state_machine.cpp +++ b/src/animation/state_machine.cpp @@ -4,6 +4,7 @@ #include "rive/animation/state_machine_layer.hpp" #include "rive/animation/state_machine_input.hpp" #include "rive/animation/state_machine_listener.hpp" +#include "rive/data_bind/data_bind.hpp" using namespace rive; diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index 67545456..98cc3ce9 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp @@ -27,6 +27,8 @@ #include "rive/viewmodel/viewmodel_instance_trigger.hpp" #include "rive/artboard_component_list.hpp" #include "rive/constraints/draggable_constraint.hpp" +#include "rive/data_bind/data_bind_context.hpp" +#include "rive/data_bind/data_bind.hpp" #include "rive/data_bind_flags.hpp" #include "rive/event_report.hpp" #include "rive/gesture_click_phase.hpp" diff --git a/src/artboard.cpp b/src/artboard.cpp index d0269989..c8cdfb95 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp @@ -23,6 +23,7 @@ #include "rive/nested_artboard_leaf.hpp" #include "rive/nested_artboard_layout.hpp" #include "rive/joystick.hpp" +#include "rive/data_bind/data_bind.hpp" #include "rive/data_bind_flags.hpp" #include "rive/animation/nested_bool.hpp" #include "rive/animation/nested_number.hpp" @@ -1666,7 +1667,7 @@ void Artboard::bindViewModelInstance(rcp viewModelInstance, bool Artboard::isAncestor(const Artboard* artboard) { - if (m_artboardSource == artboard) + if (artboard != nullptr && m_artboardSource == artboard->artboardSource()) { return true; } diff --git a/src/bindable_artboard.cpp b/src/bindable_artboard.cpp new file mode 100644 index 00000000..d8b65d48 --- /dev/null +++ b/src/bindable_artboard.cpp @@ -0,0 +1,8 @@ +#include "rive/bindable_artboard.hpp" + +using namespace rive; + +BindableArtboard::BindableArtboard(rcp file, + std::unique_ptr artboard) : + m_file(file), m_artboard(std::move(artboard)) +{} \ No newline at end of file diff --git a/src/command_server.cpp b/src/command_server.cpp index d06f4ac4..67baa221 100644 --- a/src/command_server.cpp +++ b/src/command_server.cpp @@ -293,7 +293,15 @@ ArtboardInstance* CommandServer::getArtboardInstance( { assert(std::this_thread::get_id() == m_threadID); auto it = m_artboards.find(handle); - return it != m_artboards.end() ? it->second.get() : nullptr; + return it != m_artboards.end() ? it->second.get()->artboard() : nullptr; +} + +rcp CommandServer::getBindableArtboard( + ArtboardHandle handle) const +{ + assert(std::this_thread::get_id() == m_threadID); + auto it = m_artboards.find(handle); + return it != m_artboards.end() ? it->second : nullptr; } StateMachineInstance* CommandServer::getStateMachineInstance( @@ -519,7 +527,7 @@ bool CommandServer::processCommands() if (file != nullptr) { m_fileDependencies[handle] = {}; - m_files[handle] = std::move(file); + m_files[handle] = file; std::unique_lock messageLock( m_commandQueue->m_messageMutex); @@ -823,12 +831,9 @@ bool CommandServer::processCommands() if (rive::File* file = getFile(fileHandle)) { if (auto artboard = name.empty() - ? file->artboardDefault() - : file->artboardNamed(name)) + ? file->bindableArtboardDefault() + : file->bindableArtboardNamed(name)) { - assert(m_fileDependencies.find(fileHandle) != - m_fileDependencies.end()); - m_fileDependencies[fileHandle].push_back(handle); m_artboardDependencies[handle] = {}; m_artboards[handle] = std::move(artboard); } @@ -984,8 +989,7 @@ bool CommandServer::processCommands() { if (viewModelInstanceName.empty()) { - instance = - ref_rcp(viewModel->createDefaultInstance()); + instance = viewModel->createDefaultInstance(); if (instance == nullptr) { ErrorReporter( @@ -1000,9 +1004,8 @@ bool CommandServer::processCommands() } else { - instance = - ref_rcp(viewModel->createInstanceFromName( - viewModelInstanceName)); + instance = viewModel->createInstanceFromName( + viewModelInstanceName); if (instance == nullptr) { ErrorReporter( @@ -1019,7 +1022,7 @@ bool CommandServer::processCommands() } else { - instance = ref_rcp(viewModel->createInstance()); + instance = viewModel->createInstance(); if (instance == nullptr) { ErrorReporter( @@ -1313,8 +1316,7 @@ bool CommandServer::processCommands() if (auto nestedViewModel = rootViewInstance->propertyViewModel(path)) { - m_viewModels[nestedViewHandle] = - ref_rcp(nestedViewModel); + m_viewModels[nestedViewHandle] = nestedViewModel; } else { @@ -1365,8 +1367,7 @@ bool CommandServer::processCommands() auto viewModelInstance = list->instanceAt(index); if (viewModelInstance) { - m_viewModels[listViewHandle] = - ref_rcp(viewModelInstance); + m_viewModels[listViewHandle] = viewModelInstance; } else { @@ -2244,14 +2245,14 @@ bool CommandServer::processCommands() } case DataType::artboard: { - if (auto artboard = - getArtboardInstance(artboadHandle)) + if (auto bindableArtboard = + getBindableArtboard(artboadHandle)) { if (auto artboardProperty = viewModelInstance->propertyArtboard( value.metaData.name)) { - artboardProperty->value(artboard); + artboardProperty->value(bindableArtboard); } else { diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp index 20630cfa..ac75fc0c 100644 --- a/src/data_bind/data_bind.cpp +++ b/src/data_bind/data_bind.cpp @@ -391,4 +391,8 @@ bool DataBind::advance(float elapsedTime) return converter()->advance(elapsedTime); } return false; -} \ No newline at end of file +} + +void DataBind::file(File* value) { m_file = value; }; + +File* DataBind::file() const { return m_file; }; \ No newline at end of file diff --git a/src/file.cpp b/src/file.cpp index 7cade2ea..fddd6c92 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -1,4 +1,5 @@ #include "rive/file.hpp" +#include "rive/bindable_artboard.hpp" #include "rive/runtime_header.hpp" #include "rive/animation/animation.hpp" #include "rive/artboard_component_list.hpp" @@ -38,6 +39,7 @@ #include "rive/animation/blend_state_direct.hpp" #include "rive/animation/transition_property_viewmodel_comparator.hpp" #include "rive/constraints/scrolling/scroll_physics.hpp" +#include "rive/data_bind/data_bind.hpp" #include "rive/data_bind/bindable_property.hpp" #include "rive/data_bind/bindable_property_artboard.hpp" #include "rive/data_bind/bindable_property_asset.hpp" @@ -589,6 +591,27 @@ std::unique_ptr File::artboardNamed(std::string name) const return ab ? ab->instance() : nullptr; } +rcp File::bindableArtboardNamed(std::string name) const +{ + auto ab = this->artboardNamed(name); + return ab ? make_rcp(ref_rcp(this), std::move(ab)) + : nullptr; +} + +rcp File::bindableArtboardDefault() const +{ + auto ab = this->artboardDefault(); + return ab ? make_rcp(ref_rcp(this), std::move(ab)) + : nullptr; +} + +rcp File::internalBindableArtboardFromArtboard( + Artboard* artboard) const +{ + auto ab = artboard ? artboard->instance() : nullptr; + return ab ? make_rcp(nullptr, std::move(ab)) : nullptr; +} + void File::completeViewModelInstance( rcp viewModelInstance) const { diff --git a/src/importers/backboard_importer.cpp b/src/importers/backboard_importer.cpp index 76ce4fb0..825a7acf 100644 --- a/src/importers/backboard_importer.cpp +++ b/src/importers/backboard_importer.cpp @@ -9,6 +9,7 @@ #include "rive/constraints/scrolling/scroll_physics.hpp" #include "rive/viewmodel/viewmodel.hpp" #include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/file.hpp" #include "rive/data_bind/converters/data_converter.hpp" #include "rive/data_bind/converters/data_converter_group_item.hpp" #include "rive/data_bind/converters/data_converter_range_mapper.hpp" diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index fd0aff9e..bd75f49d 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp @@ -64,9 +64,10 @@ Artboard* NestedArtboard::findArtboard( } if (viewModelInstanceArtboard->asset() != nullptr) { - if (!parentArtboard()->isAncestor(viewModelInstanceArtboard->asset())) + if (!parentArtboard()->isAncestor( + viewModelInstanceArtboard->asset()->artboard())) { - return viewModelInstanceArtboard->asset(); + return viewModelInstanceArtboard->asset()->artboard(); } return nullptr; } diff --git a/src/viewmodel/runtime/viewmodel_instance_artboard_runtime.cpp b/src/viewmodel/runtime/viewmodel_instance_artboard_runtime.cpp index 486b681e..161b3b21 100644 --- a/src/viewmodel/runtime/viewmodel_instance_artboard_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_instance_artboard_runtime.cpp @@ -4,13 +4,15 @@ // Default namespace for Rive Cpp code using namespace rive; -void ViewModelInstanceArtboardRuntime::value(Artboard* artboard) +void ViewModelInstanceArtboardRuntime::value( + rcp bindableArtboard) { - m_viewModelInstanceValue->as()->asset(artboard); + m_viewModelInstanceValue->as()->asset( + bindableArtboard); } #ifdef TESTING -Artboard* ViewModelInstanceArtboardRuntime::testing_value() +rcp ViewModelInstanceArtboardRuntime::testing_value() { return m_viewModelInstanceValue->as()->asset(); } diff --git a/src/viewmodel/runtime/viewmodel_instance_list_runtime.cpp b/src/viewmodel/runtime/viewmodel_instance_list_runtime.cpp index 1d5bedde..aa63f0cc 100644 --- a/src/viewmodel/runtime/viewmodel_instance_list_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_instance_list_runtime.cpp @@ -6,7 +6,8 @@ // Default namespace for Rive Cpp code using namespace rive; -ViewModelInstanceRuntime* ViewModelInstanceListRuntime::instanceAt(int index) +rcp ViewModelInstanceListRuntime::instanceAt( + int index) { auto listItems = m_viewModelInstanceValue->as()->listItems(); @@ -22,12 +23,12 @@ ViewModelInstanceRuntime* ViewModelInstanceListRuntime::instanceAt(int index) auto it = m_itemsMap.find(listItem); if (it != m_itemsMap.end()) { - return it->second.get(); + return rcp(it->second); } auto instanceRuntime = make_rcp(listItem->viewModelInstance()); m_itemsMap[listItem] = instanceRuntime; - return instanceRuntime.get(); + return rcp(instanceRuntime); } void ViewModelInstanceListRuntime::addInstance( diff --git a/src/viewmodel/runtime/viewmodel_instance_runtime.cpp b/src/viewmodel/runtime/viewmodel_instance_runtime.cpp index c019e140..6d6966e4 100644 --- a/src/viewmodel/runtime/viewmodel_instance_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_instance_runtime.cpp @@ -296,14 +296,14 @@ rcp ViewModelInstanceRuntime::instanceRuntime( return nullptr; } -ViewModelInstanceRuntime* ViewModelInstanceRuntime::propertyViewModel( +rcp ViewModelInstanceRuntime::propertyViewModel( const std::string& path) const { const auto propertyName = getPropertyNameFromPath(path); auto viewModelInstance = viewModelInstanceFromFullPath(path); if (viewModelInstance != nullptr) { - return viewModelInstance->instanceRuntime(propertyName).get(); + return viewModelInstance->instanceRuntime(propertyName); } return nullptr; } diff --git a/src/viewmodel/runtime/viewmodel_runtime.cpp b/src/viewmodel/runtime/viewmodel_runtime.cpp index 324581f3..f625b8c9 100644 --- a/src/viewmodel/runtime/viewmodel_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_runtime.cpp @@ -108,7 +108,7 @@ std::vector ViewModelRuntime::instanceNames() const return names; } -ViewModelInstanceRuntime* ViewModelRuntime::createInstanceFromIndex( +rcp ViewModelRuntime::createInstanceFromIndex( const size_t index) const { auto viewModelInstance = m_viewModel->instance(index); @@ -129,7 +129,7 @@ ViewModelInstanceRuntime* ViewModelRuntime::createInstanceFromIndex( return nullptr; } -ViewModelInstanceRuntime* ViewModelRuntime::createInstanceFromName( +rcp ViewModelRuntime::createInstanceFromName( const std::string& name) const { auto viewModelInstance = m_viewModel->instance(name); @@ -151,7 +151,7 @@ ViewModelInstanceRuntime* ViewModelRuntime::createInstanceFromName( return nullptr; } -ViewModelInstanceRuntime* ViewModelRuntime::createDefaultInstance() const +rcp ViewModelRuntime::createDefaultInstance() const { auto viewModelInstance = m_file->createDefaultViewModelInstance(m_viewModel); @@ -164,21 +164,20 @@ ViewModelInstanceRuntime* ViewModelRuntime::createDefaultInstance() const return createInstance(); } -ViewModelInstanceRuntime* ViewModelRuntime::createInstance() const +rcp ViewModelRuntime::createInstance() const { auto instance = m_file->createViewModelInstance(m_viewModel); return createRuntimeInstance(instance); } -ViewModelInstanceRuntime* ViewModelRuntime::createRuntimeInstance( +rcp ViewModelRuntime::createRuntimeInstance( rcp instance) const { if (instance != nullptr) { auto viewModelInstanceRuntime = rcp( new ViewModelInstanceRuntime(instance)); - m_viewModelInstanceRuntimes.push_back(viewModelInstanceRuntime); - return viewModelInstanceRuntime.get(); + return viewModelInstanceRuntime; } return nullptr; } \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_artboard.cpp b/src/viewmodel/viewmodel_instance_artboard.cpp index 31f32686..0ec4c711 100644 --- a/src/viewmodel/viewmodel_instance_artboard.cpp +++ b/src/viewmodel/viewmodel_instance_artboard.cpp @@ -10,7 +10,7 @@ using namespace rive; void ViewModelInstanceArtboard::propertyValueChanged() { - m_artboard = nullptr; + m_bindableArtboard = nullptr; addDirt(ComponentDirt::Bindings); #ifdef WITH_RIVE_TOOLS if (m_changedCallback != nullptr) @@ -21,9 +21,9 @@ void ViewModelInstanceArtboard::propertyValueChanged() onValueChanged(); } -void ViewModelInstanceArtboard::asset(Artboard* value) +void ViewModelInstanceArtboard::asset(rcp value) { propertyValue(-1); - m_artboard = value; + m_bindableArtboard = value; addDirt(ComponentDirt::Bindings); } \ No newline at end of file diff --git a/tests/common/rive_mgr.cpp b/tests/common/rive_mgr.cpp index 6bc74d60..73110fc8 100644 --- a/tests/common/rive_mgr.cpp +++ b/tests/common/rive_mgr.cpp @@ -4,6 +4,7 @@ #include "rive/artboard.hpp" #include "rive/factory.hpp" #include "rive/file.hpp" +#include "rive/refcnt.hpp" struct AutoClose { diff --git a/tests/goldens/goldens.cpp b/tests/goldens/goldens.cpp index f0cb82fa..831e254c 100644 --- a/tests/goldens/goldens.cpp +++ b/tests/goldens/goldens.cpp @@ -14,6 +14,7 @@ #include "rive/artboard.hpp" #include "rive/renderer.hpp" #include "rive/file.hpp" +#include "rive/refcnt.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/static_scene.hpp" #include diff --git a/tests/player/player.cpp b/tests/player/player.cpp index 5d253b01..743dcf9a 100644 --- a/tests/player/player.cpp +++ b/tests/player/player.cpp @@ -8,6 +8,7 @@ #include "common/test_harness.hpp" #include "common/testing_window.hpp" #include "rive/artboard.hpp" +#include "rive/refcnt.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/file.hpp" #include "rive/text/font_hb.hpp" diff --git a/tests/unit_tests/runtime/command_queue_test.cpp b/tests/unit_tests/runtime/command_queue_test.cpp index 820ecbf1..f1c5b434 100644 --- a/tests/unit_tests/runtime/command_queue_test.cpp +++ b/tests/unit_tests/runtime/command_queue_test.cpp @@ -175,9 +175,8 @@ TEST_CASE("artboard management", "[CommandQueue]") // Deleting the file first should now delete the artboard as well. commandQueue->deleteFile(fileHandle); - commandQueue->runOnce([fileHandle, artboardHandle1](CommandServer* server) { + commandQueue->runOnce([fileHandle](CommandServer* server) { CHECK(server->getFile(fileHandle) == nullptr); - CHECK(server->getArtboardInstance(artboardHandle1) == nullptr); }); commandQueue->deleteArtboard(artboardHandle1); @@ -792,7 +791,7 @@ TEST_CASE("View Models", "[CommandQueue]") auto list = server->getViewModelInstance(bviewModel)->propertyList("Test List"); CHECK(list != nullptr); - CHECK(list->instanceAt(0) == + CHECK(list->instanceAt(0).get() == server->getViewModelInstance(listViewModel)); }); @@ -1628,7 +1627,7 @@ public: CHECK(nested != nullptr); auto property = instance->propertyViewModel(name); CHECK(property != nullptr); - CHECK(property == nested); + CHECK(property.get() == nested); }); // There is no requesting for nested view models @@ -1807,20 +1806,24 @@ TEST_CASE("View Model Property Set/Get", "[CommandQueue]") }); // Same for artboards. + auto bindableArtboardHandle = + commandQueue->instantiateDefaultArtboard(fileHandle); commandQueue->setViewModelInstanceArtboard(tester.m_handle, "Test Artboard", - artboardHandle); + bindableArtboardHandle); - commandQueue->runOnce([artboardHandle, + commandQueue->runOnce([bindableArtboardHandle, handle = tester.m_handle](CommandServer* server) { - auto artboard = server->getArtboardInstance(artboardHandle); - CHECK(artboard != nullptr); + auto bindableArtboard = + server->getBindableArtboard(bindableArtboardHandle); + CHECK(bindableArtboard != nullptr); auto viewModel = server->getViewModelInstance(handle); CHECK(viewModel != nullptr); auto artboardProperty = viewModel->propertyArtboard("Test Artboard"); CHECK(artboardProperty != nullptr); - CHECK(artboardProperty->testing_value() == artboard); + CHECK(artboardProperty->testing_value() == bindableArtboard); }); + commandQueue->deleteArtboard(bindableArtboardHandle); auto badImageHandle = commandQueue->decodeImage(std::vector(1024 * 1024, {})); @@ -2512,8 +2515,8 @@ public: CHECK(value2Instance != nullptr); auto property = instance->propertyList(name); CHECK(property != nullptr); - CHECK(property->instanceAt(index) == value1Instance); - CHECK(property->instanceAt(index2) == value2Instance); + CHECK(property->instanceAt(index).get() == value1Instance); + CHECK(property->instanceAt(index2).get() == value2Instance); }); } @@ -2532,23 +2535,23 @@ public: auto property = instance->propertyList(name); CHECK(property != nullptr); auto valueModel = server->getViewModelInstance(value); - CHECK(property->instanceAt(index) == valueModel); + CHECK(property->instanceAt(index).get() == valueModel); }); } void pushExpectation(std::string name, ViewModelInstanceHandle value) { m_queue->appendViewModelInstanceListViewModel(m_handle, name, value); - m_queue->runOnce( - [handle = m_handle, name, value](CommandServer* server) { - auto instance = server->getViewModelInstance(handle); - CHECK(instance != nullptr); - auto property = instance->propertyList(name); - CHECK(property != nullptr); - auto valueModel = server->getViewModelInstance(value); - CHECK(property->instanceAt(static_cast(property->size()) - - 1) == valueModel); - }); + m_queue->runOnce([handle = m_handle, name, value]( + CommandServer* server) { + auto instance = server->getViewModelInstance(handle); + CHECK(instance != nullptr); + auto property = instance->propertyList(name); + CHECK(property != nullptr); + auto valueModel = server->getViewModelInstance(value); + CHECK(property->instanceAt(static_cast(property->size()) - 1) + .get() == valueModel); + }); } void pushBadExpectation(std::string name, diff --git a/tests/unit_tests/runtime/data_binding_artboards_test.cpp b/tests/unit_tests/runtime/data_binding_artboards_test.cpp index 1bd09b39..d7df4076 100644 --- a/tests/unit_tests/runtime/data_binding_artboards_test.cpp +++ b/tests/unit_tests/runtime/data_binding_artboards_test.cpp @@ -68,7 +68,7 @@ TEST_CASE("Test data binding artboards from same and different sources", // Change source to local artboard at index 1 - auto ch1Source = file->artboard("ch1"); + auto ch1Source = file->bindableArtboardNamed("ch1"); vmiArtboard->asset(ch1Source); silver.addFrame(); stateMachine->advanceAndApply(0.1f); @@ -76,14 +76,14 @@ TEST_CASE("Test data binding artboards from same and different sources", // Change source to local artboard at index 2 - auto ch2Source = file->artboard("ch2"); + auto ch2Source = file->bindableArtboardNamed("ch2"); vmiArtboard->asset(ch2Source); silver.addFrame(); stateMachine->advanceAndApply(0.1f); artboard->draw(renderer.get()); // Change source to external artboard "source_1" - auto source1 = sourceFile->artboard("source_1"); + auto source1 = sourceFile->bindableArtboardNamed("source_1"); vmiArtboard->asset(source1); silver.addFrame(); stateMachine->advanceAndApply(0.1f); @@ -102,7 +102,7 @@ TEST_CASE("Test data binding artboards from same and different sources", artboard->draw(renderer.get()); // Change source to external artboard "source_2" - auto source2 = sourceFile->artboard("source_2"); + auto source2 = sourceFile->bindableArtboardNamed("source_2"); vmiArtboard->asset(source2); silver.addFrame(); stateMachine->advanceAndApply(0.1f); @@ -166,7 +166,7 @@ TEST_CASE("Test recursive data binding artboards is skipped", "[data binding]") artboard->draw(renderer.get()); // Change source to artboard "recursive-grand-child-1" - auto child1Source = file->artboard("recursive-grand-child-1"); + auto child1Source = file->bindableArtboardNamed("recursive-grand-child-1"); vmiArtboard->asset(child1Source); silver.addFrame(); stateMachine->advanceAndApply(0.1f); @@ -174,7 +174,7 @@ TEST_CASE("Test recursive data binding artboards is skipped", "[data binding]") // Changing source to artboard "recursive-parent" does not change the // content because it's an ancestor of itself - auto parentSource = file->artboard("recursive-parent"); + auto parentSource = file->bindableArtboardNamed("recursive-parent"); vmiArtboard->asset(parentSource); silver.addFrame(); stateMachine->advanceAndApply(0.1f); @@ -182,18 +182,74 @@ TEST_CASE("Test recursive data binding artboards is skipped", "[data binding]") // Changing source to artboard "recursive-grand-parent" does not change the // content because it's an ancestor of itself - auto grandParentSource = file->artboard("recursive-grand-parent"); + auto grandParentSource = + file->bindableArtboardNamed("recursive-grand-parent"); vmiArtboard->asset(grandParentSource); silver.addFrame(); stateMachine->advanceAndApply(0.1f); artboard->draw(renderer.get()); // Change source to artboard "recursive-grand-child-2" - auto child2Source = file->artboard("recursive-grand-child-2"); + auto child2Source = file->bindableArtboardNamed("recursive-grand-child-2"); vmiArtboard->asset(child2Source); silver.addFrame(); stateMachine->advanceAndApply(0.1f); artboard->draw(renderer.get()); CHECK(silver.matches("data_binding_artboards_test_recursive")); +} + +TEST_CASE("Test default data binding artboard from different source", + "[data binding]") +{ + // Note: the data_binding_artboards_source_test has a view model created + // that matches the view model the original artboards have. This is a + // temporary "hack" to validate that the artboard gets correctly bound + SerializingFactory silver; + auto file = ReadRiveFile("assets/data_binding_artboards_test.riv", &silver); + auto sourceFile = + ReadRiveFile("assets/data_binding_artboards_source_test.riv", &silver); + + auto artboard = file->artboardDefault(); + silver.frameSize(artboard->width(), artboard->height()); + + REQUIRE(artboard != nullptr); + auto stateMachine = artboard->stateMachineAt(0); + int viewModelId = artboard.get()->viewModelId(); + + auto vmi = viewModelId == -1 + ? file->createViewModelInstance(artboard.get()) + : file->createViewModelInstance(viewModelId, 0); + + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.1f); + auto renderer = silver.makeRenderer(); + artboard->draw(renderer.get()); + + auto vmiArtboard = + vmi->propertyValue("ab")->as(); + silver.addFrame(); + stateMachine->advanceAndApply(0.1f); + artboard->draw(renderer.get()); + // Change source to local artboard at index 1 + + auto ch1Source = file->bindableArtboardNamed("ch1"); + vmiArtboard->asset(ch1Source); + silver.addFrame(); + stateMachine->advanceAndApply(0.1f); + artboard->draw(renderer.get()); + // Change source to local artboard at index 2 + + auto ch2Source = file->bindableArtboardNamed("ch2"); + vmiArtboard->asset(ch2Source); + silver.addFrame(); + stateMachine->advanceAndApply(0.1f); + artboard->draw(renderer.get()); + // Change source to default bindable artboard + auto source1 = sourceFile->bindableArtboardDefault(); + vmiArtboard->asset(source1); + silver.addFrame(); + stateMachine->advanceAndApply(0.1f); + artboard->draw(renderer.get()); + CHECK(silver.matches("data_binding_artboards_default_test")); } \ No newline at end of file diff --git a/tests/unit_tests/silvers/data_binding_artboards_default_test.sriv b/tests/unit_tests/silvers/data_binding_artboards_default_test.sriv new file mode 100644 index 0000000000000000000000000000000000000000..eac30882d15adadeceeb953531bf78698326710d GIT binary patch literal 30016 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 a9px7iHAoXU^nd%cwSW74tN-iQ*8T@+6+r+1 literal 0 HcmV?d00001 diff --git a/tests/unit_tests/silvers/data_binding_artboards_test.sriv b/tests/unit_tests/silvers/data_binding_artboards_test.sriv index deef2b7d29b1e07dc69fe09335fcfbc8efc1a94b..63c75dec539d8b329fa8cc4bc62b1568e7c3d215 100644 GIT binary patch delta 733 zcmaixOK1~O6o$EGu3p@Xfu2kfs-~n(Vl!zTof*x;%w&?7L_sLsi@Fh4cF_=*>8`FS zo@S>)1(}Uf>1^D%D1s;i#l0v2!RkU(T!_}kow*{}sCRX^|2hA6e$MSTfz_SBy?|oS zIK+SQC(~(|{=D=2n@ne5X6@$d@A4!~KtgCqND3_lDNn9h4G`9>EWo!>7s=ZZ2B}orPIpl!ml0QbFzCfA@t$GLRA49LxzV3t3O|A@UxE z#;Xu+P8`DbN*H6qDSVbLkgN|@R2zc}io2w8Y=+UCkG(q)LuzCbVt zAq*YMqd^$M-w)pY)F6V9CySqcX|#m~jWk=d8|bhIZ6ea+A)AG%EiSnqhwkYR;&7eQ zn>~xZ21cm1Z5vUHDlLXFrNuGsy!`T`r?t%vi+kE#VY8Uq7E?MzIgBc4ZSX2pw{ICH zno1HV)Fpul=lS~Yp44q{dRSdz397X&yMwg}-7h%Ykgs9axo?L2CWo7}HcfrPaoR`! zf(E_940Ri}3ihvV)dwyLE@MF$&`r+ea6y`!v1M#-`DF6*#_AU!iOGf8_eUC7XstJ^ zj3wP#OS|H?IHcq}7Hu@IdFx7JT6N1{MqRR)JzH}A=@QF&B_BMKB{Lkxypo$8Su)LG zoW8v#BZoO<AC9MOH@=r= mMhQ!bE@N5I6|9`4AMK}D<%E8>Uu0#E79S*C;!ypcuKxkoee_NM diff --git a/tests/unit_tests/silvers/data_binding_artboards_test_recursive.sriv b/tests/unit_tests/silvers/data_binding_artboards_test_recursive.sriv index bf9812697560581c6a3860da3e466c104db85c14..a56abf8a1f0ab2828eb5a697f0ea9398f60a4242 100644 GIT binary patch delta 169 zcmaFLw~c>8EVD4Hn5bCyo2DQ9GOXgF;$YU~MrNtWHLQmwJF^%oigSPftAwZoSdLXv zRC42^BR~0Br9`E`jLC_t_qCv^q(!A6s$@iEAgW|VWg!eXQ8~DBNvLvpQTc|3KYxH$ TD~Kw9S#Ya$*p1l~z*YkQj7T=1 delta 71 zcmdnS|CDb-EHkGJtC*-*_nW34lNYevo9xSC#3sf80+Sc8+(%+^Lqug*#YM$8&tVf` JWD^G~0|3|v5MlrT diff --git a/tests/unit_tests/silvers/scripted_animated_oval.sriv b/tests/unit_tests/silvers/scripted_animated_oval.sriv index 8dea74acbc9df31ab04fbad345fea73ca772861f..314dd2d51eea6936ce0f63ef1f3e1f6baac2a0d9 100644 GIT binary patch delta 5331 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 wB4qg!<+!~$4_y1cM99{|r*^er8iyh($ delta 5329 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