Runtime Text! Not ready for review! But feel free to peek :) Starting to add support for runtime text. This is the bulk of the implementation marrying the Core objects to the text engine we've been working on at the runtime level. PRing in draft early to let tests run. Also refactors some class and file names to disambiguate more clearly with the Core objects. Diffs= 051769242 Runtime Text! (#4741)
diff --git a/.rive_head b/.rive_head index f7b09e1..141837b 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -331ad0d55068e5059f3e9c85af5382836e278e4e +051769242f3e6b4d9cde3e1a71e0cb4ac04ecb93
diff --git a/dev/defs/animation/animation.json b/dev/defs/animation/animation.json index e3bdf9e..0f4e3d0 100644 --- a/dev/defs/animation/animation.json +++ b/dev/defs/animation/animation.json
@@ -33,6 +33,16 @@ }, "description": "Order this animation shows up in the animations list.", "runtime": false + }, + "folderId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 282, + "string": "folderid" + }, + "description": "Id of the folder this animation belongs to", + "runtime": false } } } \ No newline at end of file
diff --git a/dev/defs/animation/animation_folder.json b/dev/defs/animation/animation_folder.json new file mode 100644 index 0000000..e04d9d8 --- /dev/null +++ b/dev/defs/animation/animation_folder.json
@@ -0,0 +1,9 @@ +{ + "name": "AnimationFolder", + "key": { + "int": 143, + "string": "animationfolder" + }, + "extends": "animation/animation.json", + "runtime": false +} \ No newline at end of file
diff --git a/dev/defs/animation/keyframe_string.json b/dev/defs/animation/keyframe_string.json new file mode 100644 index 0000000..81fec8b --- /dev/null +++ b/dev/defs/animation/keyframe_string.json
@@ -0,0 +1,19 @@ +{ + "name": "KeyFrameString", + "key": { + "int": 142, + "string": "keyframestring" + }, + "extends": "animation/keyframe.json", + "properties": { + "value": { + "type": "String", + "typeRuntime": "String", + "initialValue": "''", + "key": { + "int": 280, + "string": "value" + } + } + } +} \ No newline at end of file
diff --git a/dev/defs/animation/linear_animation.json b/dev/defs/animation/linear_animation.json index f12b4ea..bdc19fc 100644 --- a/dev/defs/animation/linear_animation.json +++ b/dev/defs/animation/linear_animation.json
@@ -101,6 +101,50 @@ "description": "Viewport end in frames.", "runtime": false, "coop": false + }, + "scrollOffset": { + "type": "double", + "initialValue": "0.0", + "key": { + "int": 256, + "string": "scrolloffset" + }, + "description": "Scroll offset of the key frame view", + "runtime": false, + "coop": false + }, + "expandedComponents": { + "type": "List<Id>", + "initialValue": "[]", + "key": { + "int": 266, + "string": "expandedcomponents" + }, + "description": "List of components that are expanded on the timeline view", + "runtime": false, + "coop": false + }, + "timelineMode": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 275, + "string": "timelinemode" + }, + "description": "Edit time timeline mode", + "runtime": false, + "coop": false + }, + "showSelected": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 278, + "string": "showselected" + }, + "description": "Whether to only show selected components on the timeline hierarchy", + "runtime": false, + "coop": false } } } \ No newline at end of file
diff --git a/dev/defs/animation/state_machine.json b/dev/defs/animation/state_machine.json index c73a1cc..fa97a25 100644 --- a/dev/defs/animation/state_machine.json +++ b/dev/defs/animation/state_machine.json
@@ -16,6 +16,72 @@ "description": "Id of the currently editing layer.", "runtime": false, "coop": false + }, + "isListenersPanelOpen": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 250, + "string": "islistenerspanelopen" + }, + "description": "Toggle determining whether the listeners panel is open", + "runtime": false, + "coop": false + }, + "isInputsPanelOpen": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 251, + "string": "isinputspanelopen" + }, + "description": "Toggle determining whether the inputs panel is open", + "runtime": false, + "coop": false + }, + "isConsolePanelOpen": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 252, + "string": "isconsolepanelopen" + }, + "description": "Toggle determining whether the console panel is open", + "runtime": false, + "coop": false + }, + "listenersPanelSize": { + "type": "double", + "initialValue": "200", + "key": { + "int": 253, + "string": "listenerspanelsize" + }, + "description": "Size of the listeners panel", + "runtime": false, + "coop": false + }, + "inputsPanelSize": { + "type": "double", + "initialValue": "200", + "key": { + "int": 254, + "string": "inputspanelsize" + }, + "description": "Size of the inputs panel", + "runtime": false, + "coop": false + }, + "consolePanelSize": { + "type": "double", + "initialValue": "200", + "key": { + "int": 255, + "string": "consolepanelsize" + }, + "description": "Size of the inputs panel", + "runtime": false, + "coop": false } } } \ No newline at end of file
diff --git a/dev/defs/artboard.json b/dev/defs/artboard.json index 935d8d0..53158b7 100644 --- a/dev/defs/artboard.json +++ b/dev/defs/artboard.json
@@ -90,6 +90,39 @@ "string": "defaultStateMachineId" }, "description": "The default StateMachine attached to this artboard automatically when it is initialized." + }, + "animationsScrollOffset": { + "type": "double", + "initialValue": "0.0", + "key": { + "int": 257, + "string": "animationsscrolloffset" + }, + "description": "Scroll offset of the animation list", + "runtime": false, + "coop": false + }, + "expandedComponents": { + "type": "List<Id>", + "initialValue": "[]", + "key": { + "int": 267, + "string": "expandedcomponents" + }, + "description": "List of expanded components", + "runtime": false, + "coop": false + }, + "expandedAnimationFolders": { + "type": "List<Id>", + "initialValue": "[]", + "key": { + "int": 283, + "string": "expandedanimationfolders" + }, + "description": "List of expanded animation folders", + "runtime": false, + "coop": false } } } \ No newline at end of file
diff --git a/dev/defs/assets/font_asset.json b/dev/defs/assets/font_asset.json new file mode 100644 index 0000000..cd39ee8 --- /dev/null +++ b/dev/defs/assets/font_asset.json
@@ -0,0 +1,8 @@ +{ + "name": "FontAsset", + "key": { + "int": 141, + "string": "fontasset" + }, + "extends": "assets/file_asset.json" +} \ No newline at end of file
diff --git a/dev/defs/assets/image_asset.json b/dev/defs/assets/image_asset.json index 6b60274..eab997e 100644 --- a/dev/defs/assets/image_asset.json +++ b/dev/defs/assets/image_asset.json
@@ -4,5 +4,27 @@ "int": 105, "string": "imageasset" }, - "extends": "assets/drawable_asset.json" + "extends": "assets/drawable_asset.json", + "properties": { + "format": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 241, + "string": "format" + }, + "description": "Image format we want to use for this assets's mutations", + "runtime": false + }, + "quality": { + "type": "double", + "initialValue": "0.75", + "key": { + "int": 242, + "string": "quality" + }, + "description": "Quality percentage for this image mutation", + "runtime": false + } + } } \ No newline at end of file
diff --git a/dev/defs/assets/layer_image_asset.json b/dev/defs/assets/layer_image_asset.json index 0c42783..055c8cc 100644 --- a/dev/defs/assets/layer_image_asset.json +++ b/dev/defs/assets/layer_image_asset.json
@@ -14,7 +14,8 @@ "int": 233, "string": "layer" }, - "description": "Layer ID as it is analysed by the backend" + "description": "Layer ID as it is analysed by the backend", + "runtime": false }, "x": { "type": "double", @@ -23,7 +24,8 @@ "int": 234, "string": "x" }, - "description": "x offset for this layer" + "description": "x offset for this layer", + "runtime": false }, "y": { "type": "double", @@ -32,7 +34,8 @@ "int": 235, "string": "y" }, - "description": "y offset for this layer" + "description": "y offset for this layer", + "runtime": false } } } \ No newline at end of file
diff --git a/dev/defs/assets/lottie_asset.json b/dev/defs/assets/lottie_asset.json new file mode 100644 index 0000000..ade6cb1 --- /dev/null +++ b/dev/defs/assets/lottie_asset.json
@@ -0,0 +1,9 @@ +{ + "name": "LottieAsset", + "key": { + "int": 133, + "string": "lottieasset" + }, + "extends": "assets/drawable_asset.json", + "runtime": false +} \ No newline at end of file
diff --git a/dev/defs/assets/svg_asset.json b/dev/defs/assets/svg_asset.json new file mode 100644 index 0000000..47489b6 --- /dev/null +++ b/dev/defs/assets/svg_asset.json
@@ -0,0 +1,9 @@ +{ + "name": "SVGAsset", + "key": { + "int": 132, + "string": "svgasset" + }, + "extends": "assets/drawable_asset.json", + "runtime": false +} \ No newline at end of file
diff --git a/dev/defs/backboard.json b/dev/defs/backboard.json index 27b085c..7d709df 100644 --- a/dev/defs/backboard.json +++ b/dev/defs/backboard.json
@@ -46,6 +46,105 @@ "description": "Edit time mode.", "runtime": false, "coop": false + }, + "treeMode": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 247, + "string": "treemode" + }, + "description": "Edit time hierarchy/assets/events mode.", + "runtime": false, + "coop": false + }, + "animationPanelSize": { + "type": "double", + "initialValue": "300.0", + "key": { + "int": 258, + "string": "animationpanelsize" + }, + "description": "Size of the animation panel", + "runtime": false, + "coop": false + }, + "hierarchyPanelSize": { + "type": "double", + "initialValue": "300.0", + "key": { + "int": 259, + "string": "hierarchypanelsize" + }, + "description": "Size of the Hierarchy panel", + "runtime": false, + "coop": false + }, + "stageX": { + "type": "double", + "initialValue": "0", + "key": { + "int": 260, + "string": "stagex" + }, + "description": "X translation value of the stage in the editor.", + "runtime": false, + "coop": false + }, + "stageY": { + "type": "double", + "initialValue": "0", + "key": { + "int": 261, + "string": "stagey" + }, + "description": "Y translation value of the stage in the editor.", + "runtime": false, + "coop": false + }, + "stageZoom": { + "type": "double", + "initialValue": "0", + "key": { + "int": 262, + "string": "stagezoom" + }, + "description": "Zoom value of the stage in the editor. Initial value is 0 to indicate it is not set.", + "runtime": false, + "coop": false + }, + "rulersEnabled": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 263, + "string": "rulersenabled" + }, + "description": "If rulers are enabled for this file", + "runtime": false, + "coop": false + }, + "snappingEnabled": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 264, + "string": "snappingenabled" + }, + "description": "If snapping are enabled for this file", + "runtime": false, + "coop": false + }, + "bonesEnabled": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 265, + "string": "bonesenabled" + }, + "description": "If bones are enabled for this file", + "runtime": false, + "coop": false } } } \ No newline at end of file
diff --git a/dev/defs/text/text.json b/dev/defs/text/text.json new file mode 100644 index 0000000..d7cb62d --- /dev/null +++ b/dev/defs/text/text.json
@@ -0,0 +1,55 @@ +{ + "name": "Text", + "key": { + "int": 134, + "string": "text" + }, + "extends": "drawable.json", + "properties": { + "alignValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 281, + "string": "alignvalue" + } + }, + "sizingValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 284, + "string": "sizingvalue" + } + }, + "overflowValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 287, + "string": "overflowvalue" + }, + "description": "One of visible, hidden, clipped, ellipsis." + }, + "width": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 285, + "string": "width" + }, + "description": "Width of the text object." + }, + "height": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 286, + "string": "height" + }, + "description": "Height of the text object." + } + } +} \ No newline at end of file
diff --git a/dev/defs/text/text_style.json b/dev/defs/text/text_style.json new file mode 100644 index 0000000..16624fa --- /dev/null +++ b/dev/defs/text/text_style.json
@@ -0,0 +1,29 @@ +{ + "name": "TextStyle", + "key": { + "int": 137, + "string": "textstyle" + }, + "extends": "container_component.json", + "properties": { + "fontSize": { + "type": "double", + "initialValue": "12", + "animates": true, + "key": { + "int": 274, + "string": "fontsize" + } + }, + "fontAssetId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 279, + "string": "fontassetid" + } + } + } +} \ No newline at end of file
diff --git a/dev/defs/text/text_style_axis.json b/dev/defs/text/text_style_axis.json new file mode 100644 index 0000000..23524e9 --- /dev/null +++ b/dev/defs/text/text_style_axis.json
@@ -0,0 +1,27 @@ +{ + "name": "TextStyleAxis", + "key": { + "int": 144, + "string": "textStyleAxis" + }, + "extends": "component.json", + "properties": { + "tag": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 289, + "string": "tag" + } + }, + "axisValue": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 288, + "string": "axisValue" + } + } + } +} \ No newline at end of file
diff --git a/dev/defs/text/text_value_run.json b/dev/defs/text/text_value_run.json new file mode 100644 index 0000000..be4ee8c --- /dev/null +++ b/dev/defs/text/text_value_run.json
@@ -0,0 +1,31 @@ +{ + "name": "TextValueRun", + "key": { + "int": 135, + "string": "textvaluerun" + }, + "extends": "component.json", + "properties": { + "styleId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 272, + "string": "styleid" + }, + "description": "The id of the style to be applied to this run." + }, + "text": { + "type": "String", + "initialValue": "''", + "animates": true, + "key": { + "int": 268, + "string": "text_value" + }, + "description": "The text string value." + } + } +} \ No newline at end of file
diff --git a/include/rive/animation/keyframe_bool.hpp b/include/rive/animation/keyframe_bool.hpp index 88a234f..226fd87 100644 --- a/include/rive/animation/keyframe_bool.hpp +++ b/include/rive/animation/keyframe_bool.hpp
@@ -1,7 +1,7 @@ #ifndef _RIVE_KEY_FRAME_BOOL_HPP_ #define _RIVE_KEY_FRAME_BOOL_HPP_ #include "rive/generated/animation/keyframe_bool_base.hpp" -#include <stdio.h> + namespace rive { class KeyFrameBool : public KeyFrameBoolBase
diff --git a/include/rive/animation/keyframe_id.hpp b/include/rive/animation/keyframe_id.hpp index cf706a1..5182c6e 100644 --- a/include/rive/animation/keyframe_id.hpp +++ b/include/rive/animation/keyframe_id.hpp
@@ -1,7 +1,7 @@ #ifndef _RIVE_KEY_FRAME_ID_HPP_ #define _RIVE_KEY_FRAME_ID_HPP_ #include "rive/generated/animation/keyframe_id_base.hpp" -#include <stdio.h> + namespace rive { class KeyFrameId : public KeyFrameIdBase
diff --git a/include/rive/animation/keyframe_string.hpp b/include/rive/animation/keyframe_string.hpp new file mode 100644 index 0000000..4749dc2 --- /dev/null +++ b/include/rive/animation/keyframe_string.hpp
@@ -0,0 +1,19 @@ +#ifndef _RIVE_KEY_FRAME_STRING_HPP_ +#define _RIVE_KEY_FRAME_STRING_HPP_ +#include "rive/generated/animation/keyframe_string_base.hpp" + +namespace rive +{ +class KeyFrameString : public KeyFrameStringBase +{ +public: + void apply(Core* object, int propertyKey, float mix) override; + void applyInterpolation(Core* object, + int propertyKey, + float seconds, + const KeyFrame* nextFrame, + float mix) override; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 937a286..7fe73eb 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -124,6 +124,19 @@ return nullptr; } + template <typename T = Component> std::vector<T*> find() + { + std::vector<T*> results; + for (auto object : m_Objects) + { + if (object != nullptr && object->is<T>()) + { + results.push_back(static_cast<T*>(object)); + } + } + return results; + } + size_t animationCount() const { return m_Animations.size(); } std::string animationNameAt(size_t index) const;
diff --git a/include/rive/assets/file_asset_referencer.hpp b/include/rive/assets/file_asset_referencer.hpp index fa0be00..df300e7 100644 --- a/include/rive/assets/file_asset_referencer.hpp +++ b/include/rive/assets/file_asset_referencer.hpp
@@ -1,6 +1,9 @@ #ifndef _RIVE_FILE_ASSET_REFERENCER_HPP_ #define _RIVE_FILE_ASSET_REFERENCER_HPP_ +#include <vector> +#include "rive/importers/import_stack.hpp" + namespace rive { class FileAsset; @@ -9,6 +12,7 @@ public: virtual ~FileAssetReferencer() {} virtual void assets(const std::vector<FileAsset*>& assets) = 0; + StatusCode registerReferencer(ImportStack& importStack); }; } // namespace rive
diff --git a/include/rive/assets/font_asset.hpp b/include/rive/assets/font_asset.hpp new file mode 100644 index 0000000..effffc3 --- /dev/null +++ b/include/rive/assets/font_asset.hpp
@@ -0,0 +1,22 @@ +#ifndef _RIVE_FONT_ASSET_HPP_ +#define _RIVE_FONT_ASSET_HPP_ +#include "rive/generated/assets/font_asset_base.hpp" +#include "rive/text_engine.hpp" +#include "rive/refcnt.hpp" + +namespace rive +{ +class FontAsset : public FontAssetBase +{ +public: + bool decode(Span<const uint8_t>, Factory*) override; + std::string fileExtension() override; + const rcp<Font> font() const { return m_font; } + void font(rcp<Font> font); + +private: + rcp<Font> m_font; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/factory.hpp b/include/rive/factory.hpp index 6d69bad..354d95f 100644 --- a/include/rive/factory.hpp +++ b/include/rive/factory.hpp
@@ -6,7 +6,7 @@ #define _RIVE_FACTORY_HPP_ #include "rive/renderer.hpp" -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #include "rive/refcnt.hpp" #include "rive/span.hpp" #include "rive/math/aabb.hpp" @@ -58,7 +58,7 @@ virtual std::unique_ptr<RenderImage> decodeImage(Span<const uint8_t>) = 0; - virtual rcp<Font> decodeFont(Span<const uint8_t>) { return nullptr; } + virtual rcp<Font> decodeFont(Span<const uint8_t>); // Non-virtual helpers
diff --git a/include/rive/generated/animation/keyframe_string_base.hpp b/include/rive/generated/animation/keyframe_string_base.hpp new file mode 100644 index 0000000..cadab51 --- /dev/null +++ b/include/rive/generated/animation/keyframe_string_base.hpp
@@ -0,0 +1,72 @@ +#ifndef _RIVE_KEY_FRAME_STRING_BASE_HPP_ +#define _RIVE_KEY_FRAME_STRING_BASE_HPP_ +#include <string> +#include "rive/animation/keyframe.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class KeyFrameStringBase : public KeyFrame +{ +protected: + typedef KeyFrame Super; + +public: + static const uint16_t typeKey = 142; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case KeyFrameStringBase::typeKey: + case KeyFrameBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 280; + +private: + std::string m_Value = ""; + +public: + inline const std::string& value() const { return m_Value; } + void value(std::string value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const KeyFrameStringBase& object) + { + m_Value = object.m_Value; + KeyFrame::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreStringType::deserialize(reader); + return true; + } + return KeyFrame::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/assets/font_asset_base.hpp b/include/rive/generated/assets/font_asset_base.hpp new file mode 100644 index 0000000..fa9203f --- /dev/null +++ b/include/rive/generated/assets/font_asset_base.hpp
@@ -0,0 +1,37 @@ +#ifndef _RIVE_FONT_ASSET_BASE_HPP_ +#define _RIVE_FONT_ASSET_BASE_HPP_ +#include "rive/assets/file_asset.hpp" +namespace rive +{ +class FontAssetBase : public FileAsset +{ +protected: + typedef FileAsset Super; + +public: + static const uint16_t typeKey = 141; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case FontAssetBase::typeKey: + case FileAssetBase::typeKey: + case AssetBase::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/core_registry.hpp b/include/rive/generated/core_registry.hpp index 5b0cfc7..e8147ec 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -22,6 +22,7 @@ #include "rive/animation/keyframe_color.hpp" #include "rive/animation/keyframe_double.hpp" #include "rive/animation/keyframe_id.hpp" +#include "rive/animation/keyframe_string.hpp" #include "rive/animation/layer_state.hpp" #include "rive/animation/linear_animation.hpp" #include "rive/animation/listener_action.hpp" @@ -59,6 +60,7 @@ #include "rive/assets/file_asset.hpp" #include "rive/assets/file_asset_contents.hpp" #include "rive/assets/folder.hpp" +#include "rive/assets/font_asset.hpp" #include "rive/assets/image_asset.hpp" #include "rive/backboard.hpp" #include "rive/bones/bone.hpp" @@ -116,6 +118,10 @@ #include "rive/shapes/straight_vertex.hpp" #include "rive/shapes/triangle.hpp" #include "rive/shapes/vertex.hpp" +#include "rive/text/text.hpp" +#include "rive/text/text_style.hpp" +#include "rive/text/text_style_axis.hpp" +#include "rive/text/text_value_run.hpp" #include "rive/transform_component.hpp" #include "rive/world_transform_component.hpp" namespace rive @@ -153,6 +159,8 @@ return new NestedTrigger(); case KeyedObjectBase::typeKey: return new KeyedObject(); + case AnimationBase::typeKey: + return new Animation(); case BlendAnimationDirectBase::typeKey: return new BlendAnimationDirect(); case StateMachineNumberBase::typeKey: @@ -179,8 +187,8 @@ return new AnyState(); case StateMachineLayerBase::typeKey: return new StateMachineLayer(); - case AnimationBase::typeKey: - return new Animation(); + case KeyFrameStringBase::typeKey: + return new KeyFrameString(); case ListenerNumberChangeBase::typeKey: return new ListenerNumberChange(); case CubicEaseInterpolatorBase::typeKey: @@ -287,10 +295,20 @@ return new Tendon(); case CubicWeightBase::typeKey: return new CubicWeight(); + case TextStyleBase::typeKey: + return new TextStyle(); + case TextStyleAxisBase::typeKey: + return new TextStyleAxis(); + case TextBase::typeKey: + return new Text(); + case TextValueRunBase::typeKey: + return new TextValueRun(); case FolderBase::typeKey: return new Folder(); case ImageAssetBase::typeKey: return new ImageAsset(); + case FontAssetBase::typeKey: + return new FontAsset(); case FileAssetContentsBase::typeKey: return new FileAssetContents(); } @@ -303,11 +321,17 @@ case ComponentBase::namePropertyKey: object->as<ComponentBase>()->name(value); break; + case AnimationBase::namePropertyKey: + object->as<AnimationBase>()->name(value); + break; case StateMachineComponentBase::namePropertyKey: object->as<StateMachineComponentBase>()->name(value); break; - case AnimationBase::namePropertyKey: - object->as<AnimationBase>()->name(value); + case KeyFrameStringBase::valuePropertyKey: + object->as<KeyFrameStringBase>()->value(value); + break; + case TextValueRunBase::textPropertyKey: + object->as<TextValueRunBase>()->text(value); break; case AssetBase::namePropertyKey: object->as<AssetBase>()->name(value); @@ -495,6 +519,24 @@ case CubicWeightBase::outIndicesPropertyKey: object->as<CubicWeightBase>()->outIndices(value); break; + case TextStyleBase::fontAssetIdPropertyKey: + object->as<TextStyleBase>()->fontAssetId(value); + break; + case TextStyleAxisBase::tagPropertyKey: + object->as<TextStyleAxisBase>()->tag(value); + break; + case TextBase::alignValuePropertyKey: + object->as<TextBase>()->alignValue(value); + break; + case TextBase::sizingValuePropertyKey: + object->as<TextBase>()->sizingValue(value); + break; + case TextBase::overflowValuePropertyKey: + object->as<TextBase>()->overflowValue(value); + break; + case TextValueRunBase::styleIdPropertyKey: + object->as<TextValueRunBase>()->styleId(value); + break; case FileAssetBase::assetIdPropertyKey: object->as<FileAssetBase>()->assetId(value); break; @@ -753,6 +795,18 @@ case TendonBase::tyPropertyKey: object->as<TendonBase>()->ty(value); break; + case TextStyleBase::fontSizePropertyKey: + object->as<TextStyleBase>()->fontSize(value); + break; + case TextStyleAxisBase::axisValuePropertyKey: + object->as<TextStyleAxisBase>()->axisValue(value); + break; + case TextBase::widthPropertyKey: + object->as<TextBase>()->width(value); + break; + case TextBase::heightPropertyKey: + object->as<TextBase>()->height(value); + break; case DrawableAssetBase::heightPropertyKey: object->as<DrawableAssetBase>()->height(value); break; @@ -845,10 +899,14 @@ { case ComponentBase::namePropertyKey: return object->as<ComponentBase>()->name(); - case StateMachineComponentBase::namePropertyKey: - return object->as<StateMachineComponentBase>()->name(); case AnimationBase::namePropertyKey: return object->as<AnimationBase>()->name(); + case StateMachineComponentBase::namePropertyKey: + return object->as<StateMachineComponentBase>()->name(); + case KeyFrameStringBase::valuePropertyKey: + return object->as<KeyFrameStringBase>()->value(); + case TextValueRunBase::textPropertyKey: + return object->as<TextValueRunBase>()->text(); case AssetBase::namePropertyKey: return object->as<AssetBase>()->name(); } @@ -976,6 +1034,18 @@ return object->as<CubicWeightBase>()->outValues(); case CubicWeightBase::outIndicesPropertyKey: return object->as<CubicWeightBase>()->outIndices(); + case TextStyleBase::fontAssetIdPropertyKey: + return object->as<TextStyleBase>()->fontAssetId(); + case TextStyleAxisBase::tagPropertyKey: + return object->as<TextStyleAxisBase>()->tag(); + case TextBase::alignValuePropertyKey: + return object->as<TextBase>()->alignValue(); + case TextBase::sizingValuePropertyKey: + return object->as<TextBase>()->sizingValue(); + case TextBase::overflowValuePropertyKey: + return object->as<TextBase>()->overflowValue(); + case TextValueRunBase::styleIdPropertyKey: + return object->as<TextValueRunBase>()->styleId(); case FileAssetBase::assetIdPropertyKey: return object->as<FileAssetBase>()->assetId(); } @@ -1151,6 +1221,14 @@ return object->as<TendonBase>()->tx(); case TendonBase::tyPropertyKey: return object->as<TendonBase>()->ty(); + case TextStyleBase::fontSizePropertyKey: + return object->as<TextStyleBase>()->fontSize(); + case TextStyleAxisBase::axisValuePropertyKey: + return object->as<TextStyleAxisBase>()->axisValue(); + case TextBase::widthPropertyKey: + return object->as<TextBase>()->width(); + case TextBase::heightPropertyKey: + return object->as<TextBase>()->height(); case DrawableAssetBase::heightPropertyKey: return object->as<DrawableAssetBase>()->height(); case DrawableAssetBase::widthPropertyKey: @@ -1221,8 +1299,10 @@ switch (propertyKey) { case ComponentBase::namePropertyKey: - case StateMachineComponentBase::namePropertyKey: case AnimationBase::namePropertyKey: + case StateMachineComponentBase::namePropertyKey: + case KeyFrameStringBase::valuePropertyKey: + case TextValueRunBase::textPropertyKey: case AssetBase::namePropertyKey: return CoreStringType::id; case ComponentBase::parentIdPropertyKey: @@ -1284,6 +1364,12 @@ case CubicWeightBase::inIndicesPropertyKey: case CubicWeightBase::outValuesPropertyKey: case CubicWeightBase::outIndicesPropertyKey: + case TextStyleBase::fontAssetIdPropertyKey: + case TextStyleAxisBase::tagPropertyKey: + case TextBase::alignValuePropertyKey: + case TextBase::sizingValuePropertyKey: + case TextBase::overflowValuePropertyKey: + case TextValueRunBase::styleIdPropertyKey: case FileAssetBase::assetIdPropertyKey: return CoreUintType::id; case ConstraintBase::strengthPropertyKey: @@ -1369,6 +1455,10 @@ case TendonBase::yyPropertyKey: case TendonBase::txPropertyKey: case TendonBase::tyPropertyKey: + case TextStyleBase::fontSizePropertyKey: + case TextStyleAxisBase::axisValuePropertyKey: + case TextBase::widthPropertyKey: + case TextBase::heightPropertyKey: case DrawableAssetBase::heightPropertyKey: case DrawableAssetBase::widthPropertyKey: return CoreDoubleType::id;
diff --git a/include/rive/generated/text/text_base.hpp b/include/rive/generated/text/text_base.hpp new file mode 100644 index 0000000..3d5bcf0 --- /dev/null +++ b/include/rive/generated/text/text_base.hpp
@@ -0,0 +1,149 @@ +#ifndef _RIVE_TEXT_BASE_HPP_ +#define _RIVE_TEXT_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/drawable.hpp" +namespace rive +{ +class TextBase : public Drawable +{ +protected: + typedef Drawable Super; + +public: + static const uint16_t typeKey = 134; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TextBase::typeKey: + case DrawableBase::typeKey: + case NodeBase::typeKey: + case TransformComponentBase::typeKey: + case WorldTransformComponentBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t alignValuePropertyKey = 281; + static const uint16_t sizingValuePropertyKey = 284; + static const uint16_t overflowValuePropertyKey = 287; + static const uint16_t widthPropertyKey = 285; + static const uint16_t heightPropertyKey = 286; + +private: + uint32_t m_AlignValue = 0; + uint32_t m_SizingValue = 0; + uint32_t m_OverflowValue = 0; + float m_Width = 0.0f; + float m_Height = 0.0f; + +public: + inline uint32_t alignValue() const { return m_AlignValue; } + void alignValue(uint32_t value) + { + if (m_AlignValue == value) + { + return; + } + m_AlignValue = value; + alignValueChanged(); + } + + inline uint32_t sizingValue() const { return m_SizingValue; } + void sizingValue(uint32_t value) + { + if (m_SizingValue == value) + { + return; + } + m_SizingValue = value; + sizingValueChanged(); + } + + inline uint32_t overflowValue() const { return m_OverflowValue; } + void overflowValue(uint32_t value) + { + if (m_OverflowValue == value) + { + return; + } + m_OverflowValue = value; + overflowValueChanged(); + } + + inline float width() const { return m_Width; } + void width(float value) + { + if (m_Width == value) + { + return; + } + m_Width = value; + widthChanged(); + } + + inline float height() const { return m_Height; } + void height(float value) + { + if (m_Height == value) + { + return; + } + m_Height = value; + heightChanged(); + } + + Core* clone() const override; + void copy(const TextBase& object) + { + m_AlignValue = object.m_AlignValue; + m_SizingValue = object.m_SizingValue; + m_OverflowValue = object.m_OverflowValue; + m_Width = object.m_Width; + m_Height = object.m_Height; + Drawable::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case alignValuePropertyKey: + m_AlignValue = CoreUintType::deserialize(reader); + return true; + case sizingValuePropertyKey: + m_SizingValue = CoreUintType::deserialize(reader); + return true; + case overflowValuePropertyKey: + m_OverflowValue = CoreUintType::deserialize(reader); + return true; + case widthPropertyKey: + m_Width = CoreDoubleType::deserialize(reader); + return true; + case heightPropertyKey: + m_Height = CoreDoubleType::deserialize(reader); + return true; + } + return Drawable::deserialize(propertyKey, reader); + } + +protected: + virtual void alignValueChanged() {} + virtual void sizingValueChanged() {} + virtual void overflowValueChanged() {} + virtual void widthChanged() {} + virtual void heightChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/text/text_style_axis_base.hpp b/include/rive/generated/text/text_style_axis_base.hpp new file mode 100644 index 0000000..0b3f43b --- /dev/null +++ b/include/rive/generated/text/text_style_axis_base.hpp
@@ -0,0 +1,90 @@ +#ifndef _RIVE_TEXT_STYLE_AXIS_BASE_HPP_ +#define _RIVE_TEXT_STYLE_AXIS_BASE_HPP_ +#include "rive/component.hpp" +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TextStyleAxisBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 144; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TextStyleAxisBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t tagPropertyKey = 289; + static const uint16_t axisValuePropertyKey = 288; + +private: + uint32_t m_Tag = 0; + float m_AxisValue = 0.0f; + +public: + inline uint32_t tag() const { return m_Tag; } + void tag(uint32_t value) + { + if (m_Tag == value) + { + return; + } + m_Tag = value; + tagChanged(); + } + + inline float axisValue() const { return m_AxisValue; } + void axisValue(float value) + { + if (m_AxisValue == value) + { + return; + } + m_AxisValue = value; + axisValueChanged(); + } + + Core* clone() const override; + void copy(const TextStyleAxisBase& object) + { + m_Tag = object.m_Tag; + m_AxisValue = object.m_AxisValue; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case tagPropertyKey: + m_Tag = CoreUintType::deserialize(reader); + return true; + case axisValuePropertyKey: + m_AxisValue = CoreDoubleType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void tagChanged() {} + virtual void axisValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/text/text_style_base.hpp b/include/rive/generated/text/text_style_base.hpp new file mode 100644 index 0000000..b6b8beb --- /dev/null +++ b/include/rive/generated/text/text_style_base.hpp
@@ -0,0 +1,91 @@ +#ifndef _RIVE_TEXT_STYLE_BASE_HPP_ +#define _RIVE_TEXT_STYLE_BASE_HPP_ +#include "rive/container_component.hpp" +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TextStyleBase : public ContainerComponent +{ +protected: + typedef ContainerComponent Super; + +public: + static const uint16_t typeKey = 137; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TextStyleBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t fontSizePropertyKey = 274; + static const uint16_t fontAssetIdPropertyKey = 279; + +private: + float m_FontSize = 12.0f; + uint32_t m_FontAssetId = -1; + +public: + inline float fontSize() const { return m_FontSize; } + void fontSize(float value) + { + if (m_FontSize == value) + { + return; + } + m_FontSize = value; + fontSizeChanged(); + } + + inline uint32_t fontAssetId() const { return m_FontAssetId; } + void fontAssetId(uint32_t value) + { + if (m_FontAssetId == value) + { + return; + } + m_FontAssetId = value; + fontAssetIdChanged(); + } + + Core* clone() const override; + void copy(const TextStyleBase& object) + { + m_FontSize = object.m_FontSize; + m_FontAssetId = object.m_FontAssetId; + ContainerComponent::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case fontSizePropertyKey: + m_FontSize = CoreDoubleType::deserialize(reader); + return true; + case fontAssetIdPropertyKey: + m_FontAssetId = CoreUintType::deserialize(reader); + return true; + } + return ContainerComponent::deserialize(propertyKey, reader); + } + +protected: + virtual void fontSizeChanged() {} + virtual void fontAssetIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/text/text_value_run_base.hpp b/include/rive/generated/text/text_value_run_base.hpp new file mode 100644 index 0000000..79ab4f7 --- /dev/null +++ b/include/rive/generated/text/text_value_run_base.hpp
@@ -0,0 +1,91 @@ +#ifndef _RIVE_TEXT_VALUE_RUN_BASE_HPP_ +#define _RIVE_TEXT_VALUE_RUN_BASE_HPP_ +#include <string> +#include "rive/component.hpp" +#include "rive/core/field_types/core_string_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TextValueRunBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 135; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TextValueRunBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t styleIdPropertyKey = 272; + static const uint16_t textPropertyKey = 268; + +private: + uint32_t m_StyleId = -1; + std::string m_Text = ""; + +public: + inline uint32_t styleId() const { return m_StyleId; } + void styleId(uint32_t value) + { + if (m_StyleId == value) + { + return; + } + m_StyleId = value; + styleIdChanged(); + } + + inline const std::string& text() const { return m_Text; } + void text(std::string value) + { + if (m_Text == value) + { + return; + } + m_Text = value; + textChanged(); + } + + Core* clone() const override; + void copy(const TextValueRunBase& object) + { + m_StyleId = object.m_StyleId; + m_Text = object.m_Text; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case styleIdPropertyKey: + m_StyleId = CoreUintType::deserialize(reader); + return true; + case textPropertyKey: + m_Text = CoreStringType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void styleIdChanged() {} + virtual void textChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/text/font_hb.hpp b/include/rive/text/font_hb.hpp index dc92123..7030b89 100644 --- a/include/rive/text/font_hb.hpp +++ b/include/rive/text/font_hb.hpp
@@ -6,7 +6,7 @@ #define _RIVE_FONT_HB_HPP_ #include "rive/factory.hpp" -#include "rive/text.hpp" +#include "rive/text_engine.hpp" struct hb_font_t; struct hb_draw_funcs_t;
diff --git a/include/rive/text/text.hpp b/include/rive/text/text.hpp new file mode 100644 index 0000000..686c0af --- /dev/null +++ b/include/rive/text/text.hpp
@@ -0,0 +1,151 @@ +#ifndef _RIVE_TEXT_CORE_HPP_ +#define _RIVE_TEXT_CORE_HPP_ +#include "rive/generated/text/text_base.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/text_engine.hpp" +#include "rive/simple_array.hpp" + +namespace rive +{ + +enum class TextSizing : uint8_t +{ + autoWidth, + autoHeight, + fixed +}; + +enum class TextOverflow : uint8_t +{ + visible, + hidden, + clipped, + ellipsis +}; + +class OrderedLine; + +// STL-style iterator for individual glyphs in a line, simplfies call sites from +// needing to iterate both runs and glyphs within those runs per line. A single +// iterator allows iterating all the glyphs in the line and provides the correct +// run they belong to (this also takes into account bidi which can put the runs +// in different order from how they were provided by the line breaker). +// +// for (auto [run, glyphIndex] : orderedLine) { ... } +// +class GlyphItr +{ +public: + GlyphItr() = default; + GlyphItr(const OrderedLine* line, const GlyphRun* run, size_t glyphIndex) : + m_line(line), m_run(run), m_glyphIndex(glyphIndex) + {} + + bool operator!=(const GlyphItr& that) const + { + return m_run != that.m_run || m_glyphIndex != that.m_glyphIndex; + } + bool operator==(const GlyphItr& that) const + { + return m_run == that.m_run && m_glyphIndex == that.m_glyphIndex; + } + + GlyphItr& operator++(); + + std::tuple<const GlyphRun*, size_t> operator*() const { return {m_run, m_glyphIndex}; } + +private: + const OrderedLine* m_line; + const GlyphRun* m_run; + size_t m_glyphIndex; +}; + +// Represents a line of text with runs ordered visually. Also tracks logical +// start/end which will defer when using bidi. +class OrderedLine +{ +public: + OrderedLine(const Paragraph& paragraph, const GlyphLine& line); + const GlyphRun* startLogical() const { return m_startLogical; } + const GlyphRun* endLogical() const { return m_endLogical; } + const GlyphLine* glyphLine() const { return m_line; } + const std::vector<const GlyphRun*>& runs() const { return m_runs; } + + GlyphItr begin() const + { + auto run = m_runs[0]; + return GlyphItr(this, run, startGlyphIndex(run)); + } + + GlyphItr end() const + { + auto run = lastRun(); + return GlyphItr(this, run, endGlyphIndex(run)); + } + +private: + const GlyphRun* m_startLogical; + const GlyphRun* m_endLogical; + const GlyphLine* m_line; + std::vector<const GlyphRun*> m_runs; + +public: + const GlyphRun* lastRun() const { return m_runs.back(); } + size_t startGlyphIndex(const GlyphRun* run) const + { + switch (run->dir) + { + case TextDirection::ltr: + return m_startLogical == run ? m_line->startGlyphIndex : 0; + case TextDirection::rtl: + return (m_endLogical == run ? m_line->endGlyphIndex : run->glyphs.size()) - 1; + } + RIVE_UNREACHABLE(); + } + size_t endGlyphIndex(const GlyphRun* run) const + { + switch (run->dir) + { + case TextDirection::ltr: + return m_endLogical == run ? m_line->endGlyphIndex : run->glyphs.size(); + case TextDirection::rtl: + return (m_startLogical == run ? m_line->startGlyphIndex : 0) - 1; + } + RIVE_UNREACHABLE(); + } +}; + +class TextStyle; +class Text : public TextBase +{ +public: + void draw(Renderer* renderer) override; + Core* hitTest(HitInfo*, const Mat2D&) override; + void addRun(TextValueRun* run); + void markShapeDirty(); + void update(ComponentDirt value) override; + + TextSizing sizing() { return (TextSizing)sizingValue(); } + TextOverflow overflow() { return (TextOverflow)overflowValue(); } + void buildRenderStyles(); + +protected: + void alignValueChanged() override; + void sizingValueChanged() override; + void overflowValueChanged() override; + void widthChanged() override; + void heightChanged() override; + +private: +#ifdef WITH_RIVE_TEXT + std::vector<TextValueRun*> m_runs; + std::vector<TextStyle*> m_renderStyles; + SimpleArray<Paragraph> m_shape; + SimpleArray<SimpleArray<GlyphLine>> m_lines; + // Runs ordered by paragraph line. + std::vector<OrderedLine> m_orderedLines; +#endif +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/text/text_style.hpp b/include/rive/text/text_style.hpp new file mode 100644 index 0000000..20521c8 --- /dev/null +++ b/include/rive/text/text_style.hpp
@@ -0,0 +1,44 @@ +#ifndef _RIVE_TEXT_STYLE_HPP_ +#define _RIVE_TEXT_STYLE_HPP_ +#include "rive/generated/text/text_style_base.hpp" +#include "rive/shapes/shape_paint_container.hpp" +#include "rive/assets/file_asset_referencer.hpp" +#include "rive/assets/file_asset.hpp" +#include "rive/assets/font_asset.hpp" + +namespace rive +{ +class FontAsset; +class Font; +class FileAsset; +class Renderer; +class RenderPath; + +class TextStyle : public TextStyleBase, public ShapePaintContainer, public FileAssetReferencer +{ +private: + Artboard* getArtboard() override { return artboard(); } + +public: + TextStyle(); + void buildDependencies() override; + const rcp<Font> font() const; + void assets(const std::vector<FileAsset*>& assets) override; + StatusCode import(ImportStack& importStack) override; + + bool addPath(const RawPath& rawPath); + void resetPath(); + void draw(Renderer* renderer); + Core* clone() const override; + +protected: + void fontSizeChanged() override; + +private: + FontAsset* m_fontAsset = nullptr; + std::unique_ptr<RenderPath> m_path; + bool m_hasContents = false; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/text/text_style_axis.hpp b/include/rive/text/text_style_axis.hpp new file mode 100644 index 0000000..3a9084a --- /dev/null +++ b/include/rive/text/text_style_axis.hpp
@@ -0,0 +1,13 @@ +#ifndef _RIVE_TEXT_STYLE_AXIS_HPP_ +#define _RIVE_TEXT_STYLE_AXIS_HPP_ +#include "rive/generated/text/text_style_axis_base.hpp" +#include <stdio.h> +namespace rive +{ +class TextStyleAxis : public TextStyleAxisBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/text/text_value_run.hpp b/include/rive/text/text_value_run.hpp new file mode 100644 index 0000000..bb6554e --- /dev/null +++ b/include/rive/text/text_value_run.hpp
@@ -0,0 +1,23 @@ +#ifndef _RIVE_TEXT_VALUE_RUN_HPP_ +#define _RIVE_TEXT_VALUE_RUN_HPP_ +#include "rive/generated/text/text_value_run_base.hpp" + +namespace rive +{ +class TextStyle; +class TextValueRun : public TextValueRunBase +{ +public: + StatusCode onAddedClean(CoreContext* context) override; + StatusCode onAddedDirty(CoreContext* context) override; + TextStyle* style() { return m_style; } + +protected: + void textChanged() override; + +private: + TextStyle* m_style = nullptr; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/utils/rive_utf.hpp b/include/rive/text/utf.hpp similarity index 90% rename from include/utils/rive_utf.hpp rename to include/rive/text/utf.hpp index e5a79aa..54b8f60 100644 --- a/include/utils/rive_utf.hpp +++ b/include/rive/text/utf.hpp
@@ -1,11 +1,7 @@ -/* - * Copyright 2022 Rive - */ - #ifndef _RIVE_UTF_HPP_ #define _RIVE_UTF_HPP_ -#include "rive/text.hpp" +#include "rive/text_engine.hpp" namespace rive {
diff --git a/include/rive/text.hpp b/include/rive/text_engine.hpp similarity index 98% rename from include/rive/text.hpp rename to include/rive/text_engine.hpp index 3f550d2..40618c9 100644 --- a/include/rive/text.hpp +++ b/include/rive/text_engine.hpp
@@ -2,8 +2,8 @@ * Copyright 2022 Rive */ -#ifndef _RIVE_TEXT_HPP_ -#define _RIVE_TEXT_HPP_ +#ifndef _RIVE_TEXT_ENGINE_HPP_ +#define _RIVE_TEXT_ENGINE_HPP_ #include "rive/math/raw_path.hpp" #include "rive/refcnt.hpp"
diff --git a/src/animation/keyframe_bool.cpp b/src/animation/keyframe_bool.cpp index 401a655..2d89d8d 100644 --- a/src/animation/keyframe_bool.cpp +++ b/src/animation/keyframe_bool.cpp
@@ -1,4 +1,4 @@ -#include "rive/animation/keyframe_id.hpp" +#include "rive/animation/keyframe_bool.hpp" #include "rive/generated/core_registry.hpp" using namespace rive;
diff --git a/src/animation/keyframe_string.cpp b/src/animation/keyframe_string.cpp new file mode 100644 index 0000000..7968dda --- /dev/null +++ b/src/animation/keyframe_string.cpp
@@ -0,0 +1,18 @@ +#include "rive/animation/keyframe_string.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +void KeyFrameString::apply(Core* object, int propertyKey, float mix) +{ + CoreRegistry::setString(object, propertyKey, value()); +} + +void KeyFrameString::applyInterpolation(Core* object, + int propertyKey, + float currentTime, + const KeyFrame* nextFrame, + float mix) +{ + CoreRegistry::setString(object, propertyKey, value()); +} \ No newline at end of file
diff --git a/src/assets/file_asset_referencer.cpp b/src/assets/file_asset_referencer.cpp new file mode 100644 index 0000000..bbecf39 --- /dev/null +++ b/src/assets/file_asset_referencer.cpp
@@ -0,0 +1,17 @@ +#include "rive/assets/file_asset_referencer.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +StatusCode FileAssetReferencer::registerReferencer(ImportStack& importStack) +{ + auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addFileAssetReferencer(this); + + return StatusCode::Ok; +} \ No newline at end of file
diff --git a/src/assets/font_asset.cpp b/src/assets/font_asset.cpp new file mode 100644 index 0000000..9340704 --- /dev/null +++ b/src/assets/font_asset.cpp
@@ -0,0 +1,14 @@ +#include "rive/assets/font_asset.hpp" +#include "rive/artboard.hpp" +#include "rive/factory.hpp" + +using namespace rive; + +bool FontAsset::decode(Span<const uint8_t> data, Factory* factory) +{ + m_font = factory->decodeFont(data); + return m_font != nullptr; +} +std::string FontAsset::fileExtension() { return "ttf"; } + +void FontAsset::font(rcp<Font> font) { m_font = std::move(font); } \ No newline at end of file
diff --git a/src/factory.cpp b/src/factory.cpp index 8be9edf..7b4aad8 100644 --- a/src/factory.cpp +++ b/src/factory.cpp
@@ -5,6 +5,9 @@ #include "rive/factory.hpp" #include "rive/math/aabb.hpp" #include "rive/math/raw_path.hpp" +#ifdef WITH_RIVE_TEXT +#include "rive/text/font_hb.hpp" +#endif using namespace rive; @@ -14,3 +17,12 @@ rawPath.addRect(r); return makeRenderPath(rawPath, FillRule::nonZero); } + +rcp<Font> Factory::decodeFont(Span<const uint8_t> span) +{ +#ifdef WITH_RIVE_TEXT + return HBFont::Decode(span); +#else + return nullptr; +#endif +} \ No newline at end of file
diff --git a/src/file.cpp b/src/file.cpp index d7f86fb..2aea9cc 100644 --- a/src/file.cpp +++ b/src/file.cpp
@@ -199,6 +199,7 @@ } break; case ImageAsset::typeKey: + case FontAsset::typeKey: { auto fa = object->as<FileAsset>(); m_FileAssets.push_back(std::unique_ptr<FileAsset>(fa)); @@ -275,6 +276,7 @@ stackObject = new StateMachineListenerImporter(object->as<StateMachineListener>()); break; case ImageAsset::typeKey: + case FontAsset::typeKey: stackObject = new FileAssetImporter(object->as<FileAsset>(), m_AssetResolver, m_Factory); stackType = FileAsset::typeKey;
diff --git a/src/generated/animation/keyframe_string_base.cpp b/src/generated/animation/keyframe_string_base.cpp new file mode 100644 index 0000000..ff1fa86 --- /dev/null +++ b/src/generated/animation/keyframe_string_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/animation/keyframe_string_base.hpp" +#include "rive/animation/keyframe_string.hpp" + +using namespace rive; + +Core* KeyFrameStringBase::clone() const +{ + auto cloned = new KeyFrameString(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/assets/font_asset_base.cpp b/src/generated/assets/font_asset_base.cpp new file mode 100644 index 0000000..95101bd --- /dev/null +++ b/src/generated/assets/font_asset_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/assets/font_asset_base.hpp" +#include "rive/assets/font_asset.hpp" + +using namespace rive; + +Core* FontAssetBase::clone() const +{ + auto cloned = new FontAsset(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/text/text_base.cpp b/src/generated/text/text_base.cpp new file mode 100644 index 0000000..7c34405 --- /dev/null +++ b/src/generated/text/text_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/text/text_base.hpp" +#include "rive/text/text.hpp" + +using namespace rive; + +Core* TextBase::clone() const +{ + auto cloned = new Text(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/text/text_style_axis_base.cpp b/src/generated/text/text_style_axis_base.cpp new file mode 100644 index 0000000..9f32f7f --- /dev/null +++ b/src/generated/text/text_style_axis_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/text/text_style_axis_base.hpp" +#include "rive/text/text_style_axis.hpp" + +using namespace rive; + +Core* TextStyleAxisBase::clone() const +{ + auto cloned = new TextStyleAxis(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/text/text_style_base.cpp b/src/generated/text/text_style_base.cpp new file mode 100644 index 0000000..4fa7bcc --- /dev/null +++ b/src/generated/text/text_style_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/text/text_style_base.hpp" +#include "rive/text/text_style.hpp" + +using namespace rive; + +Core* TextStyleBase::clone() const +{ + auto cloned = new TextStyle(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/text/text_value_run_base.cpp b/src/generated/text/text_value_run_base.cpp new file mode 100644 index 0000000..732cc3b --- /dev/null +++ b/src/generated/text/text_value_run_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/text/text_value_run_base.hpp" +#include "rive/text/text_value_run.hpp" + +using namespace rive; + +Core* TextValueRunBase::clone() const +{ + auto cloned = new TextValueRun(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/math/raw_path.cpp b/src/math/raw_path.cpp index 1529900..73d5b9f 100644 --- a/src/math/raw_path.cpp +++ b/src/math/raw_path.cpp
@@ -289,7 +289,10 @@ result->close(); break; case PathVerb::quad: - RIVE_UNREACHABLE(); + // TODO: actually supports quads. + result->cubic(Vec2D::lerp(pts[0], pts[1], 2 / 3.f), + Vec2D::lerp(pts[2], pts[1], 2 / 3.f), + pts[2]); } } }
diff --git a/src/renderer.cpp b/src/renderer.cpp index 2d83a56..62a2d4f 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp
@@ -1,6 +1,7 @@ #include "rive/math/mat2d.hpp" #include "rive/renderer.hpp" #include "rive/rive_counter.hpp" +#include "rive/text_engine.hpp" using namespace rive; @@ -99,8 +100,6 @@ RenderPath::RenderPath() { Counter::update(Counter::kPath, 1); } RenderPath::~RenderPath() { Counter::update(Counter::kPath, -1); } -#include "rive/text.hpp" - static bool isWhiteSpace(Unichar c) { return c <= ' ' || c == 0x2028; } SimpleArray<Paragraph> Font::shapeText(Span<const Unichar> text, Span<const TextRun> runs) const
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp index bc00c82..9635f82 100644 --- a/src/shapes/image.cpp +++ b/src/shapes/image.cpp
@@ -70,13 +70,11 @@ StatusCode Image::import(ImportStack& importStack) { - auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey); - if (backboardImporter == nullptr) + auto result = registerReferencer(importStack); + if (result != StatusCode::Ok) { - return StatusCode::MissingObject; + return result; } - backboardImporter->addFileAssetReferencer(this); - return Super::import(importStack); }
diff --git a/src/shapes/paint/linear_gradient.cpp b/src/shapes/paint/linear_gradient.cpp index cc68efa..2acbe7d 100644 --- a/src/shapes/paint/linear_gradient.cpp +++ b/src/shapes/paint/linear_gradient.cpp
@@ -36,12 +36,23 @@ // Parent's parent must be a shape paint container. assert(ShapePaintContainer::from(parentsParent) != nullptr); - // TODO: see if artboard should inherit from some TransformComponent - // that can return a world transform. We store the container just for - // doing the transform to world in update. If it's the artboard, then - // we're already in world so no need to transform. - m_ShapePaintContainer = parentsParent->is<Node>() ? parentsParent->as<Node>() : nullptr; - parentsParent->addDependent(this); + // We need to find the container that owns our world space transform. + // This is the first node up the chain (or none, meaning we are in world + // space). + m_ShapePaintContainer = nullptr; + while (parentsParent != nullptr) + { + if (parentsParent->is<Node>()) + { + m_ShapePaintContainer = parentsParent->as<Node>(); + break; + } + parentsParent = parentsParent->parent(); + } + if (parentsParent != nullptr) + { + parentsParent->addDependent(this); + } } }
diff --git a/src/shapes/shape_paint_container.cpp b/src/shapes/shape_paint_container.cpp index bb8d5c5..458038c 100644 --- a/src/shapes/shape_paint_container.cpp +++ b/src/shapes/shape_paint_container.cpp
@@ -7,6 +7,7 @@ #include "rive/shapes/paint/fill.hpp" #include "rive/shapes/paint/stroke.hpp" #include "rive/shapes/shape.hpp" +#include "rive/text/text_style.hpp" using namespace rive; @@ -18,6 +19,8 @@ return component->as<Artboard>(); case Shape::typeKey: return component->as<Shape>(); + case TextStyle::typeKey: + return component->as<TextStyle>(); } return nullptr; }
diff --git a/src/text/font_hb.cpp b/src/text/font_hb.cpp index 7652662..26b8f22 100644 --- a/src/text/font_hb.cpp +++ b/src/text/font_hb.cpp
@@ -2,7 +2,7 @@ * Copyright 2022 Rive */ -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #ifdef WITH_RIVE_TEXT #include "rive/text/font_hb.hpp"
diff --git a/src/text/line_breaker.cpp b/src/text/line_breaker.cpp index 56d5b55..e9be9ad 100644 --- a/src/text/line_breaker.cpp +++ b/src/text/line_breaker.cpp
@@ -2,7 +2,7 @@ * Copyright 2022 Rive */ -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #include <limits> #include <algorithm> using namespace rive;
diff --git a/src/text/text.cpp b/src/text/text.cpp index 42188c7..bd8532d 100644 --- a/src/text/text.cpp +++ b/src/text/text.cpp
@@ -1,19 +1,276 @@ -#include "rive/text.hpp" - +#include "rive/text/text.hpp" using namespace rive; +#ifdef WITH_RIVE_TEXT +#include "rive/text_engine.hpp" +#include "rive/component_dirt.hpp" +#include "rive/text/utf.hpp" +#include "rive/text/text_style.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/shapes/paint/shape_paint.hpp" -std::vector<Font::Axis> Font::getAxes() const +GlyphItr& GlyphItr::operator++() { - std::vector<rive::Font::Axis> axes; - const uint16_t count = getAxisCount(); - if (count > 0) - { - axes.resize(count); + m_glyphIndex += m_run->dir == TextDirection::ltr ? 1 : -1; - for (uint16_t i = 0; i < count; ++i) + // Did we reach the end of the run? + if (m_glyphIndex == m_line->endGlyphIndex(m_run) && m_run != m_line->lastRun()) + { + m_run++; + m_glyphIndex = m_line->startGlyphIndex(m_run); + } + return *this; +} + +OrderedLine::OrderedLine(const Paragraph& paragraph, const GlyphLine& line) : m_line(&line) +{ + std::vector<const GlyphRun*> logicalRuns; + const SimpleArray<GlyphRun>& glyphRuns = paragraph.runs; + + for (uint32_t i = line.startRunIndex; i < line.endRunIndex + 1; i++) + { + logicalRuns.push_back(&glyphRuns[i]); + } + if (paragraph.baseDirection == TextDirection::ltr || logicalRuns.empty()) + { + m_startLogical = logicalRuns.front(); + m_endLogical = logicalRuns.back(); + m_runs = std::move(logicalRuns); + } + else + { + std::vector<const GlyphRun*> visualRuns; + visualRuns.reserve(logicalRuns.size()); + + auto itr = logicalRuns.rbegin(); + auto end = logicalRuns.rend(); + const GlyphRun* first = *itr; + visualRuns.push_back(first); + size_t ltrIndex = 0; + TextDirection previousDirection = first->dir; + while (++itr != end) { - axes.push_back(getAxis(i)); + const GlyphRun* run = *itr; + if (run->dir == TextDirection::ltr && previousDirection == run->dir) + { + visualRuns.insert(visualRuns.begin() + ltrIndex, run); + } + else + { + if (run->dir == TextDirection::ltr) + { + ltrIndex = visualRuns.size(); + } + visualRuns.push_back(run); + } + previousDirection = run->dir; + } + m_runs = std::move(visualRuns); + } +} + +void Text::buildRenderStyles() +{ + for (TextStyle* style : m_renderStyles) + { + style->resetPath(); + } + m_renderStyles.clear(); + + // Build up ordered runs as we go. + int paragraphIndex = 0; + int lineIndex = 0; + for (const SimpleArray<GlyphLine>& paragraphLines : m_lines) + { + const Paragraph& paragraph = m_shape[paragraphIndex++]; + float y = 0.0f; + for (const GlyphLine& line : paragraphLines) + { + if (lineIndex >= m_orderedLines.size()) + { + // We need to still compute this line's ordered runs. + m_orderedLines.emplace_back(OrderedLine(paragraph, line)); + } + const OrderedLine& orderedLine = m_orderedLines[lineIndex]; + float x = 0.0f; + float renderY = y + line.baseline; + for (auto glyphItr : orderedLine) + { + const GlyphRun* run = std::get<0>(glyphItr); + size_t glyphIndex = std::get<1>(glyphItr); + const Font* font = run->font.get(); + GlyphID glyphId = run->glyphs[glyphIndex]; + + // TODO: profile if this should be cached. + RawPath path = font->getPath(glyphId); + // If we do end up caching these, we'll want to not + // transformInPlace and just transform instead. + path.transformInPlace(Mat2D(run->size, 0.0f, 0.0f, run->size, x, renderY)); + x += run->advances[glyphIndex]; + + assert(run->styleId < m_runs.size()); + TextStyle* style = m_runs[run->styleId]->style(); + // TextValueRun::onAddedDirty botches loading if it cannot + // resolve a style, so we're confident we have a style here. + assert(style != nullptr); + if (style->addPath(path)) + { + // This was the first path added to the style, so let's mark + // it in our draw list. + m_renderStyles.push_back(style); + } + } + lineIndex++; } } - return axes; } +void Text::draw(Renderer* renderer) +{ + if (!clip(renderer)) + { + // We didn't clip, so make sure to save as we'll be doing some + // transformations. + renderer->save(); + } + renderer->transform(worldTransform()); + for (auto style : m_renderStyles) + { + style->draw(renderer); + } + renderer->restore(); +} + +void Text::addRun(TextValueRun* run) { m_runs.push_back(run); } + +void Text::markShapeDirty() { addDirt(ComponentDirt::Path); } + +void Text::alignValueChanged() { markShapeDirty(); } + +void Text::sizingValueChanged() { markShapeDirty(); } + +void Text::overflowValueChanged() +{ + if (sizing() != TextSizing::autoWidth) + { + markShapeDirty(); + } +} + +void Text::widthChanged() +{ + if (sizing() != TextSizing::autoWidth) + { + markShapeDirty(); + } +} + +void Text::heightChanged() +{ + if (sizing() == TextSizing::fixed) + { + markShapeDirty(); + } +} + +static rive::TextRun append(std::vector<Unichar>& unichars, + rcp<Font> font, + float size, + const std::string& text, + uint16_t styleId) +{ + const uint8_t* ptr = (const uint8_t*)text.c_str(); + uint32_t n = 0; + while (*ptr) + { + unichars.push_back(UTF::NextUTF8(&ptr)); + n += 1; + } + return {std::move(font), size, n, 0, styleId}; +} + +static SimpleArray<SimpleArray<GlyphLine>> breakLines(const SimpleArray<Paragraph>& paragraphs, + float width, + TextAlign align) +{ + bool autoWidth = width == -1.0f; + float paragraphWidth = width; + + SimpleArray<SimpleArray<GlyphLine>> lines(paragraphs.size()); + + size_t paragraphIndex = 0; + for (auto& para : paragraphs) + { + lines[paragraphIndex] = GlyphLine::BreakLines(para.runs, autoWidth ? -1.0f : width); + if (autoWidth) + { + paragraphWidth = std::max(paragraphWidth, + GlyphLine::ComputeMaxWidth(lines[paragraphIndex], para.runs)); + } + paragraphIndex++; + } + paragraphIndex = 0; + for (auto& para : paragraphs) + { + GlyphLine::ComputeLineSpacing(lines[paragraphIndex++], para.runs, paragraphWidth, align); + } + return lines; +} + +void Text::update(ComponentDirt value) +{ + Super::update(value); + + if (hasDirt(value, ComponentDirt::Path)) + { + std::vector<Unichar> unichars; + std::vector<TextRun> runs; + uint16_t runIndex = 0; + for (auto valueRun : m_runs) + { + auto style = valueRun->style(); + const std::string& text = valueRun->text(); + if (style == nullptr || style->font() == nullptr || text.empty()) + { + runIndex++; + continue; + } + runs.emplace_back(append(unichars, style->font(), style->fontSize(), text, runIndex++)); + } + if (!runs.empty()) + { + m_shape = runs[0].font->shapeText(unichars, runs); + m_lines = breakLines(m_shape, + sizing() == TextSizing::autoWidth ? -1.0f : width(), + (TextAlign)alignValue()); + + m_orderedLines.clear(); + } + // This could later be flagged as dirty and called only when actually + // rendered the first time, for now we do it on update cycle in tandem + // with shaping. + buildRenderStyles(); + } +} + +Core* Text::hitTest(HitInfo*, const Mat2D&) +{ + if (renderOpacity() == 0.0f) + { + return nullptr; + } + + return nullptr; +} + +#else +// Text disabled. +void Text::draw(Renderer* renderer) {} +Core* Text::hitTest(HitInfo*, const Mat2D&) { return nullptr; } +void Text::addRun(TextValueRun* run) {} +void Text::markShapeDirty() {} +void Text::update(ComponentDirt value) {} +void Text::alignValueChanged() {} +void Text::sizingValueChanged() {} +void Text::overflowValueChanged() {} +void Text::widthChanged() {} +void Text::heightChanged() {} +#endif \ No newline at end of file
diff --git a/src/text/text_engine.cpp b/src/text/text_engine.cpp new file mode 100644 index 0000000..c57ee77 --- /dev/null +++ b/src/text/text_engine.cpp
@@ -0,0 +1,21 @@ +#ifdef WITH_RIVE_TEXT +#include "rive/text_engine.hpp" + +using namespace rive; + +std::vector<Font::Axis> Font::getAxes() const +{ + std::vector<Font::Axis> axes; + const uint16_t count = getAxisCount(); + if (count > 0) + { + axes.resize(count); + + for (uint16_t i = 0; i < count; ++i) + { + axes.push_back(getAxis(i)); + } + } + return axes; +} +#endif \ No newline at end of file
diff --git a/src/text/text_style.cpp b/src/text/text_style.cpp new file mode 100644 index 0000000..860b5bb --- /dev/null +++ b/src/text/text_style.cpp
@@ -0,0 +1,85 @@ +#include "rive/text/text_style.hpp" +#include "rive/renderer.hpp" +#include "rive/shapes/paint/shape_paint.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" +#include "rive/text/text.hpp" +#include "rive/artboard.hpp" +#include "rive/factory.hpp" + +using namespace rive; + +// satisfy unique_ptr +TextStyle::TextStyle() {} + +void TextStyle::buildDependencies() +{ + Super::buildDependencies(); + auto factory = getArtboard()->factory(); + m_path = factory->makeEmptyRenderPath(); +} + +void TextStyle::resetPath() +{ + m_path->reset(); + m_hasContents = false; +} + +bool TextStyle::addPath(const RawPath& rawPath) +{ + bool hadContents = m_hasContents; + rawPath.addTo(m_path.get()); + m_hasContents = true; + return !hadContents; +} + +void TextStyle::draw(Renderer* renderer) +{ + auto path = m_path.get(); + for (auto shapePaint : m_ShapePaints) + { + if (!shapePaint->isVisible()) + { + continue; + } + shapePaint->draw(renderer, path); + } +} + +void TextStyle::assets(const std::vector<FileAsset*>& assets) +{ + + if ((size_t)fontAssetId() >= assets.size()) + { + return; + } + auto asset = assets[fontAssetId()]; + if (asset->is<FontAsset>()) + { + m_fontAsset = asset->as<FontAsset>(); + } +} + +const rcp<Font> TextStyle::font() const +{ + return m_fontAsset == nullptr ? nullptr : m_fontAsset->font(); +} + +StatusCode TextStyle::import(ImportStack& importStack) +{ + auto result = registerReferencer(importStack); + if (result != StatusCode::Ok) + { + return result; + } + return Super::import(importStack); +} + +void TextStyle::fontSizeChanged() { parent()->as<Text>()->markShapeDirty(); } + +Core* TextStyle::clone() const +{ + TextStyle* twin = TextStyleBase::clone()->as<TextStyle>(); + twin->m_fontAsset = m_fontAsset; + return twin; +} \ No newline at end of file
diff --git a/src/text/text_value_run.cpp b/src/text/text_value_run.cpp new file mode 100644 index 0000000..63bad16 --- /dev/null +++ b/src/text/text_value_run.cpp
@@ -0,0 +1,44 @@ +#include "rive/core_context.hpp" +#include "rive/text/text.hpp" +#include "rive/text/text_style.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/container_component.hpp" + +using namespace rive; + +void TextValueRun::textChanged() { parent()->as<Text>()->markShapeDirty(); } + +StatusCode TextValueRun::onAddedClean(CoreContext* context) +{ + StatusCode code = Super::onAddedClean(context); + if (code != StatusCode::Ok) + { + return code; + } + + if (parent() != nullptr && parent()->is<Text>()) + { + parent()->as<Text>()->addRun(this); + return StatusCode::Ok; + } + + return StatusCode::MissingObject; +} + +StatusCode TextValueRun::onAddedDirty(CoreContext* context) +{ + StatusCode code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + auto coreObject = context->resolve(styleId()); + if (coreObject == nullptr || !coreObject->is<TextStyle>()) + { + return StatusCode::MissingObject; + } + + m_style = static_cast<TextStyle*>(coreObject); + + return StatusCode::Ok; +} \ No newline at end of file
diff --git a/utils/rive_utf.cpp b/src/text/utf.cpp similarity index 94% rename from utils/rive_utf.cpp rename to src/text/utf.cpp index 3ff6fdf..1d69243 100644 --- a/utils/rive_utf.cpp +++ b/src/text/utf.cpp
@@ -1,8 +1,4 @@ -/* - * Copyright 2022 Rive - */ - -#include "utils/rive_utf.hpp" +#include "rive/text/utf.hpp" #include "rive/core/type_conversions.hpp" using namespace rive;
diff --git a/test/assets/hello_world.riv b/test/assets/hello_world.riv new file mode 100644 index 0000000..dfc05c6 --- /dev/null +++ b/test/assets/hello_world.riv Binary files differ
diff --git a/test/assets/new_text.riv b/test/assets/new_text.riv new file mode 100644 index 0000000..cf9ffb7 --- /dev/null +++ b/test/assets/new_text.riv Binary files differ
diff --git a/test/fallback_font_test.cpp b/test/fallback_font_test.cpp index 3250cb8..5700a68 100644 --- a/test/fallback_font_test.cpp +++ b/test/fallback_font_test.cpp
@@ -4,9 +4,9 @@ #include <rive/simple_array.hpp> #include <catch.hpp> -#include <rive/text.hpp> +#include <rive/text_engine.hpp> #include <rive/text/font_hb.hpp> -#include "utils/rive_utf.hpp" +#include "rive/text/utf.hpp" using namespace rive;
diff --git a/test/line_break_test.cpp b/test/line_break_test.cpp index 34e2bd0..0335c84 100644 --- a/test/line_break_test.cpp +++ b/test/line_break_test.cpp
@@ -4,9 +4,9 @@ #include <rive/simple_array.hpp> #include <catch.hpp> -#include <rive/text.hpp> +#include <rive/text_engine.hpp> #include <rive/text/font_hb.hpp> -#include "utils/rive_utf.hpp" +#include "rive/text/utf.hpp" using namespace rive;
diff --git a/test/simple_array_test.cpp b/test/simple_array_test.cpp index 82b5fc2..132c10a 100644 --- a/test/simple_array_test.cpp +++ b/test/simple_array_test.cpp
@@ -4,7 +4,7 @@ #include <rive/simple_array.hpp> #include <catch.hpp> -#include <rive/text.hpp> +#include <rive/text_engine.hpp> using namespace rive;
diff --git a/test/text_test.cpp b/test/text_test.cpp new file mode 100644 index 0000000..7d28a71 --- /dev/null +++ b/test/text_test.cpp
@@ -0,0 +1,44 @@ +#include "rive/text/text.hpp" +#include "rive/text/text_style.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive_file_reader.hpp" +#include "rive_testing.hpp" +#include <utils/no_op_renderer.hpp> + +TEST_CASE("file with text loads correctly", "[text]") +{ + auto file = ReadRiveFile("../../test/assets/new_text.riv"); + auto artboard = file->artboard(); + + auto textObjects = artboard->find<rive::Text>(); + REQUIRE(textObjects.size() == 5); + + auto styleObjects = artboard->find<rive::TextStyle>(); + REQUIRE(styleObjects.size() == 13); + + auto runObjects = artboard->find<rive::TextValueRun>(); + REQUIRE(runObjects.size() == 22); + + artboard->advance(0.0f); + rive::NoOpRenderer renderer; + artboard->draw(&renderer); +} + +TEST_CASE("simple text loads", "[text]") +{ + auto file = ReadRiveFile("../../test/assets/hello_world.riv"); + auto artboard = file->artboard(); + + auto textObjects = artboard->find<rive::Text>(); + REQUIRE(textObjects.size() == 1); + + auto styleObjects = artboard->find<rive::TextStyle>(); + REQUIRE(styleObjects.size() == 1); + + auto runObjects = artboard->find<rive::TextValueRun>(); + REQUIRE(runObjects.size() == 1); + + artboard->advance(0.0f); + rive::NoOpRenderer renderer; + artboard->draw(&renderer); +} \ No newline at end of file
diff --git a/viewer/build/premake5_viewer.lua b/viewer/build/premake5_viewer.lua index 26cce3d..3e5f9fb 100644 --- a/viewer/build/premake5_viewer.lua +++ b/viewer/build/premake5_viewer.lua
@@ -45,7 +45,6 @@ links { 'rive', 'rive_harfbuzz', - -- 'rive_fribidi' 'rive_sheenbidi' }
diff --git a/viewer/include/viewer/viewer_host.hpp b/viewer/include/viewer/viewer_host.hpp index 1993cfe..00ddb5a 100644 --- a/viewer/include/viewer/viewer_host.hpp +++ b/viewer/include/viewer/viewer_host.hpp
@@ -7,7 +7,7 @@ #include "rive/factory.hpp" #include "rive/renderer.hpp" -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #include "sokol_gfx.h"
diff --git a/viewer/src/viewer_content/text_content.cpp b/viewer/src/viewer_content/text_content.cpp index a84612f..405130f 100644 --- a/viewer/src/viewer_content/text_content.cpp +++ b/viewer/src/viewer_content/text_content.cpp
@@ -3,12 +3,12 @@ */ #include "viewer/viewer_content.hpp" -#include "utils/rive_utf.hpp" +#include "rive/text/utf.hpp" #include "rive/math/raw_path.hpp" #include "rive/factory.hpp" #include "rive/refcnt.hpp" -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #include "rive/text/font_hb.hpp" #include <algorithm>
diff --git a/viewer/src/viewer_content/textpath_content.cpp b/viewer/src/viewer_content/textpath_content.cpp index 4f90389..7c9071b 100644 --- a/viewer/src/viewer_content/textpath_content.cpp +++ b/viewer/src/viewer_content/textpath_content.cpp
@@ -4,12 +4,12 @@ */ #include "viewer/viewer_content.hpp" -#include "utils/rive_utf.hpp" +#include "rive/text/utf.hpp" #include "rive/math/raw_path.hpp" #include "rive/refcnt.hpp" #include "rive/factory.hpp" -#include "rive/text.hpp" +#include "rive/text_engine.hpp" #include "rive/math/contour_measure.hpp" using namespace rive;