Yoga layout runtimes Starting to implement layout component with Yoga in CPP. Layouts works on iOS, macOS, Android and web runtimes. https://github.com/rive-app/rive/assets/186340/e09e639a-d38e-46b8-951d-a5ecc392b53a Diffs= 6c76b425f Yoga layout runtimes (#6787) Co-authored-by: Luigi Rosso <luigi.rosso@gmail.com> Co-authored-by: Philip Chung <philterdesign@gmail.com>
diff --git a/.rive_head b/.rive_head index 918acd8..499eaf1 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -097b68f5616951d83b0c5b28345e6bfc9f0b1a5b +6c76b425f887a8adb3beb8ba8263200b9cdbb2c9
diff --git a/build.sh b/build.sh index ec40d62..e89795d 100755 --- a/build.sh +++ b/build.sh
@@ -32,8 +32,8 @@ else build() { echo "Building Rive for platform=$platform option=$OPTION" - echo premake5 gmake2 --with_rive_text --with_rive_audio=system "$1" - PREMAKE="premake5 gmake2 --with_rive_text --with_rive_audio=system $1" + echo premake5 gmake2 --with_rive_text --with_rive_audio=system --with_rive_layout "$1" + PREMAKE="premake5 gmake2 --with_rive_text --with_rive_audio=system --with_rive_layout $1" eval "$PREMAKE" if [ "$OPTION" = "clean" ]; then make clean
diff --git a/build/premake5.lua b/build/premake5.lua index dfe2dd1..576761c 100644 --- a/build/premake5.lua +++ b/build/premake5.lua
@@ -25,11 +25,16 @@ 'MA_NO_RESOURCE_MANAGER', }) end +filter({ 'options:with_rive_layout' }) +do + defines({ 'WITH_RIVE_LAYOUT' }) +end filter({}) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_harfbuzz.lua')) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_sheenbidi.lua')) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_miniaudio.lua')) +dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_yoga.lua')) project('rive') do @@ -43,8 +48,11 @@ harfbuzz .. '/src', sheenbidi .. '/Headers', miniaudio, + yoga }) + defines({ 'YOGA_EXPORT=' }) + files({ '../src/**.cpp' }) flags({ 'FatalCompileWarnings' }) @@ -205,3 +213,8 @@ description = 'The audio mode to use.', allowed = { { 'disabled' }, { 'system' }, { 'external' } }, }) + +newoption({ + trigger = 'with_rive_layout', + description = 'Compiles in layout features.', +}) \ No newline at end of file
diff --git a/dependencies/premake5_yoga.lua b/dependencies/premake5_yoga.lua new file mode 100644 index 0000000..d4149e7 --- /dev/null +++ b/dependencies/premake5_yoga.lua
@@ -0,0 +1,138 @@ +local dependency = require('dependency') +yoga = dependency.github('rive-app/yoga', 'rive_changes_v2_0_1') + +workspace('rive') +configurations({ 'debug', 'release' }) + +project('rive_yoga') +do + kind('StaticLib') + language('C++') + cppdialect('C++11') + targetdir('%{cfg.system}/cache/bin/%{cfg.buildcfg}/') + objdir('%{cfg.system}/cache/obj/%{cfg.buildcfg}/') + warnings('Off') + + defines({ 'YOGA_EXPORT=' }) + + includedirs({ yoga }) + + files({ + yoga .. '/yoga/Utils.cpp', + yoga .. '/yoga/YGConfig.cpp', + yoga .. '/yoga/YGLayout.cpp', + yoga .. '/yoga/YGEnums.cpp', + yoga .. '/yoga/YGNodePrint.cpp', + yoga .. '/yoga/YGNode.cpp', + yoga .. '/yoga/YGValue.cpp', + yoga .. '/yoga/YGStyle.cpp', + yoga .. '/yoga/Yoga.cpp', + yoga .. '/yoga/event/event.cpp', + yoga .. '/yoga/log.cpp', + }) + + buildoptions({ '-Wall', '-pedantic' }) + + linkoptions({ '-r' }) + + filter('system:emscripten') + do + buildoptions({ '-pthread' }) + end + + filter('configurations:debug') + do + defines({ 'DEBUG' }) + symbols('On') + end + + filter('toolset:clang') + do + flags({ 'FatalWarnings' }) + buildoptions({ + '-Werror=format', + '-Wimplicit-int-conversion', + '-Werror=vla', + }) + end + + filter('configurations:release') + do + buildoptions({ '-Oz' }) + defines({ 'RELEASE', 'NDEBUG' }) + optimize('On') + end + + filter({ 'system:macosx', 'options:variant=runtime' }) + do + buildoptions({ + '-Wimplicit-float-conversion -fembed-bitcode -arch arm64 -arch x86_64 -isysroot' + .. (os.getenv('MACOS_SYSROOT') or ''), + }) + end + + filter({ 'system:macosx', 'configurations:release' }) + do + buildoptions({ '-flto=full' }) + end + + filter({ 'system:ios' }) + do + buildoptions({ '-flto=full' }) + end + + filter('system:windows') + do + architecture('x64') + defines({ '_USE_MATH_DEFINES' }) + end + + filter({ 'system:ios', 'options:variant=system' }) + do + buildoptions({ + '-mios-version-min=13.0 -fembed-bitcode -arch arm64 -isysroot ' + .. (os.getenv('IOS_SYSROOT') or ''), + }) + end + + filter({ 'system:ios', 'options:variant=emulator' }) + do + buildoptions({ + '--target=arm64-apple-ios13.0.0-simulator', + '-mios-version-min=13.0 -arch arm64 -arch x86_64 -isysroot ' + .. (os.getenv('IOS_SYSROOT') or ''), + }) + targetdir('%{cfg.system}_sim/cache/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}_sim/cache/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'configurations:release' }) + do + buildoptions({ '-flto=full' }) + end + + -- Is there a way to pass 'arch' as a variable here? + filter({ 'system:android', 'options:arch=x86' }) + do + targetdir('%{cfg.system}/cache/x86/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/x86/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=x64' }) + do + targetdir('%{cfg.system}/cache/x64/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/x64/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=arm' }) + do + targetdir('%{cfg.system}/cache/arm/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/arm/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=arm64' }) + do + targetdir('%{cfg.system}/cache/arm64/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/arm64/obj/%{cfg.buildcfg}') + end +end
diff --git a/dependencies/premake5_yoga_v2.lua b/dependencies/premake5_yoga_v2.lua new file mode 100644 index 0000000..4583132 --- /dev/null +++ b/dependencies/premake5_yoga_v2.lua
@@ -0,0 +1,28 @@ +dofile('rive_build_config.lua') + +local dependency = require('dependency') +yoga = dependency.github('rive-app/yoga', 'rive_changes_v2_0_1') + +project('rive_yoga') +do + kind('StaticLib') + warnings('Off') + + defines({ 'YOGA_EXPORT=' }) + + includedirs({ yoga }) + + files({ + yoga .. '/yoga/Utils.cpp', + yoga .. '/yoga/YGConfig.cpp', + yoga .. '/yoga/YGLayout.cpp', + yoga .. '/yoga/YGEnums.cpp', + yoga .. '/yoga/YGNodePrint.cpp', + yoga .. '/yoga/YGNode.cpp', + yoga .. '/yoga/YGValue.cpp', + yoga .. '/yoga/YGStyle.cpp', + yoga .. '/yoga/Yoga.cpp', + yoga .. '/yoga/event/event.cpp', + yoga .. '/yoga/log.cpp', + }) +end
diff --git a/dev/defs/artboard.json b/dev/defs/artboard.json index 628ce9b..4d482a9 100644 --- a/dev/defs/artboard.json +++ b/dev/defs/artboard.json
@@ -4,35 +4,8 @@ "int": 1, "string": "artboard" }, - "extends": "world_transform_component.json", + "extends": "layout_component.json", "properties": { - "clip": { - "type": "bool", - "initialValue": "true", - "key": { - "int": 196, - "string": "clip" - }, - "description": "True when the artboard bounds clip its contents." - }, - "width": { - "type": "double", - "initialValue": "0", - "key": { - "int": 7, - "string": "w" - }, - "description": "Width of the artboard." - }, - "height": { - "type": "double", - "initialValue": "0", - "key": { - "int": 8, - "string": "h" - }, - "description": "Height of the artboard." - }, "x": { "type": "double", "initialValue": "0",
diff --git a/dev/defs/layout/layout_component_style.json b/dev/defs/layout/layout_component_style.json new file mode 100644 index 0000000..d3da49c --- /dev/null +++ b/dev/defs/layout/layout_component_style.json
@@ -0,0 +1,307 @@ +{ + "name": "LayoutComponentStyle", + "key": { + "int": 420, + "string": "layoutcomponentstyle" + }, + "extends": "component.json", + "properties": { + "layoutFlags0": { + "type": "uint", + "initialValue": "0x5000412", + "key": { + "int": 495, + "string": "layoutflags0" + }, + "description": "First BitFlags for layout styles." + }, + "layoutFlags1": { + "type": "uint", + "initialValue": "0x00", + "key": { + "int": 496, + "string": "layoutflags1" + }, + "description": "Second BitFlags for layout styles." + }, + "layoutFlags2": { + "type": "uint", + "initialValue": "0x00", + "key": { + "int": 497, + "string": "layoutflags2" + }, + "description": "Third BitFlags for layout styles." + }, + "gapHorizontal": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 498, + "string": "gaphorizontal" + }, + "description": "Horizontal gap between children in layout component" + }, + "gapVertical": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 499, + "string": "gapvertical" + }, + "description": "Vertical gap between children in layout component" + }, + "maxWidth": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 500, + "string": "maxwidth" + }, + "description": "Max width of the item." + }, + "maxHeight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 501, + "string": "maxheight" + }, + "description": "Max height of the item." + }, + "minWidth": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 502, + "string": "minwidth" + }, + "description": "Min width of the item." + }, + "minHeight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 503, + "string": "minheight" + }, + "description": "Min height of the item." + }, + "borderLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 504, + "string": "borderleft" + }, + "description": "Left border value." + }, + "borderRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 505, + "string": "borderright" + }, + "description": "Right border value." + }, + "borderTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 506, + "string": "bordertop" + }, + "description": "Top border value." + }, + "borderBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 507, + "string": "borderbottom" + }, + "description": "Bottom border value." + }, + "marginLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 508, + "string": "marginleft" + }, + "description": "Left margin value." + }, + "marginRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 509, + "string": "marginright" + }, + "description": "Right margin value." + }, + "marginTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 510, + "string": "margintop" + }, + "description": "Top margin value." + }, + "marginBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 511, + "string": "marginbottom" + }, + "description": "Bottom margin value." + }, + "paddingLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 512, + "string": "paddingleft" + }, + "description": "Left padding value." + }, + "paddingRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 513, + "string": "paddingright" + }, + "description": "Right padding value." + }, + "paddingTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 514, + "string": "paddingtop" + }, + "description": "Top padding value." + }, + "paddingBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 515, + "string": "paddingbottom" + }, + "description": "Bottom padding value." + }, + "positionLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 516, + "string": "positionleft" + }, + "description": "Left position value." + }, + "positionRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 517, + "string": "positionright" + }, + "description": "Right position value." + }, + "positionTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 518, + "string": "positiontop" + }, + "description": "Top position value." + }, + "positionBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 519, + "string": "positionbottom" + }, + "description": "Bottom position value." + }, + "flex": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 520, + "string": "flex" + }, + "description": "Flex value." + }, + "flexGrow": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 521, + "string": "flexgrow" + }, + "description": "Flex grow value." + }, + "flexShrink": { + "type": "double", + "initialValue": "1", + "animates": true, + "key": { + "int": 522, + "string": "flexshrink" + }, + "description": "Flex shrink value." + }, + "flexBasis": { + "type": "double", + "initialValue": "1", + "animates": true, + "key": { + "int": 523, + "string": "flexbasis" + }, + "description": "Flex basis value." + }, + "aspectRatio": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 524, + "string": "aspectratio" + }, + "description": "Aspect ratio value." + } + } +} \ No newline at end of file
diff --git a/dev/defs/layout_component.json b/dev/defs/layout_component.json new file mode 100644 index 0000000..ac24d51 --- /dev/null +++ b/dev/defs/layout_component.json
@@ -0,0 +1,50 @@ +{ + "name": "LayoutComponent", + "key": { + "int": 409, + "string": "layoutcomponent" + }, + "extends": "world_transform_component.json", + "properties": { + "clip": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 196, + "string": "clip" + }, + "description": "True when the layout component bounds clip its contents." + }, + "width": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 7, + "string": "w" + }, + "description": "Initial width of the item." + }, + "height": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 8, + "string": "h" + }, + "description": "Initial height of the item." + }, + "styleId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 494, + "string": "styleid" + }, + "description": "LayoutStyle that defines the styling for this LayoutComponent" + } + } +} \ No newline at end of file
diff --git a/dev/defs/layout_component_absolute.json b/dev/defs/layout_component_absolute.json new file mode 100644 index 0000000..a239d43 --- /dev/null +++ b/dev/defs/layout_component_absolute.json
@@ -0,0 +1,19 @@ +{ + "name": "AbsoluteLayoutComponent", + "key": { + "int": 423, + "string": "absolutelayoutcomponent" + }, + "extends": "layout_component.json", + "properties": { + "constraints": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 535, + "string": "constraints" + }, + "runtime": false + } + } +} \ No newline at end of file
diff --git a/dev/defs/nested_artboard.json b/dev/defs/nested_artboard.json index abe086a..e6a5211 100644 --- a/dev/defs/nested_artboard.json +++ b/dev/defs/nested_artboard.json
@@ -16,6 +16,22 @@ "string": "artboardid" }, "description": "Identifier used to track the Artboard nested." + }, + "fit": { + "type": "uint", + "key": { + "int": 538, + "string": "fit" + }, + "description": "Fit type for the nested artboard's runtime artboard." + }, + "alignment": { + "type": "uint", + "key": { + "int": 539, + "string": "alignment" + }, + "description": "Alignment type for the nested artboard's runtime artboard." } } } \ No newline at end of file
diff --git a/dev/test.sh b/dev/test.sh index 4e4047b..0167ca2 100755 --- a/dev/test.sh +++ b/dev/test.sh
@@ -75,7 +75,7 @@ popd export PREMAKE_PATH="$RUNTIME/dependencies/export-compile-commands":"$RUNTIME/build":"$PREMAKE_PATH" -PREMAKE_COMMANDS="--with_rive_text --with_rive_audio=external --config=$CONFIG" +PREMAKE_COMMANDS="--with_rive_text --with_rive_audio=external --with_rive_layout --config=$CONFIG" out_dir() { echo "out/$CONFIG"
diff --git a/dev/test/premake5.lua b/dev/test/premake5.lua index 7fe5e83..00db702 100644 --- a/dev/test/premake5.lua +++ b/dev/test/premake5.lua
@@ -7,6 +7,8 @@ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO', 'WITH_RIVE_AUDIO_TOOLS', + 'WITH_RIVE_LAYOUT', + 'YOGA_EXPORT=' }) dofile(path.join(path.getabsolute('../../'), 'premake5_v2.lua')) @@ -22,12 +24,14 @@ '../../include', '../../decoders/include', miniaudio, + yoga, }) links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi', + 'rive_yoga', 'rive_decoders', 'libpng', 'zlib',
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index d93d91d..97d6afd 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -15,6 +15,7 @@ #include "rive/math/raw_path.hpp" #include <queue> +#include <unordered_set> #include <vector> namespace rive @@ -64,6 +65,9 @@ Drawable* m_FirstDrawable = nullptr; bool m_IsInstance = false; bool m_FrameOrigin = true; + std::unordered_set<LayoutComponent*> m_dirtyLayout; + float m_originalWidth = 0; + float m_originalHeight = 0; #ifdef EXTERNAL_RIVE_AUDIO_ENGINE rcp<AudioEngine> m_audioEngine; @@ -106,6 +110,11 @@ void update(ComponentDirt value) override; void onDirty(ComponentDirt dirt) override; + void markLayoutDirty(LayoutComponent* layoutComponent) + { + m_dirtyLayout.insert(layoutComponent); + } + bool advance(double elapsedSeconds); bool hasChangedDrawOrderInLastUpdate() { return m_HasChangedDrawOrderInLastUpdate; } Drawable* firstDrawable() { return m_FirstDrawable; } @@ -129,6 +138,9 @@ NestedArtboard* nestedArtboard(const std::string& name) const; NestedArtboard* nestedArtboardAtPath(const std::string& path) const; + float originalWidth() { return m_originalWidth; } + float originalHeight() { return m_originalHeight; } + AABB bounds() const; // Can we hide these from the public? (they use playable) @@ -219,6 +231,8 @@ artboardClone->m_Factory = m_Factory; artboardClone->m_FrameOrigin = m_FrameOrigin; artboardClone->m_IsInstance = true; + artboardClone->m_originalWidth = m_originalWidth; + artboardClone->m_originalHeight = m_originalHeight; std::vector<Core*>& cloneObjects = artboardClone->m_Objects; cloneObjects.push_back(artboardClone.get()); @@ -267,6 +281,23 @@ /// relative to the bounds. void frameOrigin(bool value); + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + bool result = ArtboardBase::deserialize(propertyKey, reader); + switch (propertyKey) + { + case widthPropertyKey: + m_originalWidth = width(); + break; + case heightPropertyKey: + m_originalHeight = height(); + break; + default: + break; + } + return result; + } + StatusCode import(ImportStack& importStack) override; float volume() const;
diff --git a/include/rive/bounds_provider.hpp b/include/rive/bounds_provider.hpp new file mode 100644 index 0000000..7f3ca83 --- /dev/null +++ b/include/rive/bounds_provider.hpp
@@ -0,0 +1,17 @@ +#ifndef _RIVE_BOUNDS_PROVIDER_HPP_ +#define _RIVE_BOUNDS_PROVIDER_HPP_ + +#include "rive/math/aabb.hpp" +#include "rive/math/mat2d.hpp" + +namespace rive +{ + +class BoundsProvider +{ +public: + virtual ~BoundsProvider() {} + virtual AABB computeBounds(Mat2D toParent); +}; +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/generated/artboard_base.hpp b/include/rive/generated/artboard_base.hpp index feac51f..54a378b 100644 --- a/include/rive/generated/artboard_base.hpp +++ b/include/rive/generated/artboard_base.hpp
@@ -1,15 +1,14 @@ #ifndef _RIVE_ARTBOARD_BASE_HPP_ #define _RIVE_ARTBOARD_BASE_HPP_ -#include "rive/core/field_types/core_bool_type.hpp" #include "rive/core/field_types/core_double_type.hpp" #include "rive/core/field_types/core_uint_type.hpp" -#include "rive/world_transform_component.hpp" +#include "rive/layout_component.hpp" namespace rive { -class ArtboardBase : public WorldTransformComponent +class ArtboardBase : public LayoutComponent { protected: - typedef WorldTransformComponent Super; + typedef LayoutComponent Super; public: static const uint16_t typeKey = 1; @@ -21,6 +20,7 @@ switch (typeKey) { case ArtboardBase::typeKey: + case LayoutComponentBase::typeKey: case WorldTransformComponentBase::typeKey: case ContainerComponentBase::typeKey: case ComponentBase::typeKey: @@ -32,9 +32,6 @@ uint16_t coreType() const override { return typeKey; } - static const uint16_t clipPropertyKey = 196; - static const uint16_t widthPropertyKey = 7; - static const uint16_t heightPropertyKey = 8; static const uint16_t xPropertyKey = 9; static const uint16_t yPropertyKey = 10; static const uint16_t originXPropertyKey = 11; @@ -42,9 +39,6 @@ static const uint16_t defaultStateMachineIdPropertyKey = 236; private: - bool m_Clip = true; - float m_Width = 0.0f; - float m_Height = 0.0f; float m_X = 0.0f; float m_Y = 0.0f; float m_OriginX = 0.0f; @@ -52,39 +46,6 @@ uint32_t m_DefaultStateMachineId = -1; public: - inline bool clip() const { return m_Clip; } - void clip(bool value) - { - if (m_Clip == value) - { - return; - } - m_Clip = value; - clipChanged(); - } - - 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(); - } - inline float x() const { return m_X; } void x(float value) { @@ -143,30 +104,18 @@ Core* clone() const override; void copy(const ArtboardBase& object) { - m_Clip = object.m_Clip; - m_Width = object.m_Width; - m_Height = object.m_Height; m_X = object.m_X; m_Y = object.m_Y; m_OriginX = object.m_OriginX; m_OriginY = object.m_OriginY; m_DefaultStateMachineId = object.m_DefaultStateMachineId; - WorldTransformComponent::copy(object); + LayoutComponent::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { switch (propertyKey) { - case clipPropertyKey: - m_Clip = CoreBoolType::deserialize(reader); - return true; - case widthPropertyKey: - m_Width = CoreDoubleType::deserialize(reader); - return true; - case heightPropertyKey: - m_Height = CoreDoubleType::deserialize(reader); - return true; case xPropertyKey: m_X = CoreDoubleType::deserialize(reader); return true; @@ -183,13 +132,10 @@ m_DefaultStateMachineId = CoreUintType::deserialize(reader); return true; } - return WorldTransformComponent::deserialize(propertyKey, reader); + return LayoutComponent::deserialize(propertyKey, reader); } protected: - virtual void clipChanged() {} - virtual void widthChanged() {} - virtual void heightChanged() {} virtual void xChanged() {} virtual void yChanged() {} virtual void originXChanged() {}
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index c306c1f..f941648 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -104,6 +104,9 @@ #include "rive/drawable.hpp" #include "rive/event.hpp" #include "rive/joystick.hpp" +#include "rive/layout/layout_component_style.hpp" +#include "rive/layout_component.hpp" +#include "rive/layout_component_absolute.hpp" #include "rive/nested_animation.hpp" #include "rive/nested_artboard.hpp" #include "rive/node.hpp" @@ -183,6 +186,8 @@ return new NestedArtboard(); case SoloBase::typeKey: return new Solo(); + case LayoutComponentStyleBase::typeKey: + return new LayoutComponentStyle(); case ListenerFireEventBase::typeKey: return new ListenerFireEvent(); case NestedSimpleAnimationBase::typeKey: @@ -325,12 +330,16 @@ return new DrawRules(); case CustomPropertyBooleanBase::typeKey: return new CustomPropertyBoolean(); + case LayoutComponentBase::typeKey: + return new LayoutComponent(); case ArtboardBase::typeKey: return new Artboard(); case JoystickBase::typeKey: return new Joystick(); case BackboardBase::typeKey: return new Backboard(); + case AbsoluteLayoutComponentBase::typeKey: + return new AbsoluteLayoutComponent(); case OpenUrlEventBase::typeKey: return new OpenUrlEvent(); case WeightBase::typeKey: @@ -451,12 +460,27 @@ case NestedArtboardBase::artboardIdPropertyKey: object->as<NestedArtboardBase>()->artboardId(value); break; + case NestedArtboardBase::fitPropertyKey: + object->as<NestedArtboardBase>()->fit(value); + break; + case NestedArtboardBase::alignmentPropertyKey: + object->as<NestedArtboardBase>()->alignment(value); + break; case NestedAnimationBase::animationIdPropertyKey: object->as<NestedAnimationBase>()->animationId(value); break; case SoloBase::activeComponentIdPropertyKey: object->as<SoloBase>()->activeComponentId(value); break; + case LayoutComponentStyleBase::layoutFlags0PropertyKey: + object->as<LayoutComponentStyleBase>()->layoutFlags0(value); + break; + case LayoutComponentStyleBase::layoutFlags1PropertyKey: + object->as<LayoutComponentStyleBase>()->layoutFlags1(value); + break; + case LayoutComponentStyleBase::layoutFlags2PropertyKey: + object->as<LayoutComponentStyleBase>()->layoutFlags2(value); + break; case ListenerFireEventBase::eventIdPropertyKey: object->as<ListenerFireEventBase>()->eventId(value); break; @@ -604,6 +628,9 @@ case DrawRulesBase::drawTargetIdPropertyKey: object->as<DrawRulesBase>()->drawTargetId(value); break; + case LayoutComponentBase::styleIdPropertyKey: + object->as<LayoutComponentBase>()->styleId(value); + break; case ArtboardBase::defaultStateMachineIdPropertyKey: object->as<ArtboardBase>()->defaultStateMachineId(value); break; @@ -754,6 +781,87 @@ case NodeBase::yPropertyKey: object->as<NodeBase>()->y(value); break; + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + object->as<LayoutComponentStyleBase>()->gapHorizontal(value); + break; + case LayoutComponentStyleBase::gapVerticalPropertyKey: + object->as<LayoutComponentStyleBase>()->gapVertical(value); + break; + case LayoutComponentStyleBase::maxWidthPropertyKey: + object->as<LayoutComponentStyleBase>()->maxWidth(value); + break; + case LayoutComponentStyleBase::maxHeightPropertyKey: + object->as<LayoutComponentStyleBase>()->maxHeight(value); + break; + case LayoutComponentStyleBase::minWidthPropertyKey: + object->as<LayoutComponentStyleBase>()->minWidth(value); + break; + case LayoutComponentStyleBase::minHeightPropertyKey: + object->as<LayoutComponentStyleBase>()->minHeight(value); + break; + case LayoutComponentStyleBase::borderLeftPropertyKey: + object->as<LayoutComponentStyleBase>()->borderLeft(value); + break; + case LayoutComponentStyleBase::borderRightPropertyKey: + object->as<LayoutComponentStyleBase>()->borderRight(value); + break; + case LayoutComponentStyleBase::borderTopPropertyKey: + object->as<LayoutComponentStyleBase>()->borderTop(value); + break; + case LayoutComponentStyleBase::borderBottomPropertyKey: + object->as<LayoutComponentStyleBase>()->borderBottom(value); + break; + case LayoutComponentStyleBase::marginLeftPropertyKey: + object->as<LayoutComponentStyleBase>()->marginLeft(value); + break; + case LayoutComponentStyleBase::marginRightPropertyKey: + object->as<LayoutComponentStyleBase>()->marginRight(value); + break; + case LayoutComponentStyleBase::marginTopPropertyKey: + object->as<LayoutComponentStyleBase>()->marginTop(value); + break; + case LayoutComponentStyleBase::marginBottomPropertyKey: + object->as<LayoutComponentStyleBase>()->marginBottom(value); + break; + case LayoutComponentStyleBase::paddingLeftPropertyKey: + object->as<LayoutComponentStyleBase>()->paddingLeft(value); + break; + case LayoutComponentStyleBase::paddingRightPropertyKey: + object->as<LayoutComponentStyleBase>()->paddingRight(value); + break; + case LayoutComponentStyleBase::paddingTopPropertyKey: + object->as<LayoutComponentStyleBase>()->paddingTop(value); + break; + case LayoutComponentStyleBase::paddingBottomPropertyKey: + object->as<LayoutComponentStyleBase>()->paddingBottom(value); + break; + case LayoutComponentStyleBase::positionLeftPropertyKey: + object->as<LayoutComponentStyleBase>()->positionLeft(value); + break; + case LayoutComponentStyleBase::positionRightPropertyKey: + object->as<LayoutComponentStyleBase>()->positionRight(value); + break; + case LayoutComponentStyleBase::positionTopPropertyKey: + object->as<LayoutComponentStyleBase>()->positionTop(value); + break; + case LayoutComponentStyleBase::positionBottomPropertyKey: + object->as<LayoutComponentStyleBase>()->positionBottom(value); + break; + case LayoutComponentStyleBase::flexPropertyKey: + object->as<LayoutComponentStyleBase>()->flex(value); + break; + case LayoutComponentStyleBase::flexGrowPropertyKey: + object->as<LayoutComponentStyleBase>()->flexGrow(value); + break; + case LayoutComponentStyleBase::flexShrinkPropertyKey: + object->as<LayoutComponentStyleBase>()->flexShrink(value); + break; + case LayoutComponentStyleBase::flexBasisPropertyKey: + object->as<LayoutComponentStyleBase>()->flexBasis(value); + break; + case LayoutComponentStyleBase::aspectRatioPropertyKey: + object->as<LayoutComponentStyleBase>()->aspectRatio(value); + break; case NestedLinearAnimationBase::mixPropertyKey: object->as<NestedLinearAnimationBase>()->mix(value); break; @@ -928,11 +1036,11 @@ case CubicDetachedVertexBase::outDistancePropertyKey: object->as<CubicDetachedVertexBase>()->outDistance(value); break; - case ArtboardBase::widthPropertyKey: - object->as<ArtboardBase>()->width(value); + case LayoutComponentBase::widthPropertyKey: + object->as<LayoutComponentBase>()->width(value); break; - case ArtboardBase::heightPropertyKey: - object->as<ArtboardBase>()->height(value); + case LayoutComponentBase::heightPropertyKey: + object->as<LayoutComponentBase>()->height(value); break; case ArtboardBase::xPropertyKey: object->as<ArtboardBase>()->x(value); @@ -1171,8 +1279,8 @@ case CustomPropertyBooleanBase::propertyValuePropertyKey: object->as<CustomPropertyBooleanBase>()->propertyValue(value); break; - case ArtboardBase::clipPropertyKey: - object->as<ArtboardBase>()->clip(value); + case LayoutComponentBase::clipPropertyKey: + object->as<LayoutComponentBase>()->clip(value); break; case TextModifierRangeBase::clampPropertyKey: object->as<TextModifierRangeBase>()->clamp(value); @@ -1259,10 +1367,20 @@ return object->as<DrawableBase>()->drawableFlags(); case NestedArtboardBase::artboardIdPropertyKey: return object->as<NestedArtboardBase>()->artboardId(); + case NestedArtboardBase::fitPropertyKey: + return object->as<NestedArtboardBase>()->fit(); + case NestedArtboardBase::alignmentPropertyKey: + return object->as<NestedArtboardBase>()->alignment(); case NestedAnimationBase::animationIdPropertyKey: return object->as<NestedAnimationBase>()->animationId(); case SoloBase::activeComponentIdPropertyKey: return object->as<SoloBase>()->activeComponentId(); + case LayoutComponentStyleBase::layoutFlags0PropertyKey: + return object->as<LayoutComponentStyleBase>()->layoutFlags0(); + case LayoutComponentStyleBase::layoutFlags1PropertyKey: + return object->as<LayoutComponentStyleBase>()->layoutFlags1(); + case LayoutComponentStyleBase::layoutFlags2PropertyKey: + return object->as<LayoutComponentStyleBase>()->layoutFlags2(); case ListenerFireEventBase::eventIdPropertyKey: return object->as<ListenerFireEventBase>()->eventId(); case LayerStateBase::flagsPropertyKey: @@ -1361,6 +1479,8 @@ return object->as<ImageBase>()->assetId(); case DrawRulesBase::drawTargetIdPropertyKey: return object->as<DrawRulesBase>()->drawTargetId(); + case LayoutComponentBase::styleIdPropertyKey: + return object->as<LayoutComponentBase>()->styleId(); case ArtboardBase::defaultStateMachineIdPropertyKey: return object->as<ArtboardBase>()->defaultStateMachineId(); case JoystickBase::xIdPropertyKey: @@ -1464,6 +1584,60 @@ return object->as<NodeBase>()->x(); case NodeBase::yPropertyKey: return object->as<NodeBase>()->y(); + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + return object->as<LayoutComponentStyleBase>()->gapHorizontal(); + case LayoutComponentStyleBase::gapVerticalPropertyKey: + return object->as<LayoutComponentStyleBase>()->gapVertical(); + case LayoutComponentStyleBase::maxWidthPropertyKey: + return object->as<LayoutComponentStyleBase>()->maxWidth(); + case LayoutComponentStyleBase::maxHeightPropertyKey: + return object->as<LayoutComponentStyleBase>()->maxHeight(); + case LayoutComponentStyleBase::minWidthPropertyKey: + return object->as<LayoutComponentStyleBase>()->minWidth(); + case LayoutComponentStyleBase::minHeightPropertyKey: + return object->as<LayoutComponentStyleBase>()->minHeight(); + case LayoutComponentStyleBase::borderLeftPropertyKey: + return object->as<LayoutComponentStyleBase>()->borderLeft(); + case LayoutComponentStyleBase::borderRightPropertyKey: + return object->as<LayoutComponentStyleBase>()->borderRight(); + case LayoutComponentStyleBase::borderTopPropertyKey: + return object->as<LayoutComponentStyleBase>()->borderTop(); + case LayoutComponentStyleBase::borderBottomPropertyKey: + return object->as<LayoutComponentStyleBase>()->borderBottom(); + case LayoutComponentStyleBase::marginLeftPropertyKey: + return object->as<LayoutComponentStyleBase>()->marginLeft(); + case LayoutComponentStyleBase::marginRightPropertyKey: + return object->as<LayoutComponentStyleBase>()->marginRight(); + case LayoutComponentStyleBase::marginTopPropertyKey: + return object->as<LayoutComponentStyleBase>()->marginTop(); + case LayoutComponentStyleBase::marginBottomPropertyKey: + return object->as<LayoutComponentStyleBase>()->marginBottom(); + case LayoutComponentStyleBase::paddingLeftPropertyKey: + return object->as<LayoutComponentStyleBase>()->paddingLeft(); + case LayoutComponentStyleBase::paddingRightPropertyKey: + return object->as<LayoutComponentStyleBase>()->paddingRight(); + case LayoutComponentStyleBase::paddingTopPropertyKey: + return object->as<LayoutComponentStyleBase>()->paddingTop(); + case LayoutComponentStyleBase::paddingBottomPropertyKey: + return object->as<LayoutComponentStyleBase>()->paddingBottom(); + case LayoutComponentStyleBase::positionLeftPropertyKey: + return object->as<LayoutComponentStyleBase>()->positionLeft(); + case LayoutComponentStyleBase::positionRightPropertyKey: + return object->as<LayoutComponentStyleBase>()->positionRight(); + case LayoutComponentStyleBase::positionTopPropertyKey: + return object->as<LayoutComponentStyleBase>()->positionTop(); + case LayoutComponentStyleBase::positionBottomPropertyKey: + return object->as<LayoutComponentStyleBase>()->positionBottom(); + case LayoutComponentStyleBase::flexPropertyKey: + return object->as<LayoutComponentStyleBase>()->flex(); + case LayoutComponentStyleBase::flexGrowPropertyKey: + return object->as<LayoutComponentStyleBase>()->flexGrow(); + case LayoutComponentStyleBase::flexShrinkPropertyKey: + return object->as<LayoutComponentStyleBase>()->flexShrink(); + case LayoutComponentStyleBase::flexBasisPropertyKey: + return object->as<LayoutComponentStyleBase>()->flexBasis(); + case LayoutComponentStyleBase::aspectRatioPropertyKey: + return object->as<LayoutComponentStyleBase>()->aspectRatio(); case NestedLinearAnimationBase::mixPropertyKey: return object->as<NestedLinearAnimationBase>()->mix(); case NestedSimpleAnimationBase::speedPropertyKey: @@ -1580,10 +1754,10 @@ return object->as<CubicDetachedVertexBase>()->outRotation(); case CubicDetachedVertexBase::outDistancePropertyKey: return object->as<CubicDetachedVertexBase>()->outDistance(); - case ArtboardBase::widthPropertyKey: - return object->as<ArtboardBase>()->width(); - case ArtboardBase::heightPropertyKey: - return object->as<ArtboardBase>()->height(); + case LayoutComponentBase::widthPropertyKey: + return object->as<LayoutComponentBase>()->width(); + case LayoutComponentBase::heightPropertyKey: + return object->as<LayoutComponentBase>()->height(); case ArtboardBase::xPropertyKey: return object->as<ArtboardBase>()->x(); case ArtboardBase::yPropertyKey: @@ -1745,8 +1919,8 @@ return object->as<ClippingShapeBase>()->isVisible(); case CustomPropertyBooleanBase::propertyValuePropertyKey: return object->as<CustomPropertyBooleanBase>()->propertyValue(); - case ArtboardBase::clipPropertyKey: - return object->as<ArtboardBase>()->clip(); + case LayoutComponentBase::clipPropertyKey: + return object->as<LayoutComponentBase>()->clip(); case TextModifierRangeBase::clampPropertyKey: return object->as<TextModifierRangeBase>()->clamp(); } @@ -1791,8 +1965,13 @@ case DrawableBase::blendModeValuePropertyKey: case DrawableBase::drawableFlagsPropertyKey: case NestedArtboardBase::artboardIdPropertyKey: + case NestedArtboardBase::fitPropertyKey: + case NestedArtboardBase::alignmentPropertyKey: case NestedAnimationBase::animationIdPropertyKey: case SoloBase::activeComponentIdPropertyKey: + case LayoutComponentStyleBase::layoutFlags0PropertyKey: + case LayoutComponentStyleBase::layoutFlags1PropertyKey: + case LayoutComponentStyleBase::layoutFlags2PropertyKey: case ListenerFireEventBase::eventIdPropertyKey: case LayerStateBase::flagsPropertyKey: case ListenerInputChangeBase::inputIdPropertyKey: @@ -1842,6 +2021,7 @@ case PolygonBase::pointsPropertyKey: case ImageBase::assetIdPropertyKey: case DrawRulesBase::drawTargetIdPropertyKey: + case LayoutComponentBase::styleIdPropertyKey: case ArtboardBase::defaultStateMachineIdPropertyKey: case JoystickBase::xIdPropertyKey: case JoystickBase::yIdPropertyKey: @@ -1891,6 +2071,33 @@ case TransformComponentBase::scaleYPropertyKey: case NodeBase::xPropertyKey: case NodeBase::yPropertyKey: + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + case LayoutComponentStyleBase::gapVerticalPropertyKey: + case LayoutComponentStyleBase::maxWidthPropertyKey: + case LayoutComponentStyleBase::maxHeightPropertyKey: + case LayoutComponentStyleBase::minWidthPropertyKey: + case LayoutComponentStyleBase::minHeightPropertyKey: + case LayoutComponentStyleBase::borderLeftPropertyKey: + case LayoutComponentStyleBase::borderRightPropertyKey: + case LayoutComponentStyleBase::borderTopPropertyKey: + case LayoutComponentStyleBase::borderBottomPropertyKey: + case LayoutComponentStyleBase::marginLeftPropertyKey: + case LayoutComponentStyleBase::marginRightPropertyKey: + case LayoutComponentStyleBase::marginTopPropertyKey: + case LayoutComponentStyleBase::marginBottomPropertyKey: + case LayoutComponentStyleBase::paddingLeftPropertyKey: + case LayoutComponentStyleBase::paddingRightPropertyKey: + case LayoutComponentStyleBase::paddingTopPropertyKey: + case LayoutComponentStyleBase::paddingBottomPropertyKey: + case LayoutComponentStyleBase::positionLeftPropertyKey: + case LayoutComponentStyleBase::positionRightPropertyKey: + case LayoutComponentStyleBase::positionTopPropertyKey: + case LayoutComponentStyleBase::positionBottomPropertyKey: + case LayoutComponentStyleBase::flexPropertyKey: + case LayoutComponentStyleBase::flexGrowPropertyKey: + case LayoutComponentStyleBase::flexShrinkPropertyKey: + case LayoutComponentStyleBase::flexBasisPropertyKey: + case LayoutComponentStyleBase::aspectRatioPropertyKey: case NestedLinearAnimationBase::mixPropertyKey: case NestedSimpleAnimationBase::speedPropertyKey: case AdvanceableStateBase::speedPropertyKey: @@ -1949,8 +2156,8 @@ case CubicDetachedVertexBase::inDistancePropertyKey: case CubicDetachedVertexBase::outRotationPropertyKey: case CubicDetachedVertexBase::outDistancePropertyKey: - case ArtboardBase::widthPropertyKey: - case ArtboardBase::heightPropertyKey: + case LayoutComponentBase::widthPropertyKey: + case LayoutComponentBase::heightPropertyKey: case ArtboardBase::xPropertyKey: case ArtboardBase::yPropertyKey: case ArtboardBase::originXPropertyKey: @@ -2029,7 +2236,7 @@ case RectangleBase::linkCornerRadiusPropertyKey: case ClippingShapeBase::isVisiblePropertyKey: case CustomPropertyBooleanBase::propertyValuePropertyKey: - case ArtboardBase::clipPropertyKey: + case LayoutComponentBase::clipPropertyKey: case TextModifierRangeBase::clampPropertyKey: return CoreBoolType::id; case KeyFrameColorBase::valuePropertyKey: @@ -2101,10 +2308,20 @@ return object->is<DrawableBase>(); case NestedArtboardBase::artboardIdPropertyKey: return object->is<NestedArtboardBase>(); + case NestedArtboardBase::fitPropertyKey: + return object->is<NestedArtboardBase>(); + case NestedArtboardBase::alignmentPropertyKey: + return object->is<NestedArtboardBase>(); case NestedAnimationBase::animationIdPropertyKey: return object->is<NestedAnimationBase>(); case SoloBase::activeComponentIdPropertyKey: return object->is<SoloBase>(); + case LayoutComponentStyleBase::layoutFlags0PropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::layoutFlags1PropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::layoutFlags2PropertyKey: + return object->is<LayoutComponentStyleBase>(); case ListenerFireEventBase::eventIdPropertyKey: return object->is<ListenerFireEventBase>(); case LayerStateBase::flagsPropertyKey: @@ -2203,6 +2420,8 @@ return object->is<ImageBase>(); case DrawRulesBase::drawTargetIdPropertyKey: return object->is<DrawRulesBase>(); + case LayoutComponentBase::styleIdPropertyKey: + return object->is<LayoutComponentBase>(); case ArtboardBase::defaultStateMachineIdPropertyKey: return object->is<ArtboardBase>(); case JoystickBase::xIdPropertyKey: @@ -2299,6 +2518,60 @@ return object->is<NodeBase>(); case NodeBase::yPropertyKey: return object->is<NodeBase>(); + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::gapVerticalPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::maxWidthPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::maxHeightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::minWidthPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::minHeightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::borderLeftPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::borderRightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::borderTopPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::borderBottomPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::marginLeftPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::marginRightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::marginTopPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::marginBottomPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::paddingLeftPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::paddingRightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::paddingTopPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::paddingBottomPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::positionLeftPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::positionRightPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::positionTopPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::positionBottomPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::flexPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::flexGrowPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::flexShrinkPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::flexBasisPropertyKey: + return object->is<LayoutComponentStyleBase>(); + case LayoutComponentStyleBase::aspectRatioPropertyKey: + return object->is<LayoutComponentStyleBase>(); case NestedLinearAnimationBase::mixPropertyKey: return object->is<NestedLinearAnimationBase>(); case NestedSimpleAnimationBase::speedPropertyKey: @@ -2415,10 +2688,10 @@ return object->is<CubicDetachedVertexBase>(); case CubicDetachedVertexBase::outDistancePropertyKey: return object->is<CubicDetachedVertexBase>(); - case ArtboardBase::widthPropertyKey: - return object->is<ArtboardBase>(); - case ArtboardBase::heightPropertyKey: - return object->is<ArtboardBase>(); + case LayoutComponentBase::widthPropertyKey: + return object->is<LayoutComponentBase>(); + case LayoutComponentBase::heightPropertyKey: + return object->is<LayoutComponentBase>(); case ArtboardBase::xPropertyKey: return object->is<ArtboardBase>(); case ArtboardBase::yPropertyKey: @@ -2573,8 +2846,8 @@ return object->is<ClippingShapeBase>(); case CustomPropertyBooleanBase::propertyValuePropertyKey: return object->is<CustomPropertyBooleanBase>(); - case ArtboardBase::clipPropertyKey: - return object->is<ArtboardBase>(); + case LayoutComponentBase::clipPropertyKey: + return object->is<LayoutComponentBase>(); case TextModifierRangeBase::clampPropertyKey: return object->is<TextModifierRangeBase>(); case NestedTriggerBase::firePropertyKey:
diff --git a/include/rive/generated/layout/layout_component_style_base.hpp b/include/rive/generated/layout/layout_component_style_base.hpp new file mode 100644 index 0000000..28dd01b --- /dev/null +++ b/include/rive/generated/layout/layout_component_style_base.hpp
@@ -0,0 +1,594 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_STYLE_BASE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_STYLE_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 LayoutComponentStyleBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 420; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case LayoutComponentStyleBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t layoutFlags0PropertyKey = 495; + static const uint16_t layoutFlags1PropertyKey = 496; + static const uint16_t layoutFlags2PropertyKey = 497; + static const uint16_t gapHorizontalPropertyKey = 498; + static const uint16_t gapVerticalPropertyKey = 499; + static const uint16_t maxWidthPropertyKey = 500; + static const uint16_t maxHeightPropertyKey = 501; + static const uint16_t minWidthPropertyKey = 502; + static const uint16_t minHeightPropertyKey = 503; + static const uint16_t borderLeftPropertyKey = 504; + static const uint16_t borderRightPropertyKey = 505; + static const uint16_t borderTopPropertyKey = 506; + static const uint16_t borderBottomPropertyKey = 507; + static const uint16_t marginLeftPropertyKey = 508; + static const uint16_t marginRightPropertyKey = 509; + static const uint16_t marginTopPropertyKey = 510; + static const uint16_t marginBottomPropertyKey = 511; + static const uint16_t paddingLeftPropertyKey = 512; + static const uint16_t paddingRightPropertyKey = 513; + static const uint16_t paddingTopPropertyKey = 514; + static const uint16_t paddingBottomPropertyKey = 515; + static const uint16_t positionLeftPropertyKey = 516; + static const uint16_t positionRightPropertyKey = 517; + static const uint16_t positionTopPropertyKey = 518; + static const uint16_t positionBottomPropertyKey = 519; + static const uint16_t flexPropertyKey = 520; + static const uint16_t flexGrowPropertyKey = 521; + static const uint16_t flexShrinkPropertyKey = 522; + static const uint16_t flexBasisPropertyKey = 523; + static const uint16_t aspectRatioPropertyKey = 524; + +private: + uint32_t m_LayoutFlags0 = 0x5000412; + uint32_t m_LayoutFlags1 = 0x00; + uint32_t m_LayoutFlags2 = 0x00; + float m_GapHorizontal = 0.0f; + float m_GapVertical = 0.0f; + float m_MaxWidth = 0.0f; + float m_MaxHeight = 0.0f; + float m_MinWidth = 0.0f; + float m_MinHeight = 0.0f; + float m_BorderLeft = 0.0f; + float m_BorderRight = 0.0f; + float m_BorderTop = 0.0f; + float m_BorderBottom = 0.0f; + float m_MarginLeft = 0.0f; + float m_MarginRight = 0.0f; + float m_MarginTop = 0.0f; + float m_MarginBottom = 0.0f; + float m_PaddingLeft = 0.0f; + float m_PaddingRight = 0.0f; + float m_PaddingTop = 0.0f; + float m_PaddingBottom = 0.0f; + float m_PositionLeft = 0.0f; + float m_PositionRight = 0.0f; + float m_PositionTop = 0.0f; + float m_PositionBottom = 0.0f; + float m_Flex = 0.0f; + float m_FlexGrow = 0.0f; + float m_FlexShrink = 1.0f; + float m_FlexBasis = 1.0f; + float m_AspectRatio = 0.0f; + +public: + inline uint32_t layoutFlags0() const { return m_LayoutFlags0; } + void layoutFlags0(uint32_t value) + { + if (m_LayoutFlags0 == value) + { + return; + } + m_LayoutFlags0 = value; + layoutFlags0Changed(); + } + + inline uint32_t layoutFlags1() const { return m_LayoutFlags1; } + void layoutFlags1(uint32_t value) + { + if (m_LayoutFlags1 == value) + { + return; + } + m_LayoutFlags1 = value; + layoutFlags1Changed(); + } + + inline uint32_t layoutFlags2() const { return m_LayoutFlags2; } + void layoutFlags2(uint32_t value) + { + if (m_LayoutFlags2 == value) + { + return; + } + m_LayoutFlags2 = value; + layoutFlags2Changed(); + } + + inline float gapHorizontal() const { return m_GapHorizontal; } + void gapHorizontal(float value) + { + if (m_GapHorizontal == value) + { + return; + } + m_GapHorizontal = value; + gapHorizontalChanged(); + } + + inline float gapVertical() const { return m_GapVertical; } + void gapVertical(float value) + { + if (m_GapVertical == value) + { + return; + } + m_GapVertical = value; + gapVerticalChanged(); + } + + inline float maxWidth() const { return m_MaxWidth; } + void maxWidth(float value) + { + if (m_MaxWidth == value) + { + return; + } + m_MaxWidth = value; + maxWidthChanged(); + } + + inline float maxHeight() const { return m_MaxHeight; } + void maxHeight(float value) + { + if (m_MaxHeight == value) + { + return; + } + m_MaxHeight = value; + maxHeightChanged(); + } + + inline float minWidth() const { return m_MinWidth; } + void minWidth(float value) + { + if (m_MinWidth == value) + { + return; + } + m_MinWidth = value; + minWidthChanged(); + } + + inline float minHeight() const { return m_MinHeight; } + void minHeight(float value) + { + if (m_MinHeight == value) + { + return; + } + m_MinHeight = value; + minHeightChanged(); + } + + inline float borderLeft() const { return m_BorderLeft; } + void borderLeft(float value) + { + if (m_BorderLeft == value) + { + return; + } + m_BorderLeft = value; + borderLeftChanged(); + } + + inline float borderRight() const { return m_BorderRight; } + void borderRight(float value) + { + if (m_BorderRight == value) + { + return; + } + m_BorderRight = value; + borderRightChanged(); + } + + inline float borderTop() const { return m_BorderTop; } + void borderTop(float value) + { + if (m_BorderTop == value) + { + return; + } + m_BorderTop = value; + borderTopChanged(); + } + + inline float borderBottom() const { return m_BorderBottom; } + void borderBottom(float value) + { + if (m_BorderBottom == value) + { + return; + } + m_BorderBottom = value; + borderBottomChanged(); + } + + inline float marginLeft() const { return m_MarginLeft; } + void marginLeft(float value) + { + if (m_MarginLeft == value) + { + return; + } + m_MarginLeft = value; + marginLeftChanged(); + } + + inline float marginRight() const { return m_MarginRight; } + void marginRight(float value) + { + if (m_MarginRight == value) + { + return; + } + m_MarginRight = value; + marginRightChanged(); + } + + inline float marginTop() const { return m_MarginTop; } + void marginTop(float value) + { + if (m_MarginTop == value) + { + return; + } + m_MarginTop = value; + marginTopChanged(); + } + + inline float marginBottom() const { return m_MarginBottom; } + void marginBottom(float value) + { + if (m_MarginBottom == value) + { + return; + } + m_MarginBottom = value; + marginBottomChanged(); + } + + inline float paddingLeft() const { return m_PaddingLeft; } + void paddingLeft(float value) + { + if (m_PaddingLeft == value) + { + return; + } + m_PaddingLeft = value; + paddingLeftChanged(); + } + + inline float paddingRight() const { return m_PaddingRight; } + void paddingRight(float value) + { + if (m_PaddingRight == value) + { + return; + } + m_PaddingRight = value; + paddingRightChanged(); + } + + inline float paddingTop() const { return m_PaddingTop; } + void paddingTop(float value) + { + if (m_PaddingTop == value) + { + return; + } + m_PaddingTop = value; + paddingTopChanged(); + } + + inline float paddingBottom() const { return m_PaddingBottom; } + void paddingBottom(float value) + { + if (m_PaddingBottom == value) + { + return; + } + m_PaddingBottom = value; + paddingBottomChanged(); + } + + inline float positionLeft() const { return m_PositionLeft; } + void positionLeft(float value) + { + if (m_PositionLeft == value) + { + return; + } + m_PositionLeft = value; + positionLeftChanged(); + } + + inline float positionRight() const { return m_PositionRight; } + void positionRight(float value) + { + if (m_PositionRight == value) + { + return; + } + m_PositionRight = value; + positionRightChanged(); + } + + inline float positionTop() const { return m_PositionTop; } + void positionTop(float value) + { + if (m_PositionTop == value) + { + return; + } + m_PositionTop = value; + positionTopChanged(); + } + + inline float positionBottom() const { return m_PositionBottom; } + void positionBottom(float value) + { + if (m_PositionBottom == value) + { + return; + } + m_PositionBottom = value; + positionBottomChanged(); + } + + inline float flex() const { return m_Flex; } + void flex(float value) + { + if (m_Flex == value) + { + return; + } + m_Flex = value; + flexChanged(); + } + + inline float flexGrow() const { return m_FlexGrow; } + void flexGrow(float value) + { + if (m_FlexGrow == value) + { + return; + } + m_FlexGrow = value; + flexGrowChanged(); + } + + inline float flexShrink() const { return m_FlexShrink; } + void flexShrink(float value) + { + if (m_FlexShrink == value) + { + return; + } + m_FlexShrink = value; + flexShrinkChanged(); + } + + inline float flexBasis() const { return m_FlexBasis; } + void flexBasis(float value) + { + if (m_FlexBasis == value) + { + return; + } + m_FlexBasis = value; + flexBasisChanged(); + } + + inline float aspectRatio() const { return m_AspectRatio; } + void aspectRatio(float value) + { + if (m_AspectRatio == value) + { + return; + } + m_AspectRatio = value; + aspectRatioChanged(); + } + + Core* clone() const override; + void copy(const LayoutComponentStyleBase& object) + { + m_LayoutFlags0 = object.m_LayoutFlags0; + m_LayoutFlags1 = object.m_LayoutFlags1; + m_LayoutFlags2 = object.m_LayoutFlags2; + m_GapHorizontal = object.m_GapHorizontal; + m_GapVertical = object.m_GapVertical; + m_MaxWidth = object.m_MaxWidth; + m_MaxHeight = object.m_MaxHeight; + m_MinWidth = object.m_MinWidth; + m_MinHeight = object.m_MinHeight; + m_BorderLeft = object.m_BorderLeft; + m_BorderRight = object.m_BorderRight; + m_BorderTop = object.m_BorderTop; + m_BorderBottom = object.m_BorderBottom; + m_MarginLeft = object.m_MarginLeft; + m_MarginRight = object.m_MarginRight; + m_MarginTop = object.m_MarginTop; + m_MarginBottom = object.m_MarginBottom; + m_PaddingLeft = object.m_PaddingLeft; + m_PaddingRight = object.m_PaddingRight; + m_PaddingTop = object.m_PaddingTop; + m_PaddingBottom = object.m_PaddingBottom; + m_PositionLeft = object.m_PositionLeft; + m_PositionRight = object.m_PositionRight; + m_PositionTop = object.m_PositionTop; + m_PositionBottom = object.m_PositionBottom; + m_Flex = object.m_Flex; + m_FlexGrow = object.m_FlexGrow; + m_FlexShrink = object.m_FlexShrink; + m_FlexBasis = object.m_FlexBasis; + m_AspectRatio = object.m_AspectRatio; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case layoutFlags0PropertyKey: + m_LayoutFlags0 = CoreUintType::deserialize(reader); + return true; + case layoutFlags1PropertyKey: + m_LayoutFlags1 = CoreUintType::deserialize(reader); + return true; + case layoutFlags2PropertyKey: + m_LayoutFlags2 = CoreUintType::deserialize(reader); + return true; + case gapHorizontalPropertyKey: + m_GapHorizontal = CoreDoubleType::deserialize(reader); + return true; + case gapVerticalPropertyKey: + m_GapVertical = CoreDoubleType::deserialize(reader); + return true; + case maxWidthPropertyKey: + m_MaxWidth = CoreDoubleType::deserialize(reader); + return true; + case maxHeightPropertyKey: + m_MaxHeight = CoreDoubleType::deserialize(reader); + return true; + case minWidthPropertyKey: + m_MinWidth = CoreDoubleType::deserialize(reader); + return true; + case minHeightPropertyKey: + m_MinHeight = CoreDoubleType::deserialize(reader); + return true; + case borderLeftPropertyKey: + m_BorderLeft = CoreDoubleType::deserialize(reader); + return true; + case borderRightPropertyKey: + m_BorderRight = CoreDoubleType::deserialize(reader); + return true; + case borderTopPropertyKey: + m_BorderTop = CoreDoubleType::deserialize(reader); + return true; + case borderBottomPropertyKey: + m_BorderBottom = CoreDoubleType::deserialize(reader); + return true; + case marginLeftPropertyKey: + m_MarginLeft = CoreDoubleType::deserialize(reader); + return true; + case marginRightPropertyKey: + m_MarginRight = CoreDoubleType::deserialize(reader); + return true; + case marginTopPropertyKey: + m_MarginTop = CoreDoubleType::deserialize(reader); + return true; + case marginBottomPropertyKey: + m_MarginBottom = CoreDoubleType::deserialize(reader); + return true; + case paddingLeftPropertyKey: + m_PaddingLeft = CoreDoubleType::deserialize(reader); + return true; + case paddingRightPropertyKey: + m_PaddingRight = CoreDoubleType::deserialize(reader); + return true; + case paddingTopPropertyKey: + m_PaddingTop = CoreDoubleType::deserialize(reader); + return true; + case paddingBottomPropertyKey: + m_PaddingBottom = CoreDoubleType::deserialize(reader); + return true; + case positionLeftPropertyKey: + m_PositionLeft = CoreDoubleType::deserialize(reader); + return true; + case positionRightPropertyKey: + m_PositionRight = CoreDoubleType::deserialize(reader); + return true; + case positionTopPropertyKey: + m_PositionTop = CoreDoubleType::deserialize(reader); + return true; + case positionBottomPropertyKey: + m_PositionBottom = CoreDoubleType::deserialize(reader); + return true; + case flexPropertyKey: + m_Flex = CoreDoubleType::deserialize(reader); + return true; + case flexGrowPropertyKey: + m_FlexGrow = CoreDoubleType::deserialize(reader); + return true; + case flexShrinkPropertyKey: + m_FlexShrink = CoreDoubleType::deserialize(reader); + return true; + case flexBasisPropertyKey: + m_FlexBasis = CoreDoubleType::deserialize(reader); + return true; + case aspectRatioPropertyKey: + m_AspectRatio = CoreDoubleType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void layoutFlags0Changed() {} + virtual void layoutFlags1Changed() {} + virtual void layoutFlags2Changed() {} + virtual void gapHorizontalChanged() {} + virtual void gapVerticalChanged() {} + virtual void maxWidthChanged() {} + virtual void maxHeightChanged() {} + virtual void minWidthChanged() {} + virtual void minHeightChanged() {} + virtual void borderLeftChanged() {} + virtual void borderRightChanged() {} + virtual void borderTopChanged() {} + virtual void borderBottomChanged() {} + virtual void marginLeftChanged() {} + virtual void marginRightChanged() {} + virtual void marginTopChanged() {} + virtual void marginBottomChanged() {} + virtual void paddingLeftChanged() {} + virtual void paddingRightChanged() {} + virtual void paddingTopChanged() {} + virtual void paddingBottomChanged() {} + virtual void positionLeftChanged() {} + virtual void positionRightChanged() {} + virtual void positionTopChanged() {} + virtual void positionBottomChanged() {} + virtual void flexChanged() {} + virtual void flexGrowChanged() {} + virtual void flexShrinkChanged() {} + virtual void flexBasisChanged() {} + virtual void aspectRatioChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/layout_component_absolute_base.hpp b/include/rive/generated/layout_component_absolute_base.hpp new file mode 100644 index 0000000..b61486f --- /dev/null +++ b/include/rive/generated/layout_component_absolute_base.hpp
@@ -0,0 +1,39 @@ +#ifndef _RIVE_ABSOLUTE_LAYOUT_COMPONENT_BASE_HPP_ +#define _RIVE_ABSOLUTE_LAYOUT_COMPONENT_BASE_HPP_ +#include "rive/layout_component.hpp" +namespace rive +{ +class AbsoluteLayoutComponentBase : public LayoutComponent +{ +protected: + typedef LayoutComponent Super; + +public: + static const uint16_t typeKey = 423; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case AbsoluteLayoutComponentBase::typeKey: + case LayoutComponentBase::typeKey: + case WorldTransformComponentBase::typeKey: + case ContainerComponentBase::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/layout_component_base.hpp b/include/rive/generated/layout_component_base.hpp new file mode 100644 index 0000000..e0c508a --- /dev/null +++ b/include/rive/generated/layout_component_base.hpp
@@ -0,0 +1,129 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_BASE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_BASE_HPP_ +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/world_transform_component.hpp" +namespace rive +{ +class LayoutComponentBase : public WorldTransformComponent +{ +protected: + typedef WorldTransformComponent Super; + +public: + static const uint16_t typeKey = 409; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case LayoutComponentBase::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 clipPropertyKey = 196; + static const uint16_t widthPropertyKey = 7; + static const uint16_t heightPropertyKey = 8; + static const uint16_t styleIdPropertyKey = 494; + +private: + bool m_Clip = true; + float m_Width = 0.0f; + float m_Height = 0.0f; + uint32_t m_StyleId = -1; + +public: + inline bool clip() const { return m_Clip; } + void clip(bool value) + { + if (m_Clip == value) + { + return; + } + m_Clip = value; + clipChanged(); + } + + 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(); + } + + inline uint32_t styleId() const { return m_StyleId; } + void styleId(uint32_t value) + { + if (m_StyleId == value) + { + return; + } + m_StyleId = value; + styleIdChanged(); + } + + Core* clone() const override; + void copy(const LayoutComponentBase& object) + { + m_Clip = object.m_Clip; + m_Width = object.m_Width; + m_Height = object.m_Height; + m_StyleId = object.m_StyleId; + WorldTransformComponent::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case clipPropertyKey: + m_Clip = CoreBoolType::deserialize(reader); + return true; + case widthPropertyKey: + m_Width = CoreDoubleType::deserialize(reader); + return true; + case heightPropertyKey: + m_Height = CoreDoubleType::deserialize(reader); + return true; + case styleIdPropertyKey: + m_StyleId = CoreUintType::deserialize(reader); + return true; + } + return WorldTransformComponent::deserialize(propertyKey, reader); + } + +protected: + virtual void clipChanged() {} + virtual void widthChanged() {} + virtual void heightChanged() {} + virtual void styleIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/nested_artboard_base.hpp b/include/rive/generated/nested_artboard_base.hpp index ce5d874..141aa60 100644 --- a/include/rive/generated/nested_artboard_base.hpp +++ b/include/rive/generated/nested_artboard_base.hpp
@@ -34,9 +34,13 @@ uint16_t coreType() const override { return typeKey; } static const uint16_t artboardIdPropertyKey = 197; + static const uint16_t fitPropertyKey = 538; + static const uint16_t alignmentPropertyKey = 539; private: uint32_t m_ArtboardId = -1; + uint32_t m_Fit = 0; + uint32_t m_Alignment = 0; public: inline uint32_t artboardId() const { return m_ArtboardId; } @@ -50,10 +54,34 @@ artboardIdChanged(); } + inline uint32_t fit() const { return m_Fit; } + void fit(uint32_t value) + { + if (m_Fit == value) + { + return; + } + m_Fit = value; + fitChanged(); + } + + inline uint32_t alignment() const { return m_Alignment; } + void alignment(uint32_t value) + { + if (m_Alignment == value) + { + return; + } + m_Alignment = value; + alignmentChanged(); + } + Core* clone() const override; void copy(const NestedArtboardBase& object) { m_ArtboardId = object.m_ArtboardId; + m_Fit = object.m_Fit; + m_Alignment = object.m_Alignment; Drawable::copy(object); } @@ -64,12 +92,20 @@ case artboardIdPropertyKey: m_ArtboardId = CoreUintType::deserialize(reader); return true; + case fitPropertyKey: + m_Fit = CoreUintType::deserialize(reader); + return true; + case alignmentPropertyKey: + m_Alignment = CoreUintType::deserialize(reader); + return true; } return Drawable::deserialize(propertyKey, reader); } protected: virtual void artboardIdChanged() {} + virtual void fitChanged() {} + virtual void alignmentChanged() {} }; } // namespace rive
diff --git a/include/rive/layout/layout_component_style.hpp b/include/rive/layout/layout_component_style.hpp new file mode 100644 index 0000000..277373f --- /dev/null +++ b/include/rive/layout/layout_component_style.hpp
@@ -0,0 +1,134 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_STYLE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_STYLE_HPP_ +#include "rive/generated/layout/layout_component_style_base.hpp" +#include "rive/math/bit_field_loc.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "yoga/Yoga.h" +#endif +#include <stdio.h> +namespace rive +{ +// ---- Flags 0 +static BitFieldLoc DisplayBits = BitFieldLoc(0, 0); +static BitFieldLoc PositionTypeBits = BitFieldLoc(1, 2); +static BitFieldLoc FlexDirectionBits = BitFieldLoc(3, 4); +static BitFieldLoc DirectionBits = BitFieldLoc(5, 6); +static BitFieldLoc AlignContentBits = BitFieldLoc(7, 9); +static BitFieldLoc AlignItemsBits = BitFieldLoc(10, 12); +static BitFieldLoc AlignSelfBits = BitFieldLoc(13, 15); +static BitFieldLoc JustifyContentBits = BitFieldLoc(16, 18); +static BitFieldLoc FlexWrapBits = BitFieldLoc(19, 20); +static BitFieldLoc OverflowBits = BitFieldLoc(21, 22); +static BitFieldLoc IntrinsicallySizedBits = BitFieldLoc(23, 23); +static BitFieldLoc WidthUnitsBits = BitFieldLoc(24, 25); +static BitFieldLoc HeightUnitsBits = BitFieldLoc(26, 27); + +// ---- Flags 1 +static BitFieldLoc BorderLeftUnitsBits = BitFieldLoc(0, 1); +static BitFieldLoc BorderRightUnitsBits = BitFieldLoc(2, 3); +static BitFieldLoc BorderTopUnitsBits = BitFieldLoc(4, 5); +static BitFieldLoc BorderBottomUnitsBits = BitFieldLoc(6, 7); +static BitFieldLoc MarginLeftUnitsBits = BitFieldLoc(8, 9); +static BitFieldLoc MarginRightUnitsBits = BitFieldLoc(10, 11); +static BitFieldLoc MarginTopUnitsBits = BitFieldLoc(12, 13); +static BitFieldLoc MarginBottomUnitsBits = BitFieldLoc(14, 15); +static BitFieldLoc PaddingLeftUnitsBits = BitFieldLoc(16, 17); +static BitFieldLoc PaddingRightUnitsBits = BitFieldLoc(18, 19); +static BitFieldLoc PaddingTopUnitsBits = BitFieldLoc(20, 21); +static BitFieldLoc PaddingBottomUnitsBits = BitFieldLoc(22, 23); +static BitFieldLoc PositionLeftUnitsBits = BitFieldLoc(24, 25); +static BitFieldLoc PositionRightUnitsBits = BitFieldLoc(26, 27); +static BitFieldLoc PositionTopUnitsBits = BitFieldLoc(28, 29); +static BitFieldLoc PositionBottomUnitsBits = BitFieldLoc(30, 31); + +// ---- Flags 2 +static BitFieldLoc GapHorizontalUnitsBits = BitFieldLoc(0, 1); +static BitFieldLoc GapVerticalUnitsBits = BitFieldLoc(2, 3); +static BitFieldLoc MinWidthUnitsBits = BitFieldLoc(4, 5); +static BitFieldLoc MinHeightUnitsBits = BitFieldLoc(6, 7); +static BitFieldLoc MaxWidthUnitsBits = BitFieldLoc(8, 9); +static BitFieldLoc MaxHeightUnitsBits = BitFieldLoc(10, 11); + +class LayoutComponentStyle : public LayoutComponentStyleBase +{ +public: + LayoutComponentStyle() {} + +#ifdef WITH_RIVE_LAYOUT + YGDisplay display(); + YGPositionType positionType(); + + YGFlexDirection flexDirection(); + YGDirection direction(); + YGWrap flexWrap(); + YGOverflow overflow(); + + YGAlign alignItems(); + YGAlign alignSelf(); + YGAlign alignContent(); + YGJustify justifyContent(); + bool intrinsicallySized(); + YGUnit widthUnits(); + YGUnit heightUnits(); + + YGUnit borderLeftUnits(); + YGUnit borderRightUnits(); + YGUnit borderTopUnits(); + YGUnit borderBottomUnits(); + YGUnit marginLeftUnits(); + YGUnit marginRightUnits(); + YGUnit marginTopUnits(); + YGUnit marginBottomUnits(); + YGUnit paddingLeftUnits(); + YGUnit paddingRightUnits(); + YGUnit paddingTopUnits(); + YGUnit paddingBottomUnits(); + YGUnit positionLeftUnits(); + YGUnit positionRightUnits(); + YGUnit positionTopUnits(); + YGUnit positionBottomUnits(); + + YGUnit gapHorizontalUnits(); + YGUnit gapVerticalUnits(); + YGUnit maxWidthUnits(); + YGUnit maxHeightUnits(); + YGUnit minWidthUnits(); + YGUnit minHeightUnits(); +#endif + + void markLayoutNodeDirty(); + + void layoutFlags0Changed() override; + void layoutFlags1Changed() override; + void layoutFlags2Changed() override; + void flexChanged() override; + void flexGrowChanged() override; + void flexShrinkChanged() override; + void flexBasisChanged() override; + void aspectRatioChanged() override; + void gapHorizontalChanged() override; + void gapVerticalChanged() override; + void maxWidthChanged() override; + void maxHeightChanged() override; + void minWidthChanged() override; + void minHeightChanged() override; + void borderLeftChanged() override; + void borderRightChanged() override; + void borderTopChanged() override; + void borderBottomChanged() override; + void marginLeftChanged() override; + void marginRightChanged() override; + void marginTopChanged() override; + void marginBottomChanged() override; + void paddingLeftChanged() override; + void paddingRightChanged() override; + void paddingTopChanged() override; + void paddingBottomChanged() override; + void positionLeftChanged() override; + void positionRightChanged() override; + void positionTopChanged() override; + void positionBottomChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/layout_component.hpp b/include/rive/layout_component.hpp new file mode 100644 index 0000000..e7c5d55 --- /dev/null +++ b/include/rive/layout_component.hpp
@@ -0,0 +1,94 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_HPP_ +#define _RIVE_LAYOUT_COMPONENT_HPP_ +#include "rive/generated/layout_component_base.hpp" +#include "rive/layout/layout_component_style.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "yoga/YGNode.h" +#include "yoga/YGStyle.h" +#include "yoga/Yoga.h" +#endif +#include <stdio.h> +namespace rive +{ +#ifndef WITH_RIVE_LAYOUT +class YGNodeRef +{ +public: + YGNodeRef() {} +}; +class YGStyle +{ +public: + YGStyle() {} +}; +#endif +class LayoutComponent : public LayoutComponentBase +{ +private: + LayoutComponentStyle* m_style = nullptr; + YGNodeRef m_layoutNode; + YGStyle* m_layoutStyle; + float m_layoutSizeWidth = 0; + float m_layoutSizeHeight = 0; + float m_layoutLocationX = 0; + float m_layoutLocationY = 0; + +#ifdef WITH_RIVE_LAYOUT +private: + void syncLayoutChildren(); + void propagateSizeToChildren(ContainerComponent* component); + AABB findMaxIntrinsicSize(ContainerComponent* component, AABB maxIntrinsicSize); + +protected: + void calculateLayout(); +#endif + +public: + LayoutComponentStyle* style() { return m_style; } + void style(LayoutComponentStyle* style) { m_style = style; } + +#ifdef WITH_RIVE_LAYOUT + YGNodeRef layoutNode() { return m_layoutNode; } + + YGStyle* layoutStyle() { return m_layoutStyle; } + + LayoutComponent() + { + m_layoutNode = new YGNode(); + m_layoutStyle = new YGStyle(); + } + + ~LayoutComponent() + { + YGNodeFreeRecursive(m_layoutNode); + delete m_layoutStyle; + } + void syncStyle(); + void propagateSize(); + void updateLayoutBounds(); + void update(ComponentDirt value) override; + StatusCode onAddedDirty(CoreContext* context) override; + +#else + LayoutComponent() + { + m_layoutNode = YGNodeRef(); + auto s = new YGStyle(); + m_layoutStyle = s; + m_layoutSizeWidth = 0; + m_layoutSizeHeight = 0; + m_layoutLocationX = 0; + m_layoutLocationY = 0; + } +#endif + void buildDependencies() override; + + void markLayoutNodeDirty(); + void clipChanged() override; + void widthChanged() override; + void heightChanged() override; + void styleIdChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/layout_component_absolute.hpp b/include/rive/layout_component_absolute.hpp new file mode 100644 index 0000000..955f64c --- /dev/null +++ b/include/rive/layout_component_absolute.hpp
@@ -0,0 +1,13 @@ +#ifndef _RIVE_ABSOLUTE_LAYOUT_COMPONENT_HPP_ +#define _RIVE_ABSOLUTE_LAYOUT_COMPONENT_HPP_ +#include "rive/generated/layout_component_absolute_base.hpp" +#include <stdio.h> +namespace rive +{ +class AbsoluteLayoutComponent : public AbsoluteLayoutComponentBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/math/bit_field_loc.hpp b/include/rive/math/bit_field_loc.hpp new file mode 100644 index 0000000..7c0c983 --- /dev/null +++ b/include/rive/math/bit_field_loc.hpp
@@ -0,0 +1,28 @@ +#ifndef _RIVE_BIT_FIELD_LOC_HPP_ +#define _RIVE_BIT_FIELD_LOC_HPP_ + +#include <cmath> +#include <stdio.h> +#include <cstdint> +#include <tuple> +#include <vector> + +namespace rive +{ + +class BitFieldLoc +{ +public: + BitFieldLoc(uint32_t start, uint32_t end); + + uint32_t read(uint32_t bits); + uint32_t write(uint32_t bits, uint32_t value); + +private: + uint32_t m_start; + uint32_t m_count; + uint32_t m_mask; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index 14bd7c1..3eac7dc 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp
@@ -8,6 +8,31 @@ namespace rive { + +enum class NestedArtboardFitType : uint8_t +{ + fill, // Default value - scales to fill available view without maintaining aspect ratio + contain, + cover, + fitWidth, + fitHeight, + resizeArtboard, + none, +}; + +enum class NestedArtboardAlignmentType : uint8_t +{ + center, // Default value + topLeft, + topCenter, + topRight, + centerLeft, + centerRight, + bottomLeft, + bottomCenter, + bottomRight, +}; + class ArtboardInstance; class NestedAnimation; class NestedInput; @@ -20,6 +45,8 @@ Artboard* m_Artboard = nullptr; // might point to m_Instance, and might not std::unique_ptr<ArtboardInstance> m_Instance; // may be null std::vector<NestedAnimation*> m_NestedAnimations; + float m_layoutScaleX = NAN; + float m_layoutScaleY = NAN; public: NestedArtboard(); @@ -45,6 +72,17 @@ NestedInput* input(std::string name) const; NestedInput* input(std::string name, std::string stateMachineName) const; + NestedArtboardAlignmentType alignmentType() const + { + return (NestedArtboardAlignmentType)alignment(); + } + NestedArtboardFitType fitType() const { return (NestedArtboardFitType)fit(); } + float effectiveScaleX() { return std::isnan(m_layoutScaleX) ? scaleX() : m_layoutScaleX; } + float effectiveScaleY() { return std::isnan(m_layoutScaleY) ? scaleY() : m_layoutScaleY; } + + AABB computeIntrinsicSize(AABB min, AABB max) override; + void controlSize(AABB size) override; + /// Convert a world space (relative to the artboard that this /// NestedArtboard is a child of) to the local space of the Artboard /// nested within. Returns true when the conversion succeeds, and false
diff --git a/include/rive/shapes/image.hpp b/include/rive/shapes/image.hpp index 554c25b..60a0c72 100644 --- a/include/rive/shapes/image.hpp +++ b/include/rive/shapes/image.hpp
@@ -24,6 +24,8 @@ void setAsset(FileAsset*) override; uint32_t assetId() override; Core* clone() const override; + AABB computeIntrinsicSize(AABB min, AABB max) override; + void controlSize(AABB size) override; }; } // namespace rive
diff --git a/include/rive/shapes/parametric_path.hpp b/include/rive/shapes/parametric_path.hpp index 03f872a..7bb3204 100644 --- a/include/rive/shapes/parametric_path.hpp +++ b/include/rive/shapes/parametric_path.hpp
@@ -1,10 +1,15 @@ #ifndef _RIVE_PARAMETRIC_PATH_HPP_ #define _RIVE_PARAMETRIC_PATH_HPP_ +#include "rive/math/aabb.hpp" #include "rive/generated/shapes/parametric_path_base.hpp" namespace rive { class ParametricPath : public ParametricPathBase { +public: + AABB computeIntrinsicSize(AABB min, AABB max) override; + void controlSize(AABB size) override; + protected: void widthChanged() override; void heightChanged() override;
diff --git a/include/rive/text/text.hpp b/include/rive/text/text.hpp index bed19f2..073af1d 100644 --- a/include/rive/text/text.hpp +++ b/include/rive/text/text.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_TEXT_CORE_HPP_ #define _RIVE_TEXT_CORE_HPP_ #include "rive/generated/text/text_base.hpp" +#include "rive/math/aabb.hpp" #include "rive/text/text_value_run.hpp" #include "rive/text_engine.hpp" #include "rive/simple_array.hpp" @@ -170,12 +171,16 @@ Core* hitTest(HitInfo*, const Mat2D&) override; void addRun(TextValueRun* run); void addModifierGroup(TextModifierGroup* group); - void markShapeDirty(); + void markShapeDirty(bool sendToLayout = true); void modifierShapeDirty(); void markPaintDirty(); void update(ComponentDirt value) override; TextSizing sizing() const { return (TextSizing)sizingValue(); } + TextSizing effectiveSizing() const + { + return std::isnan(m_layoutHeight) ? sizing() : TextSizing::fixed; + } TextOverflow overflow() const { return (TextOverflow)overflowValue(); } TextOrigin textOrigin() const { return (TextOrigin)originValue(); } void overflow(TextOverflow value) { return overflowValue((uint32_t)value); } @@ -185,6 +190,11 @@ AABB localBounds() const override; void originXChanged() override; void originYChanged() override; + + AABB computeIntrinsicSize(AABB min, AABB max) override; + void controlSize(AABB size) override; + float effectiveWidth() { return std::isnan(m_layoutWidth) ? width() : m_layoutWidth; } + float effectiveHeight() { return std::isnan(m_layoutHeight) ? height() : m_layoutHeight; } #ifdef WITH_RIVE_TEXT const std::vector<TextValueRun*>& runs() const { return m_runs; } #endif @@ -235,6 +245,9 @@ GlyphLookup m_glyphLookup; #endif + float m_layoutWidth = NAN; + float m_layoutHeight = NAN; + AABB measure(AABB maxSize); }; } // namespace rive
diff --git a/include/rive/transform_component.hpp b/include/rive/transform_component.hpp index 8b13bce..6b63a11 100644 --- a/include/rive/transform_component.hpp +++ b/include/rive/transform_component.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_TRANSFORM_COMPONENT_HPP_ #define _RIVE_TRANSFORM_COMPONENT_HPP_ #include "rive/generated/transform_component_base.hpp" +#include "rive/math/aabb.hpp" #include "rive/math/mat2d.hpp" namespace rive @@ -47,6 +48,9 @@ void addConstraint(Constraint* constraint); virtual AABB localBounds() const; void markDirtyIfConstrained(); + + virtual AABB computeIntrinsicSize(AABB min, AABB max) { return AABB(); } + virtual void controlSize(AABB size) {} }; } // namespace rive
diff --git a/premake5_v2.lua b/premake5_v2.lua index 5311fb7..44244a2 100644 --- a/premake5_v2.lua +++ b/premake5_v2.lua
@@ -22,12 +22,17 @@ 'MA_NO_RESOURCE_MANAGER', }) end +filter({ 'options:with_rive_layout' }) +do + defines({ 'WITH_RIVE_LAYOUT' }) +end filter({}) dependencies = path.getabsolute('dependencies/') dofile(path.join(dependencies, 'premake5_harfbuzz_v2.lua')) dofile(path.join(dependencies, 'premake5_sheenbidi_v2.lua')) dofile(path.join(dependencies, 'premake5_miniaudio_v2.lua')) +dofile(path.join(dependencies, 'premake5_yoga_v2.lua')) project('rive') do @@ -38,8 +43,11 @@ harfbuzz .. '/src', sheenbidi .. '/Headers', miniaudio, + yoga, }) + defines({ 'YOGA_EXPORT=' }) + files({ 'src/**.cpp' }) flags({ 'FatalCompileWarnings' }) @@ -157,3 +165,8 @@ description = 'The audio mode to use.', allowed = { { 'disabled' }, { 'system' }, { 'external' } }, }) + +newoption({ + trigger = 'with_rive_layout', + description = 'Compiles in layout features.', +}) \ No newline at end of file
diff --git a/src/artboard.cpp b/src/artboard.cpp index 6fb08e7..9a9b741 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp
@@ -509,6 +509,26 @@ bool Artboard::advance(double elapsedSeconds) { m_HasChangedDrawOrderInLastUpdate = false; +#ifdef WITH_RIVE_LAYOUT + if (!m_dirtyLayout.empty()) + { + syncStyle(); + for (auto layout : m_dirtyLayout) + { + layout->syncStyle(); + } + m_dirtyLayout.clear(); + calculateLayout(); + for (auto dep : m_DependencyOrder) + { + if (dep->is<LayoutComponent>()) + { + auto layout = dep->as<LayoutComponent>(); + layout->updateLayoutBounds(); + } + } + } +#endif if (m_JoysticksApplyBeforeUpdate) { for (auto joystick : m_Joysticks)
diff --git a/src/generated/layout/layout_component_style_base.cpp b/src/generated/layout/layout_component_style_base.cpp new file mode 100644 index 0000000..22c4da8 --- /dev/null +++ b/src/generated/layout/layout_component_style_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/layout/layout_component_style_base.hpp" +#include "rive/layout/layout_component_style.hpp" + +using namespace rive; + +Core* LayoutComponentStyleBase::clone() const +{ + auto cloned = new LayoutComponentStyle(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/layout_component_absolute_base.cpp b/src/generated/layout_component_absolute_base.cpp new file mode 100644 index 0000000..c35c4a8 --- /dev/null +++ b/src/generated/layout_component_absolute_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/layout_component_absolute_base.hpp" +#include "rive/layout_component_absolute.hpp" + +using namespace rive; + +Core* AbsoluteLayoutComponentBase::clone() const +{ + auto cloned = new AbsoluteLayoutComponent(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/layout_component_base.cpp b/src/generated/layout_component_base.cpp new file mode 100644 index 0000000..13e6c86 --- /dev/null +++ b/src/generated/layout_component_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/layout_component_base.hpp" +#include "rive/layout_component.hpp" + +using namespace rive; + +Core* LayoutComponentBase::clone() const +{ + auto cloned = new LayoutComponent(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/layout/layout_component_style.cpp b/src/layout/layout_component_style.cpp new file mode 100644 index 0000000..882d752 --- /dev/null +++ b/src/layout/layout_component_style.cpp
@@ -0,0 +1,203 @@ +#include "rive/layout_component.hpp" +#include "rive/layout/layout_component_style.hpp" +#include <vector> + +using namespace rive; + +#ifdef WITH_RIVE_LAYOUT +YGDisplay LayoutComponentStyle::display() { return YGDisplay(DisplayBits.read(layoutFlags0())); } + +YGPositionType LayoutComponentStyle::positionType() +{ + return YGPositionType(PositionTypeBits.read(layoutFlags0())); +} + +YGFlexDirection LayoutComponentStyle::flexDirection() +{ + return YGFlexDirection(FlexDirectionBits.read(layoutFlags0())); +} + +YGDirection LayoutComponentStyle::direction() +{ + return YGDirection(DirectionBits.read(layoutFlags0())); +} + +YGWrap LayoutComponentStyle::flexWrap() { return YGWrap(FlexWrapBits.read(layoutFlags0())); } + +YGAlign LayoutComponentStyle::alignItems() { return YGAlign(AlignItemsBits.read(layoutFlags0())); } + +YGAlign LayoutComponentStyle::alignSelf() { return YGAlign(AlignSelfBits.read(layoutFlags0())); } + +YGAlign LayoutComponentStyle::alignContent() +{ + return YGAlign(AlignContentBits.read(layoutFlags0())); +} + +YGJustify LayoutComponentStyle::justifyContent() +{ + return YGJustify(JustifyContentBits.read(layoutFlags0())); +} + +YGOverflow LayoutComponentStyle::overflow() +{ + return YGOverflow(OverflowBits.read(layoutFlags0())); +} + +bool LayoutComponentStyle::intrinsicallySized() +{ + return IntrinsicallySizedBits.read(layoutFlags0()) == 1; +} + +YGUnit LayoutComponentStyle::widthUnits() { return YGUnit(WidthUnitsBits.read(layoutFlags0())); } + +YGUnit LayoutComponentStyle::heightUnits() { return YGUnit(HeightUnitsBits.read(layoutFlags0())); } + +YGUnit LayoutComponentStyle::borderLeftUnits() +{ + return YGUnit(BorderLeftUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::borderRightUnits() +{ + return YGUnit(BorderRightUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::borderTopUnits() +{ + return YGUnit(BorderTopUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::borderBottomUnits() +{ + return YGUnit(BorderBottomUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::marginLeftUnits() +{ + return YGUnit(MarginLeftUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::marginRightUnits() +{ + return YGUnit(MarginRightUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::marginTopUnits() +{ + return YGUnit(MarginTopUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::marginBottomUnits() +{ + return YGUnit(MarginBottomUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::paddingLeftUnits() +{ + return YGUnit(PaddingLeftUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::paddingRightUnits() +{ + return YGUnit(PaddingRightUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::paddingTopUnits() +{ + return YGUnit(PaddingTopUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::paddingBottomUnits() +{ + return YGUnit(PaddingBottomUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::positionLeftUnits() +{ + return YGUnit(PositionLeftUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::positionRightUnits() +{ + return YGUnit(PositionRightUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::positionTopUnits() +{ + return YGUnit(PositionTopUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::positionBottomUnits() +{ + return YGUnit(PositionBottomUnitsBits.read(layoutFlags1())); +} + +YGUnit LayoutComponentStyle::gapHorizontalUnits() +{ + return YGUnit(GapHorizontalUnitsBits.read(layoutFlags2())); +} + +YGUnit LayoutComponentStyle::gapVerticalUnits() +{ + return YGUnit(GapVerticalUnitsBits.read(layoutFlags2())); +} + +YGUnit LayoutComponentStyle::maxWidthUnits() +{ + return YGUnit(MaxWidthUnitsBits.read(layoutFlags2())); +} + +YGUnit LayoutComponentStyle::maxHeightUnits() +{ + return YGUnit(MaxHeightUnitsBits.read(layoutFlags2())); +} + +YGUnit LayoutComponentStyle::minWidthUnits() +{ + return YGUnit(MinWidthUnitsBits.read(layoutFlags2())); +} +YGUnit LayoutComponentStyle::minHeightUnits() +{ + return YGUnit(MinHeightUnitsBits.read(layoutFlags2())); +} +void LayoutComponentStyle::markLayoutNodeDirty() +{ + if (parent()->is<LayoutComponent>()) + { + parent()->as<LayoutComponent>()->markLayoutNodeDirty(); + } +} +#else +void LayoutComponentStyle::markLayoutNodeDirty() {} +#endif + +void LayoutComponentStyle::layoutFlags0Changed() { markLayoutNodeDirty(); } +void LayoutComponentStyle::layoutFlags1Changed() { markLayoutNodeDirty(); } +void LayoutComponentStyle::layoutFlags2Changed() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexGrowChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexShrinkChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexBasisChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::aspectRatioChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapHorizontalChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapVerticalChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxWidthChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxHeightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minWidthChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minHeightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionBottomChanged() { markLayoutNodeDirty(); } \ No newline at end of file
diff --git a/src/layout_component.cpp b/src/layout_component.cpp new file mode 100644 index 0000000..ac908f6 --- /dev/null +++ b/src/layout_component.cpp
@@ -0,0 +1,278 @@ +#include "rive/artboard.hpp" +#include "rive/layout_component.hpp" +#include "rive/node.hpp" +#include "rive/math/aabb.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "rive/transform_component.hpp" +#include "yoga/YGEnums.h" +#include "yoga/YGFloatOptional.h" +#endif +#include <vector> + +using namespace rive; + +void LayoutComponent::buildDependencies() +{ + Super::buildDependencies(); + if (parent() != nullptr) + { + parent()->addDependent(this); + } +} + +#ifdef WITH_RIVE_LAYOUT +StatusCode LayoutComponent::onAddedDirty(CoreContext* context) +{ + auto code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + + auto coreStyle = context->resolve(styleId()); + if (coreStyle == nullptr || !coreStyle->is<LayoutComponentStyle>()) + { + return StatusCode::MissingObject; + } + m_style = static_cast<LayoutComponentStyle*>(coreStyle); + addChild(m_style); + artboard()->markLayoutDirty(this); + if (parent() != nullptr && parent()->is<LayoutComponent>()) + { + parent()->as<LayoutComponent>()->syncLayoutChildren(); + } + return StatusCode::Ok; +} + +void LayoutComponent::update(ComponentDirt value) +{ + if (hasDirt(value, ComponentDirt::WorldTransform)) + { + Mat2D parentWorld = parent()->is<WorldTransformComponent>() + ? (parent()->as<WorldTransformComponent>())->worldTransform() + : Mat2D(); + auto transform = Mat2D(); + transform[4] = m_layoutLocationX; + transform[5] = m_layoutLocationY; + + auto multipliedTransform = Mat2D::multiply(parentWorld, transform); + m_WorldTransform = multipliedTransform; + } +} + +AABB LayoutComponent::findMaxIntrinsicSize(ContainerComponent* component, AABB maxIntrinsicSize) +{ + auto intrinsicSize = maxIntrinsicSize; + for (auto child : component->children()) + { + if (child->is<LayoutComponent>()) + { + continue; + } + if (child->is<TransformComponent>()) + { + auto sizableChild = child->as<TransformComponent>(); + auto minSize = + AABB::fromLTWH(0, + 0, + style()->minWidthUnits() == YGUnitPoint ? style()->minWidth() : 0, + style()->minHeightUnits() == YGUnitPoint ? style()->minHeight() : 0); + auto maxSize = AABB::fromLTWH( + 0, + 0, + style()->maxWidthUnits() == YGUnitPoint ? style()->maxWidth() + : std::numeric_limits<float>::infinity(), + style()->maxHeightUnits() == YGUnitPoint ? style()->maxHeight() + : std::numeric_limits<float>::infinity()); + auto size = sizableChild->computeIntrinsicSize(minSize, maxSize); + intrinsicSize = AABB::fromLTWH(0, + 0, + std::max(maxIntrinsicSize.width(), size.width()), + std::max(maxIntrinsicSize.height(), size.height())); + } + if (child->is<ContainerComponent>()) + { + return findMaxIntrinsicSize(child->as<ContainerComponent>(), intrinsicSize); + } + } + return intrinsicSize; +} + +void LayoutComponent::syncStyle() +{ + if (style() == nullptr || layoutStyle() == nullptr || layoutNode() == nullptr) + { + return; + } + bool setIntrinsicWidth = false; + bool setIntrinsicHeight = false; + if (style()->intrinsicallySized() && + (style()->widthUnits() == YGUnitAuto || style()->heightUnits() == YGUnitAuto)) + { + AABB intrinsicSize = findMaxIntrinsicSize(this, AABB()); + bool foundIntrinsicSize = intrinsicSize.width() != 0 || intrinsicSize.height() != 0; + + if (foundIntrinsicSize) + { + if (style()->widthUnits() == YGUnitAuto) + { + setIntrinsicWidth = true; + layoutStyle()->dimensions()[YGDimensionWidth] = + YGValue{intrinsicSize.width(), YGUnitPoint}; + } + if (style()->heightUnits() == YGUnitAuto) + { + setIntrinsicHeight = true; + layoutStyle()->dimensions()[YGDimensionHeight] = + YGValue{intrinsicSize.height(), YGUnitPoint}; + } + } + } + if (!setIntrinsicWidth) + { + layoutStyle()->dimensions()[YGDimensionWidth] = YGValue{width(), style()->widthUnits()}; + } + if (!setIntrinsicHeight) + { + layoutStyle()->dimensions()[YGDimensionHeight] = YGValue{height(), style()->heightUnits()}; + } + layoutStyle()->minDimensions()[YGDimensionWidth] = + YGValue{style()->minWidth(), style()->minWidthUnits()}; + layoutStyle()->minDimensions()[YGDimensionHeight] = + YGValue{style()->minHeight(), style()->minHeightUnits()}; + layoutStyle()->maxDimensions()[YGDimensionWidth] = + YGValue{style()->maxWidth(), style()->maxWidthUnits()}; + layoutStyle()->maxDimensions()[YGDimensionHeight] = + YGValue{style()->maxHeight(), style()->maxHeightUnits()}; + + layoutStyle()->gap()[YGGutterColumn] = + YGValue{style()->gapHorizontal(), style()->gapHorizontalUnits()}; + layoutStyle()->gap()[YGGutterRow] = + YGValue{style()->gapVertical(), style()->gapVerticalUnits()}; + layoutStyle()->border()[YGEdgeLeft] = + YGValue{style()->borderLeft(), style()->borderLeftUnits()}; + layoutStyle()->border()[YGEdgeRight] = + YGValue{style()->borderRight(), style()->borderRightUnits()}; + layoutStyle()->border()[YGEdgeTop] = YGValue{style()->borderTop(), style()->borderTopUnits()}; + layoutStyle()->border()[YGEdgeBottom] = + YGValue{style()->borderBottom(), style()->borderBottomUnits()}; + layoutStyle()->margin()[YGEdgeLeft] = + YGValue{style()->marginLeft(), style()->marginLeftUnits()}; + layoutStyle()->margin()[YGEdgeRight] = + YGValue{style()->marginRight(), style()->marginRightUnits()}; + layoutStyle()->margin()[YGEdgeTop] = YGValue{style()->marginTop(), style()->marginTopUnits()}; + layoutStyle()->margin()[YGEdgeBottom] = + YGValue{style()->marginBottom(), style()->marginBottomUnits()}; + layoutStyle()->padding()[YGEdgeLeft] = + YGValue{style()->paddingLeft(), style()->paddingLeftUnits()}; + layoutStyle()->padding()[YGEdgeRight] = + YGValue{style()->paddingRight(), style()->paddingRightUnits()}; + layoutStyle()->padding()[YGEdgeTop] = + YGValue{style()->paddingTop(), style()->paddingTopUnits()}; + layoutStyle()->padding()[YGEdgeBottom] = + YGValue{style()->paddingBottom(), style()->paddingBottomUnits()}; + layoutStyle()->position()[YGEdgeLeft] = + YGValue{style()->positionLeft(), style()->positionLeftUnits()}; + layoutStyle()->position()[YGEdgeRight] = + YGValue{style()->positionRight(), style()->positionRightUnits()}; + layoutStyle()->position()[YGEdgeTop] = + YGValue{style()->positionTop(), style()->positionTopUnits()}; + layoutStyle()->position()[YGEdgeBottom] = + YGValue{style()->positionBottom(), style()->positionBottomUnits()}; + + layoutStyle()->display() = style()->display(); + layoutStyle()->positionType() = style()->positionType(); + layoutStyle()->flex() = YGFloatOptional(style()->flex()); + layoutStyle()->flexGrow() = YGFloatOptional(style()->flexGrow()); + layoutStyle()->flexShrink() = YGFloatOptional(style()->flexShrink()); + // layoutStyle()->flexBasis() = style()->flexBasis(); + layoutStyle()->flexDirection() = style()->flexDirection(); + layoutStyle()->flexWrap() = style()->flexWrap(); + layoutStyle()->alignItems() = style()->alignItems(); + layoutStyle()->alignContent() = style()->alignContent(); + layoutStyle()->alignSelf() = style()->alignSelf(); + layoutStyle()->justifyContent() = style()->justifyContent(); + + layoutNode()->setStyle(*layoutStyle()); +} + +void LayoutComponent::syncLayoutChildren() +{ + YGNodeRemoveAllChildren(layoutNode()); + int index = 0; + for (size_t i = 0; i < children().size(); i++) + { + Component* child = children()[i]; + if (child->is<LayoutComponent>()) + { + YGNodeInsertChild(layoutNode(), child->as<LayoutComponent>()->layoutNode(), index); + index += 1; + } + } +} + +void LayoutComponent::propagateSize() +{ + if (artboard() == this) + { + return; + } + propagateSizeToChildren(this); +} + +void LayoutComponent::propagateSizeToChildren(ContainerComponent* component) +{ + for (auto child : component->children()) + { + if (child->is<LayoutComponent>() || child->coreType() == NodeBase::typeKey) + { + continue; + } + if (child->is<TransformComponent>()) + { + auto sizableChild = child->as<TransformComponent>(); + sizableChild->controlSize(AABB::fromLTWH(0, 0, m_layoutSizeWidth, m_layoutSizeHeight)); + } + if (child->is<ContainerComponent>()) + { + propagateSizeToChildren(child->as<ContainerComponent>()); + } + } +} + +void LayoutComponent::calculateLayout() +{ + YGNodeCalculateLayout(layoutNode(), width(), height(), YGDirection::YGDirectionInherit); +} + +void LayoutComponent::updateLayoutBounds() +{ + auto left = YGNodeLayoutGetLeft(layoutNode()); + auto top = YGNodeLayoutGetTop(layoutNode()); + auto width = YGNodeLayoutGetWidth(layoutNode()); + auto height = YGNodeLayoutGetHeight(layoutNode()); + if (left != m_layoutLocationX || top != m_layoutLocationY || width != m_layoutSizeWidth || + height != m_layoutSizeHeight) + { + m_layoutLocationX = left; + m_layoutLocationY = top; + m_layoutSizeWidth = width; + m_layoutSizeHeight = height; + propagateSize(); + markWorldTransformDirty(); + } +} + +void LayoutComponent::markLayoutNodeDirty() +{ + layoutNode()->markDirtyAndPropagate(); + artboard()->markLayoutDirty(this); +} +#else +void LayoutComponent::markLayoutNodeDirty() {} +#endif + +void LayoutComponent::clipChanged() { markLayoutNodeDirty(); } +void LayoutComponent::widthChanged() { markLayoutNodeDirty(); } +void LayoutComponent::heightChanged() { markLayoutNodeDirty(); } +void LayoutComponent::styleIdChanged() { markLayoutNodeDirty(); } \ No newline at end of file
diff --git a/src/math/bit_field_loc.cpp b/src/math/bit_field_loc.cpp new file mode 100644 index 0000000..377b10a --- /dev/null +++ b/src/math/bit_field_loc.cpp
@@ -0,0 +1,20 @@ +#include "rive/math/bit_field_loc.hpp" +#include <cassert> + +using namespace rive; + +BitFieldLoc::BitFieldLoc(uint32_t start, uint32_t end) : m_start(start) +{ + assert(end >= start); + assert(end < 32); + + m_count = end - start + 1; + m_mask = ((1 << (end - start + 1)) - 1) << start; +} + +uint32_t BitFieldLoc::read(uint32_t bits) { return (bits & m_mask) >> m_start; } + +uint32_t BitFieldLoc::write(uint32_t bits, uint32_t value) +{ + return (bits & ~m_mask) | ((value << m_start) & m_mask); +} \ No newline at end of file
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index 1c58040..7bf7df2 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp
@@ -230,4 +230,19 @@ *local = toMountedArtboard * world; return true; +} + +AABB NestedArtboard::computeIntrinsicSize(AABB min, AABB max) { return max; } + +void NestedArtboard::controlSize(AABB size) +{ + auto newScaleX = size.width() / m_Artboard->originalWidth(); + auto newScaleY = size.height() / m_Artboard->originalHeight(); + if (newScaleX != scaleX() || newScaleY != scaleY()) + { + // TODO: Support nested artboard fit & alignment + scaleX(newScaleX); + scaleY(newScaleY); + addDirt(ComponentDirt::WorldTransform, false); + } } \ No newline at end of file
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp index 021f84b..c2f21cf 100644 --- a/src/shapes/image.cpp +++ b/src/shapes/image.cpp
@@ -121,3 +121,18 @@ void Image::setMesh(Mesh* mesh) { m_Mesh = mesh; } Mesh* Image::mesh() const { return m_Mesh; } + +AABB Image::computeIntrinsicSize(AABB min, AABB max) { return max; } + +void Image::controlSize(AABB size) +{ + auto renderImage = imageAsset()->renderImage(); + auto newScaleX = size.width() / renderImage->width(); + auto newScaleY = size.height() / renderImage->height(); + if (newScaleX != scaleX() || newScaleY != scaleY()) + { + scaleX(newScaleX); + scaleY(newScaleY); + addDirt(ComponentDirt::WorldTransform, false); + } +} \ No newline at end of file
diff --git a/src/shapes/parametric_path.cpp b/src/shapes/parametric_path.cpp index 923d1d7..534dfc4 100644 --- a/src/shapes/parametric_path.cpp +++ b/src/shapes/parametric_path.cpp
@@ -1,7 +1,20 @@ +#include "rive/math/aabb.hpp" #include "rive/shapes/parametric_path.hpp" using namespace rive; +AABB ParametricPath::computeIntrinsicSize(AABB min, AABB max) +{ + return AABB::fromLTWH(0, 0, width(), height()); +} + +void ParametricPath::controlSize(AABB size) +{ + width(size.width()); + height(size.height()); + markWorldTransformDirty(); +} + void ParametricPath::widthChanged() { markPathDirty(); } void ParametricPath::heightChanged() { markPathDirty(); } void ParametricPath::originXChanged() { markPathDirty(); }
diff --git a/src/text/text.cpp b/src/text/text.cpp index 56bbbfd..8431140 100644 --- a/src/text/text.cpp +++ b/src/text/text.cpp
@@ -241,6 +241,18 @@ return true; } +AABB Text::computeIntrinsicSize(AABB min, AABB max) { return measure(max); } + +void Text::controlSize(AABB size) +{ + if (m_layoutWidth != size.width() || m_layoutHeight != size.height()) + { + m_layoutWidth = size.width(); + m_layoutHeight = size.height(); + markShapeDirty(false); + } +} + void Text::buildRenderStyles() { for (TextStyle* style : m_renderStyles) @@ -270,7 +282,8 @@ bool isEllipsisLineLast = false; // Find the line to put the ellipsis on (line before the one that // overflows). - bool wantEllipsis = overflow() == TextOverflow::ellipsis && sizing() == TextSizing::fixed; + bool wantEllipsis = + overflow() == TextOverflow::ellipsis && effectiveSizing() == TextSizing::fixed; int lastLineIndex = -1; for (const SimpleArray<GlyphLine>& paragraphLines : m_lines) @@ -287,7 +300,7 @@ maxWidth = width; } lastLineIndex++; - if (wantEllipsis && y + line.bottom <= height()) + if (wantEllipsis && y + line.bottom <= effectiveHeight()) { ellipsisLine++; } @@ -308,16 +321,16 @@ int lineIndex = 0; paragraphIndex = 0; - switch (sizing()) + switch (effectiveSizing()) { case TextSizing::autoWidth: m_bounds = AABB(0.0f, minY, maxWidth, std::max(minY, y - paragraphSpace)); break; case TextSizing::autoHeight: - m_bounds = AABB(0.0f, minY, width(), std::max(minY, y - paragraphSpace)); + m_bounds = AABB(0.0f, minY, effectiveWidth(), std::max(minY, y - paragraphSpace)); break; case TextSizing::fixed: - m_bounds = AABB(0.0f, minY, width(), minY + height()); + m_bounds = AABB(0.0f, minY, effectiveWidth(), minY + effectiveHeight()); break; } @@ -366,13 +379,14 @@ switch (overflow()) { case TextOverflow::hidden: - if (sizing() == TextSizing::fixed && y + line.bottom > height()) + if (effectiveSizing() == TextSizing::fixed && + y + line.bottom > effectiveHeight()) { return; } break; case TextOverflow::clipped: - if (sizing() == TextSizing::fixed && y + line.top > height()) + if (effectiveSizing() == TextSizing::fixed && y + line.top > effectiveHeight()) { return; } @@ -386,7 +400,7 @@ // We need to still compute this line's ordered runs. m_orderedLines.emplace_back(OrderedLine(paragraph, line, - width(), + effectiveWidth(), ellipsisLine == lineIndex, isEllipsisLineLast, &m_ellipsisRun)); @@ -512,7 +526,7 @@ void Text::addModifierGroup(TextModifierGroup* group) { m_modifierGroups.push_back(group); } -void Text::markShapeDirty() +void Text::markShapeDirty(bool sendToLayout) { addDirt(ComponentDirt::Path); for (TextModifierGroup* group : m_modifierGroups) @@ -520,6 +534,18 @@ group->clearRangeMaps(); } markWorldTransformDirty(); +#ifdef WITH_RIVE_LAYOUT + if (sendToLayout) + { + for (ContainerComponent* p = parent(); p != nullptr; p = p->parent()) + { + if (p->is<LayoutComponent>()) + { + p->as<LayoutComponent>()->markLayoutNodeDirty(); + } + } + } +#endif } void Text::modifierShapeDirty() { addDirt(ComponentDirt::Path); } @@ -532,7 +558,7 @@ void Text::overflowValueChanged() { - if (sizing() != TextSizing::autoWidth) + if (effectiveSizing() != TextSizing::autoWidth) { markShapeDirty(); } @@ -540,7 +566,7 @@ void Text::widthChanged() { - if (sizing() != TextSizing::autoWidth) + if (effectiveSizing() != TextSizing::autoWidth) { markShapeDirty(); } @@ -550,7 +576,7 @@ void Text::heightChanged() { - if (sizing() == TextSizing::fixed) + if (effectiveSizing() == TextSizing::fixed) { markShapeDirty(); } @@ -670,9 +696,10 @@ makeStyled(m_modifierStyledText, false); auto runs = m_modifierStyledText.runs(); m_modifierShape = runs[0].font->shapeText(m_modifierStyledText.unichars(), runs); - m_modifierLines = breakLines(m_modifierShape, - sizing() == TextSizing::autoWidth ? -1.0f : width(), - (TextAlign)alignValue()); + m_modifierLines = + breakLines(m_modifierShape, + effectiveSizing() == TextSizing::autoWidth ? -1.0f : effectiveWidth(), + (TextAlign)alignValue()); m_glyphLookup.compute(m_modifierStyledText.unichars(), m_modifierShape); uint32_t textSize = (uint32_t)m_modifierStyledText.unichars().size(); for (TextModifierGroup* group : m_modifierGroups) @@ -688,9 +715,10 @@ { auto runs = m_styledText.runs(); m_shape = runs[0].font->shapeText(m_styledText.unichars(), runs); - m_lines = breakLines(m_shape, - sizing() == TextSizing::autoWidth ? -1.0f : width(), - (TextAlign)alignValue()); + m_lines = + breakLines(m_shape, + effectiveSizing() == TextSizing::autoWidth ? -1.0f : effectiveWidth(), + (TextAlign)alignValue()); if (!precomputeModifierCoverage && haveModifiers()) { m_glyphLookup.compute(m_styledText.unichars(), m_shape); @@ -732,6 +760,64 @@ } } +AABB Text::measure(AABB maxSize) +{ + if (makeStyled(m_styledText)) + { + const float paragraphSpace = paragraphSpacing(); + auto runs = m_styledText.runs(); + auto shape = runs[0].font->shapeText(m_styledText.unichars(), runs); + auto lines = + breakLines(shape, + effectiveSizing() == TextSizing::autoWidth ? -1.0f : effectiveWidth(), + (TextAlign)alignValue()); + float y = 0; + float minY = 0; + int paragraphIndex = 0; + float maxWidth = 0; + + if (textOrigin() == TextOrigin::baseline && !lines.empty() && !lines[0].empty()) + { + y -= m_lines[0][0].baseline; + minY = y; + } + for (const SimpleArray<GlyphLine>& paragraphLines : lines) + { + const Paragraph& paragraph = shape[paragraphIndex++]; + for (const GlyphLine& line : paragraphLines) + { + const GlyphRun& endRun = paragraph.runs[line.endRunIndex]; + const GlyphRun& startRun = paragraph.runs[line.startRunIndex]; + float width = endRun.xpos[line.endGlyphIndex] - + startRun.xpos[line.startGlyphIndex] - endRun.letterSpacing; + if (width > maxWidth) + { + maxWidth = width; + } + } + if (!paragraphLines.empty()) + { + y += paragraphLines.back().bottom; + } + y += paragraphSpace; + } + + switch (sizing()) + { + case TextSizing::autoWidth: + return AABB::fromLTWH(0, 0, maxWidth, std::max(minY, y - paragraphSpace)); + break; + case TextSizing::autoHeight: + return AABB::fromLTWH(0, 0, width(), std::max(minY, y - paragraphSpace)); + break; + case TextSizing::fixed: + return AABB::fromLTWH(0, 0, width(), minY + height()); + break; + } + } + return AABB(); +} + AABB Text::localBounds() const { float width = m_bounds.width(); @@ -775,7 +861,7 @@ Core* Text::hitTest(HitInfo*, const Mat2D&) { return nullptr; } void Text::addRun(TextValueRun* run) {} void Text::addModifierGroup(TextModifierGroup* group) {} -void Text::markShapeDirty() {} +void Text::markShapeDirty(bool sendToLayout) {} void Text::update(ComponentDirt value) {} void Text::alignValueChanged() {} void Text::sizingValueChanged() {} @@ -791,4 +877,6 @@ void Text::originValueChanged() {} void Text::originXChanged() {} void Text::originYChanged() {} +AABB Text::computeIntrinsicSize(AABB min, AABB max) { return AABB(); } +void Text::controlSize(AABB size) {} #endif \ No newline at end of file
diff --git a/tess/build/premake5_tess.lua b/tess/build/premake5_tess.lua index 7a07170..7f93b18 100644 --- a/tess/build/premake5_tess.lua +++ b/tess/build/premake5_tess.lua
@@ -21,6 +21,7 @@ dependencies .. '/sokol', dependencies .. '/earcut.hpp/include/mapbox', dependencies .. '/libtess2/Include', + yoga, }) files({ '../src/**.cpp', dependencies .. '/libtess2/Source/**.c' }) buildoptions({ '-Wall', '-fno-exceptions', '-fno-rtti', '-Werror=format' }) @@ -79,11 +80,12 @@ rive .. '/include', dependencies .. '/sokol', dependencies .. '/earcut.hpp/include/mapbox', + yoga, }) files({ '../test/**.cpp', rive .. 'utils/no_op_factory.cpp' }) - links({ 'rive_tess_renderer', 'rive', 'rive_harfbuzz', 'rive_sheenbidi' }) + links({ 'rive_tess_renderer', 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' }) buildoptions({ '-Wall', '-fno-exceptions', '-fno-rtti', '-Werror=format' }) - defines({ 'TESTING' }) + defines({ 'TESTING', 'YOGA_EXPORT=' }) filter('configurations:debug') do
diff --git a/test/assets/layout/layout_center.riv b/test/assets/layout/layout_center.riv new file mode 100644 index 0000000..f64c66c --- /dev/null +++ b/test/assets/layout/layout_center.riv Binary files differ
diff --git a/test/assets/layout/layout_horizontal.riv b/test/assets/layout/layout_horizontal.riv new file mode 100644 index 0000000..7813fa9 --- /dev/null +++ b/test/assets/layout/layout_horizontal.riv Binary files differ
diff --git a/test/assets/layout/layout_horizontal_gaps.riv b/test/assets/layout/layout_horizontal_gaps.riv new file mode 100644 index 0000000..a10a936 --- /dev/null +++ b/test/assets/layout/layout_horizontal_gaps.riv Binary files differ
diff --git a/test/assets/layout/layout_horizontal_wrap.riv b/test/assets/layout/layout_horizontal_wrap.riv new file mode 100644 index 0000000..f851c6b --- /dev/null +++ b/test/assets/layout/layout_horizontal_wrap.riv Binary files differ
diff --git a/test/assets/layout/layout_vertical.riv b/test/assets/layout/layout_vertical.riv new file mode 100644 index 0000000..35248ab --- /dev/null +++ b/test/assets/layout/layout_vertical.riv Binary files differ
diff --git a/test/layout_test.cpp b/test/layout_test.cpp new file mode 100644 index 0000000..08db250 --- /dev/null +++ b/test/layout_test.cpp
@@ -0,0 +1,123 @@ +#include <rive/math/transform_components.hpp> +#include <rive/shapes/rectangle.hpp> +#include "utils/no_op_factory.hpp" +#include "rive_file_reader.hpp" +#include "rive_testing.hpp" +#include <catch.hpp> +#include <cstdio> + +TEST_CASE("LayoutComponent FlexDirection row", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") != nullptr); + auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") != nullptr); + auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") != nullptr); + auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 100); + REQUIRE(target3Components.x() == 200); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 0); + REQUIRE(target3Components.y() == 0); +} + +TEST_CASE("LayoutComponent FlexDirection column", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_vertical.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") != nullptr); + auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") != nullptr); + auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") != nullptr); + auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 0); + REQUIRE(target3Components.x() == 0); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 100); + REQUIRE(target3Components.y() == 200); +} + +TEST_CASE("LayoutComponent FlexDirection row with gap", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal_gaps.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") != nullptr); + auto target1 = artboard->find<rive::LayoutComponent>("LayoutComponent1"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent2") != nullptr); + auto target2 = artboard->find<rive::LayoutComponent>("LayoutComponent2"); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent3") != nullptr); + auto target3 = artboard->find<rive::LayoutComponent>("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 110); + REQUIRE(target3Components.x() == 220); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 0); + REQUIRE(target3Components.y() == 0); +} + +TEST_CASE("LayoutComponent FlexDirection row with wrap", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal_wrap.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent6") != nullptr); + auto target = artboard->find<rive::LayoutComponent>("LayoutComponent6"); + + artboard->advance(0.0f); + auto targetComponents = target->worldTransform().decompose(); + + REQUIRE(targetComponents.x() == 0); + REQUIRE(targetComponents.y() == 100); +} + +TEST_CASE("LayoutComponent Center using alignItems and justifyContent", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_center.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find<rive::LayoutComponent>("LayoutComponent1") != nullptr); + auto target = artboard->find<rive::LayoutComponent>("LayoutComponent1"); + + artboard->advance(0.0f); + auto targetComponents = target->worldTransform().decompose(); + + REQUIRE(targetComponents.x() == 200); + REQUIRE(targetComponents.y() == 200); +} \ No newline at end of file
diff --git a/viewer/build/macosx/build_viewer.sh b/viewer/build/macosx/build_viewer.sh index f41f0fe..8053947 100755 --- a/viewer/build/macosx/build_viewer.sh +++ b/viewer/build/macosx/build_viewer.sh
@@ -57,7 +57,7 @@ pushd .. OUT=out/$RENDERER/$GRAPHICS/$CONFIG -$PREMAKE --scripts=../../build --file=./premake5_viewer.lua --config=$CONFIG --out=$OUT gmake2 --graphics=$GRAPHICS --renderer=$RENDERER --with_rive_tools --with_rive_text --with_rive_audio=system +$PREMAKE --scripts=../../build --file=./premake5_viewer.lua --config=$CONFIG --out=$OUT gmake2 --graphics=$GRAPHICS --renderer=$RENDERER --with_rive_tools --with_rive_text --with_rive_audio=system --with_rive_layout for var in "$@"; do if [[ $var = "clean" ]]; then
diff --git a/viewer/build/premake5_viewer.lua b/viewer/build/premake5_viewer.lua index 741c497..9418d8c 100644 --- a/viewer/build/premake5_viewer.lua +++ b/viewer/build/premake5_viewer.lua
@@ -24,7 +24,7 @@ end kind('ConsoleApp') - defines({ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO' }) + defines({ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO', 'WITH_RIVE_LAYOUT', 'YOGA_EXPORT=' }) includedirs({ '../include', @@ -34,9 +34,10 @@ dependencies .. '/sokol', dependencies .. '/imgui', miniaudio, + yoga, }) - links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi' }) + links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' }) libdirs({ rive .. '/build/%{cfg.system}/bin/%{cfg.buildcfg}' })