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