1/* 2 * Copyright (c) 2023-2023 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 { KeyCode } from '@ohos.multimodalInput.keyCode' 17import MeasureText from '@ohos.measure' 18 19export declare type TabTitleBarMenuItem = { 20 value: ResourceStr 21 isEnabled: boolean 22 action?: () => void 23} 24 25export declare type TabTitleBarTabItem = { 26 title: ResourceStr 27 icon?: ResourceStr 28} 29 30const PUBLIC_MORE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAIGNIUk0AAHomAACAhAAA+' + 31 'gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAO' + 32 'xAAADsQBlSsOGwAABEZJREFUeNrt3D1rFFEUBuAxhmAhFlYpUohYiYWFRcAmKAhWK2pjo1iKf8BCMIKFf8BarCyMhVj4VZhGSKEg2FqJyCKWIhY' + 33 'WnstMINgYsh+cmfs88BICydxw7jmzu2HvNg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBN+3r6dx+LXIqsRpa7FF8j48hm5Fn3Peo9mAEYRd' + 34 'YjJ3f582Vj7nZfUe/eDsCRyMPI2h5/fyNyI/JDT6v3Tvt7sBllE15ETkxwjeORi5G3ke/6W737MgBnI68jh6ZwrcORq5HnhkC9+zAA5YXXy8jBK' + 35 'V5zKXIu8jjyS7+rd+YBeNVtyrSVO9PRyBM9r94LSTfjWuTUDK9/eYIXeENUbb0zDsBi5PYc1rmj79U74wCszuih+F/ljrSi/+uud8YBGA10rayq' + 36 'rnfGAVgb6FpZVV3vjAOwPNC1sqq63hkHYGWga2VVdb0XKt/8Rf1fd70zDsB4jmt5u3Tl9a59AMb6v+56ZxyArYGulVXV9c44ABtzXOup/q+73hk' + 37 'H4N2cHio/Rj7r/7rrnXEAfkfuz2Gddb2v3ln/DfpgxneLzaY9xE3l9c46AH8iVyI/Z3Dt8nB/Xc+rd5H5QMy3yJemPVs6zY0edc9HUe/0Z4I/dQ' + 38 '/N5Vjd0oTXKp9QcKFpD2qj3r0YgO1NeRM507TH6/bifeR85IMeV++d+vTBWOV9JDcjt5rdv6uw3M3uRR7pa/Xu+wBsOxA53bTnTP/3UX1b3fNQ1' + 39 'BsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqyr6d/97HIpchqZLlL8TUyjmxGnnXfo96DGYBRZD1ycpc/Xzbm' + 40 'bvcV9e7tAByJPIys7fH3NyI3Ij/0tHrvtL8Hm1E24UXkxATXOB65GHkb+a6/1bsvA3A28jpyaArXOhy5GnluCNS7DwNQXni9jByc4jWXIucijyO' + 41 '/9Lt6Zx6AV92mTFu5Mx2NPNHz6r2QdDOuRU7N8PqXJ3iBN0TV1jvjACxGbs9hnTv6Xr0zDsDqjB6K/1XuSCv6v+56ZxyA0UDXyqrqemccgLWBrp' + 42 'VV1fXOOADLA10rq6rrnXEAVga6VlZV13uh8s1f1P911zvjAIznuJa3S1de79oHYKz/6653xgHYGuhaWVVd74wDsDHHtZ7q/7rrnXEA3s3pofJj5' + 43 'LP+r7veGQfgd+T+HNZZ1/vqnfXfoA9mfLfYbNpD3FRe76wD8CdyJfJzBtcuD/fX9bx6F5kPxHyLfGnas6XT3OhR93wU9U5/JvhT99BcjtUtTXit' + 44 '8gkFF5r2oDbq3YsB2N6UN5EzTXu8bi/eR85HPuhx9d6pTx+MVd5HcjNyq9n9uwrL3exe5JG+Vu++D8C2A5HTTXvO9H8f1bfVPQ9FvQEAAAAAAAA' + 45 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAgCn7C9HjBtwWfXpKAAAAAElFTkSuQmCC' 46 47@Component 48export struct TabTitleBar { 49 tabItems: Array<TabTitleBarTabItem> 50 menuItems: Array<TabTitleBarMenuItem> 51 @BuilderParam swiperContent: () => void 52 53 @State tabWidth: number = 0 54 @State currentIndex: number = 0 55 56 static readonly totalHeight = 56 57 static readonly correctionOffset = -40.0 58 static readonly gradientMaskWidth = 24 59 private static instanceCount = 0 60 61 private menuSectionWidth = 0 62 private tabOffsets = Array<number>() 63 private imageWidths = Array<number>() 64 65 private scroller: Scroller = new Scroller() 66 private swiperController: SwiperController = new SwiperController() 67 private settings: RenderingContextSettings = new RenderingContextSettings(true) 68 private leftContext2D: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) 69 private rightContext2D: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) 70 71 @Builder 72 GradientMask(context2D: CanvasRenderingContext2D, x0: number, y0: number, x1: number, y1: number) { 73 Column() { 74 Canvas(context2D) 75 .width(TabTitleBar.gradientMaskWidth) 76 .height(TabTitleBar.totalHeight) 77 .onReady(() => { 78 var grad = context2D.createLinearGradient(x0, y0, x1, y1) 79 grad.addColorStop(0.0, '#ffffffff') 80 grad.addColorStop(1, '#00ffffff') 81 context2D.fillStyle = grad 82 context2D.fillRect(0, 0, TabTitleBar.gradientMaskWidth, TabTitleBar.totalHeight) 83 }) 84 } 85 .width(TabTitleBar.gradientMaskWidth) 86 .height(TabTitleBar.totalHeight) 87 } 88 89 aboutToAppear() { 90 this.tabItems.forEach((_elem) => { 91 this.imageWidths.push(0) 92 }) 93 this.loadOffsets() 94 } 95 96 loadOffsets() { 97 this.tabOffsets.length = 0 98 99 let tabOffset = 0 100 this.tabOffsets.push(tabOffset) 101 tabOffset += TabContentItem.marginFirst 102 103 this.tabItems.forEach((tabItem, index) => { 104 if (tabItem.icon !== undefined) { 105 if (Math.abs(this.imageWidths[index]) > TabContentItem.imageHotZoneWidth) { 106 tabOffset += this.imageWidths[index] 107 } else { 108 tabOffset += TabContentItem.imageHotZoneWidth 109 } 110 } else { 111 tabOffset += TabContentItem.paddingLeft 112 tabOffset += px2vp(MeasureText.measureText({ 113 textContent: tabItem.title.toString(), 114 fontSize: 18, 115 fontWeight: FontWeight.Medium, 116 })) 117 tabOffset += TabContentItem.paddingRight 118 } 119 this.tabOffsets.push(tabOffset) 120 }) 121 } 122 123 build() { 124 Column() { 125 Flex({ 126 justifyContent: FlexAlign.SpaceBetween, 127 alignItems: ItemAlign.Stretch 128 }) { 129 Stack({ alignContent: Alignment.End }) { 130 Stack({ alignContent: Alignment.Start }) { 131 Column() { 132 List({ initialIndex: 0, scroller: this.scroller, space: 0 }) { 133 ForEach(this.tabItems, (tabItem, index) => { 134 ListItem() { 135 TabContentItem({ 136 item: tabItem, 137 index: index, 138 maxIndex: this.tabItems.length - 1, 139 currentIndex: this.currentIndex, 140 onCustomClick: (itemIndex) => this.currentIndex = itemIndex, 141 onImageComplete: (width) => { 142 this.imageWidths[index] = width 143 this.loadOffsets() 144 } 145 }) 146 } 147 }) 148 } 149 .width('100%') 150 .height(TabTitleBar.totalHeight) 151 .constraintSize({ maxWidth: this.tabWidth }) 152 .edgeEffect(EdgeEffect.Spring) 153 .listDirection(Axis.Horizontal) 154 .scrollBar(BarState.Off) 155 } 156 this.GradientMask(this.leftContext2D, 0, TabTitleBar.totalHeight / 2, 157 TabTitleBar.gradientMaskWidth, TabTitleBar.totalHeight / 2) 158 } 159 this.GradientMask(this.rightContext2D, TabTitleBar.gradientMaskWidth, 160 TabTitleBar.totalHeight / 2, 0, TabTitleBar.totalHeight / 2) 161 } 162 163 if (this.menuItems !== undefined && this.menuItems.length > 0) { 164 CollapsibleMenuSection({ menuItems: this.menuItems, index: 1 + TabTitleBar.instanceCount++ }) 165 .height(TabTitleBar.totalHeight) 166 .onAreaChange((_oldValue, newValue) => { 167 this.menuSectionWidth = Number(newValue.width) 168 }) 169 } 170 } 171 .backgroundColor($r('sys.color.ohos_id_color_background')) 172 .margin({ right: $r('sys.float.ohos_id_max_padding_end') }) 173 .onAreaChange((_oldValue, newValue) => { 174 this.tabWidth = Number(newValue.width) - this.menuSectionWidth 175 }) 176 177 Column() { 178 Swiper(this.swiperController) { this.swiperContent() } 179 .index(this.currentIndex) 180 .itemSpace(0) 181 .indicator(false) 182 .width('100%') 183 .height('100%') 184 .curve(Curve.Friction) 185 .onChange((index) => { 186 const offset = this.tabOffsets[index] + TabTitleBar.correctionOffset 187 this.currentIndex = index 188 this.scroller.scrollTo({ 189 xOffset: offset > 0 ? offset : 0, 190 yOffset: 0, 191 animation: { 192 duration: 300, 193 curve: Curve.EaseInOut 194 } 195 }) 196 }) 197 .onAppear(() => { 198 this.scroller.scrollToIndex(this.currentIndex) 199 this.scroller.scrollBy(TabTitleBar.correctionOffset, 0) 200 }) 201 } 202 } 203 } 204} 205 206@Component 207struct CollapsibleMenuSection { 208 menuItems: Array<TabTitleBarMenuItem> 209 index: number 210 211 static readonly maxCountOfVisibleItems = 1 212 private static readonly focusPadding = 4 213 private static readonly marginsNum = 2 214 private firstFocusableIndex = -1 215 216 @State isPopupShown: boolean = false 217 218 @State isMoreIconOnFocus: boolean = false 219 @State isMoreIconOnHover: boolean = false 220 @State isMoreIconOnClick: boolean = false 221 222 getMoreIconFgColor() { 223 return this.isMoreIconOnClick 224 ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') 225 : $r('sys.color.ohos_id_color_titlebar_icon') 226 } 227 228 getMoreIconBgColor() { 229 if (this.isMoreIconOnClick) { 230 return $r('sys.color.ohos_id_color_click_effect') 231 } else if (this.isMoreIconOnHover) { 232 return $r('sys.color.ohos_id_color_hover') 233 } else { 234 return Color.Transparent 235 } 236 } 237 238 aboutToAppear() { 239 this.menuItems.forEach((item, index) => { 240 if (item.isEnabled && this.firstFocusableIndex == -1 && index > CollapsibleMenuSection.maxCountOfVisibleItems - 2) { 241 this.firstFocusableIndex = this.index * 1000 + index + 1 242 } 243 }) 244 } 245 246 build() { 247 Column() { 248 Row() { 249 if (this.menuItems.length <= CollapsibleMenuSection.maxCountOfVisibleItems) { 250 ForEach(this.menuItems, (item, index) => { 251 ImageMenuItem({ item: item, index: this.index * 1000 + index + 1 }) 252 }) 253 } else { 254 ForEach(this.menuItems.slice(0, CollapsibleMenuSection.maxCountOfVisibleItems - 1), (item, index) => { 255 ImageMenuItem({ item: item, index: this.index * 1000 + index + 1 }) 256 }) 257 258 Row() { 259 Image(PUBLIC_MORE) 260 .width(ImageMenuItem.imageSize) 261 .height(ImageMenuItem.imageSize) 262 .focusable(true) 263 } 264 .width(ImageMenuItem.imageHotZoneWidth) 265 .height(ImageMenuItem.imageHotZoneWidth) 266 .borderRadius(ImageMenuItem.buttonBorderRadius) 267 .foregroundColor(this.getMoreIconFgColor()) 268 .backgroundColor(this.getMoreIconBgColor()) 269 .justifyContent(FlexAlign.Center) 270 .stateStyles({ 271 focused: { 272 .border({ 273 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 274 width: ImageMenuItem.focusBorderWidth, 275 color: $r('sys.color.ohos_id_color_focused_outline'), 276 style: BorderStyle.Solid 277 }) 278 }, 279 normal: { 280 .border({ 281 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 282 width: 0 283 }) 284 } 285 }) 286 .onFocus(() => this.isMoreIconOnFocus = true) 287 .onBlur(() => this.isMoreIconOnFocus = false) 288 .onHover((isOn) => this.isMoreIconOnHover = isOn) 289 .onKeyEvent((event) => { 290 if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { 291 return 292 } 293 if (event.type === KeyType.Down) { 294 this.isMoreIconOnClick = true 295 } 296 if (event.type === KeyType.Up) { 297 this.isMoreIconOnClick = false 298 } 299 }) 300 .onTouch((event) => { 301 if (event.type === TouchType.Down) { 302 this.isMoreIconOnClick = true 303 } 304 if (event.type === TouchType.Up) { 305 this.isMoreIconOnClick = false 306 } 307 }) 308 .onClick(() => this.isPopupShown = true) 309 .bindPopup(this.isPopupShown, { 310 builder: this.popupBuilder, 311 placement: Placement.Bottom, 312 popupColor: Color.White, 313 enableArrow: false, 314 onStateChange: (e) => { 315 this.isPopupShown = e.isVisible 316 if (!e.isVisible) { 317 this.isMoreIconOnClick = false 318 } 319 } 320 }) 321 } 322 } 323 } 324 .height('100%') 325 .justifyContent(FlexAlign.Center) 326 } 327 328 @Builder 329 popupBuilder() { 330 Column() { 331 ForEach(this.menuItems.slice(CollapsibleMenuSection.maxCountOfVisibleItems - 1, this.menuItems.length), (item, index) => { 332 ImageMenuItem({ item: item, index: this.index * 1000 + CollapsibleMenuSection.maxCountOfVisibleItems + index }) 333 }) 334 } 335 .width(ImageMenuItem.imageHotZoneWidth + CollapsibleMenuSection.focusPadding * CollapsibleMenuSection.marginsNum) 336 .margin({ top: CollapsibleMenuSection.focusPadding, bottom: CollapsibleMenuSection.focusPadding }) 337 .onAppear(() => { 338 focusControl.requestFocus(ImageMenuItem.focusablePrefix + this.firstFocusableIndex) 339 }) 340 } 341} 342 343@Component 344struct TabContentItem { 345 item: TabTitleBarTabItem 346 index: number 347 maxIndex: number 348 onCustomClick?: (index: number) => void 349 onImageComplete?: (width: number) => void 350 351 @Prop currentIndex: number 352 353 @State isOnFocus: boolean = false 354 @State isOnHover: boolean = false 355 @State isOnClick: boolean = false 356 @State tabWidth: number = 0 357 358 @State imageWidth: number = 24 359 @State imageHeight: number = 24 360 361 static readonly imageSize = 24 362 static readonly imageHotZoneWidth = 48 363 static readonly imageMagnificationFactor = 1.4 364 static readonly buttonBorderRadius = 8 365 static readonly focusBorderWidth = 2 366 static readonly paddingLeft = 8 367 static readonly paddingRight = 8 368 static readonly marginFirst = 16 369 370 getBgColor() { 371 if (this.isOnClick) { 372 return $r('sys.color.ohos_id_color_click_effect') 373 } else if (this.isOnHover) { 374 return $r('sys.color.ohos_id_color_hover') 375 } else { 376 return Color.Transparent 377 } 378 } 379 380 getBorderAttr() { 381 if (this.isOnFocus) { 382 return { 383 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 384 width: TabContentItem.focusBorderWidth, 385 color: $r('sys.color.ohos_id_color_focused_outline'), 386 style: BorderStyle.Solid 387 } 388 } 389 return { width: 0 } 390 } 391 392 getImageScaleFactor(): number { 393 return this.index === this.currentIndex ? TabContentItem.imageMagnificationFactor : 1 394 } 395 396 getImageLayoutWidth(): number { 397 return TabContentItem.imageSize / Math.max(this.imageHeight, 1.0) * this.imageWidth 398 } 399 400 build() { 401 Stack() { 402 Row() { 403 Column() { 404 if (this.item.icon === undefined) { 405 Text(this.item.title) 406 .fontSize(this.index === this.currentIndex 407 ? $r('sys.float.ohos_id_text_size_headline7') 408 : $r('sys.float.ohos_id_text_size_headline9')) 409 .fontColor(this.index === this.currentIndex 410 ? $r('sys.color.ohos_id_color_titlebar_text') 411 : $r('sys.color.ohos_id_color_titlebar_text_off')) 412 .fontWeight(FontWeight.Medium) 413 .focusable(true) 414 .animation({ duration: 300 }) 415 .padding({ 416 top: this.index === this.currentIndex ? 6 : 10, 417 left: TabContentItem.paddingLeft, 418 bottom: 2, 419 right: TabContentItem.paddingRight 420 }) 421 .onFocus(() => this.isOnFocus = true) 422 .onBlur(() => this.isOnFocus = false) 423 .onHover((isOn) => this.isOnHover = isOn) 424 .onKeyEvent((event) => { 425 if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { 426 return 427 } 428 if (event.type === KeyType.Down) { 429 this.isOnClick = true 430 } 431 if (event.type === KeyType.Up) { 432 this.isOnClick = false 433 } 434 }) 435 .onTouch((event) => { 436 if (event.type === TouchType.Down) { 437 this.isOnClick = true 438 } 439 if (event.type === TouchType.Up) { 440 this.isOnClick = false 441 } 442 }) 443 .onClick(() => this.onCustomClick && this.onCustomClick(this.index)) 444 } else { 445 Row() { 446 Image(this.item.icon) 447 .alt(this.item.title) 448 .width(this.getImageLayoutWidth()) 449 .height(TabContentItem.imageSize) 450 .objectFit(ImageFit.Fill) 451 .scale({ 452 x: this.getImageScaleFactor(), 453 y: this.getImageScaleFactor() 454 }) 455 .animation({ duration: 300 }) 456 .hitTestBehavior(HitTestMode.None) 457 .focusable(true) 458 .onComplete((event) => { 459 if (!this.onImageComplete) { 460 return 461 } 462 this.imageWidth = px2vp(event.width) 463 this.imageHeight = px2vp(event.height) 464 this.onImageComplete(px2vp(event.componentWidth) 465 + TabContentItem.paddingLeft + TabContentItem.paddingRight) 466 }) 467 .onError((event) => { 468 if (!this.onImageComplete) { 469 return 470 } 471 this.onImageComplete(px2vp(event.componentWidth) 472 + TabContentItem.paddingLeft + TabContentItem.paddingRight) 473 }) 474 } 475 .width(this.getImageLayoutWidth() * this.getImageScaleFactor() 476 + TabContentItem.paddingLeft + TabContentItem.paddingRight) 477 .constraintSize({ 478 minWidth: TabContentItem.imageHotZoneWidth, 479 minHeight: TabContentItem.imageHotZoneWidth 480 }) 481 .animation({ duration: 300 }) 482 .justifyContent(FlexAlign.Center) 483 .onFocus(() => this.isOnFocus = true) 484 .onBlur(() => this.isOnFocus = false) 485 .onHover((isOn) => this.isOnHover = isOn) 486 .onKeyEvent((event) => { 487 if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { 488 return 489 } 490 if (event.type === KeyType.Down) { 491 this.isOnClick = true 492 } 493 if (event.type === KeyType.Up) { 494 this.isOnClick = false 495 } 496 }) 497 .onTouch((event) => { 498 if (event.type === TouchType.Down) { 499 this.isOnClick = true 500 } 501 if (event.type === TouchType.Up) { 502 this.isOnClick = false 503 } 504 }) 505 .onClick(() => this.onCustomClick && this.onCustomClick(this.index)) 506 } 507 } 508 .justifyContent(FlexAlign.Center) 509 } 510 .height(TabTitleBar.totalHeight) 511 .alignItems(VerticalAlign.Center) 512 .justifyContent(FlexAlign.Center) 513 .borderRadius(TabContentItem.buttonBorderRadius) 514 .backgroundColor(this.getBgColor()) 515 .onAreaChange((_oldValue, newValue) => { 516 this.tabWidth = Number(newValue.width) 517 }) 518 519 if (this.isOnFocus && this.tabWidth > 0) { 520 Row() 521 .width(this.tabWidth) 522 .height(TabTitleBar.totalHeight) 523 .hitTestBehavior(HitTestMode.None) 524 .borderRadius(TabContentItem.buttonBorderRadius) 525 .stateStyles({ 526 focused: { 527 .border(this.getBorderAttr()) 528 }, 529 normal: { 530 .border({ 531 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 532 width: 0 533 }) 534 } 535 }) 536 } 537 } 538 .margin({ 539 left: this.index === 0 ? TabContentItem.marginFirst : 0, 540 right: this.index === this.maxIndex ? 12 : 0 541 }) 542 } 543} 544 545@Component 546struct ImageMenuItem { 547 item: TabTitleBarMenuItem 548 index: number 549 550 static readonly imageSize = 24 551 static readonly imageHotZoneWidth = 48 552 static readonly buttonBorderRadius = 8 553 static readonly focusBorderWidth = 2 554 static readonly disabledImageOpacity = 0.4 555 static readonly focusablePrefix = "Id-TabTitleBar-ImageMenuItem-" 556 557 @State isOnFocus: boolean = false 558 @State isOnHover: boolean = false 559 @State isOnClick: boolean = false 560 561 getFgColor() { 562 return this.isOnClick 563 ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') 564 : $r('sys.color.ohos_id_color_titlebar_icon') 565 } 566 567 getBgColor() { 568 if (this.isOnClick) { 569 return $r('sys.color.ohos_id_color_click_effect') 570 } else if (this.isOnHover) { 571 return $r('sys.color.ohos_id_color_hover') 572 } else { 573 return Color.Transparent 574 } 575 } 576 577 build() { 578 Row() { 579 Image(this.item.value) 580 .width(ImageMenuItem.imageSize) 581 .height(ImageMenuItem.imageSize) 582 .focusable(this.item.isEnabled) 583 .key(ImageMenuItem.focusablePrefix + this.index) 584 } 585 .width(ImageMenuItem.imageHotZoneWidth) 586 .height(ImageMenuItem.imageHotZoneWidth) 587 .borderRadius(ImageMenuItem.buttonBorderRadius) 588 .foregroundColor(this.getFgColor()) 589 .backgroundColor(this.getBgColor()) 590 .justifyContent(FlexAlign.Center) 591 .opacity(this.item.isEnabled ? 1 : ImageMenuItem.disabledImageOpacity) 592 .stateStyles({ 593 focused: { 594 .border({ 595 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 596 width: ImageMenuItem.focusBorderWidth, 597 color: $r('sys.color.ohos_id_color_focused_outline'), 598 style: BorderStyle.Solid 599 }) 600 }, 601 normal: { 602 .border({ 603 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 604 width: 0 605 }) 606 } 607 }) 608 .onFocus(() => { 609 if (!this.item.isEnabled) { 610 return 611 } 612 this.isOnFocus = true 613 }) 614 .onBlur(() => this.isOnFocus = false) 615 .onHover((isOn) => { 616 if (!this.item.isEnabled) { 617 return 618 } 619 this.isOnHover = isOn 620 }) 621 .onKeyEvent((event) => { 622 if (!this.item.isEnabled) { 623 return 624 } 625 if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { 626 return 627 } 628 if (event.type === KeyType.Down) { 629 this.isOnClick = true 630 } 631 if (event.type === KeyType.Up) { 632 this.isOnClick = false 633 } 634 }) 635 .onTouch((event) => { 636 if (!this.item.isEnabled) { 637 return 638 } 639 if (event.type === TouchType.Down) { 640 this.isOnClick = true 641 } 642 if (event.type === TouchType.Up) { 643 this.isOnClick = false 644 } 645 }) 646 .onClick(() => this.item.isEnabled && this.item.action && this.item.action()) 647 } 648} 649 650export default { TabTitleBar }