• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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