| #include "rive/component.hpp" |
| #include "rive/file.hpp" |
| #include "rive/artboard_component_list.hpp" |
| #include "rive/constraints/layout_constraint.hpp" |
| #include "rive/constraints/list_constraint.hpp" |
| #include "rive/constraints/scrolling/scroll_constraint.hpp" |
| #include "rive/layout_component.hpp" |
| #include "rive/viewmodel/viewmodel_instance_symbol_list_index.hpp" |
| #include "rive/world_transform_component.hpp" |
| #include "rive/layout/layout_data.hpp" |
| |
| using namespace rive; |
| |
| ArtboardComponentList::ArtboardComponentList() {} |
| ArtboardComponentList::~ArtboardComponentList() { clear(); } |
| |
| void ArtboardComponentList::clear() |
| { |
| for (auto& artboard : m_artboardInstancesMap) |
| { |
| artboard.second.reset(); |
| } |
| for (auto& sm : m_stateMachinesMap) |
| { |
| sm.second.reset(); |
| } |
| m_artboardInstancesMap.clear(); |
| m_stateMachinesMap.clear(); |
| m_listItems.clear(); |
| m_artboardsMap.clear(); |
| m_resourcePool.clear(); |
| m_stateMachinesPool.clear(); |
| m_artboardOverridesMap.clear(); |
| } |
| |
| rcp<ViewModelInstanceListItem> ArtboardComponentList::listItem(int index) |
| { |
| if (index < m_listItems.size()) |
| { |
| return m_listItems[index]; |
| } |
| return nullptr; |
| } |
| ArtboardInstance* ArtboardComponentList::artboardInstance(int index) |
| { |
| if (index < m_listItems.size()) |
| { |
| auto item = listItem(index); |
| auto itr = m_artboardInstancesMap.find(item); |
| if (itr != m_artboardInstancesMap.end()) |
| { |
| return m_artboardInstancesMap[item].get(); |
| } |
| } |
| return nullptr; |
| } |
| StateMachineInstance* ArtboardComponentList::stateMachineInstance(int index) |
| { |
| if (index < m_listItems.size()) |
| { |
| auto item = listItem(index); |
| auto itr = m_stateMachinesMap.find(item); |
| if (itr != m_stateMachinesMap.end()) |
| { |
| return m_stateMachinesMap[item].get(); |
| } |
| } |
| return nullptr; |
| } |
| |
| #ifdef WITH_RIVE_LAYOUT |
| void* ArtboardComponentList::layoutNode(int index) |
| { |
| auto artboard = artboardInstance(index); |
| if (artboard != nullptr) |
| { |
| return static_cast<void*>(&artboard->takeLayoutData()->node); |
| } |
| return nullptr; |
| } |
| #endif |
| |
| void ArtboardComponentList::markLayoutNodeDirty( |
| bool shouldForceUpdateLayoutBounds) |
| { |
| bool parentIsRow = mainAxisIsRow(); |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| artboard->parentIsRow(parentIsRow); |
| } |
| } |
| } |
| |
| void ArtboardComponentList::updateLayoutBounds(bool animate) |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| artboard->updateLayoutBounds(animate); |
| auto bounds = artboard->layoutBounds(); |
| setItemSize(Vec2D(bounds.width(), bounds.height()), i); |
| } |
| } |
| #endif |
| computeLayoutBounds(); |
| } |
| |
| bool ArtboardComponentList::syncStyleChanges() |
| { |
| bool changed = false; |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| bool artboardChanged = artboard->syncStyleChanges(); |
| if (artboardChanged) |
| { |
| changed = true; |
| } |
| } |
| } |
| return changed; |
| } |
| |
| Artboard* ArtboardComponentList::findArtboard( |
| const rcp<ViewModelInstanceListItem>& listItem) const |
| { |
| auto viewModelInstance = listItem->viewModelInstance(); |
| if (viewModelInstance == nullptr) |
| { |
| return nullptr; |
| } |
| auto artboard = m_artboardsMap.find(viewModelInstance->viewModelId()); |
| if (artboard != m_artboardsMap.end()) |
| { |
| return artboard->second; |
| } |
| auto artboards = m_file->artboards(); |
| for (auto& artboard : artboards) |
| { |
| if (artboard->viewModelId() == viewModelInstance->viewModelId()) |
| { |
| m_artboardsMap[viewModelInstance->viewModelId()] = artboard; |
| return artboard; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| void ArtboardComponentList::disposeListItem( |
| const rcp<ViewModelInstanceListItem>& listItem) |
| { |
| removeArtboard(listItem); |
| } |
| |
| std::unique_ptr<ArtboardInstance> ArtboardComponentList::createArtboard( |
| Component* target, |
| rcp<ViewModelInstanceListItem> listItem) const |
| { |
| auto artboard = findArtboard(listItem); |
| if (artboard != nullptr) |
| { |
| auto inst = artboard->instance(); |
| return inst; |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<StateMachineInstance> ArtboardComponentList:: |
| createStateMachineInstance(Component* target, ArtboardInstance* artboard) |
| { |
| if (artboard != nullptr) |
| { |
| auto stateMachineInstance = artboard->stateMachineAt(0); |
| linkStateMachineToArtboard(stateMachineInstance.get(), artboard); |
| return stateMachineInstance; |
| } |
| return nullptr; |
| } |
| |
| void ArtboardComponentList::linkStateMachineToArtboard( |
| StateMachineInstance* stateMachineInstance, |
| ArtboardInstance* artboard) |
| { |
| if (artboard != nullptr && stateMachineInstance != nullptr) |
| { |
| auto dataContext = artboard->dataContext(); |
| stateMachineInstance->dataContext(dataContext); |
| // TODO: @hernan added this to make sure data binds are procesed in the |
| // current frame instead of waiting for the next run. But might not be |
| // necessary. Needs more testing. |
| stateMachineInstance->updateDataBinds(false); |
| } |
| } |
| |
| bool ArtboardComponentList ::listsAreEqual( |
| std::vector<rcp<ViewModelInstanceListItem>>* list, |
| std::vector<rcp<ViewModelInstanceListItem>>* compared) |
| { |
| if (!list || !compared) |
| { |
| return false; |
| } |
| if (list->size() != compared->size()) |
| { |
| return false; |
| } |
| size_t index = 0; |
| for (auto& item : *list) |
| { |
| if (item != (*compared)[index]) |
| { |
| return false; |
| } |
| ++index; |
| } |
| return true; |
| } |
| |
| void ArtboardComponentList::updateList( |
| std::vector<rcp<ViewModelInstanceListItem>>* list) |
| { |
| if (listsAreEqual(&m_listItems, list)) |
| { |
| return; |
| } |
| m_oldItems.clear(); |
| m_oldItems.assign(m_listItems.begin(), m_listItems.end()); |
| m_listItems.clear(); |
| m_listItems.assign(list->begin(), list->end()); |
| m_artboardSizes.clear(); |
| |
| auto p = layoutParent(); |
| if (p != nullptr) |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| p->clearLayoutChildren(); |
| #endif |
| } |
| // We need to dispose old items after the layout children of the parent have |
| // updated to ensure no bad YGNodes are being hosted from the old data |
| // during clearLayoutChildren. |
| for (auto item : m_oldItems) |
| { |
| auto it = std::find(m_listItems.begin(), m_listItems.end(), item); |
| if (it == m_listItems.end()) |
| { |
| disposeListItem(item); |
| } |
| } |
| uint32_t index = 0; |
| for (auto& item : m_listItems) |
| { |
| auto viewModelInstance = item->viewModelInstance(); |
| if (viewModelInstance != nullptr) |
| { |
| auto symbol = viewModelInstance->symbol( |
| ViewModelInstanceSymbolListIndexBase::typeKey); |
| if (symbol != nullptr) |
| { |
| symbol->as<ViewModelInstanceSymbolListIndex>()->propertyValue( |
| index); |
| } |
| } |
| auto artboard = findArtboard(item); |
| if (artboard != nullptr) |
| { |
| m_artboardSizes.push_back( |
| Vec2D(artboard->width(), artboard->height())); |
| } |
| auto itr = m_artboardInstancesMap.find(item); |
| if (!virtualizationEnabled() && itr == m_artboardInstancesMap.end()) |
| { |
| createArtboardAt(index); |
| } |
| index++; |
| } |
| computeLayoutBounds(); |
| syncLayoutChildren(); |
| markLayoutNodeDirty(); |
| markWorldTransformDirty(); |
| addDirt(ComponentDirt::Components); |
| } |
| |
| void ArtboardComponentList::syncLayoutChildren() |
| { |
| auto p = layoutParent(); |
| if (p != nullptr) |
| { |
| #ifdef WITH_RIVE_LAYOUT |
| p->syncLayoutChildren(); |
| #endif |
| } |
| } |
| |
| bool ArtboardComponentList::advanceComponent(float elapsedSeconds, |
| AdvanceFlags flags) |
| { |
| if (artboardCount() == 0 || isCollapsed()) |
| { |
| return false; |
| } |
| bool keepGoing = false; |
| bool advanceNested = |
| (flags & AdvanceFlags::AdvanceNested) == AdvanceFlags::AdvanceNested; |
| bool newFrame = (flags & AdvanceFlags::NewFrame) == AdvanceFlags::NewFrame; |
| auto advancingFlags = flags & ~AdvanceFlags::IsRoot; |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| if (advanceNested) |
| { |
| auto stateMachine = stateMachineInstance(i); |
| if (stateMachine != nullptr) |
| { |
| // If it is not a new frame, we |
| // first validate whether their state has changed. Then and only |
| // then we advance the state machine. This avoids triggering |
| // dirt from advances that make intermediate value changes but |
| // finally settle in the same value |
| if (!newFrame) |
| { |
| if (stateMachine->tryChangeState()) |
| { |
| if (stateMachine->advance(elapsedSeconds, newFrame)) |
| { |
| keepGoing = true; |
| } |
| } |
| } |
| else |
| { |
| if (stateMachine->advance(elapsedSeconds, newFrame)) |
| { |
| keepGoing = true; |
| } |
| } |
| } |
| } |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| if (artboard->advanceInternal(elapsedSeconds, advancingFlags)) |
| { |
| keepGoing = true; |
| } |
| if (artboard->hasDirt(ComponentDirt::Components)) |
| { |
| // The animation(s) caused the artboard to need an update. |
| addDirt(ComponentDirt::Components); |
| } |
| } |
| } |
| |
| return keepGoing; |
| } |
| |
| void ArtboardComponentList::reset() |
| { |
| for (auto& item : m_listItems) |
| { |
| if (m_shouldResetInstances) |
| { |
| auto viewModelInstance = item->viewModelInstance(); |
| if (viewModelInstance != nullptr) |
| { |
| viewModelInstance->advanced(); |
| } |
| } |
| auto itr = m_artboardInstancesMap.find(item); |
| if (itr != m_artboardInstancesMap.end()) |
| { |
| m_artboardInstancesMap[item]->reset(); |
| } |
| } |
| } |
| |
| AABB ArtboardComponentList::layoutBounds() |
| { |
| return AABB(0, 0, m_layoutSize.x, m_layoutSize.y); |
| } |
| |
| AABB ArtboardComponentList::layoutBoundsForNode(int index) |
| { |
| if (virtualizationEnabled()) |
| { |
| auto realIndex = std::fmod(index, m_listItems.size()); |
| auto gap = this->gap(); |
| float runningSize = 0; |
| bool isHorizontal = mainAxisIsRow(); |
| for (int i = 0; i < realIndex; i++) |
| { |
| auto size = m_artboardSizes[i]; |
| if (isHorizontal) |
| { |
| runningSize += size.x + gap; |
| } |
| else |
| { |
| runningSize += size.y + gap; |
| } |
| } |
| auto itemSize = m_artboardSizes[realIndex]; |
| double left = isHorizontal ? runningSize : 0; |
| double top = isHorizontal ? 0 : runningSize; |
| return AABB(left, top, left + itemSize.x, top + itemSize.y); |
| } |
| else |
| { |
| if (index >= 0 && index < numLayoutNodes()) |
| { |
| return artboardInstance(index)->layoutBounds(); |
| } |
| } |
| return AABB(); |
| } |
| |
| void ArtboardComponentList::markHostingLayoutDirty( |
| ArtboardInstance* artboardInstance) |
| { |
| // TODO: Should optimize this |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = this->artboardInstance(i); |
| if (artboard != nullptr && artboard == artboardInstance) |
| { |
| this->artboard()->markLayoutDirty(artboardInstance); |
| break; |
| } |
| } |
| markWorldTransformDirty(); |
| } |
| |
| bool ArtboardComponentList::willDraw() |
| { |
| return Super::willDraw() && m_listItems.size() > 0; |
| } |
| |
| void ArtboardComponentList::draw(Renderer* renderer) |
| { |
| if (m_needsSaveOperation) |
| { |
| renderer->save(); |
| } |
| if (virtualizationEnabled()) |
| { |
| renderer->transform( |
| parent()->as<WorldTransformComponent>()->worldTransform()); |
| if (m_visibleStartIndex != -1 && m_visibleEndIndex != -1) |
| { |
| // We need to render in the correct order so we get the correct |
| // z-index for items in cases where there is overlap |
| auto startIndex = m_visibleStartIndex % (int)m_listItems.size(); |
| auto endIndex = m_visibleEndIndex % (int)m_listItems.size(); |
| int i = startIndex; |
| while (true) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| renderer->save(); |
| auto transform = m_artboardTransforms[artboard]; |
| renderer->transform(transform); |
| artboard->draw(renderer); |
| renderer->restore(); |
| } |
| if (i == endIndex) |
| { |
| break; |
| } |
| i = (i + 1) % m_listItems.size(); |
| } |
| } |
| } |
| else |
| { |
| renderer->transform(worldTransform()); |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| renderer->save(); |
| auto transform = m_artboardTransforms[artboard]; |
| renderer->transform(transform); |
| artboard->draw(renderer); |
| renderer->restore(); |
| } |
| } |
| } |
| if (m_needsSaveOperation) |
| { |
| renderer->restore(); |
| } |
| } |
| |
| Core* ArtboardComponentList::hitTest(HitInfo*, const Mat2D&) { return nullptr; } |
| |
| bool ArtboardComponentList::hitTestHost(const Vec2D& position, |
| bool skipOnUnclipped, |
| ArtboardInstance* artboard) |
| { |
| if (artboard == nullptr) |
| { |
| return false; |
| } |
| auto bounds = artboardPosition(artboard); |
| Vec2D offset(bounds.x + position.x, bounds.y + position.y); |
| auto transform = virtualizationEnabled() |
| ? parent()->as<LayoutComponent>()->worldTransform() |
| : worldTransform(); |
| return parent()->hitTestPoint(transform * offset, skipOnUnclipped, false); |
| } |
| |
| Vec2D ArtboardComponentList::hostTransformPoint( |
| const Vec2D& vec, |
| ArtboardInstance* artboardInstance) |
| { |
| auto bounds = artboardPosition(artboardInstance); |
| Vec2D offset(bounds.x + vec.x, bounds.y + vec.y); |
| auto transform = virtualizationEnabled() |
| ? parent()->as<LayoutComponent>()->worldTransform() |
| : worldTransform(); |
| auto localVec = transform * offset; |
| auto ab = artboard(); |
| return ab ? ab->rootTransform(localVec) : localVec; |
| } |
| |
| void ArtboardComponentList::update(ComponentDirt value) |
| { |
| Super::update(value); |
| if (artboardCount() == 0) |
| { |
| return; |
| } |
| |
| if (hasDirt(value, ComponentDirt::RenderOpacity)) |
| { |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| artboard->opacity(renderOpacity()); |
| } |
| } |
| } |
| if (hasDirt(value, ComponentDirt::Components)) |
| { |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| artboard->updatePass(false); |
| } |
| } |
| } |
| } |
| |
| void ArtboardComponentList::updateWorldTransform() |
| { |
| updateArtboardsWorldTransform(); |
| Super::updateWorldTransform(); |
| } |
| |
| void ArtboardComponentList::updateArtboardsWorldTransform() |
| { |
| auto count = m_listItems.size(); |
| if (count == 0) |
| { |
| return; |
| } |
| // We only update non layout transforms here |
| if (!virtualizationEnabled()) |
| { |
| auto useLayout = layoutParent() != nullptr; |
| for (int i = 0; i < count; i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| auto bounds = useLayout ? artboard->layoutBounds() |
| : artboard->worldBounds(); |
| m_artboardTransforms[artboard] = |
| Mat2D::fromTranslate(bounds.left(), bounds.top()); |
| } |
| } |
| } |
| } |
| |
| void ArtboardComponentList::updateConstraints() |
| { |
| if (m_layoutConstraints.size() > 0) |
| { |
| for (auto parentConstraint : m_layoutConstraints) |
| { |
| parentConstraint->constrainChild(this); |
| } |
| } |
| if (m_listConstraints.size() > 0 && !virtualizationEnabled()) |
| { |
| for (auto listConstraint : m_listConstraints) |
| { |
| listConstraint->constrainList(this); |
| } |
| } |
| for (auto constraint : constraints()) |
| { |
| auto listConstraint = ListConstraint::from(constraint); |
| if (listConstraint != nullptr) |
| { |
| continue; |
| } |
| constraint->constrain(this); |
| } |
| } |
| |
| void ArtboardComponentList::internalDataContext(DataContext* value) |
| { |
| // Reconcile the existing data contexts with the new parent |
| for (auto& artboard : m_artboardInstancesMap) |
| { |
| auto dataContext = artboard.second->dataContext(); |
| if (dataContext != nullptr) |
| { |
| dataContext->parent(value); |
| artboard.second->internalDataContext(dataContext); |
| } |
| } |
| for (auto& sm : m_stateMachinesMap) |
| { |
| auto dataContext = sm.second->dataContext(); |
| if (dataContext != nullptr) |
| { |
| dataContext->parent(value); |
| sm.second->internalDataContext(dataContext); |
| } |
| } |
| } |
| |
| void ArtboardComponentList::bindViewModelInstance( |
| rcp<ViewModelInstance> viewModelInstance, |
| DataContext* parent) |
| { |
| // At the time this is called, artboards will not yet have been instanced |
| // so its essentially a no-op and the call to bindViewModelInstance on |
| // each artboard is made at the time of artboard instancing |
| } |
| |
| void ArtboardComponentList::clearDataContext() {} |
| void ArtboardComponentList::unbind() { clear(); } |
| void ArtboardComponentList::updateDataBinds() |
| { |
| for (int i = 0; i < artboardCount(); i++) |
| { |
| auto stateMachine = stateMachineInstance(i); |
| if (stateMachine != nullptr) |
| { |
| stateMachine->updateDataBinds(false); |
| } |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| artboard->updateDataBinds(); |
| } |
| } |
| } |
| |
| Vec2D ArtboardComponentList::artboardPosition(ArtboardInstance* artboard) |
| { |
| auto mat = m_artboardTransforms[artboard]; |
| return Vec2D(mat[4], mat[5]); |
| } |
| |
| bool ArtboardComponentList::worldToLocal(Vec2D world, Vec2D* local, int index) |
| { |
| assert(local != nullptr); |
| auto artboard = artboardInstance(index); |
| if (artboard == nullptr) |
| { |
| return false; |
| } |
| Vec2D offset = artboardPosition(artboard); |
| auto transform = virtualizationEnabled() |
| ? parent()->as<LayoutComponent>()->worldTransform() |
| : worldTransform(); |
| auto artboardTransform = |
| transform * Mat2D::fromTranslate(offset.x, offset.y); |
| Mat2D toMountedArtboard; |
| if (!artboardTransform.invert(&toMountedArtboard)) |
| { |
| return false; |
| } |
| |
| *local = toMountedArtboard * world; |
| |
| return true; |
| } |
| |
| void ArtboardComponentList::file(File* value) { m_file = value; } |
| File* ArtboardComponentList::file() const { return m_file; } |
| |
| Core* ArtboardComponentList::clone() const |
| { |
| auto clone = |
| static_cast<ArtboardComponentList*>(ArtboardComponentListBase::clone()); |
| clone->file(file()); |
| return clone; |
| } |
| |
| void ArtboardComponentList::createArtboardAt(int index) |
| { |
| auto item = listItem(index); |
| if (item != nullptr) |
| { |
| auto artboardCopy = createArtboard(this, item); |
| if (artboardCopy != nullptr) |
| { |
| attachArtboardOverride(artboardCopy.get(), item); |
| addArtboardAt(std::move(artboardCopy), index); |
| } |
| } |
| } |
| |
| void ArtboardComponentList::addArtboardAt( |
| std::unique_ptr<ArtboardInstance> artboard, |
| int index) |
| { |
| auto item = listItem(index); |
| if (item != nullptr) |
| { |
| auto artboardInstance = artboard.get(); |
| m_artboardInstancesMap[item] = std::move(artboard); |
| bindArtboard(artboardInstance, item); |
| if (artboardInstance != nullptr) |
| { |
| artboardInstance->host(this); |
| artboardInstance->frameOrigin(false); |
| artboardInstance->parentIsRow(mainAxisIsRow()); |
| } |
| syncLayoutChildren(); |
| auto artboard = findArtboard(item); |
| if (artboard != nullptr) |
| { |
| |
| auto& smPool = m_stateMachinesPool[artboard]; |
| if (!smPool.empty()) |
| { |
| auto sm = smPool.back().get(); |
| |
| sm->resetState(); |
| applyRecorders(sm, artboard); |
| m_stateMachinesMap[item] = std::move(smPool.back()); |
| linkStateMachineToArtboard(sm, artboardInstance); |
| smPool.pop_back(); |
| return; |
| } |
| } |
| auto stateMachineCopy = |
| createStateMachineInstance(this, artboardInstance); |
| m_stateMachinesMap[item] = std::move(stateMachineCopy); |
| } |
| } |
| |
| void ArtboardComponentList::bindArtboard( |
| ArtboardInstance* artboardInstance, |
| rcp<ViewModelInstanceListItem> listItem) |
| { |
| if (artboardInstance != nullptr) |
| { |
| auto mainArtboard = this->artboard(); |
| auto dataContext = mainArtboard->dataContext(); |
| // TODO: @hernan added this to make sure data binds are procesed in the |
| // current frame instead of waiting for the next run. But might not be |
| // necessary. Needs more testing. |
| artboardInstance->bindViewModelInstance(listItem->viewModelInstance(), |
| dataContext); |
| artboardInstance->updateDataBinds(); |
| } |
| } |
| |
| void ArtboardComponentList::removeArtboardAt(int index) |
| { |
| auto item = listItem(index); |
| removeArtboard(item); |
| } |
| |
| void ArtboardComponentList::removeArtboard(rcp<ViewModelInstanceListItem> item) |
| { |
| auto itr = m_artboardInstancesMap.find(item); |
| if (itr != m_artboardInstancesMap.end()) |
| { |
| clearArtboardOverride(itr->second.get()); |
| } |
| m_artboardInstancesMap.erase(item); |
| m_stateMachinesMap.erase(item); |
| } |
| |
| void ArtboardComponentList::createArtboardRecorders(const Artboard* artboard) |
| { |
| if (artboard == nullptr) |
| { |
| return; |
| } |
| auto recorderIt = m_propertyRecordersMap.find(artboard); |
| if (recorderIt == m_propertyRecordersMap.end()) |
| { |
| auto propertyRecorder = rivestd::make_unique<PropertyRecorder>(); |
| propertyRecorder->recordArtboard(artboard); |
| m_propertyRecordersMap[artboard] = std::move(propertyRecorder); |
| for (auto& nestedArtboard : artboard->nestedArtboards()) |
| { |
| auto sourceArtboard = nestedArtboard->sourceArtboard(); |
| createArtboardRecorders(sourceArtboard); |
| } |
| } |
| } |
| |
| void ArtboardComponentList::applyRecorders(Artboard* artboard, |
| const Artboard* sourceArtboard) |
| { |
| auto sourcedArtboardIt = m_propertyRecordersMap.find(sourceArtboard); |
| if (sourcedArtboardIt != m_propertyRecordersMap.end()) |
| { |
| auto propertyRecorder = sourcedArtboardIt->second.get(); |
| propertyRecorder->apply(artboard); |
| } |
| for (auto& nestedArtboard : artboard->nestedArtboards()) |
| { |
| applyRecorders(nestedArtboard->sourceArtboard(), |
| nestedArtboard->sourceArtboard()->artboardSource()); |
| } |
| } |
| |
| void ArtboardComponentList::applyRecorders( |
| StateMachineInstance* stateMachineInstance, |
| const Artboard* sourceArtboard) |
| { |
| auto propertyRecorder = m_propertyRecordersMap[sourceArtboard].get(); |
| propertyRecorder->apply(stateMachineInstance); |
| } |
| |
| void ArtboardComponentList::addVirtualizable(int index) |
| { |
| auto listItem = this->listItem(index); |
| if (listItem != nullptr) |
| { |
| auto artboard = findArtboard(listItem); |
| if (artboard != nullptr) |
| { |
| createArtboardRecorders(artboard); |
| auto& pool = m_resourcePool[artboard]; |
| if (pool.empty()) |
| { |
| createArtboardAt(index); |
| } |
| else |
| { |
| auto pooledArtboard = pool.back().get(); |
| applyRecorders(pooledArtboard, artboard); |
| addArtboardAt(std::move(pool.back()), index); |
| pool.pop_back(); |
| } |
| // Add components dirt so we process layout updates in the same |
| // frame that a mounted artboard is added |
| addDirt(ComponentDirt::Components); |
| auto p = layoutParent(); |
| if (p != nullptr) |
| { |
| p->markLayoutStyleDirty(); |
| } |
| } |
| } |
| } |
| |
| void ArtboardComponentList::removeVirtualizable(int index) |
| { |
| auto listItem = this->listItem(index); |
| if (listItem != nullptr) |
| { |
| auto artboard = findArtboard(listItem); |
| auto artboardInstance = std::move(m_artboardInstancesMap[listItem]); |
| if (artboard != nullptr && artboardInstance != nullptr) |
| { |
| auto& pool = m_resourcePool[artboard]; |
| pool.push_back(std::move(artboardInstance)); |
| } |
| auto smInstanceIterator = m_stateMachinesMap.find(listItem); |
| if (smInstanceIterator != m_stateMachinesMap.end()) |
| { |
| auto& smPool = m_stateMachinesPool[artboard]; |
| smPool.push_back(std::move(smInstanceIterator->second)); |
| } |
| } |
| removeArtboardAt(index); |
| } |
| |
| void ArtboardComponentList::setVirtualizablePosition(int index, Vec2D position) |
| { |
| auto artboard = this->artboardInstance(index); |
| if (artboard != nullptr) |
| { |
| m_artboardTransforms[artboard] = |
| Mat2D::fromTranslate(position.x, position.y); |
| } |
| } |
| |
| bool ArtboardComponentList::virtualizationEnabled() |
| { |
| auto virtualizer = scrollConstraint(); |
| return virtualizer != nullptr && virtualizer->virtualize(); |
| } |
| |
| ScrollConstraint* ArtboardComponentList::scrollConstraint() |
| { |
| for (auto parentConstraint : m_layoutConstraints) |
| { |
| if (parentConstraint->constraint()->is<ScrollConstraint>()) |
| { |
| return parentConstraint->constraint()->as<ScrollConstraint>(); |
| } |
| } |
| return nullptr; |
| } |
| |
| void ArtboardComponentList::computeLayoutBounds() |
| { |
| if (virtualizationEnabled()) |
| { |
| auto gap = this->gap(); |
| auto runningWidth = 0.0f; |
| auto runningHeight = 0.0f; |
| bool isHorz = mainAxisIsRow(); |
| for (int i = 0; i < m_artboardSizes.size(); i++) |
| { |
| auto size = m_artboardSizes[i]; |
| auto realGap = i == m_artboardSizes.size() - 1 ? 0 : gap; |
| if (isHorz) |
| { |
| runningWidth += size.x + realGap; |
| runningHeight = std::max(runningHeight, size.y); |
| } |
| else |
| { |
| runningWidth = std::max(runningWidth, size.x); |
| runningHeight += size.y + realGap; |
| } |
| } |
| m_layoutSize = Vec2D(runningWidth, runningHeight); |
| |
| auto scroll = scrollConstraint(); |
| if (scroll != nullptr) |
| { |
| scroll->constrainVirtualized(true); |
| } |
| } |
| } |
| |
| Vec2D ArtboardComponentList::size() { return m_layoutSize; } |
| |
| Vec2D ArtboardComponentList::itemSize(int index) |
| { |
| return index < m_artboardSizes.size() ? m_artboardSizes[index] : Vec2D(); |
| } |
| |
| void ArtboardComponentList::setItemSize(Vec2D size, int index) |
| { |
| if (index < m_artboardSizes.size()) |
| { |
| m_artboardSizes[index] = size; |
| } |
| } |
| |
| float ArtboardComponentList::gap() |
| { |
| auto p = layoutParent(); |
| if (p != nullptr) |
| { |
| return mainAxisIsRow() ? p->gapHorizontal() : p->gapVertical(); |
| } |
| return 0.0f; |
| } |
| |
| void ArtboardComponentList::attachArtboardOverride( |
| ArtboardInstance* instance, |
| rcp<ViewModelInstanceListItem> listItem) |
| { |
| |
| auto viewModelInstance = listItem->viewModelInstance(); |
| if (viewModelInstance == nullptr) |
| { |
| return; |
| } |
| auto artboards = m_file->artboards(); |
| int artboardIndex = -1; |
| for (auto& artboard : artboards) |
| { |
| artboardIndex++; |
| |
| if (artboard->viewModelId() == viewModelInstance->viewModelId()) |
| { |
| break; |
| } |
| } |
| if (artboardIndex < 0 && artboardIndex >= artboards.size()) |
| { |
| return; |
| } |
| ArtboardComponentListOverride* artboardOverride = nullptr; |
| for (auto& child : children()) |
| { |
| if (child->is<ArtboardComponentListOverride>()) |
| { |
| if (child->as<ArtboardComponentListOverride>()->artboardId() == -1) |
| { |
| artboardOverride = child->as<ArtboardComponentListOverride>(); |
| } |
| else if (child->as<ArtboardComponentListOverride>()->artboardId() == |
| artboardIndex) |
| { |
| artboardOverride = child->as<ArtboardComponentListOverride>(); |
| break; |
| } |
| } |
| } |
| if (artboardOverride != nullptr) |
| { |
| artboardOverride->addArtboard(instance); |
| } |
| } |
| |
| void ArtboardComponentList::clearArtboardOverride( |
| ArtboardInstance* artboardInstance) |
| { |
| for (auto& child : children()) |
| { |
| if (child->is<ArtboardComponentListOverride>()) |
| { |
| // Passing the artboard to all overrides in case in the future we |
| // support stacking overrides on top of each other. |
| // Currently all except one will be a no-op. |
| child->as<ArtboardComponentListOverride>()->removeArtboard( |
| artboardInstance); |
| } |
| } |
| } |
| |
| bool ArtboardComponentList::mainAxisIsRow() |
| { |
| auto p = layoutParent(); |
| return p != nullptr ? p->mainAxisIsRow() : true; |
| } |
| |
| LayoutComponent* ArtboardComponentList::layoutParent() |
| { |
| if (parent() != nullptr && parent()->is<LayoutComponent>()) |
| { |
| return parent()->as<LayoutComponent>(); |
| } |
| return nullptr; |
| } |
| |
| const Mat2D& ArtboardComponentList::listTransform() { return worldTransform(); } |
| |
| void ArtboardComponentList::listItemTransforms(std::vector<Mat2D*>& transforms) |
| { |
| auto count = m_listItems.size(); |
| for (int i = 0; i < count; i++) |
| { |
| auto artboard = artboardInstance(i); |
| if (artboard != nullptr) |
| { |
| transforms.push_back(&m_artboardTransforms[artboard]); |
| } |
| } |
| } |