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