1/* 2 * Copyright (c) 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 */ 15import curves from '@ohos.curves' 16import { KeyCode } from '@ohos.multimodalInput.keyCode' 17 18const MIN_ITEM_COUNT = 2 19const MAX_ITEM_COUNT = 5 20 21interface SegmentButtonThemeInterface { 22 FONT_COLOR: ResourceColor, 23 TAB_SELECTED_FONT_COLOR: ResourceColor, 24 CAPSULE_SELECTED_FONT_COLOR: ResourceColor, 25 FONT_SIZE: DimensionNoPercentage, 26 SELECTED_FONT_SIZE: DimensionNoPercentage, 27 BACKGROUND_COLOR: ResourceColor, 28 TAB_SELECTED_BACKGROUND_COLOR: ResourceColor, 29 CAPSULE_SELECTED_BACKGROUND_COLOR: ResourceColor, 30 FOCUS_BORDER_COLOR: ResourceColor, 31 HOVER_COlOR: ResourceColor, 32 PRESS_COLOR: ResourceColor, 33 BACKGROUND_BLUR_STYLE: BlurStyle, 34} 35 36const SegmentButtonTheme: SegmentButtonThemeInterface = { 37 FONT_COLOR: $r('sys.color.ohos_id_color_text_secondary'), 38 TAB_SELECTED_FONT_COLOR: $r('sys.color.ohos_id_color_text_primary'), 39 CAPSULE_SELECTED_FONT_COLOR: $r('sys.color.ohos_id_color_foreground_contrary'), 40 FONT_SIZE: $r('sys.float.ohos_id_text_size_body2'), 41 SELECTED_FONT_SIZE: $r('sys.float.ohos_id_text_size_body2'), 42 BACKGROUND_COLOR: $r('sys.color.ohos_id_color_button_normal'), 43 TAB_SELECTED_BACKGROUND_COLOR: $r('sys.color.ohos_id_color_foreground_contrary'), 44 CAPSULE_SELECTED_BACKGROUND_COLOR: $r('sys.color.ohos_id_color_emphasize'), 45 FOCUS_BORDER_COLOR: $r("sys.color.ohos_id_color_focused_outline"), 46 HOVER_COlOR: $r("sys.color.ohos_id_color_hover"), 47 PRESS_COLOR: $r("sys.color.ohos_id_color_click_effect"), 48 BACKGROUND_BLUR_STYLE: BlurStyle.NONE 49} 50 51interface Point { 52 x: number 53 y: number 54} 55 56function nearEqual(first: number, second: number): boolean { 57 return Math.abs(first - second) < 0.001 58} 59 60interface SegmentButtonTextItem { 61 text: ResourceStr 62} 63 64interface SegmentButtonIconItem { 65 icon: ResourceStr, 66 selectedIcon: ResourceStr 67} 68 69interface SegmentButtonIconTextItem { 70 icon: ResourceStr, 71 selectedIcon: ResourceStr, 72 text: ResourceStr 73} 74 75type DimensionNoPercentage = PX | VP | FP | LPX | Resource 76 77interface CommonSegmentButtonOptions { 78 fontColor?: ResourceColor 79 selectedFontColor?: ResourceColor 80 fontSize?: DimensionNoPercentage 81 selectedFontSize?: DimensionNoPercentage 82 fontWeight?: FontWeight 83 selectedFontWeight?: FontWeight 84 backgroundColor?: ResourceColor 85 selectedBackgroundColor?: ResourceColor 86 imageSize?: SizeOptions 87 buttonPadding?: Padding | Dimension 88 textPadding?: Padding | Dimension 89 backgroundBlurStyle?: BlurStyle 90} 91 92type ItemRestriction<T> = [T, T, T?, T?, T?] 93type SegmentButtonItemTuple = ItemRestriction<SegmentButtonTextItem> | ItemRestriction<SegmentButtonIconItem> | ItemRestriction<SegmentButtonIconTextItem> 94type SegmentButtonItemArray = Array<SegmentButtonTextItem> | Array<SegmentButtonIconItem> | Array<SegmentButtonIconTextItem> 95 96export interface TabSegmentButtonConstructionOptions extends CommonSegmentButtonOptions { 97 buttons: ItemRestriction<SegmentButtonTextItem> 98} 99 100export interface CapsuleSegmentButtonConstructionOptions extends CommonSegmentButtonOptions { 101 buttons: SegmentButtonItemTuple 102 multiply?: boolean 103} 104 105export interface TabSegmentButtonOptions extends TabSegmentButtonConstructionOptions { 106 type: "tab", 107} 108 109export interface CapsuleSegmentButtonOptions extends CapsuleSegmentButtonConstructionOptions { 110 type: "capsule" 111} 112 113interface SegmentButtonItemOptionsConstructorOptions { 114 icon?: ResourceStr 115 selectedIcon?: ResourceStr 116 text?: ResourceStr 117} 118 119@Observed 120class SegmentButtonItemOptions { 121 icon?: ResourceStr 122 selectedIcon?: ResourceStr 123 text?: ResourceStr 124 125 constructor(options: SegmentButtonItemOptionsConstructorOptions) { 126 this.icon = options.icon 127 this.selectedIcon = options.selectedIcon 128 this.text = options.text 129 } 130} 131 132@Observed 133export class SegmentButtonItemOptionsArray extends Array<SegmentButtonItemOptions> { 134 changeStartIndex: number | undefined = void 0 135 deleteCount: number | undefined = void 0 136 addLength: number | undefined = void 0 137 138 constructor(length: number) 139 140 constructor(elements: SegmentButtonItemTuple) 141 142 constructor(elementsOrLength: SegmentButtonItemTuple | number) { 143 144 super(typeof elementsOrLength === "number" ? elementsOrLength : 0); 145 146 if (typeof elementsOrLength !== "number" && elementsOrLength !== void 0) { 147 super.push(...elementsOrLength.map((element?: SegmentButtonTextItem | SegmentButtonIconItem | SegmentButtonIconTextItem) => new SegmentButtonItemOptions(element as SegmentButtonItemOptionsConstructorOptions))) 148 } 149 } 150 151 push(...items: SegmentButtonItemArray): number { 152 if (this.length + items.length > MAX_ITEM_COUNT) { 153 console.warn("Exceeded the maximum number of elements (5).") 154 return this.length 155 } 156 this.changeStartIndex = this.length 157 this.deleteCount = 0 158 this.addLength = items.length 159 return super.push(...items.map((element: SegmentButtonItemOptionsConstructorOptions) => new SegmentButtonItemOptions(element))) 160 } 161 162 pop() { 163 if (this.length <= MIN_ITEM_COUNT) { 164 console.warn("Below the minimum number of elements (2).") 165 return void 0 166 } 167 this.changeStartIndex = this.length - 1 168 this.deleteCount = 1 169 this.addLength = 0 170 return super.pop() 171 } 172 173 shift() { 174 if (this.length <= MIN_ITEM_COUNT) { 175 console.warn("Below the minimum number of elements (2).") 176 return void 0 177 } 178 this.changeStartIndex = 0 179 this.deleteCount = 1 180 this.addLength = 0 181 return super.shift() 182 } 183 184 unshift(...items: SegmentButtonItemArray): number { 185 if (this.length + items.length > MAX_ITEM_COUNT) { 186 console.warn("Exceeded the maximum number of elements (5).") 187 return this.length 188 } 189 if (items.length > 0) { 190 this.changeStartIndex = 0 191 this.deleteCount = 0 192 this.addLength = items.length 193 } 194 return super.unshift(...items.map((element: SegmentButtonItemOptionsConstructorOptions) => new SegmentButtonItemOptions(element))) 195 } 196 197 splice(start: number, deleteCount: number, ...items: SegmentButtonItemOptions[]): SegmentButtonItemOptions[] { 198 let length = (this.length - deleteCount) < 0 ? 0 : (this.length - deleteCount) 199 length += items.length 200 if (length < MIN_ITEM_COUNT) { 201 console.warn("Below the minimum number of elements (2).") 202 return [] 203 } 204 if (length > MAX_ITEM_COUNT) { 205 console.warn("Exceeded the maximum number of elements (5).") 206 return [] 207 } 208 this.changeStartIndex = start 209 this.deleteCount = deleteCount 210 this.addLength = items.length 211 return super.splice(start, deleteCount, ...items) 212 } 213 214 static create(elements: SegmentButtonItemTuple): SegmentButtonItemOptionsArray { 215 return new SegmentButtonItemOptionsArray(elements) 216 } 217} 218 219@Observed 220export class SegmentButtonOptions { 221 type: "tab" | "capsule" 222 multiply: boolean = false 223 fontColor: ResourceColor 224 selectedFontColor: ResourceColor 225 fontSize: DimensionNoPercentage 226 selectedFontSize: DimensionNoPercentage 227 fontWeight: FontWeight 228 selectedFontWeight: FontWeight 229 backgroundColor: ResourceColor 230 selectedBackgroundColor: ResourceColor 231 imageSize: SizeOptions 232 buttonPadding: Padding | Dimension 233 textPadding: Padding | Dimension 234 componentPadding: Padding | Dimension 235 showText: boolean = false 236 showIcon: boolean = false 237 iconTextRadius?: number 238 iconTextBackgroundRadius?: number 239 backgroundBlurStyle: BlurStyle 240 private _buttons: SegmentButtonItemOptionsArray | undefined = void 0 241 242 get buttons() { 243 return this._buttons 244 } 245 246 set buttons(val) { 247 if (this._buttons !== void 0 && this._buttons !== val) { 248 this.onButtonsChange?.() 249 } 250 this._buttons = val 251 } 252 253 onButtonsChange?: () => void 254 255 constructor(options: TabSegmentButtonOptions | CapsuleSegmentButtonOptions) { 256 this.fontColor = options.fontColor ?? SegmentButtonTheme.FONT_COLOR 257 this.selectedFontColor = options.selectedFontColor ?? SegmentButtonTheme.TAB_SELECTED_FONT_COLOR 258 this.fontSize = options.fontSize ?? SegmentButtonTheme.FONT_SIZE 259 this.selectedFontSize = options.selectedFontSize ?? SegmentButtonTheme.SELECTED_FONT_SIZE 260 this.fontWeight = options.fontWeight ?? FontWeight.Regular 261 this.selectedFontWeight = options.selectedFontWeight ?? FontWeight.Medium 262 this.backgroundColor = options.backgroundColor ?? SegmentButtonTheme.BACKGROUND_COLOR 263 this.selectedBackgroundColor = options.selectedBackgroundColor ?? SegmentButtonTheme.TAB_SELECTED_BACKGROUND_COLOR 264 this.imageSize = options.imageSize ?? { width: 24, height: 24 } 265 this.buttonPadding = options.buttonPadding ?? { top: 4, right: 8, bottom: 4, left: 8 } 266 this.textPadding = options.textPadding ?? 0 267 this.type = options.type 268 this.backgroundBlurStyle = options.backgroundBlurStyle ?? SegmentButtonTheme.BACKGROUND_BLUR_STYLE 269 this.buttons = new SegmentButtonItemOptionsArray(options.buttons) 270 if (this.type === "capsule") { 271 this.multiply = (options as CapsuleSegmentButtonOptions).multiply ?? false 272 this.buttons.forEach(button => { 273 this.showText ||= button.text !== void 0 274 this.showIcon ||= button.icon !== void 0 || button.selectedIcon !== void 0 275 }) 276 if (this.showText && this.showIcon) { 277 this.iconTextRadius = 12 278 this.buttonPadding = options.buttonPadding ?? { top: 6, right: 8, bottom: 6, left: 8 } 279 this.iconTextBackgroundRadius = 14 280 } 281 this.selectedFontColor = options.selectedFontColor ?? SegmentButtonTheme.CAPSULE_SELECTED_FONT_COLOR 282 this.selectedBackgroundColor = options.selectedBackgroundColor ?? SegmentButtonTheme.CAPSULE_SELECTED_BACKGROUND_COLOR 283 } else { 284 this.showText = true 285 } 286 this.componentPadding = this.multiply ? 0 : 2 287 } 288 289 static tab(options: TabSegmentButtonConstructionOptions): SegmentButtonOptions { 290 return new SegmentButtonOptions({ 291 type: "tab", 292 buttons: options.buttons, 293 fontColor: options.fontColor, 294 selectedFontColor: options.selectedFontColor, 295 fontSize: options.fontSize, 296 selectedFontSize: options.selectedFontSize, 297 fontWeight: options.fontWeight, 298 selectedFontWeight: options.selectedFontWeight, 299 backgroundColor: options.backgroundColor, 300 selectedBackgroundColor: options.selectedBackgroundColor, 301 imageSize: options.imageSize, 302 buttonPadding: options.buttonPadding, 303 textPadding: options.textPadding, 304 backgroundBlurStyle: options.backgroundBlurStyle 305 }) 306 } 307 308 static capsule(options: CapsuleSegmentButtonConstructionOptions): SegmentButtonOptions { 309 return new SegmentButtonOptions({ 310 type: "capsule", 311 buttons: options.buttons, 312 multiply: options.multiply, 313 fontColor: options.fontColor, 314 selectedFontColor: options.selectedFontColor, 315 fontSize: options.fontSize, 316 selectedFontSize: options.selectedFontSize, 317 fontWeight: options.fontWeight, 318 selectedFontWeight: options.selectedFontWeight, 319 backgroundColor: options.backgroundColor, 320 selectedBackgroundColor: options.selectedBackgroundColor, 321 imageSize: options.imageSize, 322 buttonPadding: options.buttonPadding, 323 textPadding: options.textPadding, 324 backgroundBlurStyle: options.backgroundBlurStyle 325 }) 326 } 327} 328 329@Component 330struct MultiSelectBackground { 331 @ObjectLink optionsArray: SegmentButtonItemOptionsArray 332 @ObjectLink options: SegmentButtonOptions 333 @Consume buttonBorderRadius: BorderRadiuses[] 334 @Consume buttonItemsSize: SizeOptions[] 335 336 build() { 337 Row({ space: 1 }) { 338 ForEach(this.optionsArray, (_: SegmentButtonItemOptions, index) => { 339 if (index < MAX_ITEM_COUNT) { 340 Stack() 341 .layoutWeight(1) 342 .height(this.buttonItemsSize[index].height) 343 .backgroundColor(this.options.backgroundColor ?? SegmentButtonTheme.BACKGROUND_COLOR) 344 .borderRadius(this.buttonBorderRadius[index]) 345 .backgroundBlurStyle(this.options.backgroundBlurStyle) 346 } 347 }) 348 } 349 .padding(this.options.componentPadding) 350 } 351} 352 353@Component 354struct SelectItem { 355 @ObjectLink optionsArray: SegmentButtonItemOptionsArray 356 @ObjectLink options: SegmentButtonOptions 357 @Link selectedIndexes: number[] 358 @Consume buttonItemsSize: SizeOptions[] 359 @Consume selectedItemPosition: Position 360 @Consume zoomScaleArray: number[] 361 @Consume buttonBorderRadius: BorderRadiuses[] 362 363 build() { 364 if (this.selectedIndexes !== void 0 && this.selectedIndexes.length !== 0) { 365 Stack() 366 .borderRadius(this.buttonBorderRadius[this.selectedIndexes[0]]) 367 .size(this.buttonItemsSize[this.selectedIndexes[0]]) 368 .backgroundColor(this.options.selectedBackgroundColor ?? 369 (this.options.type === 'tab' ? SegmentButtonTheme.TAB_SELECTED_BACKGROUND_COLOR : 370 SegmentButtonTheme.CAPSULE_SELECTED_BACKGROUND_COLOR)) 371 .position(this.selectedItemPosition) 372 .scale({ x: this.zoomScaleArray[this.selectedIndexes[0]], y: this.zoomScaleArray[this.selectedIndexes[0]] }) 373 .shadow(ShadowStyle.OUTER_DEFAULT_MD) 374 } 375 } 376} 377 378@Component 379struct MultiSelectItemArray { 380 @ObjectLink optionsArray: SegmentButtonItemOptionsArray 381 @ObjectLink @Watch('onOptionsChange') options: SegmentButtonOptions 382 @Link @Watch('onSelectedChange') selectedIndexes: number[] 383 @Consume buttonItemsSize: SizeOptions[] 384 @Consume zoomScaleArray: number[] 385 @Consume buttonBorderRadius: BorderRadiuses[] 386 @State multiColor: ResourceColor[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => Color.Transparent) 387 388 onOptionsChange() { 389 for (let i = 0; i < this.selectedIndexes.length; i++) { 390 this.multiColor[this.selectedIndexes[i]] = this.options.selectedBackgroundColor ?? SegmentButtonTheme.CAPSULE_SELECTED_BACKGROUND_COLOR 391 } 392 } 393 394 onSelectedChange() { 395 for (let i = 0; i < MAX_ITEM_COUNT; i++) { 396 this.multiColor[i] = Color.Transparent 397 } 398 for (let i = 0; i < this.selectedIndexes.length; i++) { 399 this.multiColor[this.selectedIndexes[i]] = this.options.selectedBackgroundColor ?? SegmentButtonTheme.CAPSULE_SELECTED_BACKGROUND_COLOR 400 } 401 } 402 403 aboutToAppear() { 404 for (let i = 0; i < this.selectedIndexes.length; i++) { 405 this.multiColor[this.selectedIndexes[i]] = this.options.selectedBackgroundColor ?? SegmentButtonTheme.CAPSULE_SELECTED_BACKGROUND_COLOR 406 } 407 } 408 409 build() { 410 Row({ space: 1 }) { 411 ForEach(this.optionsArray, (_: SegmentButtonItemOptions, index) => { 412 if (index < MAX_ITEM_COUNT) { 413 Stack() 414 .width(this.buttonItemsSize[index].width) 415 .height(this.buttonItemsSize[index].height) 416 .backgroundColor(this.multiColor[index]) 417 .borderRadius(this.buttonBorderRadius[index]) 418 } 419 }) 420 } 421 .padding(this.options.componentPadding) 422 } 423} 424 425@Component 426struct SegmentButtonItem { 427 @ObjectLink itemOptions: SegmentButtonItemOptions 428 @ObjectLink options: SegmentButtonOptions; 429 @ObjectLink property: ItemProperty 430 @Prop index: number 431 432 build() { 433 Column({ space: 2 }) { 434 if (this.options.showIcon) { 435 Image(this.property.isSelected ? this.itemOptions.selectedIcon : this.itemOptions.icon) 436 .size(this.options.imageSize ?? { width: 24, height: 24 }) 437 .focusable(this.index == 0) 438 .draggable(false) 439 .fillColor(this.property.isSelected ? (this.options.selectedFontColor ?? SegmentButtonTheme.CAPSULE_SELECTED_FONT_COLOR) : 440 (this.options.fontColor ?? SegmentButtonTheme.FONT_COLOR)) 441 } 442 if (this.options.showText) { 443 Text(this.itemOptions.text) 444 .fontColor(this.property.fontColor) 445 .fontWeight(this.property.fontWeight) 446 .fontSize(this.property.fontSize) 447 .minFontSize(9) 448 .maxFontSize(this.property.fontSize) 449 .textOverflow({ overflow: TextOverflow.Ellipsis }) 450 .maxLines(1) 451 .textAlign(TextAlign.Center) 452 .focusable(this.index == 0 && !this.options.showIcon) 453 .padding(this.options.textPadding ?? 0) 454 } 455 } 456 .justifyContent(FlexAlign.Center) 457 .padding(this.options.buttonPadding ?? ((this.options.type === 'capsule' && this.options.showText && this.options.showIcon) ? 458 { top: 6, right: 8, bottom: 6, left: 8 } : { top: 4, right: 8, bottom: 4, left: 8 })) 459 .constraintSize({ minHeight: 28 }) 460 } 461} 462 463@Observed 464class HoverColorProperty { 465 hoverColor: ResourceColor = Color.Transparent 466} 467 468@Component 469struct PressAndHoverEffect { 470 @Consume buttonItemsSize: SizeOptions[] 471 @Prop press: boolean 472 @ObjectLink colorProperty: HoverColorProperty 473 @Consume buttonBorderRadius: BorderRadiuses[] 474 pressIndex: number = 0 475 pressColor: ResourceColor = SegmentButtonTheme.PRESS_COLOR 476 477 build() { 478 Stack() 479 .size(this.buttonItemsSize[this.pressIndex]) 480 .backgroundColor(this.press ? this.pressColor : this.colorProperty.hoverColor) 481 .borderRadius(this.buttonBorderRadius[this.pressIndex]) 482 } 483} 484 485@Component 486struct SegmentButtonItemArrayComponent { 487 @ObjectLink @Watch('onOptionsArrayChange') optionsArray: SegmentButtonItemOptionsArray 488 @ObjectLink @Watch('onOptionsChange') options: SegmentButtonOptions 489 @Link selectedIndexes: number[] 490 @Consume componentSize: SizeOptions 491 @Consume buttonBorderRadius: BorderRadiuses[] 492 @Consume @Watch('onButtonItemsSizeChange') buttonItemsSize: SizeOptions[] 493 @Consume buttonItemsPosition: Position[] 494 @Consume focusIndex: number 495 @Consume zoomScaleArray: number[] 496 @Consume buttonItemProperty: ItemProperty[] 497 @Consume buttonItemsSelected: boolean[] 498 @State pressArray: boolean[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => false) 499 @State hoverColorArray: HoverColorProperty[] = Array.from({ 500 length: MAX_ITEM_COUNT 501 }, (_: Object, index) => new HoverColorProperty()) 502 @State buttonWidth: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 0) 503 @State buttonHeight: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 0) 504 private buttonItemsRealHeight: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 0) 505 506 onButtonItemsSizeChange() { 507 this.buttonItemsSize.forEach((value, index) => { 508 this.buttonWidth[index] = value.width as number 509 this.buttonHeight[index] = value.height as number 510 }) 511 } 512 513 changeSelectedIndexes(buttonsLength: number) { 514 if (this.optionsArray.changeStartIndex === void 0 || this.optionsArray.deleteCount === void 0 || this.optionsArray.addLength === void 0) { 515 return 516 } 517 if (!(this.options.multiply ?? false)) { 518 // Single-select 519 if (this.selectedIndexes[0] === void 0) { 520 return 521 } 522 if (this.selectedIndexes[0] < this.optionsArray.changeStartIndex) { 523 return 524 } 525 if (this.optionsArray.changeStartIndex + this.optionsArray.deleteCount > this.selectedIndexes[0]) { 526 if (this.options.type === 'tab') { 527 this.selectedIndexes[0] = 0 528 } else if (this.options.type === 'capsule') { 529 this.selectedIndexes = [] 530 } 531 } else { 532 this.selectedIndexes[0] = this.selectedIndexes[0] - this.optionsArray.deleteCount + this.optionsArray.addLength 533 } 534 } else { 535 // Multi-select 536 let saveIndexes = this.selectedIndexes 537 for (let i = 0; i < this.optionsArray.deleteCount; i++) { 538 let deleteIndex = saveIndexes.indexOf(this.optionsArray.changeStartIndex) 539 let indexes = saveIndexes.map(value => this.optionsArray.changeStartIndex && (value > this.optionsArray.changeStartIndex) ? value - 1 : value) 540 if (deleteIndex !== -1) { 541 indexes.splice(deleteIndex, 1) 542 } 543 saveIndexes = indexes 544 } 545 for (let i = 0; i < this.optionsArray.addLength; i++) { 546 let indexes = saveIndexes.map(value => this.optionsArray.changeStartIndex && (value >= this.optionsArray.changeStartIndex) ? value + 1 : value) 547 saveIndexes = indexes 548 } 549 this.selectedIndexes = saveIndexes 550 } 551 552 } 553 554 changeFocusIndex(buttonsLength: number) { 555 if (this.optionsArray.changeStartIndex === void 0 || this.optionsArray.deleteCount === void 0 || this.optionsArray.addLength === void 0) { 556 return 557 } 558 if (this.focusIndex === -1) { 559 return 560 } 561 if (this.focusIndex < this.optionsArray.changeStartIndex) { 562 return 563 } 564 if (this.optionsArray.changeStartIndex + this.optionsArray.deleteCount > this.focusIndex) { 565 this.focusIndex = 0 566 } else { 567 this.focusIndex = this.focusIndex - this.optionsArray.deleteCount + this.optionsArray.addLength 568 } 569 570 } 571 572 onOptionsArrayChange() { 573 if (this.options === void 0 || this.options.buttons === void 0) { 574 return 575 } 576 let buttonsLength = Math.min(this.options.buttons.length, this.buttonItemsSize.length) 577 if (this.optionsArray.changeStartIndex !== void 0 && this.optionsArray.deleteCount !== void 0 && this.optionsArray.addLength !== void 0) { 578 this.changeSelectedIndexes(buttonsLength) 579 this.changeFocusIndex(buttonsLength) 580 this.optionsArray.changeStartIndex = void 0 581 this.optionsArray.deleteCount = void 0 582 this.optionsArray.addLength = void 0 583 } 584 } 585 586 onOptionsChange() { 587 if (this.options === void 0 || this.options.buttons === void 0) { 588 return 589 } 590 this.calculateBorderRadius() 591 } 592 593 aboutToAppear() { 594 for (let index = 0; index < this.buttonItemsRealHeight.length; index++) { 595 this.buttonItemsRealHeight[index] = 0 596 } 597 } 598 599 @Builder 600 focusStack(index: number) { 601 Stack() { 602 if (index === this.focusIndex) { 603 Stack() 604 .borderRadius({ 605 topLeft: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonBorderRadius[index].topLeft as number + 4 : this.buttonBorderRadius[index].topLeft, 606 topRight: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonBorderRadius[index].topRight as number + 4 : this.buttonBorderRadius[index].topRight, 607 bottomLeft: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonBorderRadius[index].bottomLeft as number + 4 : this.buttonBorderRadius[index].bottomLeft, 608 bottomRight: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonBorderRadius[index].bottomRight as number + 4 : this.buttonBorderRadius[index].bottomRight 609 }) 610 .size({ 611 width: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonWidth[index] + 8 : this.buttonWidth[index], 612 height: this.options.type === 'capsule' && this.buttonItemsSelected[this.focusIndex] ? this.buttonHeight[index] + 8 : this.buttonHeight[index] 613 }) 614 .borderColor(SegmentButtonTheme.FOCUS_BORDER_COLOR) 615 .borderWidth(2) 616 } 617 } 618 .size({ width: 1, height: 1 }) 619 .align(Alignment.Center) 620 } 621 622 calculateBorderRadius() { 623 let borderRadiusArray: BorderRadiuses[] = Array.from({ 624 length: MAX_ITEM_COUNT 625 }, (_: Object, index): BorderRadiuses => { 626 return { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0 } 627 }) 628 for (let index = 0; index < this.buttonBorderRadius.length; index++) { 629 let halfButtonItemsSizeHeight = this.buttonItemsSize[index].height as number / 2 630 if (this.options.type === 'tab' || !(this.options.multiply ?? false)) { 631 borderRadiusArray[index].topLeft = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 632 borderRadiusArray[index].topRight = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 633 borderRadiusArray[index].bottomLeft = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 634 borderRadiusArray[index].bottomRight = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 635 } else { 636 if (index === 0) { 637 borderRadiusArray[index].topLeft = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 638 borderRadiusArray[index].topRight = 0 639 borderRadiusArray[index].bottomLeft = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 640 borderRadiusArray[index].bottomRight = 0 641 } else if (this.options.buttons && index === Math.min(this.options.buttons.length, this.buttonItemsSize.length) - 1) { 642 borderRadiusArray[index].topLeft = 0 643 borderRadiusArray[index].topRight = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 644 borderRadiusArray[index].bottomLeft = 0 645 borderRadiusArray[index].bottomRight = this.options.iconTextRadius ?? halfButtonItemsSizeHeight 646 } else { 647 borderRadiusArray[index].topLeft = 0 648 borderRadiusArray[index].topRight = 0 649 borderRadiusArray[index].bottomLeft = 0 650 borderRadiusArray[index].bottomRight = 0 651 } 652 } 653 } 654 this.buttonBorderRadius = borderRadiusArray 655 } 656 657 build() { 658 if (this.optionsArray !== void 0 && this.optionsArray.length > 1) { 659 Row({ space: 1 }) { 660 ForEach(this.optionsArray, (item: SegmentButtonItemOptions, index) => { 661 if (index < MAX_ITEM_COUNT) { 662 Stack() { 663 PressAndHoverEffect({ 664 pressIndex: index, 665 colorProperty: this.hoverColorArray[index], 666 press: this.pressArray[index] 667 }) 668 SegmentButtonItem({ 669 index: index, 670 itemOptions: item, 671 options: this.options, 672 property: this.buttonItemProperty[index] 673 }) 674 .onAreaChange((_, newValue) => { 675 // Calculate height of items 676 this.buttonItemsRealHeight[index] = newValue.height as number 677 let maxHeight = Math.max(...this.buttonItemsRealHeight.slice(0, this.options.buttons ? this.options.buttons.length : 0)) 678 for (let index = 0; index < this.buttonItemsSize.length; index++) { 679 this.buttonItemsSize[index] = { width: this.buttonItemsSize[index].width, height: maxHeight } 680 } 681 this.calculateBorderRadius() 682 }) 683 } 684 .borderRadius(this.buttonBorderRadius[index]) 685 .scale({ 686 x: this.options.type === 'capsule' && (this.options.multiply ?? false) ? 1 : this.zoomScaleArray[index], 687 y: this.options.type === 'capsule' && (this.options.multiply ?? false) ? 1 : this.zoomScaleArray[index] 688 }) 689 .layoutWeight(1) 690 .onAreaChange((_, newValue) => { 691 this.buttonItemsSize[index] = { width: newValue.width, height: this.buttonItemsSize[index].height } 692 this.buttonItemsPosition[index] = newValue.position 693 }) 694 .overlay(this.focusStack(index), { 695 align: Alignment.Center 696 }) 697 .onTouch((event: TouchEvent) => { 698 if (event.source !== SourceType.TouchScreen) { 699 return 700 } 701 if (event.type === TouchType.Down) { 702 animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => { 703 this.zoomScaleArray[index] = 0.95 704 }) 705 } else if (event.type === TouchType.Up) { 706 animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => { 707 this.zoomScaleArray[index] = 1 708 }) 709 } 710 }) 711 .onHover((isHover: boolean) => { 712 if (isHover) { 713 animateTo({ duration: 250, curve: Curve.Friction }, () => { 714 this.hoverColorArray[index].hoverColor = (SegmentButtonTheme.HOVER_COlOR) 715 }) 716 } else { 717 animateTo({ duration: 250, curve: Curve.Friction }, () => { 718 this.hoverColorArray[index].hoverColor = Color.Transparent 719 }) 720 } 721 }) 722 .onMouse((event: MouseEvent) => { 723 switch (event.action) { 724 case MouseAction.Press: 725 animateTo({ curve: curves.springMotion(0.347, 0.99) }, () => { 726 this.zoomScaleArray[index] = 0.95 727 }) 728 animateTo({ duration: 100, curve: Curve.Sharp }, () => { 729 this.pressArray[index] = true 730 }) 731 break; 732 case MouseAction.Release: 733 animateTo({ curve: curves.springMotion(0.347, 0.99) }, () => { 734 this.zoomScaleArray[index] = 1 735 }) 736 animateTo({ duration: 100, curve: Curve.Sharp }, () => { 737 this.pressArray[index] = false 738 }) 739 break; 740 } 741 }) 742 } 743 }) 744 } 745 .padding(this.options.componentPadding) 746 .onAreaChange((_, newValue) => { 747 this.componentSize = { width: newValue.width, height: newValue.height } 748 }) 749 } 750 } 751} 752 753@Observed 754class ItemProperty { 755 fontColor: ResourceColor = SegmentButtonTheme.FONT_COLOR 756 fontSize: DimensionNoPercentage = SegmentButtonTheme.FONT_SIZE 757 fontWeight: FontWeight = FontWeight.Regular 758 isSelected: boolean = false 759} 760 761@Component 762export struct SegmentButton { 763 @ObjectLink @Watch('onOptionsChange') options: SegmentButtonOptions 764 @Link @Watch('onSelectedChange') selectedIndexes: number[] 765 @Provide componentSize: SizeOptions = { width: 0, height: 0 } 766 @Provide buttonBorderRadius: BorderRadiuses[] = Array.from({ 767 length: MAX_ITEM_COUNT 768 }, (_: Object, index): BorderRadiuses => { 769 return { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0 } 770 }) 771 @Provide buttonItemsSize: SizeOptions[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index): SizeOptions => { 772 return {} 773 }) 774 @Provide @Watch('onItemsPositionChange') buttonItemsPosition: Position[] = Array.from({ 775 length: MAX_ITEM_COUNT 776 }, (_: Object, index): Position => { 777 return {} 778 }) 779 @Provide buttonItemsSelected: boolean[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => false) 780 @Provide buttonItemProperty: ItemProperty[] = Array.from({ 781 length: MAX_ITEM_COUNT 782 }, (_: Object, index) => new ItemProperty()) 783 @Provide focusIndex: number = -1 784 @Provide selectedItemPosition: Position = {} 785 @Provide zoomScaleArray: number[] = Array.from({ length: MAX_ITEM_COUNT }, (_: Object, index) => 1.0) 786 private doSelectedChangeAnimate: boolean = false 787 private isCurrentPositionSelected: boolean = false 788 private panGestureStartPoint: Point = { x: 0, y: 0 } 789 private isPanGestureMoved: boolean = false 790 791 onItemsPositionChange() { 792 if (this.options === void 0 || this.options.buttons === void 0) { 793 return 794 } 795 if (this.doSelectedChangeAnimate) { 796 this.updateAnimatedProperty(this.getSelectedChangeCurve()) 797 } else { 798 this.updateAnimatedProperty(null) 799 } 800 } 801 802 setItemsSelected() { 803 this.buttonItemsSelected.forEach((_, index) => { 804 this.buttonItemsSelected[index] = false 805 }) 806 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 807 this.selectedIndexes.forEach(index => this.buttonItemsSelected[index] = true) 808 } else { 809 this.buttonItemsSelected[this.selectedIndexes[0]] = true 810 } 811 } 812 813 updateSelectedIndexes() { 814 if (this.selectedIndexes === void 0) { 815 this.selectedIndexes = [] 816 } 817 if (this.options.type === 'tab' && this.selectedIndexes.length === 0) { 818 this.selectedIndexes[0] = 0 819 } 820 if (this.selectedIndexes.length > 1) { 821 if (this.options.type === 'tab') { 822 this.selectedIndexes = [0] 823 } 824 if (this.options.type === 'capsule' && !(this.options.multiply ?? false)) { 825 this.selectedIndexes = [] 826 } 827 } 828 let invalid = this.selectedIndexes.some(index => { 829 return (index === void 0 || index < 0 || (this.options.buttons && index >= this.options.buttons.length)) 830 }) 831 if (invalid) { 832 if (this.options.type === 'tab') { 833 this.selectedIndexes = [0] 834 } else { 835 this.selectedIndexes = [] 836 } 837 } 838 } 839 840 onOptionsChange() { 841 if (this.options === void 0 || this.options.buttons === void 0) { 842 return 843 } 844 this.updateSelectedIndexes() 845 this.setItemsSelected() 846 this.updateAnimatedProperty(null) 847 } 848 849 onSelectedChange() { 850 if (this.options === void 0 || this.options.buttons === void 0) { 851 return 852 } 853 this.updateSelectedIndexes() 854 this.setItemsSelected() 855 if (this.doSelectedChangeAnimate) { 856 this.updateAnimatedProperty(this.getSelectedChangeCurve()) 857 } else { 858 this.updateAnimatedProperty(null) 859 } 860 } 861 862 aboutToAppear() { 863 if (this.options === void 0 || this.options.buttons === void 0) { 864 return 865 } 866 this.options.onButtonsChange = () => { 867 if (this.options.type === 'tab') { 868 this.selectedIndexes = [0] 869 } else { 870 this.selectedIndexes = [] 871 } 872 } 873 this.updateSelectedIndexes() 874 this.setItemsSelected() 875 this.updateAnimatedProperty(null) 876 } 877 878 private isMouseWheelScroll(event: GestureEvent) { 879 return event.source === SourceType.Mouse && !this.isPanGestureMoved 880 } 881 882 private isMovedFromPanGestureStartPoint(x: number, y: number) { 883 return!nearEqual(x, this.panGestureStartPoint.x) || !nearEqual(y, this.panGestureStartPoint.y) 884 } 885 886 build() { 887 Stack() { 888 if (this.options !== void 0 && this.options.buttons != void 0) { 889 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 890 MultiSelectBackground({ 891 optionsArray: this.options.buttons, 892 options: this.options, 893 }) 894 } else { 895 Stack() 896 .size(this.componentSize) 897 .backgroundColor(this.options.backgroundColor ?? SegmentButtonTheme.BACKGROUND_COLOR) 898 .borderRadius(this.options.iconTextBackgroundRadius ?? this.componentSize.height as number / 2) 899 .backgroundBlurStyle(this.options.backgroundBlurStyle) 900 } 901 Stack() { 902 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 903 MultiSelectItemArray({ 904 optionsArray: this.options.buttons, 905 options: this.options, 906 selectedIndexes: $selectedIndexes 907 }) 908 } else { 909 SelectItem({ 910 optionsArray: this.options.buttons, 911 options: this.options, 912 selectedIndexes: $selectedIndexes 913 }) 914 } 915 } 916 .size(this.componentSize) 917 .borderRadius((this.options.type === 'capsule' && (this.options.multiply ?? false) ? 918 this.options.iconTextRadius : this.options.iconTextBackgroundRadius) ?? 919 this.componentSize.height as number / 2) 920 .clip(true) 921 922 SegmentButtonItemArrayComponent({ 923 optionsArray: this.options.buttons, 924 options: this.options, 925 selectedIndexes: $selectedIndexes, 926 }) 927 } 928 } 929 .onFocus(() => { 930 if (this.options === void 0) { 931 return 932 } 933 if (this.selectedIndexes === void 0 || this.selectedIndexes.length === 0) { 934 this.focusIndex = 0 935 return 936 } 937 if (this.options.type === 'tab' || !(this.options.multiply ?? false)) { 938 this.focusIndex = this.selectedIndexes[0] 939 } else { 940 this.focusIndex = Math.min(...this.selectedIndexes) 941 } 942 }) 943 .onBlur(() => { 944 this.focusIndex = -1 945 }) 946 .onKeyEvent((event: KeyEvent) => { 947 if (this.options === void 0 || this.options.buttons === void 0) { 948 return 949 } 950 if (event.type === KeyType.Down) { 951 if ((event.keyCode === KeyCode.KEYCODE_DPAD_DOWN || event.keyCode === KeyCode.KEYCODE_DPAD_RIGHT) && this.focusIndex < (Math.min(this.options.buttons.length, this.buttonItemsSize.length) - 1)) { 952 // Move to next 953 this.focusIndex = this.focusIndex + 1 954 } 955 if ((event.keyCode === KeyCode.KEYCODE_DPAD_UP || event.keyCode === KeyCode.KEYCODE_DPAD_LEFT) && this.focusIndex > 0) { 956 // Move to previous 957 this.focusIndex = this.focusIndex - 1 958 } 959 if (event.keyCode === KeyCode.KEYCODE_SPACE || event.keyCode === KeyCode.KEYCODE_ENTER || event.keyCode === KeyCode.KEYCODE_NUMPAD_ENTER) { 960 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 961 if (this.selectedIndexes.indexOf(this.focusIndex) === -1) { 962 // Select 963 this.selectedIndexes.push(this.focusIndex) 964 } else { 965 // Unselect 966 this.selectedIndexes.splice(this.selectedIndexes.indexOf(this.focusIndex), 1) 967 } 968 } else { 969 // Pressed 970 this.selectedIndexes[0] = this.focusIndex 971 } 972 } 973 } 974 }) 975 .gesture( 976 GestureGroup(GestureMode.Parallel, 977 TapGesture() 978 .onAction((event: GestureEvent) => { 979 let fingerInfo = event.fingerList.find(Boolean) 980 if (fingerInfo === void 0) { 981 return 982 } 983 if (this.options === void 0 || this.options.buttons === void 0) { 984 return 985 } 986 let selectedInfo = fingerInfo.localX 987 for (let i = 0; i < Math.min(this.options.buttons.length, this.buttonItemsSize.length); i++) { 988 selectedInfo = selectedInfo - (this.buttonItemsSize[i].width as number) 989 if (selectedInfo >= 0) { 990 continue 991 } 992 this.doSelectedChangeAnimate = 993 this.selectedIndexes[0] > Math.min(this.options.buttons.length, this.buttonItemsSize.length) ? false : true 994 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 995 if (this.selectedIndexes.indexOf(i) === -1) { 996 this.selectedIndexes.push(i) 997 } else { 998 this.selectedIndexes.splice(this.selectedIndexes.indexOf(i), 1) 999 } 1000 } else { 1001 this.selectedIndexes[0] = i 1002 } 1003 this.doSelectedChangeAnimate = false 1004 break 1005 } 1006 }), 1007 SwipeGesture() 1008 .onAction((event: GestureEvent) => { 1009 if (this.options === void 0 || this.options.buttons === void 0) { 1010 return 1011 } 1012 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 1013 // Non swipe gesture in multi-select mode 1014 return 1015 } 1016 if (this.isCurrentPositionSelected) { 1017 return 1018 } 1019 if (event.angle > 0 && this.selectedIndexes[0] !== Math.min(this.options.buttons.length, this.buttonItemsSize.length) - 1) { 1020 // Move to next 1021 this.doSelectedChangeAnimate = true 1022 this.selectedIndexes[0] = this.selectedIndexes[0] + 1 1023 this.doSelectedChangeAnimate = false 1024 } else if (event.angle < 0 && this.selectedIndexes[0] !== 0) { 1025 // Move to previous 1026 this.doSelectedChangeAnimate = true 1027 this.selectedIndexes[0] = this.selectedIndexes[0] - 1 1028 this.doSelectedChangeAnimate = false 1029 } 1030 }), 1031 PanGesture() 1032 .onActionStart((event: GestureEvent) => { 1033 if (this.options === void 0 || this.options.buttons === void 0) { 1034 return 1035 } 1036 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 1037 // Non drag gesture in multi-select mode 1038 return 1039 } 1040 let fingerInfo = event.fingerList.find(Boolean) 1041 if (fingerInfo === void 0) { 1042 return 1043 } 1044 let selectedInfo = fingerInfo.localX 1045 this.panGestureStartPoint = { x: fingerInfo.globalX, y: fingerInfo.globalY } 1046 this.isPanGestureMoved = false 1047 for (let i = 0; i < Math.min(this.options.buttons.length, this.buttonItemsSize.length); i++) { 1048 selectedInfo = selectedInfo - (this.buttonItemsSize[i].width as number) 1049 if (selectedInfo < 0) { 1050 this.isCurrentPositionSelected = i === this.selectedIndexes[0] ? true : false 1051 break 1052 } 1053 } 1054 }) 1055 .onActionUpdate((event: GestureEvent) => { 1056 if (this.options === void 0 || this.options.buttons === void 0) { 1057 return 1058 } 1059 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 1060 // Non drag gesture in multi-select mode 1061 return 1062 } 1063 if (!this.isCurrentPositionSelected) { 1064 return 1065 } 1066 let fingerInfo = event.fingerList.find(Boolean) 1067 if (fingerInfo === void 0) { 1068 return 1069 } 1070 let selectedInfo = fingerInfo.localX 1071 if (!this.isPanGestureMoved && this.isMovedFromPanGestureStartPoint(fingerInfo.globalX, fingerInfo.globalY)) { 1072 this.isPanGestureMoved = true 1073 } 1074 for (let i = 0; i < Math.min(this.options.buttons.length, this.buttonItemsSize.length); i++) { 1075 selectedInfo = selectedInfo - (this.buttonItemsSize[i].width as number) 1076 if (selectedInfo < 0) { 1077 this.doSelectedChangeAnimate = true 1078 this.selectedIndexes[0] = i 1079 this.doSelectedChangeAnimate = false 1080 break 1081 } 1082 } 1083 this.zoomScaleArray.forEach((_, index) => { 1084 if (index === this.selectedIndexes[0]) { 1085 animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => { 1086 this.zoomScaleArray[index] = 0.95 1087 }) 1088 } else { 1089 animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => { 1090 this.zoomScaleArray[index] = 1 1091 }) 1092 } 1093 }) 1094 }) 1095 .onActionEnd((event: GestureEvent) => { 1096 if (this.options === void 0 || this.options.buttons === void 0) { 1097 return 1098 } 1099 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 1100 // Non drag gesture in multi-select mode 1101 return 1102 } 1103 let fingerInfo = event.fingerList.find(Boolean) 1104 if (fingerInfo === void 0) { 1105 return 1106 } 1107 if (!this.isPanGestureMoved && this.isMovedFromPanGestureStartPoint(fingerInfo.globalX, fingerInfo.globalY)) { 1108 this.isPanGestureMoved = true 1109 } 1110 if (this.isMouseWheelScroll(event)) { 1111 let offset = event.offsetX !== 0 ? event.offsetX : event.offsetY 1112 this.doSelectedChangeAnimate = true 1113 if (offset > 0 && this.selectedIndexes[0] > 0) { 1114 this.selectedIndexes[0] -= 1 1115 } else if (offset < 0 && this.selectedIndexes[0] < Math.min(this.options.buttons.length, this.buttonItemsSize.length) - 1) { 1116 this.selectedIndexes[0] += 1 1117 } 1118 this.doSelectedChangeAnimate = false 1119 } 1120 animateTo({ curve: curves.interpolatingSpring(10, 1, 410, 38) }, () => { 1121 this.zoomScaleArray[this.selectedIndexes[0]] = 1 1122 }) 1123 this.isCurrentPositionSelected = false 1124 }) 1125 ) 1126 ) 1127 } 1128 1129 getSelectedChangeCurve(): ICurve | null { 1130 if (this.options.type === 'capsule' && (this.options.multiply ?? false)) { 1131 return null 1132 } 1133 return curves.springMotion(0.347, 0.99) 1134 } 1135 1136 updateAnimatedProperty(curve: ICurve | null) { 1137 let setAnimatedPropertyFunc = () => { 1138 this.selectedItemPosition = this.selectedIndexes.length === 0 ? { 1139 } : this.buttonItemsPosition[this.selectedIndexes[0]] 1140 this.buttonItemsSelected.forEach((selected, index) => { 1141 this.buttonItemProperty[index].fontColor = selected ? 1142 this.options.selectedFontColor ?? (this.options.type === 'tab' ? 1143 SegmentButtonTheme.TAB_SELECTED_FONT_COLOR : SegmentButtonTheme.CAPSULE_SELECTED_FONT_COLOR) : 1144 this.options.fontColor ?? SegmentButtonTheme.FONT_COLOR 1145 }) 1146 } 1147 if (curve) { 1148 animateTo({ curve: curve }, setAnimatedPropertyFunc) 1149 } else { 1150 setAnimatedPropertyFunc() 1151 } 1152 this.buttonItemsSelected.forEach((selected, index) => { 1153 this.buttonItemProperty[index].fontSize = selected ? this.options.selectedFontSize ?? SegmentButtonTheme.SELECTED_FONT_SIZE 1154 : this.options.fontSize ?? SegmentButtonTheme.FONT_SIZE 1155 this.buttonItemProperty[index].fontWeight = selected ? this.options.selectedFontWeight ?? FontWeight.Medium : 1156 this.options.fontWeight ?? FontWeight.Regular 1157 this.buttonItemProperty[index].isSelected = selected 1158 }) 1159 } 1160}