/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "hierarchy_controller.h" #include #include #include #include #include using namespace META_NS; SCENE_BEGIN_NAMESPACE() bool NodeHierarchyController::Build(const IMetadata::Ptr& data) { observer_ = GetObjectRegistry().Create(META_NS::ClassId::ObjectHierarchyObserver); CORE_ASSERT(observer_); // Do not serialize the observer. META_NS::SetObjectFlags(observer_, META_NS::ObjectFlagBits::SERIALIZE, false); observer_->OnHierarchyChanged()->AddHandler( MakeCallback(this, &NodeHierarchyController::HierarchyChanged)); return true; } void NodeHierarchyController::Destroy() { SetTarget({}, {}); observer_.reset(); } void NodeHierarchyController::SetTarget(const IObject::Ptr& hierarchyRoot, HierarchyChangeModeValue mode) { if (!observer_) { return; } target_ = hierarchyRoot; if (!hierarchyRoot) { DetachAll(); } observer_->SetTarget(hierarchyRoot, mode); if (hierarchyRoot) { AttachAll(); } } IObject::Ptr NodeHierarchyController::GetTarget() const { return observer_->GetTarget(); } BASE_NS::vector NodeHierarchyController::GetAllObserved() const { return observer_->GetAllObserved(); } bool NodeHierarchyController::AttachAll() { if (const auto root = target_.lock()) { return RunOperation({ NodeOperation::ATTACH, target_ }); } return false; } bool NodeHierarchyController::DetachAll() { if (auto root = target_.lock()) { return RunOperation({ NodeOperation::DETACH, target_ }); } return false; } void NodeHierarchyController::HierarchyChanged(const HierarchyChangedInfo& info) { if (info.change == HierarchyChangeType::ADDED) { RunOperation({ NodeOperation::ATTACH, info.object }); } else if (info.change == HierarchyChangeType::REMOVING) { RunOperation({ NodeOperation::DETACH, info.object }); } } template void IterateChildren(const BASE_NS::vector& children, bool reverse, Callback&& callback) { if (reverse) { for (auto it = children.rbegin(); it != children.rend(); ++it) { callback(*it); } } else { for (auto&& child : children) { callback(child); } } } template void IterateHierarchy(const IObject::Ptr& root, bool reverse, Callback&& callback) { if (const auto container = interface_cast(root)) { IterateChildren(container->GetAll(), reverse, callback); } if (const auto content = interface_cast(root)) { if (auto object = GetValue(content->Content())) { callback(object); } } } BASE_NS::vector NodeHierarchyController::GetAllNodes() const { BASE_NS::vector nodes; auto add = [&nodes](const INode::Ptr& node) { nodes.push_back(node); }; if (const auto root = target_.lock()) { IterateShared( root, [&add](const IObject::Ptr& object) { return true; }, TraversalType::DEPTH_FIRST_POST_ORDER); } return nodes; } void NodeHierarchyController::AttachHierarchy(const IObject::Ptr& root) { if (!root) { return; } AttachNode(interface_cast(root)); IterateHierarchy(root, false, [this](const IObject::Ptr& object) { AttachHierarchy(object); }); } void NodeHierarchyController::AttachNode(INode* const node) { if (node) { const auto state = node->GetAttachedState(); if (state == NodeState::DETACHED) { node->AttachToHierarchy(); } } } void NodeHierarchyController::DetachHierarchy(const IObject::Ptr& root) { if (!root) { return; } IterateHierarchy(root, true, [this](const IObject::Ptr& object) { DetachHierarchy(object); }); DetachNode(interface_cast(root)); } void NodeHierarchyController::DetachNode(INode* const node) { if (node) { const auto state = node->GetAttachedState(); if (state == NodeState::ATTACHED) { node->DetachFromHierarchy(); } } } bool NodeHierarchyController::RunOperation(NodeOperation operation) { auto object = operation.root_.lock(); if (!object) { return false; } // This may potentially end up calling Attach/DetachHierarchy several times // for the same subtrees, but we will accept that. Attach/Detach will only // be called once since the functions check for current state. if (auto root = operation.root_.lock()) { if (operation.operation_ == NodeOperation::ATTACH) { AttachHierarchy(root); } else if (operation.operation_ == NodeOperation::DETACH) { DetachHierarchy(root); } } return true; } void RegisterNodeHierarchyController() { META_NS::GetObjectRegistry().RegisterObjectType(); } void UnregisterNodeHierarchyController() { META_NS::GetObjectRegistry().UnregisterObjectType(); } SCENE_END_NAMESPACE()