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