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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_V2_TABS_HELPER_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_V2_TABS_HELPER_H
18 
19 #include "core/components/tab_bar/render_tab_bar.h"
20 #include "core/components/tab_bar/render_tab_content.h"
21 #include "core/components/tab_bar/tab_bar_element.h"
22 #include "core/components/tab_bar/tab_content_element.h"
23 #include "core/components_v2/tabs/tab_content_item_component.h"
24 #include "core/components_v2/tabs/tab_content_item_element.h"
25 #include "core/components_v2/tabs/tab_content_proxy_element.h"
26 #include "core/components_v2/tabs/tabs_element.h"
27 
28 namespace OHOS::Ace::V2 {
29     /*
30     Stucture of Tabs components:
31 
32     <Box>
33         <TabBar>
34             <TabBarIndicator>
35             </TabBarIndicator>
36         </TabBar>
37     </Box>
38 
39     // Last child (or first if tab bar is at the end)
40     <FlexItem>
41         <TabContent>
42         </TabContent>
43     </FlexItem>
44 
45     TabsElement
46         FlexItemElement
47             TabContentProxyElement
48                 BoxElement
49                     TabContentItemElement
50 
51 
52     first child BoxElement
53     last  child FlexItemElement
54 
55     if TabBar positioned at the .End, order reversed:
56 
57     first child FlexItemElement
58     last child BoxElement
59 
60     */
61 
62 const int32_t INVALID_TAB_BAR_INDEX = -1;
63 
64 class TabsHelper {
65 public:
66     template<class T>
FindFirstChildOfType(const RefPtr<Element> & parent)67     static RefPtr<Element> FindFirstChildOfType(const RefPtr<Element>& parent)
68     {
69         if (AceType::InstanceOf<T>(parent)) {
70             return parent;
71         }
72         for (const auto& child : parent->GetChildren()) {
73             const auto foundTypedChild = FindFirstChildOfType<T>(child);
74             if (foundTypedChild) {
75                 return foundTypedChild;
76             }
77         }
78         return nullptr;
79     }
80 
81     template<class T>
FindFirstParentOfType(const RefPtr<Element> & child)82     static std::tuple<RefPtr<Element>, RefPtr<Element>> FindFirstParentOfType(const RefPtr<Element>& child)
83     {
84         RefPtr<Element> parentElement = nullptr;
85         auto currentElement = child;
86         while (currentElement && (parentElement = currentElement->GetElementParent().Upgrade())) {
87             if (AceType::InstanceOf<T>(parentElement)) {
88                 return {parentElement, currentElement};
89             }
90             currentElement = parentElement;
91             parentElement = nullptr;
92         }
93         return {nullptr, nullptr};
94     }
95 
96     template<class T>
TraverseComponentTo(const RefPtr<Component> & component)97     static RefPtr<T> TraverseComponentTo(const RefPtr<Component>& component)
98     {
99         if (!component) {
100             LOGE("null component");
101             return nullptr;
102         }
103 
104         if (AceType::InstanceOf<T>(component)) {
105             return AceType::DynamicCast<T>(component);
106         }
107 
108         auto single = AceType::DynamicCast<SingleChild>(component);
109 
110         if (single) {
111             return TraverseComponentTo<T>(single->GetChild());
112         }
113         return nullptr;
114     }
115 
FindTabsElement(const RefPtr<Element> & child)116     static RefPtr<TabsElement> FindTabsElement(const RefPtr<Element>& child)
117     {
118         return AceType::DynamicCast<TabsElement>(std::get<0>(TabsHelper::FindFirstParentOfType<TabsElement>(child)));
119     }
120 
FindTabBarElement(const RefPtr<Element> & child)121     static RefPtr<TabBarElement> FindTabBarElement(const RefPtr<Element>& child)
122     {
123         auto tabsElement = FindTabsElement(child);
124         if (tabsElement == nullptr) {
125             LOGE("nullptr tabsElement");
126             return nullptr;
127         }
128         auto tabBarElement = TabsHelper::FindFirstChildOfType<TabBarElement>(tabsElement);
129         return AceType::DynamicCast<TabBarElement>(tabBarElement);
130     }
131 
FindTabContentElement(const RefPtr<Element> & child)132     static RefPtr<TabContentElement> FindTabContentElement(const RefPtr<Element>& child)
133     {
134         // Go up to the parent to find Tabs
135         auto tabsElement = FindTabsElement(child);
136         if (tabsElement == nullptr) {
137             LOGE("nullptr tabsElement");
138             return nullptr;
139         }
140 
141         // TabsElement
142         //    FlexItemElement
143         //       TabContentProxyElement
144         return AceType::DynamicCast<TabContentElement>(FindFirstChildOfType<TabContentElement>(tabsElement));
145     }
146 
RemoveTabBarItemById(ElementIdType id)147     static void RemoveTabBarItemById(ElementIdType id)
148     {
149         auto tabBarItemChild = ElementRegister::GetInstance()->GetElementById(id);
150         if (!tabBarItemChild) {
151             return;
152         }
153 
154         auto parent = FindFirstParentOfType<TabBarElement>(tabBarItemChild);
155         if (std::get<0>(parent) && std::get<1>(parent)) {
156             auto tabBarElement = std::get<0>(parent);
157             auto tabBarItemElement = std::get<1>(parent);
158 
159             // Update current focused index
160             AceType::DynamicCast<RenderTabBar>(tabBarElement->GetRenderNode())->
161                 AboutToRemove(tabBarItemElement->GetRenderNode());
162             tabBarElement->UpdateChild(tabBarItemElement, nullptr);
163         }
164     }
165 
AddTabBarElement(const RefPtr<Element> & host,const RefPtr<TabContentItemComponent> & tabContentItemComponent)166     static void AddTabBarElement(const RefPtr<Element>& host,
167         const RefPtr<TabContentItemComponent>& tabContentItemComponent)
168     {
169         auto tabsElement = FindTabsElement(host);
170         if (!tabsElement) {
171             LOGE("TabsElement is null");
172             return;
173         }
174 
175         // tabBar is nullptr for initial render if tab tab positioned at the .End
176         // In case if tab bar is missing we keep TabBarItemComponent(s) in memory
177         // and attach them at the end of TabsElement::PerformBuild execution.
178         auto tabBar = TabsHelper::FindTabBarElement(host);
179 
180         RefPtr<TabBarItemComponent> newBarItemComponent;
181 
182         if (tabContentItemComponent && tabContentItemComponent->HasBarBuilder()) {
183             newBarItemComponent = AceType::MakeRefPtr<TabBarItemComponent>(
184                 tabContentItemComponent->ExecuteBarBuilder());
185         } else if (tabContentItemComponent) {
186             newBarItemComponent = AceType::MakeRefPtr<TabBarItemComponent>(
187                 TabBarItemComponent::BuildWithTextIcon(
188                     tabContentItemComponent->GetBarText(), tabContentItemComponent->GetBarIcon()));
189         }
190 
191         // Link tab item and Tab bar item with id
192         const auto id = ElementRegister::GetInstance()->MakeUniqueId();
193         newBarItemComponent->SetElementId(id);
194 
195         if (tabContentItemComponent) {
196             tabContentItemComponent->SetBarElementId(id);
197         }
198 
199         // Add element to TabBar or store component to be added later
200         if (tabBar) {
201             tabBar->GetTabBarComponent()->UpdateItemStyle(newBarItemComponent);
202             tabBar->UpdateChild(nullptr, newBarItemComponent);
203         } else {
204             tabsElement->AddPendingTabBarItem(newBarItemComponent);
205         }
206     }
207 
UpdateTabBarElement(const RefPtr<Element> & host,const RefPtr<Element> & contentItemElement,const RefPtr<Component> & contentItemComponent)208     static void UpdateTabBarElement(const RefPtr<Element>& host, const RefPtr<Element>& contentItemElement,
209         const RefPtr<Component>& contentItemComponent)
210     {
211         auto tabBar = TabsHelper::FindTabBarElement(host);
212         if (!tabBar) {
213             LOGE("TabBar missing");
214             return;
215         }
216 
217         std::string text;
218         std::string icon;
219         ElementIdType id;
220 
221         auto tabContentItemElement = AceType::DynamicCast<TabContentItemElement>(contentItemElement);
222         auto tabContentItemComponent = AceType::DynamicCast<TabContentItemComponent>(contentItemComponent);
223 
224         if (tabContentItemElement) {
225             text = tabContentItemElement->GetText();
226             icon = tabContentItemElement->GetIcon();
227             id = tabContentItemElement->GetBarElementId();
228         } else if (tabContentItemComponent) {
229             text = tabContentItemComponent->GetBarText();
230             icon = tabContentItemComponent->GetBarIcon();
231             id = tabContentItemComponent->GetBarElementId();
232         } else {
233             LOGE("Neither Element nor Component provided to build tab menu item");
234             return;
235         }
236 
237         auto newBarItemComponent = AceType::MakeRefPtr<TabBarItemComponent>(
238             TabBarItemComponent::BuildWithTextIcon(text, icon));
239         tabBar->GetTabBarComponent()->UpdateItemStyle(newBarItemComponent);
240 
241         if (tabContentItemComponent && tabContentItemComponent->HasBarBuilder()) {
242             newBarItemComponent = AceType::MakeRefPtr<TabBarItemComponent>(
243                 tabContentItemComponent->ExecuteBarBuilder());
244         }
245 
246         auto tabBarItemElement = ElementRegister::GetInstance()->GetElementById(id);
247         if (!tabBarItemElement) {
248             LOGE("tabBarItemElement nullptr");
249             return;
250         }
251         newBarItemComponent->SetElementId(tabBarItemElement->GetElementId());
252         tabBar->UpdateChild(tabBarItemElement, newBarItemComponent);
253     }
254 
SetTabBarElementIndex(const RefPtr<Element> & contentItemElement,const RefPtr<Component> & contentItemComponent,int target)255     static void SetTabBarElementIndex(const RefPtr<Element>& contentItemElement,
256         const RefPtr<Component>& contentItemComponent, int target)
257     {
258         ElementIdType elementId = ElementRegister::UndefinedElementId;
259 
260         auto tabContentItemElement = AceType::DynamicCast<TabContentItemElement>(contentItemElement);
261         auto tabContentItemComponent = AceType::DynamicCast<TabContentItemComponent>(contentItemComponent);
262 
263         if (tabContentItemElement) {
264             elementId = tabContentItemElement->GetBarElementId();
265         } else if (tabContentItemComponent) {
266             elementId = tabContentItemComponent->GetBarElementId();
267         } else {
268             LOGE("Neither Element nor Component provided to build tab menu item");
269             return;
270         }
271 
272         auto tabBarItemElement = ElementRegister::GetInstance()->GetElementById(elementId);
273         if (!tabBarItemElement) {
274             LOGE("tabBarItemElement is null");
275             return;
276         }
277         auto renderNode = tabBarItemElement->GetRenderNode();
278         if (!renderNode) {
279             LOGE("renderNode for tabBarItemElement is null");
280             return;
281         }
282         auto renderTabBar = AceType::DynamicCast<RenderTabBar>(renderNode->GetParent().Upgrade());
283         if (!renderTabBar) {
284             LOGE("renderTabBar is null");
285             return;
286         }
287         renderNode->MovePosition(renderTabBar->FirstItemOffset() + target);
288     }
289 
SetIndex(const RefPtr<Element> & element,int32_t idx)290     static void SetIndex(const RefPtr<Element>& element, int32_t idx)
291     {
292         auto tabBarElement = TabsHelper::FindTabBarElement(element);
293         if (tabBarElement) {
294             tabBarElement->GetTabController()->SetIndex(idx);
295             tabBarElement->UpdateIndex(idx);
296         }
297     }
298 
GetTabBarRenderNode(const RefPtr<Element> & proxyHostElement)299     static RefPtr<RenderTabBar> GetTabBarRenderNode(const RefPtr<Element>& proxyHostElement)
300     {
301         auto tabBar = TabsHelper::FindTabBarElement(proxyHostElement);
302         if (!tabBar) {
303             LOGE("TabBar is missing");
304             return nullptr;
305         }
306         auto renderTabBar = AceType::DynamicCast<RenderTabBar>(tabBar->GetRenderNode());
307         if (!renderTabBar) {
308             LOGE("RenderTabBar is missing");
309             return nullptr;
310         }
311         return renderTabBar;
312     }
313 
GetTabBarFocusedElementIndex(const RefPtr<Element> & proxyHostElement)314     static int32_t GetTabBarFocusedElementIndex(const RefPtr<Element>& proxyHostElement)
315     {
316         auto renderTabBar = TabsHelper::GetTabBarRenderNode(proxyHostElement);
317         if (!renderTabBar) {
318             return INVALID_TAB_BAR_INDEX;
319         }
320         return renderTabBar->GetFocusedTabBarItemIndex();
321     }
322 
GetTabBarFocusedElement(const RefPtr<Element> & proxyHostElement)323     static RefPtr<RenderNode> GetTabBarFocusedElement(const RefPtr<Element>& proxyHostElement)
324     {
325         auto renderTabBar = TabsHelper::GetTabBarRenderNode(proxyHostElement);
326         if (!renderTabBar) {
327             return nullptr;
328         }
329         return renderTabBar->GetFocusedTabBarItem();
330     }
331 
GetTabBarElementIndex(const RefPtr<Element> & proxyHostElement,const RefPtr<RenderNode> & child)332     static int32_t GetTabBarElementIndex(const RefPtr<Element>& proxyHostElement, const RefPtr<RenderNode>& child)
333     {
334         auto renderTabBar = TabsHelper::GetTabBarRenderNode(proxyHostElement);
335         if (!renderTabBar) {
336             return INVALID_TAB_BAR_INDEX;
337         }
338         auto idx = renderTabBar->GetIndexForTabBarItem(child);
339         return idx;
340     }
341 
UpdateRenderTabContentCount(const RefPtr<Element> & element,int32_t count)342     static void UpdateRenderTabContentCount(const RefPtr<Element>& element, int32_t count)
343     {
344         // TODO: ContentCount should be managed by render_tab_content itself
345         // element is a TabContentItemElement
346         auto tabContent = FindTabContentElement(element);
347         auto tabContentProxyElement = AceType::DynamicCast<TabContentProxyElement>(tabContent);
348 
349         if (!tabContent || !tabContentProxyElement) {
350             LOGE("Getting  tabContent or tabContentProxyElement failed");
351             return;
352         }
353 
354         auto controller = tabContent->GetTabController();
355         controller->SetTotalCount(controller->GetTotalCount() + count);
356 
357         auto tabContentRenderNode = AceType::DynamicCast<RenderTabContent>(tabContentProxyElement->GetRenderNode());
358         if (!tabContentRenderNode) {
359             LOGE("GetRenderNode failed");
360             return;
361         }
362         tabContentRenderNode->UpdateContentCount(controller->GetTotalCount());
363     }
364 
IncTabContentRenderCount(const RefPtr<Element> & element)365     static void IncTabContentRenderCount(const RefPtr<Element>& element)
366     {
367         UpdateRenderTabContentCount(element, 1);
368     }
369 
DecTabContentRenderCount(const RefPtr<Element> & element)370     static void DecTabContentRenderCount(const RefPtr<Element>& element)
371     {
372         UpdateRenderTabContentCount(element, -1);
373     }
374 
375     static void DumpComponents(const RefPtr<Component>& component, const std::string& header = "",
376         int depth = 4, const std::string& shift = "")
377     {
378         if (depth == 0) {
379             return;
380         }
381 
382         if (!component) {
383             LOGE("null component");
384             return;
385         }
386         auto single = AceType::DynamicCast<SingleChild>(component);
387 
388         if (single) {
389             DumpComponents(single->GetChild(), "", depth -1, shift + "  ");
390         } else {
391             auto group = AceType::DynamicCast<ComponentGroup>(component);
392             if (group) {
393                 for (const auto& child : group->GetChildren()) {
394                     DumpComponents(child, "", depth -1, shift + "  ");
395                 }
396             }
397         }
398     }
399 
400     // Dump Elemens
401     static void DumpElements(const RefPtr<Element>& node,
402         const std::string& header = "", int depth = 4, const std::string& shift = "")
403     {
404         if (depth == 0) {
405             return;
406         }
407         if (!node) {
408             LOGE("null element");
409             return;
410         }
411         if (!header.empty()) {
412             LOGE("Dumping tree for  %{public}s  %{public}s id: %{public}d",
413                 AceType::TypeName(node), header.c_str(), node->GetElementId());
414         }
415 
416         for (const auto& child : node->GetChildren()) {
417             TabsHelper::DumpElements(child, "", depth -1, shift + "  ");
418         }
419     }
420 
PrintRenderNodes(const RefPtr<RenderNode> & renderNode,const std::string & text)421     static void PrintRenderNodes(const RefPtr<RenderNode>& renderNode, const std::string& text) {}
422 };
423 
424 } // namespace OHOS::Ace::V2
425 
426 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_V2_TABS_HELPER_H
427