• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}