1/*
2 * Copyright (c) 2023-2024 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 display from '@ohos.display';
17import mediaquery from '@ohos.mediaquery';
18import { Theme } from '@ohos.arkui.theme';
19import { LengthMetrics } from '@ohos.arkui.node'
20
21interface IconTheme {
22  size: SizeOptions;
23  margin: LocalizedMargin;
24  fillColor: ResourceColor;
25  borderRadius: Length;
26}
27
28interface TitleTheme {
29  margin: LocalizedMargin;
30  minFontSize: number;
31  fontWeight: FontWeight;
32  fontSize: Resource;
33  fontColor: ResourceColor;
34}
35
36interface ButtonTheme {
37  margin: LocalizedMargin;
38  padding: LocalizedPadding;
39  fontSize: Resource;
40  fontColor: ResourceColor;
41  textMargin: LocalizedMargin;
42  minFontSize: number;
43  fontWeight: FontWeight;
44  hoverColor: ResourceColor;
45  backgroundColor: ResourceColor;
46}
47
48interface MessageTheme {
49  fontSize: Resource;
50  fontColor: ResourceColor;
51  fontWeight: FontWeight;
52  plainFontColor: ResourceColor;
53}
54
55interface CloseButtonTheme {
56  size: SizeOptions;
57  image: ResourceStr;
58  imageSize: SizeOptions;
59  margin: LocalizedMargin;
60  padding: LocalizedPadding;
61  fillColor: ResourceColor;
62  hoverColor: ResourceColor;
63  backgroundColor: ResourceColor;
64}
65
66interface WindowsTheme {
67  padding: LocalizedPadding;
68}
69
70interface PopupTheme {
71  icon: IconTheme;
72  title: TitleTheme;
73  button: ButtonTheme;
74  message: MessageTheme;
75  windows: WindowsTheme;
76  closeButton: CloseButtonTheme;
77}
78
79export const defaultTheme: PopupTheme = {
80  icon: {
81    size: { width: 32, height: 32 },
82    margin: {
83      top: LengthMetrics.vp(12),
84      bottom: LengthMetrics.vp(12),
85      start: LengthMetrics.vp(12),
86      end: LengthMetrics.vp(12)
87    },
88    fillColor: '',
89    borderRadius: $r('sys.float.ohos_id_corner_radius_default_s')
90
91  },
92  title: {
93    margin: { bottom: LengthMetrics.vp(2) },
94    minFontSize: 12,
95    fontWeight: FontWeight.Medium,
96    fontSize: $r('sys.float.ohos_id_text_size_sub_title2'),
97    fontColor: $r('sys.color.font_primary')
98  },
99  button: {
100    margin: {
101      top: LengthMetrics.vp(16),
102      bottom: LengthMetrics.vp(16),
103      start: LengthMetrics.vp(16),
104      end: LengthMetrics.vp(16)
105    },
106    padding: {
107      top: LengthMetrics.vp(4),
108      bottom: LengthMetrics.vp(4),
109      start: LengthMetrics.vp(4),
110      end: LengthMetrics.vp(4)
111    },
112    fontSize: $r('sys.float.ohos_id_text_size_button2'),
113    fontColor: $r('sys.color.font_emphasize'),
114    textMargin: {
115      top: LengthMetrics.vp(8),
116      bottom: LengthMetrics.vp(8),
117      start: LengthMetrics.vp(8),
118      end: LengthMetrics.vp(8)
119    },
120    minFontSize: 9,
121    fontWeight: FontWeight.Medium,
122    hoverColor: $r('sys.color.ohos_id_color_hover'),
123    backgroundColor: $r('sys.color.ohos_id_color_background_transparent')
124  },
125  message: {
126    fontSize: $r('sys.float.ohos_id_text_size_body2'),
127    fontColor: $r('sys.color.font_secondary'),
128    fontWeight: FontWeight.Regular,
129    plainFontColor: $r('sys.color.font_primary')
130  },
131  windows: {
132    padding: {
133      top: LengthMetrics.vp(12),
134      bottom: LengthMetrics.vp(12),
135      start: LengthMetrics.vp(12),
136      end: LengthMetrics.vp(12)
137    },
138  },
139  closeButton: {
140    size: { width: 22, height: 22 },
141    imageSize: { width: 18, height: 18 },
142    padding: {
143      top: LengthMetrics.vp(2),
144      bottom: LengthMetrics.vp(2),
145      start: LengthMetrics.vp(2),
146      end: LengthMetrics.vp(2)
147    },
148    margin: {
149      top: LengthMetrics.vp(12),
150      bottom: LengthMetrics.vp(12),
151      start: LengthMetrics.vp(12),
152      end: LengthMetrics.vp(12)
153    },
154    image: $r('sys.media.ohos_ic_public_cancel'),
155    fillColor: $r('sys.color.icon_secondary'),
156    hoverColor: $r('sys.color.ohos_id_color_hover'),
157    backgroundColor: $r('sys.color.ohos_id_color_background_transparent')
158  },
159};
160
161export interface PopupTextOptions {
162  text: ResourceStr;
163  fontSize?: number | string | Resource;
164  fontColor?: ResourceColor;
165  fontWeight?: number | FontWeight | string;
166}
167
168export interface PopupButtonOptions {
169  text: ResourceStr;
170  action?: () => void;
171  fontSize?: number | string | Resource;
172  fontColor?: ResourceColor;
173}
174
175export interface PopupIconOptions {
176  image: ResourceStr;
177  width?: Dimension;
178  height?: Dimension;
179  fillColor?: ResourceColor;
180  borderRadius?: Length | BorderRadiuses;
181}
182
183export interface PopupOptions {
184  icon?: PopupIconOptions;
185  title?: PopupTextOptions;
186  message: PopupTextOptions;
187  direction?: Direction;
188  showClose?: boolean | Resource;
189  onClose?: () => void;
190  buttons?: [PopupButtonOptions?, PopupButtonOptions?];
191}
192
193const noop = () => {
194};
195
196@Builder
197export function Popup(options: PopupOptions) {
198  PopupComponent({
199    icon: options.icon,
200    title: options.title,
201    message: options.message,
202    popupDirection: options.direction,
203    showClose: options.showClose,
204    onClose: options.onClose,
205    buttons: options.buttons
206  })
207}
208
209@Component
210export struct PopupComponent {
211  private onClose: () => void = noop;
212  private theme: PopupTheme = defaultTheme;
213  @Prop icon: PopupIconOptions = { image: '' };
214  @Prop title: PopupTextOptions = { text: '' };
215  @Prop message: PopupTextOptions = { text: '' };
216  @Prop popupDirection: Direction = Direction.Auto;
217  @Prop showClose: boolean | Resource = true;
218  @Prop buttons: [PopupButtonOptions?, PopupButtonOptions?] = [{ text: '' }, { text: '' }];
219  textHeight: number = 0;
220  @State titleHeight: number = 0;
221  @State applyHeight: number = 0;
222  @State buttonHeight: number = 0;
223  @State messageMaxWeight: number | undefined = 0;
224  @State beforeScreenStatus: boolean | undefined = undefined;
225  @State currentScreenStatus: boolean = true;
226  @State applySizeOptions: ConstraintSizeOptions | undefined = undefined;
227  @State closeButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
228  @State firstButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
229  @State secondButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
230  @State closeButtonFillColorWithTheme: ResourceColor = $r('sys.color.icon_secondary');
231  private listener = mediaquery.matchMediaSync('(orientation: landscape)')
232
233  private getIconWidth(): Dimension {
234    return this.icon?.width ?? this.theme.icon.size.width as number
235  }
236
237  private getIconHeight(): Dimension {
238    return this.icon?.height ?? this.theme.icon.size.height as number
239  }
240
241  private getIconFillColor(): ResourceColor {
242    return this.icon?.fillColor ?? this.theme.icon.fillColor
243  }
244
245  private getIconBorderRadius(): Length | BorderRadiuses {
246    return this.icon?.borderRadius ?? this.theme.icon.borderRadius
247  }
248
249  private getIconMargin(): LocalizedMargin {
250    return {
251      start: new LengthMetrics(this.theme.button.margin.start.value / 2, this.theme.button.margin.start.unit),
252      end: new LengthMetrics(this.theme.icon.margin.start.value - (this.theme.button.margin.end.value / 2)
253        , this.theme.button.margin.start.unit)
254    }
255  }
256
257  private getIconImage(): ResourceStr | undefined {
258    return this.icon?.image
259  }
260
261  private getTitleText(): ResourceStr | undefined {
262    return this.title?.text
263  }
264
265  private getTitlePadding(): LocalizedPadding {
266    return {
267      start: new LengthMetrics(this.theme.button.margin.start.value / 2, this.theme.button.margin.start.unit),
268      end: this.theme.closeButton.margin.end
269    }
270  }
271
272  private getTitleMargin(): LocalizedMargin {
273    return this.theme.title.margin
274  }
275
276  private getTitleMinFontSize(): number | string | Resource {
277    return this.theme.title.minFontSize
278  }
279
280  private getTitleFontWeight(): number | FontWeight | string {
281    return this.title?.fontWeight ?? this.theme.title.fontWeight
282  }
283
284  private getTitleFontSize(): number | string | Resource {
285    return this.title?.fontSize ?? this.theme.title.fontSize
286  }
287
288  private getTitleFontColor(): ResourceColor {
289    return this.title?.fontColor ?? this.theme.title.fontColor
290  }
291
292  private getCloseButtonWidth(): Length | undefined {
293    return this.theme.closeButton.size.width
294  }
295
296  private getCloseButtonHeight(): Length | undefined {
297    return this.theme.closeButton.size.height
298  }
299
300  private getCloseButtonImage(): ResourceStr {
301    return this.theme.closeButton.image
302  }
303
304  private getCloseButtonFillColor(): ResourceColor {
305    return this.closeButtonFillColorWithTheme;
306  }
307
308  private getCloseButtonHoverColor(): ResourceColor {
309    return this.theme.closeButton.hoverColor
310  }
311
312  private getCloseButtonBackgroundColor(): ResourceColor {
313    return this.theme.closeButton.backgroundColor
314  }
315
316  private getCloseButtonPadding(): LocalizedPadding {
317    return this.theme.closeButton.padding
318  }
319
320  private getCloseButtonImageWidth(): Length | undefined {
321    return this.theme.closeButton.imageSize.width
322  }
323
324  private getCloseButtonImageHeight(): Length | undefined {
325    return this.theme.closeButton.imageSize.height
326  }
327
328  private getMessageText(): string | Resource {
329    return this.message.text
330  }
331
332  private getMessageFontSize(): number | string | Resource {
333    return this.message.fontSize ?? this.theme.message.fontSize
334  }
335
336  private getMessageFontColor(): ResourceColor {
337    let fontColor: ResourceColor
338    if (this.message.fontColor) {
339      fontColor = this.message.fontColor
340    } else {
341      if (this.title.text !== '' && this.title.text !== void (0)) {
342        fontColor = this.theme.message.fontColor
343      } else {
344        fontColor = this.theme.message.plainFontColor
345      }
346    }
347    return fontColor
348  }
349
350  private getMessagePadding(): LocalizedPadding {
351    let padding: LocalizedPadding
352    if (this.title.text !== '' && this.title.text !== void (0)) {
353      padding = { start: LengthMetrics.vp(this.theme.button.margin.start.value / 2) }
354    } else {
355      padding = {
356        start: LengthMetrics.vp(this.theme.button.margin.start.value / 2),
357        end: LengthMetrics.vp(this.theme.closeButton.margin.end.value)
358      }
359    }
360    return padding
361  }
362
363  private getMessageMaxWeight(): number | undefined {
364    let textMaxWeight: number | undefined = undefined;
365    let defaultDisplaySync: display.Display = display.getDefaultDisplaySync()
366    if (this.showClose || this.showClose === void (0)) {
367      if (px2vp(defaultDisplaySync.width) > 400) {
368        textMaxWeight = 400
369      } else {
370        textMaxWeight = px2vp(defaultDisplaySync.width) - 40 - 40
371      }
372      textMaxWeight -= (this.theme.windows.padding.start.value - (this.theme.button.margin.end.value / 2))
373      textMaxWeight -= this.theme.windows.padding.end.value
374      textMaxWeight -= this.theme.button.margin.start.value / 2
375      textMaxWeight -= this.getCloseButtonWidth() as number
376    }
377    return textMaxWeight
378  }
379
380  private getMessageFontWeight(): number | FontWeight | string {
381    return this.theme.message.fontWeight
382  }
383
384  private getButtonMargin(): LocalizedMargin {
385    return {
386      top: LengthMetrics.vp(this.theme.button.textMargin.top.value / 2 - 4),
387      bottom: LengthMetrics.vp(this.theme.button.textMargin.bottom.value / 2 - 4),
388      start: LengthMetrics.vp(this.theme.button.margin.start.value / 2 - 4),
389      end: LengthMetrics.vp(this.theme.button.margin.end.value / 2 - 4)
390    }
391  }
392
393  private getButtonTextMargin(): LocalizedMargin {
394    return { top: LengthMetrics.vp(this.theme.button.textMargin.bottom.value) }
395  }
396
397  private getButtonTextPadding(): LocalizedPadding {
398    return this.theme.button.padding
399  }
400
401  private getButtonHoverColor(): ResourceColor {
402    return this.theme.button.hoverColor
403  }
404
405  private getButtonBackgroundColor(): ResourceColor {
406    return this.theme.button.backgroundColor
407  }
408
409  private getFirstButtonText(): string | Resource | undefined {
410    return this.buttons?.[0]?.text
411  }
412
413  private getSecondButtonText(): string | Resource | undefined {
414    return this.buttons?.[1]?.text
415  }
416
417  private getFirstButtonFontSize(): number | string | Resource {
418    return this.buttons?.[0]?.fontSize ?? this.theme.button.fontSize
419  }
420
421  private getSecondButtonFontSize(): number | string | Resource {
422    return this.buttons?.[1]?.fontSize ?? this.theme.button.fontSize
423  }
424
425  private getFirstButtonFontColor(): ResourceColor {
426    return this.buttons?.[0]?.fontColor ?? this.theme.button.fontColor
427  }
428
429  private getSecondButtonFontColor(): ResourceColor {
430    return this.buttons?.[1]?.fontColor ?? this.theme.button.fontColor
431  }
432
433  private getButtonMinFontSize(): Dimension {
434    return this.theme.button.minFontSize
435  }
436
437  private getButtonFontWeight(): number | FontWeight | string {
438    return this.theme.button.fontWeight
439  }
440
441  private getWindowsPadding(): LocalizedPadding {
442    return {
443      top: this.theme.windows.padding.top,
444      bottom: LengthMetrics.vp(this.theme.windows.padding.bottom.value -
445        (this.theme.button.textMargin.bottom.value / 2)),
446      start: LengthMetrics.vp(this.theme.windows.padding.start.value -
447        (this.theme.button.margin.end.value / 2)),
448      end: this.theme.windows.padding.end
449    }
450  }
451
452  onWillApplyTheme(theme: Theme): void {
453    this.theme.title.fontColor = theme.colors.fontPrimary;
454    this.theme.button.fontColor = theme.colors.fontEmphasize;
455    this.theme.message.fontColor = theme.colors.fontSecondary;
456    this.theme.message.plainFontColor = theme.colors.fontPrimary;
457    this.closeButtonFillColorWithTheme = theme.colors.iconSecondary;
458  }
459
460  aboutToAppear() {
461    this.listener.on("change", (mediaQueryResult: mediaquery.MediaQueryResult) => {
462      this.currentScreenStatus = mediaQueryResult.matches
463    })
464  }
465
466  aboutToDisappear() {
467    this.listener.off("change")
468  }
469
470  getScrollMaxHeight(): number | undefined {
471    let scrollMaxHeight: number | undefined = undefined;
472    if (this.currentScreenStatus !== this.beforeScreenStatus) {
473      this.applySizeOptions = this.getApplyMaxSize();
474      this.beforeScreenStatus = this.currentScreenStatus
475      return scrollMaxHeight;
476    }
477    scrollMaxHeight = this.applyHeight
478    scrollMaxHeight -= this.titleHeight
479    scrollMaxHeight -= this.buttonHeight
480    scrollMaxHeight -= this.theme.windows.padding.top.value
481    scrollMaxHeight -= (this.theme.button.textMargin.bottom.value / 2)
482    scrollMaxHeight -= this.theme.title.margin.bottom.value
483    scrollMaxHeight -= (this.theme.windows.padding.bottom.value -
484      (this.theme.button.textMargin.bottom.value / 2))
485    if (Math.floor(this.textHeight) > Math.floor(scrollMaxHeight + 1)) {
486      return scrollMaxHeight
487    } else {
488      scrollMaxHeight = undefined
489      return scrollMaxHeight
490    }
491  }
492
493  private getLayoutWeight(): number {
494    let layoutWeight: number
495    if ((this.icon.image !== '' && this.icon.image !== void (0)) ||
496      (this.title.text !== '' && this.title.text !== void (0)) ||
497      (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) ||
498      (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0))) {
499      layoutWeight = 1
500    } else {
501      layoutWeight = 0
502    }
503    return layoutWeight
504  }
505
506  private getApplyMaxSize(): ConstraintSizeOptions {
507    let applyMaxWidth: number | undefined = undefined;
508    let applyMaxHeight: number | undefined = undefined;
509    let applyMaxSize: ConstraintSizeOptions | undefined = undefined;
510    let defaultDisplaySync: display.Display = display.getDefaultDisplaySync()
511    if (px2vp(defaultDisplaySync.width) > 400) {
512      applyMaxWidth = 400
513    } else {
514      applyMaxWidth = px2vp(defaultDisplaySync.width) - 40 - 40
515    }
516    if (px2vp(defaultDisplaySync.height) > 480) {
517      applyMaxHeight = 480
518    } else {
519      applyMaxHeight = px2vp(defaultDisplaySync.height) - 40 - 40
520    }
521    applyMaxSize = { maxWidth: applyMaxWidth, maxHeight: applyMaxHeight }
522    this.messageMaxWeight = this.getMessageMaxWeight()
523    return applyMaxSize
524  }
525
526  build() {
527    Row() {
528      if (this.icon.image !== '' && this.icon.image !== void (0)) {
529        Image(this.getIconImage())
530          .direction(this.popupDirection)
531          .width(this.getIconWidth())
532          .height(this.getIconHeight())
533          .margin(this.getIconMargin())
534          .fillColor(this.getIconFillColor())
535          .borderRadius(this.getIconBorderRadius())
536      }
537      if (this.title.text !== '' && this.title.text !== void (0)) {
538        Column() {
539          Flex({ alignItems: ItemAlign.Start }) {
540            Text(this.getTitleText())
541              .direction(this.popupDirection)
542              .flexGrow(1)
543              .maxLines(2)
544              .align(Alignment.Start)
545              .padding(this.getTitlePadding())
546              .minFontSize(this.getTitleMinFontSize())
547              .textOverflow({ overflow: TextOverflow.Ellipsis })
548              .fontWeight(this.getTitleFontWeight())
549              .fontSize(this.getTitleFontSize())
550              .fontColor(this.getTitleFontColor())
551              .constraintSize({ minHeight: this.getCloseButtonHeight() })
552            if (this.showClose || this.showClose === void (0)) {
553              Button() {
554                Image(this.getCloseButtonImage())
555                  .direction(this.popupDirection)
556                  .focusable(true)
557                  .width(this.getCloseButtonImageWidth())
558                  .height(this.getCloseButtonImageHeight())
559                  .fillColor(this.getCloseButtonFillColor())
560              }
561              .direction(this.popupDirection)
562              .width(this.getCloseButtonWidth())
563              .height(this.getCloseButtonHeight())
564              .padding(this.getCloseButtonPadding())
565              .backgroundColor(this.closeButtonBackgroundColor)
566              .onHover((isHover: boolean) => {
567                if (isHover) {
568                  this.closeButtonBackgroundColor = this.getCloseButtonHoverColor()
569                } else {
570                  this.closeButtonBackgroundColor = this.getCloseButtonBackgroundColor()
571                }
572              })
573              .onClick(() => {
574                if (this.onClose) {
575                  this.onClose();
576                }
577              })
578            }
579          }
580          .direction(this.popupDirection)
581          .width("100%")
582          .margin(this.getTitleMargin())
583          .onAreaChange((_, rect) => {
584            this.titleHeight = rect.height as number
585          })
586
587          Scroll() {
588            Text(this.getMessageText())
589              .direction(this.popupDirection)
590              .fontSize(this.getMessageFontSize())
591              .fontColor(this.getMessageFontColor())
592              .fontWeight(this.getMessageFontWeight())
593              .constraintSize({ minHeight: this.getCloseButtonHeight() })
594              .onAreaChange((_, rect) => {
595                this.textHeight = rect.height as number
596              })
597          }
598          .direction(this.popupDirection)
599          .width("100%")
600          .align(Alignment.TopStart)
601          .padding(this.getMessagePadding())
602          .scrollBar(BarState.Auto)
603          .scrollable(ScrollDirection.Vertical)
604          .constraintSize({ maxHeight: this.getScrollMaxHeight() })
605
606          Flex({ wrap: FlexWrap.Wrap }) {
607            if (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) {
608              Button() {
609                Text(this.getFirstButtonText())
610                  .direction(this.popupDirection)
611                  .maxLines(2)
612                  .focusable(true)
613                  .fontSize(this.getFirstButtonFontSize())
614                  .fontColor(this.getFirstButtonFontColor())
615                  .fontWeight(this.getButtonFontWeight())
616                  .minFontSize(this.getButtonMinFontSize())
617                  .textOverflow({ overflow: TextOverflow.Ellipsis })
618              }
619              .direction(this.popupDirection)
620              .margin(this.getButtonMargin())
621              .padding(this.getButtonTextPadding())
622              .backgroundColor(this.firstButtonBackgroundColor)
623              .onHover((isHover: boolean) => {
624                if (isHover) {
625                  this.firstButtonBackgroundColor = this.getButtonHoverColor()
626                }
627                else {
628                  this.firstButtonBackgroundColor = this.getButtonBackgroundColor()
629                }
630              })
631              .onClick(() => {
632                if (this.buttons?.[0]?.action) {
633                  this.buttons?.[0]?.action();
634                }
635              })
636            }
637            if (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0)) {
638              Button() {
639                Text(this.getSecondButtonText())
640                  .direction(this.popupDirection)
641                  .maxLines(2)
642                  .focusable(true)
643                  .fontSize(this.getSecondButtonFontSize())
644                  .fontColor(this.getSecondButtonFontColor())
645                  .fontWeight(this.getButtonFontWeight())
646                  .minFontSize(this.getButtonMinFontSize())
647                  .textOverflow({ overflow: TextOverflow.Ellipsis })
648              }
649              .direction(this.popupDirection)
650              .margin(this.getButtonMargin())
651              .padding(this.getButtonTextPadding())
652              .backgroundColor(this.secondButtonBackgroundColor)
653              .onHover((isHover: boolean) => {
654                if (isHover) {
655                  this.secondButtonBackgroundColor = this.getButtonHoverColor()
656                }
657                else {
658                  this.secondButtonBackgroundColor = this.getButtonBackgroundColor()
659                }
660              })
661              .onClick(() => {
662                if (this.buttons?.[1]?.action) {
663                  this.buttons?.[1]?.action();
664                }
665              })
666            }
667          }
668          .direction(this.popupDirection)
669          .margin(this.getButtonTextMargin())
670          .flexGrow(1)
671          .onAreaChange((_, rect) => {
672            this.buttonHeight = rect.height as number
673          })
674        }
675        .direction(this.popupDirection)
676        .layoutWeight(this.getLayoutWeight())
677      }
678      else {
679        Column() {
680          Row() {
681            Scroll() {
682              Text(this.getMessageText())
683                .direction(this.popupDirection)
684                .fontSize(this.getMessageFontSize())
685                .fontColor(this.getMessageFontColor())
686                .fontWeight(this.getMessageFontWeight())
687                .constraintSize({ maxWidth: this.messageMaxWeight, minHeight: this.getCloseButtonHeight() })
688                .onAreaChange((_, rect) => {
689                  this.textHeight = rect.height as number
690                })
691            }
692            .direction(this.popupDirection)
693            .layoutWeight(this.getLayoutWeight())
694            .align(Alignment.TopStart)
695            .padding(this.getMessagePadding())
696            .scrollBar(BarState.Auto)
697            .scrollable(ScrollDirection.Vertical)
698            .constraintSize({ maxHeight: this.getScrollMaxHeight() })
699
700            if (this.showClose || this.showClose === void (0)) {
701              Button() {
702                Image(this.getCloseButtonImage())
703                  .direction(this.popupDirection)
704                  .focusable(true)
705                  .width(this.getCloseButtonImageWidth())
706                  .height(this.getCloseButtonImageHeight())
707                  .fillColor(this.getCloseButtonFillColor())
708              }
709              .direction(this.popupDirection)
710              .width(this.getCloseButtonWidth())
711              .height(this.getCloseButtonHeight())
712              .padding(this.getCloseButtonPadding())
713              .backgroundColor(this.closeButtonBackgroundColor)
714              .onHover((isHover: boolean) => {
715                if (isHover) {
716                  this.closeButtonBackgroundColor = this.getCloseButtonHoverColor()
717                }
718                else {
719                  this.closeButtonBackgroundColor = this.getCloseButtonBackgroundColor()
720                }
721              })
722              .onClick(() => {
723                if (this.onClose) {
724                  this.onClose();
725                }
726              })
727            }
728          }
729          .direction(this.popupDirection)
730          .alignItems(VerticalAlign.Top)
731
732          Flex({ wrap: FlexWrap.Wrap }) {
733            if (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) {
734              Button() {
735                Text(this.getFirstButtonText())
736                  .direction(this.popupDirection)
737                  .maxLines(2)
738                  .focusable(true)
739                  .fontSize(this.getFirstButtonFontSize())
740                  .fontColor(this.getFirstButtonFontColor())
741                  .fontWeight(this.getButtonFontWeight())
742                  .minFontSize(this.getButtonMinFontSize())
743                  .textOverflow({ overflow: TextOverflow.Ellipsis })
744              }
745              .direction(this.popupDirection)
746              .margin(this.getButtonMargin())
747              .padding(this.getButtonTextPadding())
748              .backgroundColor(this.firstButtonBackgroundColor)
749              .onHover((isHover: boolean) => {
750                if (isHover) {
751                  this.firstButtonBackgroundColor = this.getButtonHoverColor()
752                }
753                else {
754                  this.firstButtonBackgroundColor = this.getButtonBackgroundColor()
755                }
756              })
757              .onClick(() => {
758                if (this.buttons?.[0]?.action) {
759                  this.buttons?.[0]?.action();
760                }
761              })
762            }
763            if (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0)) {
764              Button() {
765                Text(this.getSecondButtonText())
766                  .direction(this.popupDirection)
767                  .maxLines(2)
768                  .focusable(true)
769                  .fontSize(this.getSecondButtonFontSize())
770                  .fontColor(this.getSecondButtonFontColor())
771                  .fontWeight(this.getButtonFontWeight())
772                  .minFontSize(this.getButtonMinFontSize())
773                  .textOverflow({ overflow: TextOverflow.Ellipsis })
774              }
775              .direction(this.popupDirection)
776              .margin(this.getButtonMargin())
777              .padding(this.getButtonTextPadding())
778              .backgroundColor(this.secondButtonBackgroundColor)
779              .onHover((isHover: boolean) => {
780                if (isHover) {
781                  this.secondButtonBackgroundColor = this.getButtonHoverColor()
782                }
783                else {
784                  this.secondButtonBackgroundColor = this.getButtonBackgroundColor()
785                }
786              })
787              .onClick(() => {
788                if (this.buttons?.[1]?.action) {
789                  this.buttons?.[1]?.action();
790                }
791              })
792            }
793          }
794          .direction(this.popupDirection)
795          .margin(this.getButtonTextMargin())
796          .flexGrow(1)
797          .onAreaChange((_, rect) => {
798            this.buttonHeight = rect.height as number
799          })
800        }
801        .direction(this.popupDirection)
802        .layoutWeight(this.getLayoutWeight())
803      }
804    }
805    .direction(this.popupDirection)
806    .alignItems(VerticalAlign.Top)
807    .padding(this.getWindowsPadding())
808    .constraintSize(this.applySizeOptions)
809    .onAreaChange((_, rect) => {
810      this.applyHeight = rect.height as number
811    })
812  }
813}
814