mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
This PR removes the double advanceAndApply(0) when running goldens. There were 3 seperate issues that are resolved here. NestedInput values which were keyed on a timeline did not take effect until the next frame when they were used to drive a Blend state. Fix: NestedArtboards and ArtboardComponentLists should check if needsAdvance() in addition to tryChangeState() Layouts where positionType (absolute/relative) were keyed on a timeline were behaving inconsistently depending on whether positionLeft/positionTop were also keyed on the same frame. Fix: Make sure changing positionType only overrides the positionTop/positionLeft values if those were not keyed on the same frame as positionType Text follow path modifier was rendering incorrectly in a specific case where a Text's width was bound to a VM property which gets its value from a Path's length. Fix: Call updateDataBinds before returning in Artboard::updatePass Co-authored-by: Philip Chung <philterdesign@gmail.com>
326 lines
11 KiB
C++
326 lines
11 KiB
C++
#ifndef _RIVE_LAYOUT_COMPONENT_HPP_
|
|
#define _RIVE_LAYOUT_COMPONENT_HPP_
|
|
#include "rive/advance_flags.hpp"
|
|
#include "rive/animation/keyframe_interpolator.hpp"
|
|
#include "rive/drawable.hpp"
|
|
#include "rive/generated/layout_component_base.hpp"
|
|
#include "rive/layout/layout_measure_mode.hpp"
|
|
#include "rive/layout/layout_node_provider.hpp"
|
|
#include "rive/math/raw_path.hpp"
|
|
#include "rive/shapes/rectangle.hpp"
|
|
#include "rive/shapes/shape_paint_container.hpp"
|
|
#include "rive/advancing_component.hpp"
|
|
#include "rive/layout/layout_enums.hpp"
|
|
|
|
namespace rive
|
|
{
|
|
class AABB;
|
|
class KeyFrameInterpolator;
|
|
class LayoutData;
|
|
class LayoutComponentStyle;
|
|
class LayoutConstraint;
|
|
class Layout
|
|
{
|
|
public:
|
|
Layout() : m_left(0.0f), m_top(0.0f), m_width(0.0f), m_height(0.0f) {}
|
|
Layout(float left, float top, float width, float height) :
|
|
m_left(left), m_top(top), m_width(width), m_height(height)
|
|
{}
|
|
|
|
bool operator==(const Layout& o) const
|
|
{
|
|
return m_left == o.m_left && m_top == o.m_top && m_width == o.m_width &&
|
|
m_height == o.m_height;
|
|
}
|
|
bool operator!=(const Layout& o) const { return !(*this == o); }
|
|
|
|
static Layout lerp(const Layout& from, const Layout& to, float f)
|
|
{
|
|
float fi = 1.0f - f;
|
|
return Layout(to.m_left * f + from.m_left * fi,
|
|
to.m_top * f + from.m_top * fi,
|
|
to.m_width * f + from.m_width * fi,
|
|
to.m_height * f + from.m_height * fi);
|
|
}
|
|
|
|
float left() const { return m_left; }
|
|
float top() const { return m_top; }
|
|
float width() const { return m_width; }
|
|
float height() const { return m_height; }
|
|
|
|
private:
|
|
float m_left;
|
|
float m_top;
|
|
float m_width;
|
|
float m_height;
|
|
};
|
|
|
|
class LayoutPadding
|
|
{
|
|
public:
|
|
LayoutPadding() : m_left(0.0f), m_top(0.0f), m_right(0.0f), m_bottom(0.0f)
|
|
{}
|
|
LayoutPadding(float left, float top, float right, float bottom) :
|
|
m_left(left), m_top(top), m_right(right), m_bottom(bottom)
|
|
{}
|
|
|
|
bool operator==(const LayoutPadding& o) const
|
|
{
|
|
return m_left == o.m_left && m_top == o.m_top && m_right == o.m_right &&
|
|
m_bottom == o.m_bottom;
|
|
}
|
|
bool operator!=(const LayoutPadding& o) const { return !(*this == o); }
|
|
|
|
float left() const { return m_left; }
|
|
float top() const { return m_top; }
|
|
float right() const { return m_right; }
|
|
float bottom() const { return m_bottom; }
|
|
|
|
private:
|
|
float m_left;
|
|
float m_top;
|
|
float m_right;
|
|
float m_bottom;
|
|
};
|
|
|
|
struct LayoutAnimationData
|
|
{
|
|
float elapsedSeconds = 0.0f;
|
|
Layout from;
|
|
Layout to;
|
|
Layout interpolate(float f) const { return Layout::lerp(from, to, f); }
|
|
void copy(const LayoutAnimationData& from);
|
|
};
|
|
|
|
class LayoutComponent : public LayoutComponentBase,
|
|
public ProxyDrawing,
|
|
public ShapePaintContainer,
|
|
public AdvancingComponent,
|
|
public InterpolatorHost,
|
|
public LayoutNodeProvider
|
|
{
|
|
protected:
|
|
LayoutComponentStyle* m_style = nullptr;
|
|
LayoutData* m_layoutData;
|
|
|
|
Layout m_layout;
|
|
LayoutPadding m_layoutPadding;
|
|
|
|
LayoutAnimationData m_animationDataA;
|
|
LayoutAnimationData m_animationDataB;
|
|
bool m_isSmoothingAnimation = false;
|
|
KeyFrameInterpolator* m_inheritedInterpolator;
|
|
LayoutStyleInterpolation m_inheritedInterpolation =
|
|
LayoutStyleInterpolation::hold;
|
|
float m_inheritedInterpolationTime = 0;
|
|
LayoutDirection m_inheritedDirection = LayoutDirection::inherit;
|
|
Rectangle m_backgroundRect;
|
|
ShapePaintPath m_localPath;
|
|
ShapePaintPath m_worldPath;
|
|
DrawableProxy m_proxy;
|
|
|
|
Artboard* getArtboard() override { return artboard(); }
|
|
LayoutAnimationData* currentAnimationData();
|
|
|
|
LayoutComponent* layoutParent()
|
|
{
|
|
auto p = parent();
|
|
while (p != nullptr)
|
|
{
|
|
if (p->is<LayoutComponent>())
|
|
{
|
|
return p->as<LayoutComponent>();
|
|
}
|
|
p = p->parent();
|
|
}
|
|
return nullptr;
|
|
}
|
|
bool isCollapsed() const override;
|
|
void propagateCollapse(bool collapse);
|
|
bool collapse(bool value) override;
|
|
float computedLocalX() override { return m_layout.left(); };
|
|
float computedLocalY() override { return m_layout.top(); };
|
|
float computedWidth() override { return m_layout.width(); };
|
|
float computedHeight() override { return m_layout.height(); };
|
|
void calculateLayoutInternal(float availableWidth, float availableHeight);
|
|
|
|
private:
|
|
float m_widthOverride = NAN;
|
|
int m_widthUnitValueOverride = -1;
|
|
float m_heightOverride = NAN;
|
|
int m_heightUnitValueOverride = -1;
|
|
bool m_parentIsRow = true;
|
|
bool m_widthIntrinsicallySizeOverride = false;
|
|
bool m_heightIntrinsicallySizeOverride = false;
|
|
float m_forcedWidth = NAN;
|
|
float m_forcedHeight = NAN;
|
|
bool m_forceUpdateLayoutBounds = false;
|
|
bool m_positionLeftChanged = true;
|
|
bool m_positionTopChanged = true;
|
|
|
|
#ifdef WITH_RIVE_LAYOUT
|
|
protected:
|
|
void propagateSizeToChildren(ContainerComponent* component);
|
|
bool applyInterpolation(float elapsedSeconds, bool animate = true);
|
|
bool styleDisplayHidden() const;
|
|
#endif
|
|
|
|
public:
|
|
// Implemented for ShapePaintContainer.
|
|
const Mat2D& shapeWorldTransform() const override
|
|
{
|
|
return worldTransform();
|
|
}
|
|
|
|
LayoutComponentStyle* style() { return m_style; }
|
|
void style(LayoutComponentStyle* style) { m_style = style; }
|
|
|
|
void draw(Renderer* renderer) override;
|
|
void drawProxy(Renderer* renderer) override;
|
|
bool isProxyHidden() override { return isHidden(); }
|
|
Core* hitTest(HitInfo*, const Mat2D&) override;
|
|
bool hitTestPoint(const Vec2D& position,
|
|
bool skipOnUnclipped,
|
|
bool isPrimaryHit) override;
|
|
DrawableProxy* proxy() { return &m_proxy; };
|
|
virtual void updateRenderPath();
|
|
void update(ComponentDirt value) override;
|
|
void onDirty(ComponentDirt value) override;
|
|
AABB layoutBounds() override
|
|
{
|
|
return AABB::fromLTWH(m_layout.left(),
|
|
m_layout.top(),
|
|
m_layout.width(),
|
|
m_layout.height());
|
|
}
|
|
size_t numLayoutNodes() override { return 1; }
|
|
AABB constraintBounds() const override { return localBounds(); }
|
|
AABB localBounds() const override
|
|
{
|
|
return AABB::fromLTWH(0.0f, 0.0f, m_layout.width(), m_layout.height());
|
|
}
|
|
virtual AABB worldBounds() const
|
|
{
|
|
auto transform = worldTransform();
|
|
return AABB::fromLTWH(transform[4],
|
|
transform[5],
|
|
m_layout.width(),
|
|
m_layout.height());
|
|
}
|
|
|
|
float x() const override { return layoutX(); }
|
|
float y() const override { return layoutY(); }
|
|
float layoutX() const { return m_layout.left(); }
|
|
float layoutY() const { return m_layout.top(); }
|
|
float layoutWidth() { return m_layout.width(); }
|
|
float layoutHeight() { return m_layout.height(); }
|
|
float innerWidth()
|
|
{
|
|
return m_layout.width() - m_layoutPadding.left() -
|
|
m_layoutPadding.right();
|
|
}
|
|
float innerHeight()
|
|
{
|
|
return m_layout.height() - m_layoutPadding.top() -
|
|
m_layoutPadding.bottom();
|
|
}
|
|
float paddingLeft() { return m_layoutPadding.left(); }
|
|
float paddingRight() { return m_layoutPadding.right(); }
|
|
float paddingTop() { return m_layoutPadding.top(); }
|
|
float paddingBottom() { return m_layoutPadding.bottom(); }
|
|
|
|
float gapHorizontal();
|
|
float gapVertical();
|
|
|
|
// We provide a way for nested artboards (or other objects) to override this
|
|
// layout's width/height and unit values.
|
|
void widthOverride(float width, int unitValue = 1, bool isRow = true);
|
|
void heightOverride(float height, int unitValue = 1, bool isRow = true);
|
|
void parentIsRow(bool isRow);
|
|
void widthIntrinsicallySizeOverride(bool intrinsic);
|
|
void heightIntrinsicallySizeOverride(bool intrinsic);
|
|
virtual bool canHaveOverrides() { return false; }
|
|
bool mainAxisIsRow();
|
|
bool mainAxisIsColumn();
|
|
bool overridesKeyedInterpolation(int propertyKey) override;
|
|
bool hasShapePaints() const { return m_ShapePaints.size() > 0; }
|
|
const Rectangle* backgroundRect() const { return &m_backgroundRect; }
|
|
bool advanceComponent(float elapsedSeconds,
|
|
AdvanceFlags flags = AdvanceFlags::Animate |
|
|
AdvanceFlags::NewFrame) override;
|
|
bool isHidden() const override;
|
|
float forcedWidth() { return m_forcedWidth; }
|
|
float forcedHeight() { return m_forcedHeight; }
|
|
void forcedWidth(float width);
|
|
void forcedHeight(float height);
|
|
void updateConstraints() override;
|
|
TransformComponent* transformComponent() override
|
|
{
|
|
return this->as<TransformComponent>();
|
|
}
|
|
|
|
LayoutComponent();
|
|
~LayoutComponent();
|
|
#ifdef WITH_RIVE_LAYOUT
|
|
|
|
void* layoutNode(int index) override;
|
|
void syncStyle();
|
|
void syncLayoutChildren();
|
|
void clearLayoutChildren();
|
|
virtual void propagateSize();
|
|
void updateLayoutBounds(bool animate = true) override;
|
|
StatusCode onAddedDirty(CoreContext* context) override;
|
|
StatusCode onAddedClean(CoreContext* context) override;
|
|
bool advance(float elapsedSeconds);
|
|
bool animates();
|
|
LayoutAnimationStyle animationStyle();
|
|
KeyFrameInterpolator* interpolator();
|
|
LayoutStyleInterpolation interpolation();
|
|
float interpolationTime();
|
|
|
|
bool cascadeLayoutStyle(LayoutStyleInterpolation inheritedInterpolation,
|
|
KeyFrameInterpolator* inheritedInterpolator,
|
|
float inheritedInterpolationTime,
|
|
LayoutDirection direction);
|
|
bool setInheritedInterpolation(
|
|
LayoutStyleInterpolation inheritedInterpolation,
|
|
KeyFrameInterpolator* inheritedInterpolator,
|
|
float inheritedInterpolationTime);
|
|
void clearInheritedInterpolation();
|
|
void interruptAnimation();
|
|
bool isLeaf();
|
|
void positionTypeChanged();
|
|
void scaleTypeChanged();
|
|
void displayChanged();
|
|
void flexDirectionChanged();
|
|
void directionChanged();
|
|
LayoutDirection actualDirection();
|
|
#endif
|
|
|
|
void markPositionLeftChanged() { m_positionLeftChanged = true; }
|
|
void markPositionTopChanged() { m_positionTopChanged = true; }
|
|
void buildDependencies() override;
|
|
|
|
void markLayoutNodeDirty(
|
|
bool shouldForceUpdateLayoutBounds = false) override;
|
|
void markLayoutStyleDirty();
|
|
void clipChanged() override;
|
|
void widthChanged() override;
|
|
void heightChanged() override;
|
|
void styleIdChanged() override;
|
|
void fractionalWidthChanged() override;
|
|
void fractionalHeightChanged() override;
|
|
|
|
Vec2D measureLayout(float width,
|
|
LayoutMeasureMode widthMode,
|
|
float height,
|
|
LayoutMeasureMode heightMode) override;
|
|
|
|
ShapePaintPath* worldPath() override;
|
|
ShapePaintPath* localPath() override;
|
|
ShapePaintPath* localClockwisePath() override;
|
|
Component* pathBuilder() override;
|
|
};
|
|
} // namespace rive
|
|
|
|
#endif |