feature: add data bind font support (#13031) 25b5033ad8 Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head index da6eb28..892e121 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -ced1506e5bddd30e8d3939578f024e7d9c8b8409 +25b5033ad80758aeb3ad9acdf160a72ac35ef96b
diff --git a/dev/defs/text/text_style.json b/dev/defs/text/text_style.json index 2daad8d..a281bc4 100644 --- a/dev/defs/text/text_style.json +++ b/dev/defs/text/text_style.json
@@ -44,7 +44,8 @@ "key": { "int": 279, "string": "fontassetid" - } + }, + "bindable": true }, "familyName": { "type": "String",
diff --git a/dev/defs/viewmodel/viewmodel_instance_asset_font.json b/dev/defs/viewmodel/viewmodel_instance_asset_font.json new file mode 100644 index 0000000..ec76e39 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_asset_font.json
@@ -0,0 +1,8 @@ +{ + "name": "ViewModelInstanceAssetFont", + "key": { + "int": 1035, + "string": "viewmodelinstanceassetfont" + }, + "extends": "viewmodel/viewmodel_instance_asset.json" +}
diff --git a/dev/defs/viewmodel/viewmodel_property_asset_font.json b/dev/defs/viewmodel/viewmodel_property_asset_font.json new file mode 100644 index 0000000..8e8895d --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_asset_font.json
@@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyAssetFont", + "key": { + "int": 1034, + "string": "viewmodelpropertyassetfont" + }, + "extends": "viewmodel/viewmodel_property_asset.json" +}
diff --git a/include/rive/data_bind/bindable_property_asset.hpp b/include/rive/data_bind/bindable_property_asset.hpp index d759425..4908c3b 100644 --- a/include/rive/data_bind/bindable_property_asset.hpp +++ b/include/rive/data_bind/bindable_property_asset.hpp
@@ -2,13 +2,22 @@ #define _RIVE_BINDABLE_PROPERTY_ASSET_HPP_ #include "rive/generated/data_bind/bindable_property_asset_base.hpp" #include "rive/assets/image_asset.hpp" +#include "rive/assets/font_asset.hpp" +#include "rive/text_engine.hpp" #include <stdio.h> namespace rive { +// A single bindable asset property that can carry either a live image or a live +// font. Only one is populated for a given bind (determined by the bound asset +// kind); the other stays empty. The asset id is carried by the +// BindablePropertyId base regardless of kind. class BindablePropertyAsset : public BindablePropertyAssetBase { public: - BindablePropertyAsset() : m_fileAsset(rcp<ImageAsset>(new ImageAsset())) {} + BindablePropertyAsset() : + m_fileAsset(rcp<ImageAsset>(new ImageAsset())), + m_fontAsset(rcp<FontAsset>(new FontAsset())) + {} constexpr static uint32_t defaultValue = -1; rcp<ImageAsset> fileAsset() { return m_fileAsset; } void imageValue(RenderImage* image) @@ -16,9 +25,13 @@ m_fileAsset->renderImage(ref_rcp(image)); } RenderImage* imageValue() { return m_fileAsset->renderImage(); } + rcp<FontAsset> fontFileAsset() { return m_fontAsset; } + void fontValue(Font* font) { m_fontAsset->font(ref_rcp(font)); } + Font* fontValue() { return m_fontAsset->font().get(); } private: rcp<ImageAsset> m_fileAsset = nullptr; + rcp<FontAsset> m_fontAsset = nullptr; }; } // namespace rive
diff --git a/include/rive/data_bind/context/context_value_asset_font.hpp b/include/rive/data_bind/context/context_value_asset_font.hpp new file mode 100644 index 0000000..de276e6 --- /dev/null +++ b/include/rive/data_bind/context/context_value_asset_font.hpp
@@ -0,0 +1,21 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_ASSET_FONT_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_ASSET_FONT_HPP_ +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/data_values/data_value_asset_font.hpp" +namespace rive +{ +class FontAsset; +class DataBindContextValueAssetFont : public DataBindContextValue +{ + +public: + DataBindContextValueAssetFont(DataBind* m_dataBind); + void apply(Core* component, + uint32_t propertyKey, + bool isMainDirection, + DataBind* dataBind) override; + rcp<FontAsset> fileAsset(DataBind* dataBind); +}; +} // namespace rive + +#endif
diff --git a/include/rive/data_bind/data_values/data_type.hpp b/include/rive/data_bind/data_values/data_type.hpp index 2e7debe..e8bf667 100644 --- a/include/rive/data_bind/data_values/data_type.hpp +++ b/include/rive/data_bind/data_values/data_type.hpp
@@ -44,6 +44,9 @@ /// Artboard. artboard = 12, + /// Asset Font. + assetFont = 13, + /// Special case, this type is used to indicate it uses the input type. input = 99,
diff --git a/include/rive/data_bind/data_values/data_value_asset_font.hpp b/include/rive/data_bind/data_values/data_value_asset_font.hpp new file mode 100644 index 0000000..74068df --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_asset_font.hpp
@@ -0,0 +1,32 @@ +#ifndef _RIVE_DATA_VALUE_ASSET_FONT_HPP_ +#define _RIVE_DATA_VALUE_ASSET_FONT_HPP_ +#include "rive/data_bind/data_values/data_value_integer.hpp" +#include "rive/assets/font_asset.hpp" +#include "rive/text_engine.hpp" + +#include <iostream> +namespace rive +{ +class DataValueAssetFont : public DataValueInteger +{ +public: + DataValueAssetFont(uint32_t value) : + DataValueInteger(value), + m_fileAsset(rcp<FontAsset>(new FontAsset())) {}; + DataValueAssetFont() : + DataValueInteger(-1), m_fileAsset(rcp<FontAsset>(new FontAsset())) {}; + static const DataType typeKey = DataType::assetFont; + bool isTypeOf(DataType typeKey) const override + { + return typeKey == DataType::assetFont || typeKey == DataType::integer; + }; + constexpr static uint32_t defaultValue = -1; + rcp<FontAsset> fileAsset() { return m_fileAsset; } + void fontValue(Font* font) { m_fileAsset->font(ref_rcp(font)); } + Font* fontValue() { return m_fileAsset->font().get(); } + +private: + rcp<FontAsset> m_fileAsset = nullptr; +}; +} // namespace rive +#endif
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 0a6b546..36e1fc9 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -306,6 +306,7 @@ #include "rive/viewmodel/viewmodel_instance.hpp" #include "rive/viewmodel/viewmodel_instance_artboard.hpp" #include "rive/viewmodel/viewmodel_instance_asset.hpp" +#include "rive/viewmodel/viewmodel_instance_asset_font.hpp" #include "rive/viewmodel/viewmodel_instance_asset_image.hpp" #include "rive/viewmodel/viewmodel_instance_boolean.hpp" #include "rive/viewmodel/viewmodel_instance_color.hpp" @@ -322,6 +323,7 @@ #include "rive/viewmodel/viewmodel_property.hpp" #include "rive/viewmodel/viewmodel_property_artboard.hpp" #include "rive/viewmodel/viewmodel_property_asset.hpp" +#include "rive/viewmodel/viewmodel_property_asset_font.hpp" #include "rive/viewmodel/viewmodel_property_asset_image.hpp" #include "rive/viewmodel/viewmodel_property_boolean.hpp" #include "rive/viewmodel/viewmodel_property_color.hpp" @@ -395,6 +397,8 @@ return new ViewModelPropertyColor(); case ViewModelPropertyAssetImageBase::typeKey: return new ViewModelPropertyAssetImage(); + case ViewModelPropertyAssetFontBase::typeKey: + return new ViewModelPropertyAssetFont(); case ViewModelInstanceBooleanBase::typeKey: return new ViewModelInstanceBoolean(); case ViewModelInstanceListBase::typeKey: @@ -415,6 +419,8 @@ return new ViewModelInstanceAsset(); case ViewModelInstanceAssetImageBase::typeKey: return new ViewModelInstanceAssetImage(); + case ViewModelInstanceAssetFontBase::typeKey: + return new ViewModelInstanceAssetFont(); case DataEnumValueBase::typeKey: return new DataEnumValue(); case CustomPropertyTriggerBase::typeKey:
diff --git a/include/rive/generated/viewmodel/viewmodel_instance_asset_font_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_asset_font_base.hpp new file mode 100644 index 0000000..34041ee --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_asset_font_base.hpp
@@ -0,0 +1,38 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_BASE_HPP_ +#include "rive/viewmodel/viewmodel_instance_asset.hpp" +namespace rive +{ +class ViewModelInstanceAssetFontBase : public ViewModelInstanceAsset +{ +protected: + typedef ViewModelInstanceAsset Super; + +public: + static const uint16_t typeKey = 1035; + + /// Helper to quickly determine if a core object extends another without + /// RTTI at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceAssetFontBase::typeKey: + case ViewModelInstanceAssetBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/viewmodel/viewmodel_property_asset_font_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_asset_font_base.hpp new file mode 100644 index 0000000..76c2511 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_asset_font_base.hpp
@@ -0,0 +1,38 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_ASSET_FONT_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_ASSET_FONT_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property_asset.hpp" +namespace rive +{ +class ViewModelPropertyAssetFontBase : public ViewModelPropertyAsset +{ +protected: + typedef ViewModelPropertyAsset Super; + +public: + static const uint16_t typeKey = 1034; + + /// Helper to quickly determine if a core object extends another without + /// RTTI at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyAssetFontBase::typeKey: + case ViewModelPropertyAssetBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/viewmodel/runtime/viewmodel_instance_asset_font_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_instance_asset_font_runtime.hpp new file mode 100644 index 0000000..962d0a7 --- /dev/null +++ b/include/rive/viewmodel/runtime/viewmodel_instance_asset_font_runtime.hpp
@@ -0,0 +1,28 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_RUNTIME_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_RUNTIME_HPP_ + +#include <string> +#include <stdint.h> +#include "rive/viewmodel/runtime/viewmodel_instance_value_runtime.hpp" +#include "rive/viewmodel/viewmodel_instance_asset_font.hpp" + +namespace rive +{ + +class ViewModelInstanceAssetFontRuntime : public ViewModelInstanceValueRuntime +{ + +public: + ViewModelInstanceAssetFontRuntime( + ViewModelInstanceAssetFont* viewModelInstance) : + ViewModelInstanceValueRuntime(viewModelInstance) + {} + void value(Font* font); + const DataType dataType() override { return DataType::assetFont; } + +#ifdef TESTING + Font* testing_value(); +#endif +}; +} // namespace rive +#endif
diff --git a/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp b/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp index 65ced02..3fe49c6 100644 --- a/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp +++ b/include/rive/viewmodel/runtime/viewmodel_instance_runtime.hpp
@@ -15,6 +15,7 @@ #include "rive/viewmodel/runtime/viewmodel_instance_trigger_runtime.hpp" #include "rive/viewmodel/runtime/viewmodel_instance_list_runtime.hpp" #include "rive/viewmodel/runtime/viewmodel_instance_asset_image_runtime.hpp" +#include "rive/viewmodel/runtime/viewmodel_instance_asset_font_runtime.hpp" #include "rive/viewmodel/runtime/viewmodel_instance_artboard_runtime.hpp" #include "rive/viewmodel/runtime/viewmodel_instance_list_index_runtime.hpp" #include "rive/refcnt.hpp" @@ -51,6 +52,8 @@ const std::string& path) const; ViewModelInstanceAssetImageRuntime* propertyImage( const std::string& path) const; + ViewModelInstanceAssetFontRuntime* propertyFont( + const std::string& path) const; ViewModelInstanceArtboardRuntime* propertyArtboard( const std::string& path) const; bool replaceViewModel(const std::string& path,
diff --git a/include/rive/viewmodel/viewmodel_instance_asset_font.hpp b/include/rive/viewmodel/viewmodel_instance_asset_font.hpp new file mode 100644 index 0000000..78b5888 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_asset_font.hpp
@@ -0,0 +1,28 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_ASSET_FONT_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_asset_font_base.hpp" +#include "rive/text_engine.hpp" +#include "rive/refcnt.hpp" +#include "rive/data_bind/data_values/data_value_integer.hpp" +#include "rive/assets/font_asset.hpp" +#include <stdio.h> +namespace rive +{ +class ViewModelInstanceAssetFont : public ViewModelInstanceAssetFontBase +{ +protected: + void propertyValueChanged() override; + +public: + ViewModelInstanceAssetFont(); + void value(Font* font); + rcp<FontAsset> asset() { return m_fontAsset; } + Core* clone() const override; + void applyValue(DataValueInteger*); + +private: + rcp<FontAsset> m_fontAsset = nullptr; +}; +} // namespace rive + +#endif
diff --git a/include/rive/viewmodel/viewmodel_property_asset_font.hpp b/include/rive/viewmodel/viewmodel_property_asset_font.hpp new file mode 100644 index 0000000..18f9840 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_asset_font.hpp
@@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_ASSET_FONT_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_ASSET_FONT_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_asset_font_base.hpp" +#include <stdio.h> +namespace rive +{ +class ViewModelPropertyAssetFont : public ViewModelPropertyAssetFontBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/src/data_bind/context/context_target_value.cpp b/src/data_bind/context/context_target_value.cpp index 1230a97..17d092c 100644 --- a/src/data_bind/context/context_target_value.cpp +++ b/src/data_bind/context/context_target_value.cpp
@@ -2,6 +2,8 @@ #include "rive/data_bind/data_bind.hpp" #include "rive/data_bind/data_values/data_value.hpp" #include "rive/data_bind/data_values/data_value_asset_image.hpp" +#include "rive/data_bind/data_values/data_value_asset_font.hpp" +#include "rive/data_bind/bindable_property_asset.hpp" #include "rive/data_bind/data_values/data_value_boolean.hpp" #include "rive/data_bind/data_values/data_value_color.hpp" #include "rive/data_bind/data_values/data_value_integer.hpp" @@ -62,6 +64,12 @@ } else if (dataBind->source() != nullptr && dataBind->source()->coreType() == + ViewModelInstanceAssetFontBase::typeKey) + { + m_targetValue = new DataValueAssetFont(); + } + else if (dataBind->source() != nullptr && + dataBind->source()->coreType() == ViewModelInstanceViewModelBase::typeKey) { m_targetValue = new DataValueViewModel(); @@ -155,22 +163,39 @@ else if (dataBind->target()->coreType() == BindablePropertyAssetBase::typeKey) { + auto bindableAsset = + dataBind->target()->as<BindablePropertyAsset>(); auto value = CoreRegistry::getUint(dataBind->target(), dataBind->propertyKey()); - auto fileAsset = dataBind->target() - ->as<BindablePropertyAsset>() - ->fileAsset(); bool didChange = false; if (updateValue<DataValueInteger, int>(value)) { didChange = true; } - if (fileAsset->renderImage() != - m_targetValue->as<DataValueAssetImage>()->imageValue()) + // BindablePropertyAsset carries either a live image or a live + // font; sync whichever matches the target value's kind (the id + // above is always synced). + if (m_targetValue->is<DataValueAssetImage>()) { - m_targetValue->as<DataValueAssetImage>()->imageValue( - fileAsset->renderImage()); - didChange = true; + auto image = bindableAsset->fileAsset()->renderImage(); + if (image != + m_targetValue->as<DataValueAssetImage>()->imageValue()) + { + m_targetValue->as<DataValueAssetImage>()->imageValue( + image); + didChange = true; + } + } + else if (m_targetValue->is<DataValueAssetFont>()) + { + auto font = bindableAsset->fontValue(); + if (font != + m_targetValue->as<DataValueAssetFont>()->fontValue()) + { + m_targetValue->as<DataValueAssetFont>()->fontValue( + font); + didChange = true; + } } return didChange; }
diff --git a/src/data_bind/context/context_value.cpp b/src/data_bind/context/context_value.cpp index 3221065..6ec2b16 100644 --- a/src/data_bind/context/context_value.cpp +++ b/src/data_bind/context/context_value.cpp
@@ -11,6 +11,7 @@ #include "rive/data_bind/data_values/data_value_list.hpp" #include "rive/data_bind/data_values/data_value_symbol_list_index.hpp" #include "rive/data_bind/data_values/data_value_asset_image.hpp" +#include "rive/data_bind/data_values/data_value_asset_font.hpp" #include "rive/data_bind/data_values/data_value_artboard.hpp" #include "rive/data_bind/data_values/data_value_viewmodel.hpp" #include "rive/generated/core_registry.hpp" @@ -81,6 +82,10 @@ m_dataValue = new DataValueAssetImage( source->as<ViewModelInstanceAssetImage>()->propertyValue()); break; + case ViewModelInstanceAssetFontBase::typeKey: + m_dataValue = new DataValueAssetFont( + source->as<ViewModelInstanceAssetFont>()->propertyValue()); + break; case ViewModelInstanceArtboardBase::typeKey: m_dataValue = new DataValueArtboard( source->as<ViewModelInstanceArtboard>()->propertyValue()); @@ -145,6 +150,10 @@ m_dataValue->as<DataValueAssetImage>()->value( source->as<ViewModelInstanceAssetImage>()->propertyValue()); break; + case ViewModelInstanceAssetFontBase::typeKey: + m_dataValue->as<DataValueAssetFont>()->value( + source->as<ViewModelInstanceAssetFont>()->propertyValue()); + break; case ViewModelInstanceArtboardBase::typeKey: m_dataValue->as<DataValueArtboard>()->value( source->as<ViewModelInstanceArtboard>()->propertyValue()); @@ -232,6 +241,14 @@ dataBind); } break; + case ViewModelInstanceAssetFontBase::typeKey: + { + calculateValueAndApply<DataValueInteger, + uint32_t, + ViewModelInstanceAssetFont>(isMainDirection, + dataBind); + } + break; case ViewModelInstanceArtboardBase::typeKey: { calculateValueAndApply<DataValueInteger,
diff --git a/src/data_bind/context/context_value_asset_font.cpp b/src/data_bind/context/context_value_asset_font.cpp new file mode 100644 index 0000000..830ea09 --- /dev/null +++ b/src/data_bind/context/context_value_asset_font.cpp
@@ -0,0 +1,70 @@ +#include "rive/data_bind/context/context_value_asset_font.hpp" +#include "rive/data_bind/data_values/data_value_asset_font.hpp" +#include "rive/data_bind/bindable_property_asset.hpp" +#include "rive/generated/core_registry.hpp" +#include "rive/text/text_style.hpp" +#include "rive/file.hpp" + +using namespace rive; + +DataBindContextValueAssetFont::DataBindContextValueAssetFont( + DataBind* dataBind) : + DataBindContextValue(dataBind) +{} + +rcp<FontAsset> DataBindContextValueAssetFont::fileAsset(DataBind* dataBind) +{ + auto file = dataBind->file(); + auto source = dataBind->source(); + if (file != nullptr && source != nullptr && + source->is<ViewModelInstanceAssetFont>()) + { + + auto asset = file->asset( + source->as<ViewModelInstanceAssetFont>()->propertyValue()); + if (asset != nullptr && asset->is<FontAsset>()) + { + return static_rcp_cast<FontAsset>(asset); + } + } + return nullptr; +} + +void DataBindContextValueAssetFont::apply(Core* target, + uint32_t propertyKey, + bool isMainDirection, + DataBind* dataBind) +{ + if (target->is<TextStyle>()) + { + auto asset = fileAsset(dataBind); + if (asset != nullptr) + { + target->as<TextStyle>()->setAsset(asset); + } + else + { + auto source = dataBind->source(); + target->as<TextStyle>()->setAsset( + source->as<ViewModelInstanceAssetFont>()->asset()); + } + } + else if (target->is<BindablePropertyAsset>()) + { + auto source = dataBind->source(); + target->as<BindablePropertyAsset>()->fontValue( + source->as<ViewModelInstanceAssetFont>()->asset()->font().get()); + CoreRegistry::setUint( + target, + propertyKey, + source->as<ViewModelInstanceAssetFont>()->propertyValue()); + } + else + { + auto source = dataBind->source(); + CoreRegistry::setUint( + target, + propertyKey, + source->as<ViewModelInstanceAssetFont>()->propertyValue()); + } +}
diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp index 9f8a32c..0e1a76a 100644 --- a/src/data_bind/data_bind.cpp +++ b/src/data_bind/data_bind.cpp
@@ -17,6 +17,7 @@ #include "rive/data_bind/context/context_value.hpp" #include "rive/data_bind/context/context_value_any.hpp" #include "rive/data_bind/context/context_value_asset_image.hpp" +#include "rive/data_bind/context/context_value_asset_font.hpp" #include "rive/data_bind/context/context_value_artboard.hpp" #include "rive/data_bind/context/context_value_boolean.hpp" #include "rive/data_bind/context/context_value_number.hpp" @@ -195,6 +196,8 @@ return DataType::symbolListIndex; case ViewModelInstanceAssetImageBase::typeKey: return DataType::assetImage; + case ViewModelInstanceAssetFontBase::typeKey: + return DataType::assetFont; case ViewModelInstanceArtboardBase::typeKey: return DataType::artboard; case ViewModelInstanceViewModelBase::typeKey: @@ -278,6 +281,9 @@ case DataType::assetImage: m_ContextValue = new DataBindContextValueAssetImage(this); break; + case DataType::assetFont: + m_ContextValue = new DataBindContextValueAssetFont(this); + break; case DataType::artboard: m_ContextValue = new DataBindContextValueArtboard(this); break;
diff --git a/src/file.cpp b/src/file.cpp index 9daec55..3d66d88 100644 --- a/src/file.cpp +++ b/src/file.cpp
@@ -1177,6 +1177,9 @@ case ViewModelPropertyAssetImageBase::typeKey: viewModelInstanceValue = new ViewModelInstanceAssetImage(); break; + case ViewModelPropertyAssetFontBase::typeKey: + viewModelInstanceValue = new ViewModelInstanceAssetFont(); + break; case ViewModelPropertySymbolListIndexBase::typeKey: viewModelInstanceValue = new ViewModelInstanceSymbolListIndex();
diff --git a/src/generated/viewmodel/viewmodel_instance_asset_font_base.cpp b/src/generated/viewmodel/viewmodel_instance_asset_font_base.cpp new file mode 100644 index 0000000..1d19535 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_asset_font_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_asset_font_base.hpp" +#include "rive/viewmodel/viewmodel_instance_asset_font.hpp" + +using namespace rive; + +Core* ViewModelInstanceAssetFontBase::clone() const +{ + auto cloned = new ViewModelInstanceAssetFont(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/viewmodel/viewmodel_property_asset_font_base.cpp b/src/generated/viewmodel/viewmodel_property_asset_font_base.cpp new file mode 100644 index 0000000..e12b2cf --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_asset_font_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_asset_font_base.hpp" +#include "rive/viewmodel/viewmodel_property_asset_font.hpp" + +using namespace rive; + +Core* ViewModelPropertyAssetFontBase::clone() const +{ + auto cloned = new ViewModelPropertyAssetFont(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/text/text_style.cpp b/src/text/text_style.cpp index 4d6f4e4..9f07621 100644 --- a/src/text/text_style.cpp +++ b/src/text/text_style.cpp
@@ -139,9 +139,17 @@ void TextStyle::setAsset(rcp<FileAsset> asset) { - if (asset->is<FontAsset>()) + if (asset != nullptr && asset->is<FontAsset>()) { FileAssetReferencer::setAsset(asset); + // Changing the referenced font at runtime (e.g. via data binding) must + // reshape the text. Mirror FontAsset::font()'s dirtying so onDirty + // marks the text shape dirty and rebuilds any variable font. Guard for + // the clone/import case where the style isn't wired to its Text yet. + if (m_text != nullptr) + { + addDirt(ComponentDirt::TextShape); + } } }
diff --git a/src/viewmodel/runtime/viewmodel_instance_asset_font_runtime.cpp b/src/viewmodel/runtime/viewmodel_instance_asset_font_runtime.cpp new file mode 100644 index 0000000..8c85b68 --- /dev/null +++ b/src/viewmodel/runtime/viewmodel_instance_asset_font_runtime.cpp
@@ -0,0 +1,20 @@ + +#include "rive/viewmodel/runtime/viewmodel_instance_asset_font_runtime.hpp" + +// Default namespace for Rive Cpp code +using namespace rive; + +void ViewModelInstanceAssetFontRuntime::value(Font* font) +{ + m_viewModelInstanceValue->as<ViewModelInstanceAssetFont>()->value(font); +} + +#ifdef TESTING +Font* ViewModelInstanceAssetFontRuntime::testing_value() +{ + return m_viewModelInstanceValue->as<ViewModelInstanceAssetFont>() + ->asset() + ->font() + .get(); +} +#endif
diff --git a/src/viewmodel/runtime/viewmodel_instance_runtime.cpp b/src/viewmodel/runtime/viewmodel_instance_runtime.cpp index 62c01d6..01e58b7 100644 --- a/src/viewmodel/runtime/viewmodel_instance_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_instance_runtime.cpp
@@ -117,6 +117,9 @@ case DataType::assetImage: return viewModelInstanceRuntime->propertyImage( propertyName); + case DataType::assetFont: + return viewModelInstanceRuntime->propertyFont( + propertyName); case DataType::artboard: return viewModelInstanceRuntime->propertyArtboard( propertyName); @@ -350,6 +353,22 @@ return nullptr; } +ViewModelInstanceAssetFontRuntime* ViewModelInstanceRuntime::propertyFont( + const std::string& path) const +{ + const auto propertyName = getPropertyNameFromPath(path); + auto viewModelInstance = viewModelInstanceFromFullPath(path); + if (viewModelInstance != nullptr) + { + + return viewModelInstance + ->getPropertyInstance<ViewModelInstanceAssetFont, + ViewModelInstanceAssetFontRuntime>( + propertyName); + } + return nullptr; +} + ViewModelInstanceArtboardRuntime* ViewModelInstanceRuntime::propertyArtboard( const std::string& path) const {
diff --git a/src/viewmodel/runtime/viewmodel_runtime.cpp b/src/viewmodel/runtime/viewmodel_runtime.cpp index d92500f..f111ae0 100644 --- a/src/viewmodel/runtime/viewmodel_runtime.cpp +++ b/src/viewmodel/runtime/viewmodel_runtime.cpp
@@ -2,6 +2,7 @@ #include "rive/viewmodel/runtime/viewmodel_runtime.hpp" #include "rive/viewmodel/viewmodel.hpp" #include "rive/viewmodel/viewmodel_property_asset_image.hpp" +#include "rive/viewmodel/viewmodel_property_asset_font.hpp" #include "rive/viewmodel/viewmodel_property_string.hpp" #include "rive/viewmodel/viewmodel_property_number.hpp" #include "rive/viewmodel/viewmodel_property_boolean.hpp" @@ -89,6 +90,9 @@ case ViewModelPropertyAssetImage::typeKey: type = DataType::assetImage; break; + case ViewModelPropertyAssetFont::typeKey: + type = DataType::assetFont; + break; case ViewModelPropertyArtboard::typeKey: type = DataType::artboard; break;
diff --git a/src/viewmodel/viewmodel_instance_asset_font.cpp b/src/viewmodel/viewmodel_instance_asset_font.cpp new file mode 100644 index 0000000..d53f9c5 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_asset_font.cpp
@@ -0,0 +1,87 @@ +#include <sstream> +#include <iomanip> +#include <array> + +#include "rive/viewmodel/viewmodel_instance_asset_font.hpp" +#include "rive/component_dirt.hpp" +#include "rive/refcnt.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include "rive/data_bind/data_values/data_value_asset_font.hpp" + +using namespace rive; + +ViewModelInstanceAssetFont::ViewModelInstanceAssetFont() : + m_fontAsset(rcp<FontAsset>(new FontAsset())) +{} + +void ViewModelInstanceAssetFont::propertyValueChanged() +{ + addDirt(ComponentDirt::Bindings); +#ifdef WITH_RIVE_TOOLS + if (m_changedCallback != nullptr) + { + m_changedCallback(this, propertyValue()); + } +#endif + onValueChanged(); +} + +void ViewModelInstanceAssetFont::value(Font* font) +{ + if (m_fontAsset->font().get() == font) + { + propertyValue(-1); + return; + } +#ifdef WITH_RIVE_TOOLS + const bool alreadySentinel = (propertyValue() == static_cast<uint32_t>(-1)); +#endif + if (font == nullptr) + { + m_fontAsset->font(nullptr); + } + else + { + font->ref(); + m_fontAsset->font(rcp<Font>(font)); + } +#ifdef WITH_RIVE_TOOLS + if (!alreadySentinel) + { + propertyValue(-1); + } + else if (m_changedCallback != nullptr) + { + m_changedCallback(this, propertyValue()); + } +#else + propertyValue(-1); +#endif + addDirt(ComponentDirt::Bindings); + onValueChanged(); +} + +void ViewModelInstanceAssetFont::applyValue(DataValueInteger* dataValue) +{ + if (dataValue && dataValue->is<DataValueAssetFont>()) + { + auto font = dataValue->as<DataValueAssetFont>()->fontValue(); + value(font); + if (font) + { + return; + } + } + propertyValue(dataValue->value()); +} + +Core* ViewModelInstanceAssetFont::clone() const +{ + auto cloned = new ViewModelInstanceAssetFont(); + cloned->copy(*this); + for (const auto& asset : assets()) + { + cloned->addAsset(asset); + } + return cloned; +}
diff --git a/tests/unit_tests/assets/data_bind_font_test.riv b/tests/unit_tests/assets/data_bind_font_test.riv new file mode 100644 index 0000000..60f8c19 --- /dev/null +++ b/tests/unit_tests/assets/data_bind_font_test.riv Binary files differ
diff --git a/tests/unit_tests/assets/kablammo.ttf b/tests/unit_tests/assets/kablammo.ttf new file mode 100644 index 0000000..4f6604e --- /dev/null +++ b/tests/unit_tests/assets/kablammo.ttf Binary files differ
diff --git a/tests/unit_tests/assets/nabla.ttf b/tests/unit_tests/assets/nabla.ttf new file mode 100644 index 0000000..9bdc205 --- /dev/null +++ b/tests/unit_tests/assets/nabla.ttf Binary files differ
diff --git a/tests/unit_tests/runtime/data_binding_fonts_test.cpp b/tests/unit_tests/runtime/data_binding_fonts_test.cpp new file mode 100644 index 0000000..6821592 --- /dev/null +++ b/tests/unit_tests/runtime/data_binding_fonts_test.cpp
@@ -0,0 +1,119 @@ +#include <rive/file.hpp> +#include <rive/node.hpp> +#include <utils/no_op_renderer.hpp> +#include <rive/viewmodel/viewmodel_instance_asset_font.hpp> +#include <rive/viewmodel/viewmodel_instance_viewmodel.hpp> +#include "rive/animation/state_machine_instance.hpp" +#include "rive/assets/font_asset.hpp" +#include "rive/nested_artboard.hpp" +#include "rive/text/font_hb.hpp" +#include "rive_file_reader.hpp" +#include "utils/serializing_factory.hpp" +#include <catch.hpp> +#include <cstdio> +#include <cstring> + +using namespace rive; + +TEST_CASE("Data bind font", "[silver]") +{ + SerializingFactory silver; + auto file = ReadRiveFile("assets/data_bind_font_test.riv", &silver); + + auto artboard = file->artboardDefault(); + REQUIRE(artboard != nullptr); + + silver.frameSize(artboard->width(), artboard->height()); + + auto stateMachine = artboard->stateMachineAt(0); + REQUIRE(stateMachine != nullptr); + + auto vmi = file->createDefaultViewModelInstance(artboard.get()); + REQUIRE(vmi != nullptr); + auto renderer = silver.makeRenderer(); + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.0f); + artboard->draw(renderer.get()); + silver.addFrame(); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + + silver.addFrame(); + + // Load the kablammo ttf through the file's factory and set it as the value + // of the bound ViewModelInstanceAssetFont. This should reshape the text + // with the new font on the next advance/draw. + auto fontBytes = ReadFile("assets/kablammo.ttf"); + auto font = silver.decodeFont(fontBytes); + REQUIRE(font != nullptr); + + auto fontProperty = vmi->propertyValue("fontProperty"); + REQUIRE(fontProperty != nullptr); + REQUIRE(fontProperty->is<rive::ViewModelInstanceAssetFont>()); + fontProperty->as<rive::ViewModelInstanceAssetFont>()->value(font.get()); + + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + silver.addFrame(); + + // Click in a square that fires a listener + // that sets the font to another font value. + stateMachine->pointerDown(rive::Vec2D(490, 490)); + stateMachine->pointerUp(rive::Vec2D(490, 490)); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + + silver.addFrame(); + // Click in a square that fires a listener + // that sets the font to another font property. + stateMachine->pointerDown(rive::Vec2D(490, 20)); + stateMachine->pointerUp(rive::Vec2D(490, 20)); + stateMachine->advanceAndApply(0.016f); + artboard->draw(renderer.get()); + CHECK(silver.matches("data_bind_font_test")); +} + +// Deterministic (no golden) coverage of the ViewModelInstanceAssetFont value +// API: assigning a decoded font stores it on the property's backing FontAsset, +// and passing null clears it. +TEST_CASE("Font data bind stores and clears the font on the property", + "[data binding]") +{ + auto file = ReadRiveFile("assets/data_bind_font_test.riv"); + + auto artboard = file->artboardDefault(); + REQUIRE(artboard != nullptr); + auto stateMachine = artboard->stateMachineAt(0); + REQUIRE(stateMachine != nullptr); + auto vmi = file->createDefaultViewModelInstance(artboard.get()); + REQUIRE(vmi != nullptr); + stateMachine->bindViewModelInstance(vmi); + stateMachine->advanceAndApply(0.0f); + + auto property = vmi->propertyValue("fontProperty"); + REQUIRE(property != nullptr); + REQUIRE(property->is<rive::ViewModelInstanceAssetFont>()); + auto fontProperty = property->as<rive::ViewModelInstanceAssetFont>(); + REQUIRE(fontProperty->asset() != nullptr); + + // Assigning a decoded font stores it on the property's backing FontAsset. + auto fontBytes = ReadFile("assets/kablammo.ttf"); + auto font = HBFont::Decode(fontBytes); + REQUIRE(font != nullptr); + fontProperty->value(font.get()); + stateMachine->advanceAndApply(0.0f); + CHECK(fontProperty->asset()->font().get() == font.get()); + + // Swapping to a different font updates the backing FontAsset. + auto font2Bytes = ReadFile("assets/nabla.ttf"); + auto font2 = HBFont::Decode(font2Bytes); + REQUIRE(font2 != nullptr); + fontProperty->value(font2.get()); + stateMachine->advanceAndApply(0.0f); + CHECK(fontProperty->asset()->font().get() == font2.get()); + + // Passing null clears the backing font. + fontProperty->value(nullptr); + stateMachine->advanceAndApply(0.0f); + CHECK(fontProperty->asset()->font() == nullptr); +} \ No newline at end of file
diff --git a/tests/unit_tests/silvers/data_bind_font_test.sriv b/tests/unit_tests/silvers/data_bind_font_test.sriv new file mode 100644 index 0000000..e4c1d58 --- /dev/null +++ b/tests/unit_tests/silvers/data_bind_font_test.sriv Binary files differ