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 DEVICE_TYPE { 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: DEVICE_TYPE = DEVICE_TYPE.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 == DEVICE_TYPE.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) this.controller.changeIndex(this.currentIndex); 162 this.tabs.forEach((tab: TabItem) => { 163 if (this.tabs.indexOf(tab) == index) { 164 tab.isSelected = true; 165 } else { 166 tab.isSelected = false; 167 } 168 }) 169 let currentTab: string = TabMap[this.currentIndex] ? TabMap[this.currentIndex] : BigDataConstants.PHOTO_TAB; 170 interface Msg { 171 switchTab: string; 172 current: string; 173 } 174 let msg: Msg = { 175 switchTab: BigDataConstants.CLICK_SWITCH, 176 current: currentTab, 177 } 178 ReportToBigDataUtil.report(BigDataConstants.TAB_SWITCH_ID, msg); 179 Log.info(TAG, `select ${this.currentIndex}`); 180 } 181} 182 183// single tab 184@Component 185struct Tab { 186 @ObjectLink tabItem: TabItem; 187 @Link isSidebar: boolean; 188 index: number = 0; 189 onTabSelected?: Function; 190 private deviceType: number = 0; 191 192 build() { 193 if (this.deviceType == DEVICE_TYPE.PC_LIKE) { 194 Flex({ 195 direction: FlexDirection.Row, 196 alignItems: ItemAlign.Center, 197 justifyContent: FlexAlign.Start 198 }) { 199 Stack() { 200 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 201 .draggable(false) 202 .height($r('app.float.icon_size')) 203 .width($r('app.float.icon_size')) 204 .objectFit(ImageFit.Fill) 205 } 206 .padding({ 207 left: $r('app.float.tab_bar_text_padding_left'), 208 }) 209 210 Text(this.tabItem.name) 211 .fontSize($r('sys.float.ohos_id_text_size_sub_title2')) 212 .fontWeight(FontWeight.Medium) 213 .fontColor(this.tabItem.getTextColor()) 214 .padding({ 215 left: $r('app.float.tab_bar_text_padding_left'), 216 top: $r('app.float.tab_bar_text_padding_horizontal'), 217 bottom: $r('app.float.tab_bar_text_padding_horizontal') 218 }) 219 .height($r('app.float.menu_height')) 220 } 221 .backgroundColor(this.tabItem.isSelected ? '#DAE2F5' : $r('app.color.transparent')) 222 .borderRadius($r('app.float.single_tab_margin')) 223 .onClick((): void => { 224 this.onTabSelected && this.onTabSelected(this.index); 225 this.tabItem.isSelected = true; 226 }) 227 } else { 228 Flex({ 229 direction: FlexDirection.Column, 230 alignItems: ItemAlign.Center, 231 justifyContent: FlexAlign.Center, 232 }) { 233 Stack() { 234 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 235 .draggable(false) 236 .height($r('app.float.icon_size')) 237 .width($r('app.float.icon_size')) 238 .objectFit(ImageFit.Fill) 239 } 240 .padding({ 241 left: this.isSidebar ? 0 : $r('app.float.tab_bar_text_padding_left'), 242 }) 243 244 Text(this.tabItem.name) 245 .fontSize($r('sys.float.ohos_id_text_size_caption1')) 246 .fontFamily($r('app.string.id_text_font_family_medium')) 247 .fontColor(this.tabItem.getTextColor()) 248 .padding({ 249 left: $r('app.float.tab_bar_text_padding_horizontal'), 250 top: $r('app.float.tab_bar_text_padding_top'), 251 right: $r('app.float.tab_bar_text_padding_horizontal') 252 }) 253 } 254 .onClick((): void => { 255 this.onTabSelected && this.onTabSelected(this.index); 256 this.tabItem.isSelected = true; 257 }) 258 } 259 } 260} 261 262// phone bottom tab 263@Component 264struct TabPhone { 265 @ObjectLink tabItem: TabItem; 266 index: number = 0; 267 onTabSelected?: Function; 268 269 build() { 270 Flex({ 271 direction: FlexDirection.Column, 272 alignItems: ItemAlign.Center, 273 justifyContent: FlexAlign.Center 274 }) { 275 Image(this.tabItem.getIcon(this.tabItem.isSelected)) 276 .draggable(false) 277 .height($r('app.float.icon_size')) 278 .width($r('app.float.icon_size')) 279 .objectFit(ImageFit.Fill) 280 .margin({ 281 bottom: $r('app.float.tab_bar_image_bottom') 282 }) 283 Text(this.tabItem.name) 284 .fontSize($r('app.float.tab_bar_text_size')) 285 .fontWeight(FontWeight.Medium) 286 .fontColor(this.tabItem.getTextColor()) 287 } 288 .key('Tab' + this.tabItem.componentKey) 289 .padding({ 290 top: $r('app.float.tab_bar_padding_top'), 291 left: $r('app.float.tab_bar_padding_left'), 292 right: $r('app.float.tab_bar_padding_right'), 293 bottom: $r('app.float.tab_bar_padding_bottom'), 294 }) 295 .height($r('app.float.tab_bar_vertical_height')) 296 .borderRadius($r('app.float.single_tab_margin')) 297 } 298} 299 300// For Album Set 301@Component 302export struct TabBarForAlbumSet { 303 @Consume isTabBarShow: boolean; 304 private currentIndex: number = 0; 305 private tabs: TabItemWithText[] = []; 306 private controller: TabsController | null = null; 307 private funcOnTabSelected?: Function; 308 309 aboutToAppear(): void { 310 this.funcOnTabSelected = (index: number): void => this.onTabSelected(index); 311 this.tabs[this.currentIndex].isSelected = true; 312 this.tabs.forEach((tab: TabItemWithText) => { 313 Log.info(TAG, `${JSON.stringify(tab.name)}, ${tab.isSelected}`); 314 }); 315 } 316 317 build() { 318 if (this.isTabBarShow) { 319 Flex({ 320 direction: FlexDirection.Row, 321 justifyContent: FlexAlign.Center, 322 alignItems: ItemAlign.Start 323 }) { 324 ForEach(this.tabs, (tab: TabItemWithText) => { 325 TabWithText({ tabItemWithText: tab, index: this.tabs.indexOf(tab), onTabSelected: this.funcOnTabSelected }) 326 }, (tab: TabItemWithText): string => { 327 return (tab.name.id).toString(); 328 }) 329 } 330 .width('100%') 331 .height($r('app.float.album_set_tab_bar_height')) 332 .padding({ left: $r('app.float.max_padding_start'), right: $r('app.float.max_padding_end') }) 333 .backgroundColor($r('app.color.default_background_color')) 334 } 335 } 336 337 private onTabSelected(index: number): void { 338 Log.info(TAG, `TabBarForAlbumSet this.currentIndex: ${this.currentIndex} index: ${index}`); 339 this.currentIndex = index; 340 if (this.controller != null) this.controller.changeIndex(this.currentIndex); 341 this.tabs.forEach((tab: TabItemWithText) => { 342 tab.isSelected = false; 343 }) 344 Log.info(TAG, `select ${this.currentIndex}`); 345 } 346} 347 348// single tab which only has text 349// For Album Set 350@Component 351struct TabWithText { 352 @Consume isAlbumSetSelectedMode: boolean; 353 @ObjectLink tabItemWithText: TabItemWithText; 354 @State TabWidth: number = 0; 355 index: number = 0; 356 onTabSelected?: Function; 357 358 aboutToAppear(): void { 359 // Determine the length of the underline based on the font length 360 if (this.index == 0) { 361 this.TabWidth = px2vp(fp2px(Constants.TEXT_SIZE_SUB_TITLE2)) * 2; 362 } else { 363 this.TabWidth = px2vp(fp2px(Constants.TEXT_SIZE_SUB_TITLE2)) * 4; 364 } 365 Log.info(TAG, `index is ${this.index} and TabWidth is ${this.TabWidth}`); 366 } 367 368 build() { 369 Flex({ 370 direction: FlexDirection.Column, 371 justifyContent: FlexAlign.Center, 372 alignItems: ItemAlign.Center 373 }) { 374 Text(this.tabItemWithText.name) 375 .fontSize(this.tabItemWithText.getTextSize()) 376 .fontWeight(this.tabItemWithText.getTextWeight()) 377 .fontColor(this.tabItemWithText.getTextColor()) 378 .maxLines(1) 379 .margin({ top: $r('app.float.tab_bar_line_margin_top'), 380 left: $r('app.float.single_tab_margin'), 381 right: $r('app.float.single_tab_margin'), 382 bottom: $r('app.float.tab_bar_line_margin_top') }) 383 Column() 384 .width(this.TabWidth) 385 .height($r('app.float.tab_bar_line_height')) 386 .borderRadius($r('app.float.tab_bar_line_radius')) 387 .backgroundColor(this.tabItemWithText.getTextColor()) 388 .visibility(this.tabItemWithText.isSelected ? Visibility.Visible : Visibility.Hidden) 389 } 390 .height('100%') 391 .onClick(() => { 392 if (!this.isAlbumSetSelectedMode) { 393 this.onTabSelected && this.onTabSelected(this.index); 394 this.tabItemWithText.isSelected = true 395 } 396 }) 397 } 398}