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 16import { Log } from '../utils/Log'; 17import { TabItem, TabItemWithText } from '../model/common/TabItem'; 18import { BroadCastManager } from '../model/common/BroadCastManager'; 19import { BroadCast } from '../utils/BroadCast'; 20import { BroadCastConstants } from '../model/common/BroadCastConstants'; 21import { Constants } from '../model/common/Constants'; 22import { BigDataConstants, ReportToBigDataUtil } from '../utils/ReportToBigDataUtil'; 23 24const TAG: string = 'common_TabBar'; 25 26export enum DeviceType { 27 PHONE_LIKE, 28 PC_LIKE 29} 30 31const tabMap = [ 32BigDataConstants.PHOTO_TAB, 33BigDataConstants.ALBUM_TAB 34] 35 36@Component 37export struct TabBar { 38 @Consume isSelectedMode: boolean; 39 @Consume isAlbumSetSelectedMode: boolean; 40 @Consume isShowSideBar: boolean; 41 @Link @Watch('updateCurrentIndex') currentIndex: 0 | 1; 42 @Link isSidebar: boolean; 43 @StorageLink('sideBarBoundaryLineOpacity') sideBarBoundaryLineOpacity: number = 1; 44 @StorageLink('sideBarOpacity') sideBarOpacity: number = 1; 45 private tabs: TabItem[] = []; 46 private controller: TabsController | null = null; 47 private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast(); 48 private deviceType: DeviceType = DeviceType.PC_LIKE; 49 private funcOnTabSelected?: Function; 50 51 aboutToAppear(): void { 52 this.funcOnTabSelected = (index: number): void => this.onTabSelected(index); 53 this.tabs[this.currentIndex].isSelected = true; 54 this.tabs.forEach((tab: TabItem) => { 55 Log.info(TAG, `${JSON.stringify(tab.name)} , ${tab.iconSelected}`); 56 }); 57 } 58 59 updateCurrentIndex(): void { 60 this.onTabSelected(this.currentIndex); 61 } 62 63 build() { 64 if (this.isSidebar) { 65 if (this.deviceType == DeviceType.PC_LIKE) { 66 Row() { 67 Flex({ 68 direction: FlexDirection.Column, 69 alignItems: ItemAlign.Start, 70 justifyContent: FlexAlign.Start 71 }) { 72 ForEach(this.tabs, (tab: TabItem) => { 73 Tab({ 74 tabItem: tab, 75 index: this.tabs.indexOf(tab), 76 onTabSelected: this.funcOnTabSelected, 77 isSidebar: $isSidebar, 78 deviceType: this.deviceType 79 }) 80 }, (tab: TabItem): string => { 81 return (tab.name.id).toString(); 82 }) 83 } 84 .padding({ left: 16, top: 96, right: 16 }) 85 .flexGrow(1) 86 .backgroundColor($r('app.color.default_background_color')) 87 88 // Sidebar boundary line 89 if (this.isShowSideBar) { 90 Row() { 91 } 92 .width(0) 93 .height(Constants.PERCENT_100) 94 .border({ width: 0.5, color: $r('app.color.album_cover_gradient_start_color') }) 95 .opacity(this.sideBarBoundaryLineOpacity) 96 } 97 } 98 } else { 99 Flex({ 100 direction: FlexDirection.Column, 101 alignItems: ItemAlign.Center, 102 justifyContent: FlexAlign.Center 103 }) { 104 Column() { 105 ForEach(this.tabs, (tab: TabItem) => { 106 Stack() { 107 Tab({ 108 tabItem: tab, 109 index: this.tabs.indexOf(tab), 110 onTabSelected: this.funcOnTabSelected, 111 isSidebar: $isSidebar, 112 deviceType: this.deviceType 113 }) 114 } 115 .layoutWeight(1) 116 }, (tab: TabItem): string => { 117 return (tab.name.id).toString(); 118 }) 119 } 120 .height($r('app.float.horizontal_width')) 121 } 122 .width($r('app.float.tab_bar_width')) 123 .backgroundColor($r('app.color.default_background_color')) 124 } 125 126 } else { 127 Flex({ 128 direction: FlexDirection.Row, 129 alignItems: ItemAlign.Center, 130 justifyContent: FlexAlign.Center 131 }) { 132 ForEach(this.tabs, (tab: TabItem) => { 133 Stack() { 134 TabPhone({ tabItem: tab, index: this.tabs.indexOf(tab), onTabSelected: this.funcOnTabSelected }) 135 } 136 .layoutWeight(1) 137 .onClick(() => { 138 if (this.funcOnTabSelected) { 139 if (this.currentIndex == this.tabs.indexOf(tab)) { 140 Log.debug(TAG, `it is same: ${this.currentIndex}`); 141 this.appBroadCast.emit(BroadCastConstants.RESET_ZERO, [this.currentIndex]); 142 } 143 this.funcOnTabSelected(this.tabs.indexOf(tab)); 144 } 145 tab.isSelected = true; 146 }) 147 }, (tab: TabItem): string => { 148 return (tab.name.id).toString(); 149 }) 150 } 151 .visibility((this.isSelectedMode || this.isAlbumSetSelectedMode) ? Visibility.None : Visibility.Visible) 152 .height($r('app.float.tab_bar_vertical_height')) 153 .backgroundColor($r('app.color.default_background_color')) 154 .padding({ left: $r('app.float.max_padding_start'), right: $r('app.float.max_padding_end') }) 155 } 156 } 157 158 private onTabSelected(index: number): void { 159 Log.debug(TAG, `TabBar this.currentIndex: ${this.currentIndex} index: ${index}`); 160 this.currentIndex = index as 0 | 1; 161 if (this.controller != null) { 162 this.controller.changeIndex(this.currentIndex); 163 }; 164 this.tabs.forEach((tab: TabItem) => { 165 if (this.tabs.indexOf(tab) == index) { 166 tab.isSelected = true; 167 } else { 168 tab.isSelected = false; 169 } 170 }) 171 let currentTab: string = tabMap[this.currentIndex] ? tabMap[this.currentIndex] : BigDataConstants.PHOTO_TAB; 172 interface Msg { 173 switchTab: string; 174 current: string; 175 } 176 let msg: Msg = { 177 switchTab: BigDataConstants.CLICK_SWITCH, 178 current: currentTab, 179 } 180 ReportToBigDataUtil.report(BigDataConstants.TAB_SWITCH_ID, msg); 181 Log.info(TAG, `select ${this.currentIndex}`); 182 } 183} 184 185// single tab 186@Component 187struct Tab { 188 @ObjectLink tabItem: TabItem; 189 @Link isSidebar: boolean; 190 index: number = 0; 191 onTabSelected?: Function; 192 private deviceType: number = 0; 193 194 build() { 195 if (this.deviceType == DeviceType.PC_LIKE) { 196 Flex({ 197 direction: FlexDirection.Row, 198 alignItems: ItemAlign.Center, 199 justifyContent: FlexAlign.Start 200 }) { 201 Stack() { 202 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 203 .draggable(false) 204 .height($r('app.float.icon_size')) 205 .width($r('app.float.icon_size')) 206 .objectFit(ImageFit.Fill) 207 } 208 .padding({ 209 left: $r('app.float.tab_bar_text_padding_left'), 210 }) 211 212 Text(this.tabItem.name) 213 .fontSize($r('sys.float.ohos_id_text_size_sub_title2')) 214 .fontWeight(FontWeight.Medium) 215 .fontColor(this.tabItem.getTextColor()) 216 .padding({ 217 left: $r('app.float.tab_bar_text_padding_left'), 218 top: $r('app.float.tab_bar_text_padding_horizontal'), 219 bottom: $r('app.float.tab_bar_text_padding_horizontal') 220 }) 221 .height($r('app.float.menu_height')) 222 } 223 .backgroundColor(this.tabItem.isSelected ? '#DAE2F5' : $r('app.color.transparent')) 224 .borderRadius($r('app.float.single_tab_margin')) 225 .onClick((): void => { 226 this.onTabSelected && this.onTabSelected(this.index); 227 this.tabItem.isSelected = true; 228 }) 229 } else { 230 Flex({ 231 direction: FlexDirection.Column, 232 alignItems: ItemAlign.Center, 233 justifyContent: FlexAlign.Center, 234 }) { 235 Stack() { 236 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 237 .draggable(false) 238 .height($r('app.float.icon_size')) 239 .width($r('app.float.icon_size')) 240 .objectFit(ImageFit.Fill) 241 } 242 .padding({ 243 left: this.isSidebar ? 0 : $r('app.float.tab_bar_text_padding_left'), 244 }) 245 246 Text(this.tabItem.name) 247 .fontSize($r('sys.float.ohos_id_text_size_caption1')) 248 .fontFamily($r('app.string.id_text_font_family_medium')) 249 .fontColor(this.tabItem.getTextColor()) 250 .padding({ 251 left: $r('app.float.tab_bar_text_padding_horizontal'), 252 top: $r('app.float.tab_bar_text_padding_top'), 253 right: $r('app.float.tab_bar_text_padding_horizontal') 254 }) 255 } 256 .onClick((): void => { 257 this.onTabSelected && this.onTabSelected(this.index); 258 this.tabItem.isSelected = true; 259 }) 260 } 261 } 262} 263 264// phone bottom tab 265@Component 266struct TabPhone { 267 @ObjectLink tabItem: TabItem; 268 index: number = 0; 269 onTabSelected?: Function; 270 271 build() { 272 Flex({ 273 direction: FlexDirection.Column, 274 alignItems: ItemAlign.Center, 275 justifyContent: FlexAlign.Center 276 }) { 277 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 278 .draggable(false) 279 .height($r('app.float.icon_size')) 280 .width($r('app.float.icon_size')) 281 .objectFit(ImageFit.Fill) 282 .margin({ 283 bottom: $r('app.float.tab_bar_image_bottom') 284 }) 285 Text(this.tabItem.name) 286 .fontSize($r('app.float.tab_bar_text_size')) 287 .fontWeight(FontWeight.Medium) 288 .fontColor(this.tabItem.getTextColor()) 289 } 290 .key('Tab' + this.tabItem.componentKey) 291 .padding({ 292 top: $r('app.float.tab_bar_padding_top'), 293 left: $r('app.float.tab_bar_padding_left'), 294 right: $r('app.float.tab_bar_padding_right'), 295 bottom: $r('app.float.tab_bar_padding_bottom'), 296 }) 297 .height($r('app.float.tab_bar_vertical_height')) 298 .borderRadius($r('app.float.single_tab_margin')) 299 } 300} 301 302// For Album Set 303@Component 304export struct TabBarForAlbumSet { 305 @Consume isTabBarShow: boolean; 306 private currentIndex: number = 0; 307 private tabs: TabItemWithText[] = []; 308 private controller: TabsController | null = null; 309 private funcOnTabSelected?: Function; 310 311 aboutToAppear(): void { 312 this.funcOnTabSelected = (index: number): void => this.onTabSelected(index); 313 this.tabs[this.currentIndex].isSelected = true; 314 this.tabs.forEach((tab: TabItemWithText) => { 315 Log.info(TAG, `${JSON.stringify(tab.name)}, ${tab.isSelected}`); 316 }); 317 } 318 319 build() { 320 if (this.isTabBarShow) { 321 Flex({ 322 direction: FlexDirection.Row, 323 justifyContent: FlexAlign.Center, 324 alignItems: ItemAlign.Start 325 }) { 326 ForEach(this.tabs, (tab: TabItemWithText) => { 327 TabWithText({ tabItemWithText: tab, index: this.tabs.indexOf(tab), onTabSelected: this.funcOnTabSelected }) 328 }, (tab: TabItemWithText): string => { 329 return (tab.name.id).toString(); 330 }) 331 } 332 .width('100%') 333 .height($r('app.float.album_set_tab_bar_height')) 334 .padding({ left: $r('app.float.max_padding_start'), right: $r('app.float.max_padding_end') }) 335 .backgroundColor($r('app.color.default_background_color')) 336 } 337 } 338 339 private onTabSelected(index: number): void { 340 Log.info(TAG, `TabBarForAlbumSet this.currentIndex: ${this.currentIndex} index: ${index}`); 341 this.currentIndex = index; 342 if (this.controller != null) { 343 this.controller.changeIndex(this.currentIndex); 344 }; 345 this.tabs.forEach((tab: TabItemWithText) => { 346 tab.isSelected = false; 347 }) 348 Log.info(TAG, `select ${this.currentIndex}`); 349 } 350} 351 352// single tab which only has text 353// For Album Set 354@Component 355struct TabWithText { 356 @Consume isAlbumSetSelectedMode: boolean; 357 @ObjectLink tabItemWithText: TabItemWithText; 358 @State tabWidth: number = 0; 359 index: number = 0; 360 onTabSelected?: Function; 361 362 aboutToAppear(): void { 363 // Determine the length of the underline based on the font length 364 if (this.index == 0) { 365 this.tabWidth = px2vp(fp2px(Constants.TEXT_SIZE_SUB_TITLE2)) * 2; 366 } else { 367 this.tabWidth = px2vp(fp2px(Constants.TEXT_SIZE_SUB_TITLE2)) * 4; 368 } 369 Log.info(TAG, `index is ${this.index} and tabWidth is ${this.tabWidth}`); 370 } 371 372 build() { 373 Flex({ 374 direction: FlexDirection.Column, 375 justifyContent: FlexAlign.Center, 376 alignItems: ItemAlign.Center 377 }) { 378 Text(this.tabItemWithText.name) 379 .fontSize(this.tabItemWithText.getTextSize()) 380 .fontWeight(this.tabItemWithText.getTextWeight()) 381 .fontColor(this.tabItemWithText.getTextColor()) 382 .maxLines(1) 383 .margin({ top: $r('app.float.tab_bar_line_margin_top'), 384 left: $r('app.float.single_tab_margin'), 385 right: $r('app.float.single_tab_margin'), 386 bottom: $r('app.float.tab_bar_line_margin_top') }) 387 Column() 388 .width(this.tabWidth) 389 .height($r('app.float.tab_bar_line_height')) 390 .borderRadius($r('app.float.tab_bar_line_radius')) 391 .backgroundColor(this.tabItemWithText.getTextColor()) 392 .visibility(this.tabItemWithText.isSelected ? Visibility.Visible : Visibility.Hidden) 393 } 394 .height('100%') 395 .onClick(() => { 396 if (!this.isAlbumSetSelectedMode) { 397 this.onTabSelected && this.onTabSelected(this.index); 398 this.tabItemWithText.isSelected = true 399 } 400 }) 401 } 402}