• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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'
20import componentUtils from '@ohos.arkui.componentUtils';
21import { SymbolGlyphModifier, Configuration } from '@kit.ArkUI';
22
23const RESOURCE_TYPE_STRING = 10003;
24const RESOURCE_TYPE_FLOAT = 10002;
25const RESOURCE_TYPE_INTEGER = 10007;
26
27interface IconTheme {
28  size: SizeOptions;
29  margin: LocalizedMargin;
30  fillColor: ResourceColor;
31  borderRadius: Length;
32}
33
34interface TitleTheme {
35  margin: LocalizedMargin;
36  minFontSize: number;
37  fontWeight: FontWeight;
38  fontSize: Resource;
39  fontColor: ResourceColor;
40}
41
42interface ButtonTheme {
43  margin: LocalizedMargin;
44  padding: LocalizedPadding;
45  fontSize: Resource;
46  fontColor: ResourceColor;
47  textMargin: LocalizedMargin;
48  minFontSize: number;
49  fontWeight: FontWeight;
50  hoverColor: ResourceColor;
51  backgroundColor: ResourceColor;
52}
53
54interface MessageTheme {
55  fontSize: Resource;
56  fontColor: ResourceColor;
57  fontWeight: FontWeight;
58  plainFontColor: ResourceColor;
59}
60
61interface CloseButtonTheme {
62  size: SizeOptions;
63  margin: LocalizedMargin;
64  padding: LocalizedPadding;
65  fillColor: ResourceColor;
66  hoverColor: ResourceColor;
67  backgroundColor: ResourceColor;
68  symbolStyle: SymbolGlyphModifier;
69  symbolSize: number;
70  accessibilityMessage: Resource;
71}
72
73interface WindowsTheme {
74  padding: LocalizedPadding;
75}
76
77interface PopupTheme {
78  icon: IconTheme;
79  title: TitleTheme;
80  button: ButtonTheme;
81  message: MessageTheme;
82  windows: WindowsTheme;
83  closeButton: CloseButtonTheme;
84}
85
86export const defaultTheme: PopupTheme = {
87  icon: {
88    size: { width: 32, height: 32 },
89    margin: {
90      top: LengthMetrics.vp(12),
91      bottom: LengthMetrics.vp(12),
92      start: LengthMetrics.vp(12),
93      end: LengthMetrics.vp(12)
94    },
95    fillColor: '',
96    borderRadius: $r('sys.float.ohos_id_corner_radius_default_s')
97
98  },
99  title: {
100    margin: { bottom: LengthMetrics.vp(2) },
101    minFontSize: 12,
102    fontWeight: FontWeight.Medium,
103    fontSize: $r('sys.float.ohos_id_text_size_sub_title2'),
104    fontColor: $r('sys.color.font_primary')
105  },
106  button: {
107    margin: {
108      top: LengthMetrics.vp(16),
109      bottom: LengthMetrics.vp(16),
110      start: LengthMetrics.vp(16),
111      end: LengthMetrics.vp(16)
112    },
113    padding: {
114      top: LengthMetrics.vp(4),
115      bottom: LengthMetrics.vp(4),
116      start: LengthMetrics.vp(4),
117      end: LengthMetrics.vp(4)
118    },
119    fontSize: $r('sys.float.ohos_id_text_size_button2'),
120    fontColor: $r('sys.color.font_emphasize'),
121    textMargin: {
122      top: LengthMetrics.vp(8),
123      bottom: LengthMetrics.vp(8),
124      start: LengthMetrics.vp(8),
125      end: LengthMetrics.vp(8)
126    },
127    minFontSize: 9,
128    fontWeight: FontWeight.Medium,
129    hoverColor: $r('sys.color.ohos_id_color_hover'),
130    backgroundColor: $r('sys.color.ohos_id_color_background_transparent')
131  },
132  message: {
133    fontSize: $r('sys.float.ohos_id_text_size_body2'),
134    fontColor: $r('sys.color.font_secondary'),
135    fontWeight: FontWeight.Regular,
136    plainFontColor: $r('sys.color.font_primary')
137  },
138  windows: {
139    padding: {
140      top: LengthMetrics.vp(12),
141      bottom: LengthMetrics.vp(12),
142      start: LengthMetrics.vp(12),
143      end: LengthMetrics.vp(12)
144    },
145  },
146  closeButton: {
147    size: { width: 22, height: 22 },
148    padding: {
149      top: LengthMetrics.vp(2),
150      bottom: LengthMetrics.vp(2),
151      start: LengthMetrics.vp(2),
152      end: LengthMetrics.vp(2)
153    },
154    margin: {
155      top: LengthMetrics.vp(12),
156      bottom: LengthMetrics.vp(12),
157      start: LengthMetrics.vp(12),
158      end: LengthMetrics.vp(12)
159    },
160    symbolStyle: new SymbolGlyphModifier($r('sys.symbol.xmark')),
161    fillColor: $r('sys.color.icon_secondary'),
162    hoverColor: $r('sys.color.ohos_id_color_hover'),
163    backgroundColor: $r('sys.color.ohos_id_color_background_transparent'),
164    symbolSize: 18,
165    accessibilityMessage: $r('sys.string.off_used_for_accessibility_text')
166  },
167};
168
169export interface PopupTextOptions {
170  text: ResourceStr;
171  fontSize?: number | string | Resource;
172  fontColor?: ResourceColor;
173  fontWeight?: number | FontWeight | string;
174}
175
176export interface PopupButtonOptions {
177  text: ResourceStr;
178  action?: () => void;
179  fontSize?: number | string | Resource;
180  fontColor?: ResourceColor;
181}
182
183export interface PopupIconOptions {
184  image: ResourceStr;
185  width?: Dimension;
186  height?: Dimension;
187  fillColor?: ResourceColor;
188  borderRadius?: Length | BorderRadiuses;
189}
190
191export interface PopupOptions {
192  icon?: PopupIconOptions;
193  title?: PopupTextOptions;
194  message: PopupTextOptions;
195  direction?: Direction;
196  showClose?: boolean | Resource;
197  onClose?: () => void;
198  buttons?: [PopupButtonOptions?, PopupButtonOptions?];
199  maxWidth?: Dimension;
200}
201
202const noop = () => {
203};
204
205const POPUP_DEFAULT_MAXWIDTH = 400;
206
207@Builder
208export function Popup(options: PopupOptions) {
209  PopupComponent({
210    icon: options.icon,
211    title: options.title,
212    message: options.message,
213    popupDirection: options.direction,
214    showClose: options.showClose,
215    onClose: options.onClose,
216    buttons: options.buttons,
217    maxWidth: options.maxWidth
218  })
219}
220
221function isValidString(dimension: string, regex: RegExp): boolean {
222  const matches = dimension.match(regex);
223  if (!matches || matches.length < 3) {
224    return false;
225  }
226  const value = Number.parseFloat(matches[1]);
227  return value >= 0;
228}
229
230function isValidDimensionString(dimension: string): boolean {
231  return isValidString(dimension, new RegExp('(-?\\d+(?:\\.\\d+)?)_?(fp|vp|px|lpx|%)?$', 'i'));
232}
233
234function isValidResource(context: Context | undefined, value: Resource) {
235  const resourceManager = context?.resourceManager;
236  if (value === void (0) || value === null || resourceManager === void (0)) {
237    return false;
238  }
239  if (value.type !== RESOURCE_TYPE_STRING && value.type !== RESOURCE_TYPE_INTEGER &&
240    value.type !== RESOURCE_TYPE_FLOAT) {
241    return false;
242  }
243
244  if (value.type === RESOURCE_TYPE_INTEGER || value.type === RESOURCE_TYPE_FLOAT) {
245    if (resourceManager.getNumber(value.id) >= 0) {
246      return true;
247    } else {
248      return false;
249    }
250  }
251
252  if (value.type === RESOURCE_TYPE_STRING && !isValidDimensionString(resourceManager.getStringSync(value.id))) {
253    return false;
254  } else {
255    return true;
256  }
257}
258
259@Component
260export struct PopupComponent {
261  private onClose: () => void = noop;
262  private theme: PopupTheme = defaultTheme;
263  @Prop icon: PopupIconOptions = { image: '' };
264  @Prop maxWidth?: Dimension;
265  @Prop messageMaxWidth?: number = 0;
266  @Prop title: PopupTextOptions = { text: '' };
267  @Prop message: PopupTextOptions = { text: '' };
268  @Prop popupDirection: Direction = Direction.Auto;
269  @Prop showClose: boolean | Resource = true;
270  @Prop buttons: [PopupButtonOptions?, PopupButtonOptions?] = [{ text: '' }, { text: '' }];
271  textHeight: number = 0;
272  @State titleHeight: number = 0;
273  @State applyHeight: number = 0;
274  @State buttonHeight: number = 0;
275  @State messageMaxWeight: number | undefined = 0;
276  @State beforeScreenStatus: boolean | undefined = undefined;
277  @State currentScreenStatus: boolean = true;
278  @State applySizeOptions: ConstraintSizeOptions | undefined = undefined;
279  @State closeButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
280  @State firstButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
281  @State secondButtonBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
282  @State closeButtonFillColorWithTheme: ResourceColor = $r('sys.color.icon_secondary');
283  private listener = mediaquery.matchMediaSync('(orientation: landscape)')
284
285  private getIconWidth(): Dimension {
286    return this.icon?.width ?? this.theme.icon.size.width as number
287  }
288
289  private getIconHeight(): Dimension {
290    return this.icon?.height ?? this.theme.icon.size.height as number
291  }
292
293  private getIconFillColor(): ResourceColor {
294    return this.icon?.fillColor ?? this.theme.icon.fillColor
295  }
296
297  private getIconBorderRadius(): Length | BorderRadiuses {
298    return this.icon?.borderRadius ?? this.theme.icon.borderRadius
299  }
300
301  private getIconMargin(): LocalizedMargin {
302    return {
303      start: new LengthMetrics(this.theme.button.margin.start.value / 2, this.theme.button.margin.start.unit),
304      end: new LengthMetrics(this.theme.icon.margin.start.value - (this.theme.button.margin.end.value / 2)
305        , this.theme.button.margin.start.unit)
306    }
307  }
308
309  private getIconImage(): ResourceStr | undefined {
310    return this.icon?.image
311  }
312
313  private getTitleText(): ResourceStr | undefined {
314    return this.title?.text
315  }
316
317  private getTitlePadding(): LocalizedPadding {
318    return {
319      start: new LengthMetrics(this.theme.button.margin.start.value / 2, this.theme.button.margin.start.unit),
320      end: this.theme.closeButton.margin.end
321    }
322  }
323
324  private getTitleMargin(): LocalizedMargin {
325    return this.theme.title.margin
326  }
327
328  private getTitleMinFontSize(): number | string | Resource {
329    return this.theme.title.minFontSize
330  }
331
332  private getTitleFontWeight(): number | FontWeight | string {
333    return this.title?.fontWeight ?? this.theme.title.fontWeight
334  }
335
336  private getTitleFontSize(): number | string | Resource {
337    return this.title?.fontSize ?? this.theme.title.fontSize
338  }
339
340  private getTitleFontColor(): ResourceColor {
341    return this.title?.fontColor ?? this.theme.title.fontColor
342  }
343
344  private getCloseButtonWidth(): Length | undefined {
345    return this.theme.closeButton.size.width
346  }
347
348  private getCloseButtonHeight(): Length | undefined {
349    return this.theme.closeButton.size.height
350  }
351
352  private getCloseButtonFillColor(): ResourceColor {
353    return this.closeButtonFillColorWithTheme;
354  }
355
356  private getCloseButtonHoverColor(): ResourceColor {
357    return this.theme.closeButton.hoverColor
358  }
359
360  private getCloseButtonBackgroundColor(): ResourceColor {
361    return this.theme.closeButton.backgroundColor
362  }
363
364  private getCloseButtonPadding(): LocalizedPadding {
365    return this.theme.closeButton.padding
366  }
367
368  private getCloseButtonSymbolSize(): Length | undefined {
369    return this.theme.closeButton.symbolSize
370  }
371
372  private getMessageText(): string | Resource {
373    return this.message.text
374  }
375
376  private getMessageFontSize(): number | string | Resource {
377    return this.message.fontSize ?? this.theme.message.fontSize
378  }
379
380  private getMessageFontColor(): ResourceColor {
381    let fontColor: ResourceColor
382    if (this.message.fontColor) {
383      fontColor = this.message.fontColor
384    } else {
385      if (this.title.text !== '' && this.title.text !== void (0)) {
386        fontColor = this.theme.message.fontColor
387      } else {
388        fontColor = this.theme.message.plainFontColor
389      }
390    }
391    return fontColor
392  }
393
394  private getMessagePadding(): LocalizedPadding {
395    let padding: LocalizedPadding
396    if (this.title.text !== '' && this.title.text !== void (0)) {
397      padding = { start: LengthMetrics.vp(this.theme.button.margin.start.value / 2) }
398    } else {
399      padding = {
400        start: LengthMetrics.vp(this.theme.button.margin.start.value / 2),
401        end: LengthMetrics.vp(this.theme.closeButton.margin.end.value)
402      }
403    }
404    return padding
405  }
406
407  private getMessageMaxWeight(): number | undefined {
408    let textMaxWeight: number | undefined = undefined;
409    let defaultDisplaySync: display.Display | undefined = undefined;
410    try {
411      defaultDisplaySync = display.getDefaultDisplaySync()
412    } catch (error) {
413      console.error(`Ace Popup getDefaultDisplaySync, error: ${error.toString()}`);
414      return textMaxWeight = 400
415    }
416    if (this.showClose || this.showClose === void (0)) {
417      if (this.messageMaxWidth != undefined) {
418        if (this.maxWidth != undefined && this.maxWidth > px2vp(defaultDisplaySync.width)) {
419          textMaxWeight = px2vp(defaultDisplaySync.width)
420        } else {
421          textMaxWeight = this.messageMaxWidth
422        }
423      } else {
424        if (defaultDisplaySync.width != 0) {
425          textMaxWeight = px2vp(defaultDisplaySync.width)
426        } else {
427          // The previewer does not support the display interface to use abnormal values
428          textMaxWeight = -1
429        }
430      }
431      textMaxWeight -= (this.theme.windows.padding.start.value - (this.theme.button.margin.end.value / 2))
432      textMaxWeight -= this.theme.windows.padding.end.value
433      textMaxWeight -= this.theme.button.margin.start.value / 2
434      textMaxWeight -= this.getCloseButtonWidth() as number
435    }
436    return textMaxWeight
437  }
438
439  private getMessageFontWeight(): number | FontWeight | string {
440    return this.theme.message.fontWeight
441  }
442
443  private getButtonMargin(): LocalizedMargin {
444    return {
445      top: LengthMetrics.vp(this.theme.button.textMargin.top.value / 2 - 4),
446      bottom: LengthMetrics.vp(this.theme.button.textMargin.bottom.value / 2 - 4),
447      start: LengthMetrics.vp(this.theme.button.margin.start.value / 2 - 4),
448      end: LengthMetrics.vp(this.theme.button.margin.end.value / 2 - 4)
449    }
450  }
451
452  private getButtonTextMargin(): LocalizedMargin {
453    return { top: LengthMetrics.vp(this.theme.button.textMargin.bottom.value) }
454  }
455
456  private getButtonTextPadding(): LocalizedPadding {
457    return this.theme.button.padding
458  }
459
460  private getButtonHoverColor(): ResourceColor {
461    return this.theme.button.hoverColor
462  }
463
464  private getButtonBackgroundColor(): ResourceColor {
465    return this.theme.button.backgroundColor
466  }
467
468  private getFirstButtonText(): string | Resource | undefined {
469    return this.buttons?.[0]?.text
470  }
471
472  private getSecondButtonText(): string | Resource | undefined {
473    return this.buttons?.[1]?.text
474  }
475
476  private getFirstButtonFontSize(): number | string | Resource {
477    return this.buttons?.[0]?.fontSize ?? this.theme.button.fontSize
478  }
479
480  private getSecondButtonFontSize(): number | string | Resource {
481    return this.buttons?.[1]?.fontSize ?? this.theme.button.fontSize
482  }
483
484  private getFirstButtonFontColor(): ResourceColor {
485    return this.buttons?.[0]?.fontColor ?? this.theme.button.fontColor
486  }
487
488  private getSecondButtonFontColor(): ResourceColor {
489    return this.buttons?.[1]?.fontColor ?? this.theme.button.fontColor
490  }
491
492  private getButtonMinFontSize(): Dimension {
493    return this.theme.button.minFontSize
494  }
495
496  private getButtonFontWeight(): number | FontWeight | string {
497    return this.theme.button.fontWeight
498  }
499
500  private getWindowsPadding(): LocalizedPadding {
501    let top = this.theme.windows.padding.top;
502    let bottom =
503      LengthMetrics.vp(this.theme.windows.padding.bottom.value - (this.theme.button.textMargin.bottom.value / 2));
504    let start = LengthMetrics.vp(this.theme.windows.padding.start.value - (this.theme.button.margin.end.value / 2));
505    let end = this.theme.windows.padding.end;
506    let resolvedMaxWidth = this.toVp(this.maxWidth);
507    if (resolvedMaxWidth === 0) {
508      start = LengthMetrics.vp(0);
509      end = LengthMetrics.vp(0);
510    }
511    return {
512      top: top,
513      bottom: bottom,
514      start: start,
515      end: end
516    };
517  }
518
519  onWillApplyTheme(theme: Theme): void {
520    this.theme.title.fontColor = theme.colors.fontPrimary;
521    this.theme.button.fontColor = theme.colors.fontEmphasize;
522    this.theme.message.fontColor = theme.colors.fontSecondary;
523    this.theme.message.plainFontColor = theme.colors.fontPrimary;
524    this.closeButtonFillColorWithTheme = theme.colors.iconSecondary;
525  }
526
527  aboutToAppear() {
528    this.listener.on("change", (mediaQueryResult: mediaquery.MediaQueryResult) => {
529      this.currentScreenStatus = mediaQueryResult.matches
530    })
531  }
532
533  aboutToDisappear() {
534    this.listener.off("change")
535  }
536
537  getScrollMaxHeight(): number | undefined {
538    let scrollMaxHeight: number | undefined = undefined;
539    if (this.currentScreenStatus !== this.beforeScreenStatus) {
540      this.applySizeOptions = this.getApplyMaxSize();
541      this.beforeScreenStatus = this.currentScreenStatus
542      return scrollMaxHeight;
543    }
544    scrollMaxHeight = px2vp(componentUtils.getRectangleById('applyContent').size?.height)
545    scrollMaxHeight -= this.titleHeight
546    scrollMaxHeight -= this.buttonHeight
547    scrollMaxHeight -= this.theme.windows.padding.top.value
548    scrollMaxHeight -= (this.theme.button.textMargin.bottom.value / 2)
549    scrollMaxHeight -= this.theme.title.margin.bottom.value
550    scrollMaxHeight -= (this.theme.windows.padding.bottom.value -
551      (this.theme.button.textMargin.bottom.value / 2))
552    if (Math.floor(this.textHeight) > Math.floor(scrollMaxHeight + 1)) {
553      return scrollMaxHeight
554    } else {
555      scrollMaxHeight = undefined
556      return scrollMaxHeight
557    }
558  }
559
560  private getLayoutWeight(): number {
561    let layoutWeight: number
562    if ((this.icon.image !== '' && this.icon.image !== void (0)) ||
563      (this.title.text !== '' && this.title.text !== void (0)) ||
564      (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) ||
565      (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0))) {
566      layoutWeight = 1
567    } else {
568      layoutWeight = 0
569    }
570    return layoutWeight
571  }
572
573  private resourceToVp(value: Resource): number {
574    try {
575      if ((value as Resource).id !== -1) {
576        return px2vp(getContext(this).resourceManager.getNumber((value as Resource).id))
577      } else {
578        return px2vp(getContext(this)
579          .resourceManager
580          .getNumberByName(((value.params as string[])[0]).split('.')[2]))
581      }
582    } catch (error) {
583      return POPUP_DEFAULT_MAXWIDTH
584    }
585  }
586
587  private toVp(value: Dimension | Length | undefined): number {
588    let defaultDisplaySync: display.Display | undefined = undefined;
589    try {
590      defaultDisplaySync = display.getDefaultDisplaySync()
591    } catch (error) {
592      console.error(`Ace Popup getDefaultDisplaySync, error: ${error.toString()}`);
593      return Number.NEGATIVE_INFINITY;
594    }
595    if (value === void (0)) {
596      return Number.NEGATIVE_INFINITY
597    }
598    switch (typeof (value)) {
599      case 'number':
600        return value as number
601      case 'object':
602        try {
603          let returnValue = this.resourceToVp(value);
604          if (returnValue === 0 &&
605            !isValidResource(getContext(this), value)) {
606            return Number.NEGATIVE_INFINITY;
607          }
608          return returnValue;
609        } catch (error) {
610          return Number.NEGATIVE_INFINITY
611        }
612      case 'string':
613        let regex: RegExp = new RegExp('(-?\\d+(?:\\.\\d+)?)_?(fp|vp|px|lpx|%)?$', 'i');
614        let matches: RegExpMatchArray | null = value.match(regex);
615        if (!matches) {
616          return Number.NEGATIVE_INFINITY
617        }
618        let length: number = Number(matches?.[1] ?? 0);
619        let unit: string = matches?.[2] ?? 'vp'
620        switch (unit.toLowerCase()) {
621          case 'px':
622            length = px2vp(length)
623            break
624          case 'fp':
625            length = px2vp(fp2px(length))
626            break
627          case 'lpx':
628            length = px2vp(lpx2px(length))
629            break
630          case '%':
631            length = length / 100 * px2vp(defaultDisplaySync.width);
632            break
633          case 'vp':
634            break
635          default:
636            break
637        }
638        return length
639      default:
640        return Number.NEGATIVE_INFINITY
641    }
642  }
643
644  private getApplyMaxSize(): ConstraintSizeOptions {
645    let applyMaxWidth: number | undefined = undefined;
646    let applyMaxHeight: number | undefined = undefined;
647    let applyMaxSize: ConstraintSizeOptions | undefined = undefined;
648    let defaultDisplaySync: display.Display | undefined = undefined;
649    let maxWidthSize = 400;
650    try {
651      defaultDisplaySync = display.getDefaultDisplaySync()
652    } catch (error) {
653      console.error(`Ace Popup getDefaultDisplaySync, error: ${error.toString()}`);
654      this.messageMaxWeight = 400
655      return applyMaxSize = { maxWidth: 400, maxHeight: 480 }
656    }
657    if (this.maxWidth !== undefined) {
658      if (typeof this.maxWidth === 'number' && this.maxWidth >= 0) {
659        maxWidthSize = this.maxWidth;
660      } else if (typeof this.maxWidth === 'number' && this.maxWidth < 0) {
661        maxWidthSize = POPUP_DEFAULT_MAXWIDTH
662      } else {
663        maxWidthSize = this.toVp(this.maxWidth);
664      }
665    }
666    if (px2vp(defaultDisplaySync.width) > maxWidthSize) {
667      applyMaxWidth = maxWidthSize
668    } else {
669      if (defaultDisplaySync.width != 0) {
670        applyMaxWidth = px2vp(defaultDisplaySync.width)
671      } else {
672        // The previewer does not support the display interface to use abnormal values
673        applyMaxWidth = -1
674      }
675    }
676    if (px2vp(defaultDisplaySync.height) > 480) {
677      applyMaxHeight = 480
678    } else {
679      applyMaxHeight = px2vp(defaultDisplaySync.height) - 40 - 40
680    }
681    applyMaxSize = { maxWidth: applyMaxWidth, maxHeight: applyMaxHeight }
682    this.messageMaxWidth = applyMaxWidth;
683    this.messageMaxWeight = this.getMessageMaxWeight()
684    return applyMaxSize
685  }
686
687  private getTitleTextAlign(): TextAlign {
688    let titleAlign = TextAlign.Start;
689    if ((Configuration.getLocale().dir === 'rtl') && this.popupDirection === Direction.Auto) {
690      titleAlign = TextAlign.End;
691    }
692    return titleAlign
693  }
694
695  build() {
696    Row() {
697      if (this.icon.image !== '' && this.icon.image !== void (0)) {
698        Image(this.getIconImage())
699          .direction(this.popupDirection)
700          .width(this.getIconWidth())
701          .height(this.getIconHeight())
702          .margin(this.getIconMargin())
703          .fillColor(this.getIconFillColor())
704          .borderRadius(this.getIconBorderRadius())
705          .draggable(false)
706      }
707      if (this.title.text !== '' && this.title.text !== void (0)) {
708        Column() {
709          Flex({ alignItems: ItemAlign.Start }) {
710            Text(this.getTitleText())
711              .direction(this.popupDirection)
712              .flexGrow(1)
713              .maxLines(2)
714              .align(Alignment.Start)
715              .padding(this.getTitlePadding())
716              .minFontSize(this.getTitleMinFontSize())
717              .textOverflow({ overflow: TextOverflow.Ellipsis })
718              .fontWeight(this.getTitleFontWeight())
719              .fontSize(this.getTitleFontSize())
720              .fontColor(this.getTitleFontColor())
721              .constraintSize({ minHeight: this.getCloseButtonHeight() })
722              .textAlign(this.getTitleTextAlign())
723            if (this.showClose || this.showClose === void (0)) {
724              Button() {
725                SymbolGlyph()
726                  .fontColor([this.getCloseButtonFillColor()])
727                  .fontSize(this.getCloseButtonSymbolSize())
728                  .direction(this.popupDirection)
729                  .attributeModifier(this.theme.closeButton.symbolStyle)
730                  .focusable(true)
731                  .draggable(false)
732              }
733              .direction(this.popupDirection)
734              .width(this.getCloseButtonWidth())
735              .height(this.getCloseButtonHeight())
736              .padding(this.getCloseButtonPadding())
737              .backgroundColor(this.closeButtonBackgroundColor)
738              .flexShrink(0)
739              .accessibilityText(this.theme.closeButton.accessibilityMessage)
740              .onHover((isHover: boolean) => {
741                if (isHover) {
742                  this.closeButtonBackgroundColor = this.getCloseButtonHoverColor()
743                } else {
744                  this.closeButtonBackgroundColor = this.getCloseButtonBackgroundColor()
745                }
746              })
747              .onClick(() => {
748                if (this.onClose) {
749                  this.onClose();
750                }
751              })
752            }
753          }
754          .direction(this.popupDirection)
755          .width("100%")
756          .margin(this.getTitleMargin())
757          .onAreaChange((_, rect) => {
758            this.titleHeight = rect.height as number
759          })
760
761          Scroll() {
762            Text(this.getMessageText())
763              .direction(this.popupDirection)
764              .fontSize(this.getMessageFontSize())
765              .fontColor(this.getMessageFontColor())
766              .fontWeight(this.getMessageFontWeight())
767              .constraintSize({ minHeight: this.getCloseButtonHeight() })
768              .onAreaChange((_, rect) => {
769                this.textHeight = rect.height as number
770              })
771          }
772          .direction(this.popupDirection)
773          .width("100%")
774          .align(Alignment.TopStart)
775          .padding(this.getMessagePadding())
776          .scrollBar(BarState.Auto)
777          .scrollable(ScrollDirection.Vertical)
778          .constraintSize({ maxHeight: this.getScrollMaxHeight() })
779          .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: false })
780
781          Flex({ wrap: FlexWrap.Wrap }) {
782            if (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) {
783              Button() {
784                Text(this.getFirstButtonText())
785                  .direction(this.popupDirection)
786                  .maxLines(2)
787                  .focusable(true)
788                  .fontSize(this.getFirstButtonFontSize())
789                  .fontColor(this.getFirstButtonFontColor())
790                  .fontWeight(this.getButtonFontWeight())
791                  .minFontSize(this.getButtonMinFontSize())
792                  .textOverflow({ overflow: TextOverflow.Ellipsis })
793              }
794              .type(ButtonType.Normal)
795              .borderRadius($r('sys.float.popup_button_border_radius'))
796              .direction(this.popupDirection)
797              .margin(this.getButtonMargin())
798              .padding(this.getButtonTextPadding())
799              .backgroundColor(this.firstButtonBackgroundColor)
800              .onHover((isHover: boolean) => {
801                if (isHover) {
802                  this.firstButtonBackgroundColor = this.getButtonHoverColor()
803                } else {
804                  this.firstButtonBackgroundColor = this.getButtonBackgroundColor()
805                }
806              })
807              .onClick(() => {
808                if (this.buttons?.[0]?.action) {
809                  this.buttons?.[0]?.action();
810                }
811              })
812            }
813            if (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0)) {
814              Button() {
815                Text(this.getSecondButtonText())
816                  .direction(this.popupDirection)
817                  .maxLines(2)
818                  .focusable(true)
819                  .fontSize(this.getSecondButtonFontSize())
820                  .fontColor(this.getSecondButtonFontColor())
821                  .fontWeight(this.getButtonFontWeight())
822                  .minFontSize(this.getButtonMinFontSize())
823                  .textOverflow({ overflow: TextOverflow.Ellipsis })
824              }
825              .type(ButtonType.Normal)
826              .borderRadius($r('sys.float.popup_button_border_radius'))
827              .direction(this.popupDirection)
828              .margin(this.getButtonMargin())
829              .padding(this.getButtonTextPadding())
830              .backgroundColor(this.secondButtonBackgroundColor)
831              .onHover((isHover: boolean) => {
832                if (isHover) {
833                  this.secondButtonBackgroundColor = this.getButtonHoverColor()
834                } else {
835                  this.secondButtonBackgroundColor = this.getButtonBackgroundColor()
836                }
837              })
838              .onClick(() => {
839                if (this.buttons?.[1]?.action) {
840                  this.buttons?.[1]?.action();
841                }
842              })
843            }
844          }
845          .direction(this.popupDirection)
846          .margin(this.getButtonTextMargin())
847          .flexGrow(1)
848          .onAreaChange((_, rect) => {
849            if ((this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) ||
850              (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0))) {
851              this.buttonHeight = rect.height as number
852            } else {
853              this.buttonHeight = 0
854            }
855          })
856        }
857        .direction(this.popupDirection)
858        .layoutWeight(this.getLayoutWeight())
859      } else {
860        Column() {
861          Flex().height(0).onAreaChange((_, rect) => {
862            this.titleHeight = rect.height as number
863          })
864          Row() {
865            Scroll() {
866              Text(this.getMessageText())
867                .direction(this.popupDirection)
868                .fontSize(this.getMessageFontSize())
869                .fontColor(this.getMessageFontColor())
870                .fontWeight(this.getMessageFontWeight())
871                .constraintSize({ maxWidth: this.messageMaxWeight, minHeight: this.getCloseButtonHeight() })
872                .onAreaChange((_, rect) => {
873                  this.textHeight = rect.height as number
874                })
875            }
876            .direction(this.popupDirection)
877            .layoutWeight(this.getLayoutWeight())
878            .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: false })
879            .align(Alignment.TopStart)
880            .padding(this.getMessagePadding())
881            .scrollBar(BarState.Auto)
882            .scrollable(ScrollDirection.Vertical)
883            .constraintSize({ maxHeight: this.getScrollMaxHeight() })
884
885            if (this.showClose || this.showClose === void (0)) {
886              Button() {
887                SymbolGlyph()
888                  .fontColor([this.getCloseButtonFillColor()])
889                  .fontSize(this.getCloseButtonSymbolSize())
890                  .direction(this.popupDirection)
891                  .attributeModifier(this.theme.closeButton.symbolStyle)
892                  .focusable(true)
893                  .draggable(false)
894              }
895              .direction(this.popupDirection)
896              .width(this.getCloseButtonWidth())
897              .height(this.getCloseButtonHeight())
898              .padding(this.getCloseButtonPadding())
899              .backgroundColor(this.closeButtonBackgroundColor)
900              .flexShrink(0)
901              .accessibilityText(this.theme.closeButton.accessibilityMessage)
902              .onHover((isHover: boolean) => {
903                if (isHover) {
904                  this.closeButtonBackgroundColor = this.getCloseButtonHoverColor()
905                } else {
906                  this.closeButtonBackgroundColor = this.getCloseButtonBackgroundColor()
907                }
908              })
909              .onClick(() => {
910                if (this.onClose) {
911                  this.onClose();
912                }
913              })
914            }
915          }
916          .direction(this.popupDirection)
917          .alignItems(VerticalAlign.Top)
918
919          Flex({ wrap: FlexWrap.Wrap }) {
920            if (this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) {
921              Button() {
922                Text(this.getFirstButtonText())
923                  .direction(this.popupDirection)
924                  .maxLines(2)
925                  .focusable(true)
926                  .fontSize(this.getFirstButtonFontSize())
927                  .fontColor(this.getFirstButtonFontColor())
928                  .fontWeight(this.getButtonFontWeight())
929                  .minFontSize(this.getButtonMinFontSize())
930                  .textOverflow({ overflow: TextOverflow.Ellipsis })
931              }
932              .type(ButtonType.Normal)
933              .borderRadius($r('sys.float.popup_button_border_radius'))
934              .direction(this.popupDirection)
935              .margin(this.getButtonMargin())
936              .padding(this.getButtonTextPadding())
937              .backgroundColor(this.firstButtonBackgroundColor)
938              .onHover((isHover: boolean) => {
939                if (isHover) {
940                  this.firstButtonBackgroundColor = this.getButtonHoverColor()
941                } else {
942                  this.firstButtonBackgroundColor = this.getButtonBackgroundColor()
943                }
944              })
945              .onClick(() => {
946                if (this.buttons?.[0]?.action) {
947                  this.buttons?.[0]?.action();
948                }
949              })
950            }
951            if (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0)) {
952              Button() {
953                Text(this.getSecondButtonText())
954                  .direction(this.popupDirection)
955                  .maxLines(2)
956                  .focusable(true)
957                  .fontSize(this.getSecondButtonFontSize())
958                  .fontColor(this.getSecondButtonFontColor())
959                  .fontWeight(this.getButtonFontWeight())
960                  .minFontSize(this.getButtonMinFontSize())
961                  .textOverflow({ overflow: TextOverflow.Ellipsis })
962              }
963              .type(ButtonType.Normal)
964              .borderRadius($r('sys.float.popup_button_border_radius'))
965              .direction(this.popupDirection)
966              .margin(this.getButtonMargin())
967              .padding(this.getButtonTextPadding())
968              .backgroundColor(this.secondButtonBackgroundColor)
969              .onHover((isHover: boolean) => {
970                if (isHover) {
971                  this.secondButtonBackgroundColor = this.getButtonHoverColor()
972                } else {
973                  this.secondButtonBackgroundColor = this.getButtonBackgroundColor()
974                }
975              })
976              .onClick(() => {
977                if (this.buttons?.[1]?.action) {
978                  this.buttons?.[1]?.action();
979                }
980              })
981            }
982          }
983          .direction(this.popupDirection)
984          .margin(this.getButtonTextMargin())
985          .flexGrow(1)
986          .onAreaChange((_, rect) => {
987            if ((this.buttons?.[0]?.text !== '' && this.buttons?.[0]?.text !== void (0)) ||
988              (this.buttons?.[1]?.text !== '' && this.buttons?.[1]?.text !== void (0))) {
989              this.buttonHeight = rect.height as number
990            } else {
991              this.buttonHeight = 0
992            }
993          })
994        }
995        .direction(this.popupDirection)
996        .layoutWeight(this.getLayoutWeight())
997      }
998    }
999    .direction(this.popupDirection)
1000    .alignItems(VerticalAlign.Top)
1001    .padding(this.getWindowsPadding())
1002    .constraintSize(this.applySizeOptions)
1003    .constraintSize(this.getApplyMaxSize())
1004    .key('applyContent')
1005    .onAreaChange((_, rect) => {
1006      this.applyHeight = rect.height as number
1007    })
1008  }
1009}
1010