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