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