1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "hierarchy_controller.h"
16
17 #include <meta/api/future.h>
18 #include <meta/api/make_callback.h>
19 #include <meta/api/task.h>
20 #include <meta/interface/intf_content.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22
23 using namespace META_NS;
24
SCENE_BEGIN_NAMESPACE()25 SCENE_BEGIN_NAMESPACE()
26
27 bool NodeHierarchyController::Build(const IMetadata::Ptr& data)
28 {
29 observer_ = GetObjectRegistry().Create<IObjectHierarchyObserver>(META_NS::ClassId::ObjectHierarchyObserver);
30 CORE_ASSERT(observer_);
31
32 // Do not serialize the observer.
33 META_NS::SetObjectFlags(observer_, META_NS::ObjectFlagBits::SERIALIZE, false);
34
35 observer_->OnHierarchyChanged()->AddHandler(
36 MakeCallback<IOnHierarchyChanged>(this, &NodeHierarchyController::HierarchyChanged));
37
38 return true;
39 }
40
Destroy()41 void NodeHierarchyController::Destroy()
42 {
43 SetTarget({}, {});
44 observer_.reset();
45 }
46
SetTarget(const IObject::Ptr & hierarchyRoot,HierarchyChangeModeValue mode)47 void NodeHierarchyController::SetTarget(const IObject::Ptr& hierarchyRoot, HierarchyChangeModeValue mode)
48 {
49 if (!observer_) {
50 return;
51 }
52 target_ = hierarchyRoot;
53
54 if (!hierarchyRoot) {
55 DetachAll();
56 }
57
58 observer_->SetTarget(hierarchyRoot, mode);
59
60 if (hierarchyRoot) {
61 AttachAll();
62 }
63 }
64
GetTarget() const65 IObject::Ptr NodeHierarchyController::GetTarget() const
66 {
67 return observer_->GetTarget();
68 }
69
GetAllObserved() const70 BASE_NS::vector<IObject::Ptr> NodeHierarchyController::GetAllObserved() const
71 {
72 return observer_->GetAllObserved();
73 }
74
AttachAll()75 bool NodeHierarchyController::AttachAll()
76 {
77 if (const auto root = target_.lock()) {
78 return RunOperation({ NodeOperation::ATTACH, target_ });
79 }
80 return false;
81 }
82
DetachAll()83 bool NodeHierarchyController::DetachAll()
84 {
85 if (auto root = target_.lock()) {
86 return RunOperation({ NodeOperation::DETACH, target_ });
87 }
88 return false;
89 }
90
HierarchyChanged(const HierarchyChangedInfo & info)91 void NodeHierarchyController::HierarchyChanged(const HierarchyChangedInfo& info)
92 {
93 if (info.change == HierarchyChangeType::ADDED) {
94 RunOperation({ NodeOperation::ATTACH, info.object });
95 } else if (info.change == HierarchyChangeType::REMOVING) {
96 RunOperation({ NodeOperation::DETACH, info.object });
97 }
98 }
99
100 template<class T, class Callback>
IterateChildren(const BASE_NS::vector<T> & children,bool reverse,Callback && callback)101 void IterateChildren(const BASE_NS::vector<T>& children, bool reverse, Callback&& callback)
102 {
103 if (reverse) {
104 for (auto it = children.rbegin(); it != children.rend(); ++it) {
105 callback(*it);
106 }
107 } else {
108 for (auto&& child : children) {
109 callback(child);
110 }
111 }
112 }
113
114 template<class Callback>
IterateHierarchy(const IObject::Ptr & root,bool reverse,Callback && callback)115 void IterateHierarchy(const IObject::Ptr& root, bool reverse, Callback&& callback)
116 {
117 if (const auto container = interface_cast<IContainer>(root)) {
118 IterateChildren(container->GetAll(), reverse, callback);
119 }
120 if (const auto content = interface_cast<IContent>(root)) {
121 if (auto object = GetValue(content->Content())) {
122 callback(object);
123 }
124 }
125 }
126
GetAllNodes() const127 BASE_NS::vector<INode::Ptr> NodeHierarchyController::GetAllNodes() const
128 {
129 BASE_NS::vector<INode::Ptr> nodes;
130 auto add = [&nodes](const INode::Ptr& node) { nodes.push_back(node); };
131 if (const auto root = target_.lock()) {
132 IterateShared(
133 root, [&add](const IObject::Ptr& object) { return true; }, TraversalType::DEPTH_FIRST_POST_ORDER);
134 }
135 return nodes;
136 }
137
AttachHierarchy(const IObject::Ptr & root)138 void NodeHierarchyController::AttachHierarchy(const IObject::Ptr& root)
139 {
140 if (!root) {
141 return;
142 }
143
144 AttachNode(interface_cast<INode>(root));
145 IterateHierarchy(root, false, [this](const IObject::Ptr& object) { AttachHierarchy(object); });
146 }
147
AttachNode(INode * const node)148 void NodeHierarchyController::AttachNode(INode* const node)
149 {
150 if (node) {
151 const auto state = node->GetAttachedState();
152 if (state == NodeState::DETACHED) {
153 node->AttachToHierarchy();
154 }
155 }
156 }
157
DetachHierarchy(const IObject::Ptr & root)158 void NodeHierarchyController::DetachHierarchy(const IObject::Ptr& root)
159 {
160 if (!root) {
161 return;
162 }
163
164 IterateHierarchy(root, true, [this](const IObject::Ptr& object) { DetachHierarchy(object); });
165 DetachNode(interface_cast<INode>(root));
166 }
167
DetachNode(INode * const node)168 void NodeHierarchyController::DetachNode(INode* const node)
169 {
170 if (node) {
171 const auto state = node->GetAttachedState();
172 if (state == NodeState::ATTACHED) {
173 node->DetachFromHierarchy();
174 }
175 }
176 }
177
RunOperation(NodeOperation operation)178 bool NodeHierarchyController::RunOperation(NodeOperation operation)
179 {
180 auto object = operation.root_.lock();
181 if (!object) {
182 return false;
183 }
184
185 // This may potentially end up calling Attach/DetachHierarchy several times
186 // for the same subtrees, but we will accept that. Attach/Detach will only
187 // be called once since the functions check for current state.
188 if (auto root = operation.root_.lock()) {
189 if (operation.operation_ == NodeOperation::ATTACH) {
190 AttachHierarchy(root);
191 } else if (operation.operation_ == NodeOperation::DETACH) {
192 DetachHierarchy(root);
193 }
194 }
195
196 return true;
197 }
198
RegisterNodeHierarchyController()199 void RegisterNodeHierarchyController()
200 {
201 META_NS::GetObjectRegistry().RegisterObjectType<NodeHierarchyController>();
202 }
UnregisterNodeHierarchyController()203 void UnregisterNodeHierarchyController()
204 {
205 META_NS::GetObjectRegistry().UnregisterObjectType<NodeHierarchyController>();
206 }
207
208 SCENE_END_NAMESPACE()
209