1 /*
2 * Copyright (c) 2022-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
16 #include "core/components_ng/syntax/if_else_node.h"
17
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/pipeline/base/element_register.h"
20
21 namespace OHOS::Ace::NG {
22
GetOrCreateIfElseNode(int32_t nodeId)23 RefPtr<IfElseNode> IfElseNode::GetOrCreateIfElseNode(int32_t nodeId)
24 {
25 auto node = ElementRegister::GetInstance()->GetSpecificItemById<IfElseNode>(nodeId);
26 if (node) {
27 return node;
28 }
29 node = MakeRefPtr<IfElseNode>(nodeId);
30 ElementRegister::GetInstance()->AddUINode(node);
31 return node;
32 }
33
34 /* how if else works
35 * the transpiler generated JS code is like this(simplified, but main points shown)
36 * this.observeComponentCreation(..., () => {
37 * If.create();
38 * if (condition1) {
39 * this.ifElseBranchUpdateFunction(compilerGeneratedUniqueConstBranchId, () =>
40 * code for children first render
41 * })
42 * } else if (condition2) {
43 * this.ifElseBranchUpdateFunction(compilerGeneratedUniqueConstBranchId, () =>
44 * code for children first render
45 * })
46 * } else {
47 * // else added by the transpiler
48 * If.setBranchId(compilerGeneratedUniqueConstBranchId)
49 * }
50 * If.pop()
51 * - if re-renders whenever one of the variables used in 'condition' during last render has changed.
52 * - the dependent variables can change from one render of if to the next, depending if 'condition1' and
53 * 'condition2' bind to different variables.
54 * - If can re-render, but the branch does not change, e.g. if 'condition1' is (this.i > 20)
55 * then If will re-render whenever i changes.
56 * - eDSL to JS traspiler generates a unique id for each branch of the code inside.
57 * Thereby the framework can detect that the branch has actually changed.
58 * In this case ViewPU.ifElseBranchUpdateFunction will call IfElseNode::SetBranchId to upate
59 * and it will execute the 2nd parameter lambda function to re-generate the children.
60 * - In case of if without else, or if ... else if ... , eDSL to JS traspiler generates
61 * an extra else clause in which to set the branchId (calls IfElseNode::SetBranchId)
62 * - IfElseNode::FlushUpdateAndMarkDirty is called upon If.Pop()
63 */
SetBranchId(int32_t value,std::list<int32_t> & removedElmtId,std::list<int32_t> & reservedElmtId)64 void IfElseNode::SetBranchId(int32_t value, std::list<int32_t>& removedElmtId, std::list<int32_t>& reservedElmtId)
65 {
66 branchIdChanged_ = (branchId_ != value);
67 TAG_LOGD(AceLogTag::ACE_IF, "IfElse(%{public}d).SetBranchId branchIdChanged_: %{public}d",
68 GetId(), branchIdChanged_);
69 if (branchIdChanged_) {
70 // collect elmtIds of all children and their children up to CustomNode object
71 // these will be removed, but possibly with a delay if their is an animation
72 // list of elmtIds is sent back to calling TS ViewPU.ifElseBranchUpdateFunction()
73 Clean(false, true, branchId_);
74 CollectCleanedChildren(GetChildren(), removedElmtId, reservedElmtId, true);
75 branchId_ = value;
76 }
77 }
78
FlushUpdateAndMarkDirty()79 void IfElseNode::FlushUpdateAndMarkDirty()
80 {
81 if (branchIdChanged_) {
82 auto parent = GetParent();
83 while (parent) {
84 auto frameNode = AceType::DynamicCast<FrameNode>(parent);
85 if (frameNode) {
86 frameNode->ChildrenUpdatedFrom(0);
87 break;
88 }
89 parent = parent->GetParent();
90 }
91 // mark parent dirty to flush measure.
92 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_BY_CHILD_REQUEST);
93 }
94 branchIdChanged_ = false;
95 }
96
TryRetake(const std::string & id)97 bool IfElseNode::TryRetake(const std::string& id)
98 {
99 auto node = GetDisappearingChildById(id, branchId_);
100 if (node) {
101 ACE_SCOPED_TRACE("IfElse TryRetake validate.");
102 node->SetJSViewActive(true);
103 AddChild(node);
104 // for geometryTransition, let all reused children call UpdateGeometryTransition.
105 LayoutProperty::UpdateAllGeometryTransition(node);
106 CollectRetakenNodes(node);
107 return true;
108 }
109 return false;
110 }
111
CollectRetakenNodes(const RefPtr<UINode> & node)112 void IfElseNode::CollectRetakenNodes(const RefPtr<UINode>& node)
113 {
114 retakenElmtIds_.emplace_back(node->GetId());
115 if (GetTag() != V2::JS_VIEW_ETS_TAG) {
116 for (auto const& child : node->GetChildren()) {
117 CollectRetakenNodes(child);
118 }
119 }
120 }
121
GetRetakenElmtIds(std::list<int32_t> & retakenElmtIds)122 bool IfElseNode::GetRetakenElmtIds(std::list<int32_t>& retakenElmtIds)
123 {
124 if (retakenElmtIds_.size() == 0) {
125 return false;
126 }
127 retakenElmtIds.splice(retakenElmtIds.end(), retakenElmtIds_);
128 return true;
129 }
130
131 } // namespace OHOS::Ace::NG
132