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 int64_t accessibilityId = GetAccessibilityId();
84 if (parent) {
85 parent->NotifyChange(0, 0, accessibilityId, NotificationType::START_CHANGE_POSITION);
86 }
87 // mark parent dirty to flush measure.
88 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_BY_CHILD_REQUEST);
89 }
90 branchIdChanged_ = false;
91 }
92
TryRetake(const std::string & id)93 bool IfElseNode::TryRetake(const std::string& id)
94 {
95 auto node = GetDisappearingChildById(id, branchId_);
96 if (node) {
97 ACE_SCOPED_TRACE("IfElse TryRetake validate.");
98 node->SetJSViewActive(true);
99 AddChild(node);
100 // for geometryTransition, let all reused children call UpdateGeometryTransition.
101 LayoutProperty::UpdateAllGeometryTransition(node);
102 CollectRetakenNodes(node);
103 return true;
104 }
105 return false;
106 }
107
CollectRetakenNodes(const RefPtr<UINode> & node)108 void IfElseNode::CollectRetakenNodes(const RefPtr<UINode>& node)
109 {
110 retakenElmtIds_.emplace_back(node->GetId());
111 if (GetTag() != V2::JS_VIEW_ETS_TAG) {
112 for (auto const& child : node->GetChildren()) {
113 CollectRetakenNodes(child);
114 }
115 }
116 }
117
GetRetakenElmtIds(std::list<int32_t> & retakenElmtIds)118 bool IfElseNode::GetRetakenElmtIds(std::list<int32_t>& retakenElmtIds)
119 {
120 if (retakenElmtIds_.size() == 0) {
121 return false;
122 }
123 retakenElmtIds.splice(retakenElmtIds.end(), retakenElmtIds_);
124 return true;
125 }
126
127 } // namespace OHOS::Ace::NG
128