1 /*
2  * Copyright (c) 2021-2022 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/tab_bar/tab_content_element.h"
17 
18 #include "core/components/tab_bar/render_tab_content.h"
19 
20 namespace OHOS::Ace {
21 
TabContentElement(const std::list<RefPtr<Component>> & contents)22 TabContentElement::TabContentElement(const std::list<RefPtr<Component>>& contents)
23 {
24     contents_ = contents;
25 }
26 
CreateRenderNode()27 RefPtr<RenderNode> TabContentElement::CreateRenderNode()
28 {
29     RefPtr<RenderNode> node = ComponentGroupElement::CreateRenderNode();
30     RefPtr<RenderTabContent> tabContent = AceType::DynamicCast<RenderTabContent>(node);
31     if (tabContent) {
32         tabContent->RegisterCallback([weakTabContentElement = AceType::WeakClaim(this)](int32_t index) {
33             auto tabContent = weakTabContentElement.Upgrade();
34             if (tabContent) {
35                 tabContent->ChangeByContent(index);
36             }
37         });
38         tabContent->RegisterRequireCallback([weakTabContentElement = AceType::WeakClaim(this)](int32_t index) {
39             auto tabContent = weakTabContentElement.Upgrade();
40             if (tabContent) {
41                 tabContent->PrepareContent(index);
42             }
43         });
44         tabContent->RegisterIndicatorCallback(
45             [weakTabContentElement = AceType::WeakClaim(this)](double percent, int32_t newIndex, bool needChange) {
46                 auto tabContent = weakTabContentElement.Upgrade();
47                 if (tabContent) {
48                     tabContent->IndicatorByContent(percent, newIndex, needChange);
49                 }
50             }
51         );
52     }
53     return node;
54 }
55 
UpdateLastFocusNode()56 void TabContentElement::UpdateLastFocusNode()
57 {
58     if (IsCurrentFocus()) {
59         auto focusNode = GetCurrentFocusNode();
60         if (!focusNode || !focusNode->IsFocusable()) {
61             LostFocus();
62             itLastFocusNode_ = focusNodes_.end();
63         } else {
64             focusNode->RequestFocusImmediately();
65         }
66     } else {
67         itLastFocusNode_ = focusNodes_.end();
68     }
69 }
70 
IndicatorByContent(double percent,int32_t newIndex,bool needChange)71 void TabContentElement::IndicatorByContent(double percent, int32_t newIndex, bool needChange)
72 {
73     if (controller_) {
74         controller_->SetIndicatorByScrollContent(percent, newIndex, needChange);
75     }
76 }
77 
ChangeByContent(int32_t index)78 void TabContentElement::ChangeByContent(int32_t index)
79 {
80     if (controller_) {
81         controller_->SetIndexByScrollContent(index);
82     }
83     lastIndex_ = index;
84     UpdateLastFocusNode();
85 }
86 
ChangeByBar(int32_t index,bool isFromController)87 void TabContentElement::ChangeByBar(int32_t index, bool isFromController)
88 {
89     newBarIndex_ = index;
90     fromController_ = isFromController;
91     MarkDirty();
92 }
93 
ChangeDispatch(int32_t index)94 void TabContentElement::ChangeDispatch(int32_t index)
95 {
96     auto content = AceType::DynamicCast<RenderTabContent>(GetRenderNode());
97     if (content) {
98         content->FireDomChangeEvent(index);
99     }
100 }
101 
PrepareContent(int32_t index)102 void TabContentElement::PrepareContent(int32_t index)
103 {
104     newIndex_ = index;
105     MarkDirty();
106 }
107 
Update()108 void TabContentElement::Update()
109 {
110     if (NeedUpdate()) {
111         RefPtr<TabContentComponent> tabContent = AceType::DynamicCast<TabContentComponent>(component_);
112         if (!tabContent) {
113             LOGE("Get tabContent failed");
114             return;
115         }
116         contents_ = tabContent->GetChildren();
117         auto controller = tabContent->GetController();
118         if (controller && (controller_ != controller)) {
119             // Get index from old controller before replace.
120             if (!controller->IsIndexDefined() && controller_) {
121                 controller->SetIndexWithoutChangeContent(controller_->GetIndex());
122             }
123             controller_ = controller;
124         }
125         if (!controller_) {
126             LOGE("Get controller failed");
127             return;
128         }
129         controller_->SetContentElement(AceType::Claim(this));
130         if (controller_->GetIndex() >= static_cast<int32_t>(contents_.size())) {
131             controller_->SetIndexWithoutChangeContent(static_cast<int32_t>(contents_.size()) - 1);
132         }
133         controller_->SetTotalCount(static_cast<int32_t>(contents_.size()));
134         newComponentBuild_ = true;
135     }
136     ComponentGroupElement::Update();
137 }
138 
PerformBuild()139 void TabContentElement::PerformBuild()
140 {
141     if (contents_.empty()) {
142         ComponentGroupElement::PerformBuild();
143         return;
144     }
145     RefPtr<RenderTabContent> tabContent = AceType::DynamicCast<RenderTabContent>(renderNode_);
146     if (!tabContent || !controller_ || !renderNode_) {
147         LOGW("tabContent or controller is null.");
148         return;
149     }
150 
151     auto it = contents_.begin();
152     // if have new content requested by drag, build the new child, else build current child
153     int32_t target = newIndex_ >= 0 ? newIndex_ : controller_->GetIndex();
154     std::advance(it, target);
155     if (it == contents_.end()) {
156         LOGE("no content at index %{public}d.", target);
157         return;
158     }
159     auto childIter = childMap_.find(target);
160     if (childIter == childMap_.end()) {
161         auto newChild = UpdateChild(nullptr, *it);
162         focusIndexMap_.emplace(target);
163         childMap_.emplace(target, newChild);
164         auto renderChild = newChild->GetRenderNode();
165         if (renderChild) {
166             tabContent->AddChildContent(target, renderChild);
167         }
168         renderNode_->MarkNeedLayout();
169     } else if (NeedUpdate()) {
170         UpdateChild(childIter->second, *it);
171     }
172 
173     // process for new content requested by drag
174     if (target == newIndex_) {
175         tabContent->UpdateDragPosition(newIndex_);
176         newIndex_ = -1;
177     }
178     // process for new content requested by tab bar
179     if (target == newBarIndex_) {
180         tabContent->ChangeScroll(newBarIndex_, fromController_);
181         UpdateLastFocusNode();
182         newBarIndex_ = -1;
183     }
184     lastIndex_ = target;
185     fromController_ = false;
186 }
187 
OnFocus()188 void TabContentElement::OnFocus()
189 {
190     // invoke callback
191     FocusNode::OnFocus();
192 
193     auto focusNode = GetCurrentFocusNode();
194     if (!focusNode) {
195         LOGE("GetCurrentFocusNode failed");
196         return;
197     }
198 
199     if (focusNode->RequestFocusImmediately()) {
200         itLastFocusNode_ = std::find(focusNodes_.begin(), focusNodes_.end(), focusNode);
201     } else {
202         LOGE("RequestFocusImmediately failed");
203     }
204 }
205 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)206 bool TabContentElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
207 {
208     return false;
209 }
210 
IsFocusable() const211 bool TabContentElement::IsFocusable() const
212 {
213     auto focusNode = GetCurrentFocusNode();
214     if (focusNode) {
215         return focusNode->IsFocusable();
216     }
217     return false;
218 }
219 
GetCurrentFocusNode() const220 RefPtr<FocusNode> TabContentElement::GetCurrentFocusNode() const
221 {
222     if (!controller_) {
223         LOGE("controller is nullptr");
224         return nullptr;
225     }
226 
227     auto childIter = focusChildMap_.find(controller_->GetIndex());
228     if (childIter == focusChildMap_.end()) {
229         return nullptr;
230     }
231     return childIter->second;
232 }
233 
GetTabContentChild(int32_t index) const234 RefPtr<Element> TabContentElement::GetTabContentChild(int32_t index) const
235 {
236     auto childIter = childMap_.find(index);
237     if (childIter == childMap_.end()) {
238         return nullptr;
239     }
240     return childIter->second;
241 }
242 
243 } // namespace OHOS::Ace