| #include "rive/nested_artboard_layout.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/animation/keyframe_interpolator.hpp" |
| #include "rive/layout/layout_data.hpp" |
| #include "rive/math/aabb.hpp" |
| #include "rive/world_transform_component.hpp" |
| |
| using namespace rive; |
| |
| Core* NestedArtboardLayout::clone() const |
| { |
| NestedArtboardLayout* nestedArtboard = |
| static_cast<NestedArtboardLayout*>(NestedArtboardLayoutBase::clone()); |
| nestedArtboard->file(file()); |
| if (m_referencedArtboard == nullptr) |
| { |
| return nestedArtboard; |
| } |
| auto ni = m_referencedArtboard->instance(); |
| nestedArtboard->referencedArtboard(ni.release()); |
| return nestedArtboard; |
| } |
| |
| AABB NestedArtboardLayout::layoutBounds() |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| if (artboardInstance() != nullptr) |
| { |
| return artboardInstance()->layoutBounds(); |
| } |
| #endif |
| return AABB(); |
| } |
| |
| #ifdef WITH_RIVE_LAYOUT |
| void* NestedArtboardLayout::layoutNode(int index) |
| { |
| if (artboardInstance() == nullptr) |
| { |
| return nullptr; |
| } |
| return static_cast<void*>(&artboardInstance()->takeLayoutData()->node); |
| } |
| #endif |
| |
| void NestedArtboardLayout::markHostingLayoutDirty( |
| ArtboardInstance* artboardInstance) |
| { |
| if (artboard() != nullptr) |
| { |
| artboard()->markLayoutDirty(this->artboardInstance()); |
| artboard()->markLayoutStyleDirty(); |
| } |
| } |
| |
| void NestedArtboardLayout::markLayoutNodeDirty( |
| bool shouldForceUpdateLayoutBounds) |
| { |
| updateWidthOverride(); |
| updateHeightOverride(); |
| } |
| |
| // Where the layout put us, less the mounted artboard's own origin, applied in |
| // the parent's frame: |
| // |
| // parentWorld * translate(slot - origin) * m_Transform |
| // |
| // Applied after constraints rather than composed before them. A constraint that |
| // copies a position (follow path, translation) composes the world transform |
| // from its target and replaces our translation outright, so a placement folded |
| // into composeWorldTransform is discarded and the artboard renders off by its |
| // origin. With no constraint the two orders are the same matrix, so files that |
| // do not constrain a nested artboard are unaffected. |
| void NestedArtboardLayout::applyLayoutPlacement() |
| { |
| auto artboard = artboardInstance(); |
| if (artboard == nullptr) |
| { |
| return; |
| } |
| auto base = |
| Vec2D(artboard->layoutX(), artboard->layoutY()) - artboard->origin(); |
| auto* parentComponent = parent(); |
| if (parentComponent != nullptr && parentComponent->is<Artboard>()) |
| { |
| base += parentComponent->as<Artboard>()->origin(); |
| } |
| if (m_ParentTransformComponent != nullptr) |
| { |
| // Rotate/scale into the parent's frame; its translation cancels. |
| const Mat2D& p = m_ParentTransformComponent->worldTransform(); |
| m_WorldTransform[4] += p[0] * base.x + p[2] * base.y; |
| m_WorldTransform[5] += p[1] * base.x + p[3] * base.y; |
| } |
| else |
| { |
| m_WorldTransform[4] += base.x; |
| m_WorldTransform[5] += base.y; |
| } |
| } |
| |
| void NestedArtboardLayout::updateConstraints() |
| { |
| if (layoutConstraints().size() > 0) |
| { |
| for (auto parentConstraint : layoutConstraints()) |
| { |
| parentConstraint->constrainChild(this); |
| } |
| } |
| Super::updateConstraints(); |
| applyLayoutPlacement(); |
| } |
| |
| StatusCode NestedArtboardLayout::onAddedClean(CoreContext* context) |
| { |
| StatusCode code = Super::onAddedClean(context); |
| if (code != StatusCode::Ok) |
| { |
| return code; |
| } |
| |
| updateWidthOverride(); |
| updateHeightOverride(); |
| |
| return StatusCode::Ok; |
| } |
| |
| void NestedArtboardLayout::updateLayoutBounds(bool animate) |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| if (artboardInstance() == nullptr) |
| { |
| return; |
| } |
| artboardInstance()->updateLayoutBounds(animate); |
| #endif |
| } |
| |
| #ifdef WITH_RIVE_LAYOUT |
| bool NestedArtboardLayout::cascadeLayoutStyle( |
| LayoutStyleInterpolation inheritedInterpolation, |
| KeyFrameInterpolator* inheritedInterpolator, |
| float inheritedInterpolationTime, |
| LayoutDirection direction) |
| { |
| if (artboardInstance() != nullptr) |
| { |
| artboardInstance()->cascadeLayoutStyle(inheritedInterpolation, |
| inheritedInterpolator, |
| inheritedInterpolationTime, |
| direction); |
| } |
| return false; |
| } |
| #endif |
| |
| void NestedArtboardLayout::updateWidthOverride() |
| { |
| if (artboardInstance() == nullptr) |
| { |
| return; |
| } |
| m_styleOverrider.updateWidthOverride(artboardInstance()); |
| } |
| |
| void NestedArtboardLayout::updateHeightOverride() |
| { |
| if (artboardInstance() == nullptr) |
| { |
| return; |
| } |
| m_styleOverrider.updateHeightOverride(artboardInstance()); |
| } |
| |
| // The layout that collected us, which may sit above a container. Asking |
| // parent() reported row/not-stack for a Solo and sized the wrong axis. |
| bool NestedArtboardLayout::isRow() |
| { |
| auto* layout = owningLayout(parent()); |
| return layout != nullptr ? layout->mainAxisIsRow() : true; |
| } |
| |
| bool NestedArtboardLayout::isStack() |
| { |
| auto* layout = owningLayout(parent()); |
| return layout != nullptr && layout->isStackContainer(); |
| } |
| |
| void NestedArtboardLayout::instanceWidthChanged() { updateWidthOverride(); } |
| |
| void NestedArtboardLayout::instanceHeightChanged() { updateHeightOverride(); } |
| |
| void NestedArtboardLayout::instanceWidthUnitsValueChanged() |
| { |
| updateWidthOverride(); |
| } |
| |
| void NestedArtboardLayout::instanceHeightUnitsValueChanged() |
| { |
| updateHeightOverride(); |
| } |
| |
| void NestedArtboardLayout::instanceWidthScaleTypeChanged() |
| { |
| updateWidthOverride(); |
| } |
| |
| void NestedArtboardLayout::instanceHeightScaleTypeChanged() |
| { |
| updateHeightOverride(); |
| } |
| |
| bool NestedArtboardLayout::syncStyleChanges() |
| { |
| if (m_referencedArtboard == nullptr) |
| { |
| return false; |
| } |
| return m_referencedArtboard->syncStyleChanges(); |
| } |
| |
| void NestedArtboardLayout::updateArtboard( |
| ViewModelInstanceArtboard* viewModelInstanceArtboard) |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| // Re-collect on the layout that owns our node, not on parent(), or a |
| // container between the two leaves the swap unsynced. |
| auto* layout = owningLayout(parent()); |
| if (layout != nullptr) |
| { |
| layout->clearLayoutChildren(); |
| } |
| #endif |
| NestedArtboard::updateArtboard(viewModelInstanceArtboard); |
| updateWidthOverride(); |
| updateHeightOverride(); |
| #ifdef WITH_RIVE_LAYOUT |
| if (layout != nullptr) |
| { |
| layout->syncLayoutChildren(); |
| } |
| #endif |
| } |