1 /*
2 * Copyright (c) 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_v2/tabs/tab_content_proxy_element.h"
17
18 #include "core/components/tab_bar/render_tab_content.h"
19 #include "core/components_v2/tabs/tabs_helper.h"
20
21 namespace OHOS::Ace::V2 {
22
Update()23 void TabContentProxyElement::Update()
24 {
25 if (!component_) {
26 return;
27 }
28 RefPtr<TabContentComponent> tabContent = AceType::DynamicCast<TabContentComponent>(component_);
29 if (!tabContent) {
30 LOGE("Get tabContent failed");
31 return;
32 }
33 contents_ = tabContent->GetChildren();
34 auto controller = tabContent->GetController();
35 if (controller && (controller_ != controller)) {
36 // Get index from old controller before replace.
37 if (controller_) {
38 if (!controller->IsIndexDefined()) {
39 controller->SetIndexWithoutChangeContent(controller_->GetIndex());
40 }
41 controller->SetTotalCount(controller_->GetTotalCount());
42 }
43 controller_ = controller;
44 }
45 if (!controller_) {
46 LOGE("Get controller failed");
47 return;
48 }
49 controller_->SetContentElement(AceType::Claim(this));
50 RenderElement::Update();
51
52 SetElementId(component_->GetElementId());
53 }
54
GetChildFocusNode(const RefPtr<Element> & node)55 RefPtr<FocusNode> TabContentProxyElement::GetChildFocusNode(const RefPtr<Element>& node)
56 {
57 auto focusNode = AceType::DynamicCast<FocusNode>(node);
58 if (focusNode) {
59 return focusNode;
60 }
61 for (const auto& child : node->GetChildren()) {
62 auto resNode = GetChildFocusNode(child);
63 if (resNode) {
64 return resNode;
65 }
66 }
67 return nullptr;
68 }
69
PerformBuild()70 void TabContentProxyElement::PerformBuild()
71 {
72 if (component_) {
73 auto tabsContentComponent = AceType::DynamicCast<TabContentComponent>(component_);
74 if (!tabsContentComponent) {
75 return;
76 }
77 ElementProxyHost::UpdateChildren(tabsContentComponent->GetChildren());
78 }
79 auto tabContent = AceType::DynamicCast<RenderTabContent>(renderNode_);
80 if (!tabContent || !controller_) {
81 LOGW("tabContent or controller is null.");
82 return;
83 }
84 auto totalCount = static_cast<int32_t>(TotalCount());
85 if (newIndex_ < 0 && newBarIndex_ < 0) {
86 // verify controller index first build and flush index for render.
87 auto maxIndex = totalCount - 1;
88 if (controller_->GetIndex() >= totalCount) {
89 controller_->SetIndexWithoutChangeContent(maxIndex < 0 ? 0 : maxIndex);
90 }
91 if (controller_->GetInitialIndex() >= totalCount) {
92 controller_->SetInitialIndex(maxIndex < 0 ? 0 : maxIndex);
93 }
94 if (controller_->GetPendingIndex() >= totalCount) {
95 controller_->SetPendingIndex(maxIndex < 0 ? 0 : maxIndex);
96 }
97 controller_->SetTotalCount(totalCount < 1 ? 0 : totalCount);
98 tabContent->FlushIndex();
99 }
100 int32_t target = newIndex_ >= 0 ? newIndex_ : controller_->GetIndex();
101 auto element = GetElementByIndex(target);
102 if (!element) {
103 LOGE("GetElementByIndex failed index=[%{public}d]", target);
104 return;
105 }
106 auto renderNode = element->GetRenderNode();
107 if (!renderNode) {
108 LOGE("GetRenderNode failed");
109 return;
110 }
111 tabContent->AddChildContent(target, renderNode);
112 focusIndexMap_.emplace(target);
113 focusChildMap_[target] = GetChildFocusNode(element);
114 renderNode_->MarkNeedLayout();
115
116 // process for new content requested by drag
117 if (target == newIndex_) {
118 tabContent->UpdateDragPosition(newIndex_);
119 newIndex_ = -1;
120 }
121 // process for new content requested by tab bar
122 if (target == newBarIndex_) {
123 // That actually brings tab into view with animation
124 tabContent->ChangeScroll(newBarIndex_, fromController_);
125 UpdateLastFocusNode();
126 newBarIndex_ = -1;
127 }
128 lastIndex_ = target;
129 fromController_ = false;
130 }
131
132 // In ChangeByBar function the controller has already set index.
ChangeByBar(int32_t index,bool isFromController)133 void TabContentProxyElement::ChangeByBar(int32_t index, bool isFromController)
134 {
135 newBarIndex_ = index;
136 fromController_ = isFromController;
137 PerformBuild();
138 }
139
PrepareContent(int32_t index)140 void TabContentProxyElement::PrepareContent(int32_t index)
141 {
142 newIndex_ = index;
143 PerformBuild();
144 }
145
OnUpdateElement(const RefPtr<Element> & element,const RefPtr<Component> & component)146 RefPtr<Element> TabContentProxyElement::OnUpdateElement(
147 const RefPtr<Element>& element, const RefPtr<Component>& component)
148 {
149 bool usePartialUpdate = Container::IsCurrentUsePartialUpdate();
150
151 if (element && !component) {
152 auto tabContent = AceType::DynamicCast<RenderTabContent>(renderNode_);
153 if (!tabContent) {
154 LOGE("tabContent is null.");
155 } else {
156 auto renderNode = element->GetRenderNode();
157 if (!renderNode) {
158 LOGW("delete render child is null.");
159 } else {
160 if (!usePartialUpdate) {
161 tabContent->RemoveChildContent(renderNode);
162 } else {
163 tabContent->RemoveChildContentUpdateMap(renderNode);
164 }
165 }
166 }
167 }
168 return UpdateChild(element, component);
169 }
170
OnMakeEmptyComponent()171 RefPtr<Component> TabContentProxyElement::OnMakeEmptyComponent()
172 {
173 LOGI("tab content does not support lazyForEach yet");
174 return nullptr;
175 }
176
OnDataSourceUpdated(size_t startIndex)177 void TabContentProxyElement::OnDataSourceUpdated(size_t startIndex)
178 {
179 LOGI("tab content does not support lazyForEach yet");
180 ElementProxyHost::OnDataSourceUpdated(startIndex);
181 }
182
UpdateIndex()183 void TabContentProxyElement::UpdateIndex()
184 {
185 if (!Container::IsCurrentUsePartialUpdate()) {
186 return;
187 }
188
189 // We arrive here with new set of children (some can be dropped or added),
190 // and focus index for TabBar already adjusted to point to correct render node.
191 // Focused render node might move to new position after call to
192 // ElementProxyHost::UpdateIndex();
193 // So we have to move focus to new index (by same render node will be focused)
194
195 auto renderNode = TabsHelper::GetTabBarFocusedElement(AceType::Claim(this));
196 ElementProxyHost::UpdateIndex();
197 auto idx2 = TabsHelper::GetTabBarElementIndex(AceType::Claim(this), renderNode);
198
199 // idx2 can already be corrected value, so update focus in any case
200 idx2 = idx2 < 0 ? 0 : idx2;
201 TabsHelper::SetIndex(Referenced::Claim<Element>(this), idx2);
202 }
203
204 } // namespace OHOS::Ace::V2