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