• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #include "core/components_ng/pattern/dialog/dialog_pattern.h"
16 
17 #include <climits>
18 #include <cstdint>
19 #include <cstring>
20 #include <optional>
21 #include <string>
22 
23 #include "base/geometry/dimension.h"
24 #include "base/json/json_util.h"
25 #include "base/memory/ace_type.h"
26 #include "base/memory/referenced.h"
27 #include "base/utils/utils.h"
28 #include "bridge/common/dom/dom_type.h"
29 #include "core/common/container.h"
30 #include "core/components/button/button_theme.h"
31 #include "core/components/common/properties/alignment.h"
32 #include "core/components/theme/icon_theme.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/base/ui_node.h"
35 #include "core/components_ng/base/view_stack_processor.h"
36 #include "core/components_ng/event/gesture_event_hub.h"
37 #include "core/components_ng/layout/layout_property.h"
38 #include "core/components_ng/pattern/button/button_layout_property.h"
39 #include "core/components_ng/pattern/button/button_pattern.h"
40 #include "core/components_ng/pattern/divider/divider_layout_property.h"
41 #include "core/components_ng/pattern/divider/divider_model_ng.h"
42 #include "core/components_ng/pattern/divider/divider_pattern.h"
43 #include "core/components_ng/pattern/flex/flex_layout_algorithm.h"
44 #include "core/components_ng/pattern/flex/flex_layout_property.h"
45 #include "core/components_ng/pattern/image/image_pattern.h"
46 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
47 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
48 #include "core/components_ng/pattern/list/list_item_pattern.h"
49 #include "core/components_ng/pattern/list/list_layout_property.h"
50 #include "core/components_ng/pattern/list/list_paint_property.h"
51 #include "core/components_ng/pattern/list/list_pattern.h"
52 #include "core/components_ng/pattern/overlay/overlay_manager.h"
53 #include "core/components_ng/pattern/relative_container/relative_container_pattern.h"
54 #include "core/components_ng/pattern/relative_container/relative_container_view.h"
55 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
56 #include "core/components_ng/pattern/text/text_layout_property.h"
57 #include "core/components_ng/pattern/text/text_pattern.h"
58 #include "core/components_ng/property/calc_length.h"
59 #include "core/components_ng/property/measure_property.h"
60 #include "core/components_v2/inspector/inspector_constants.h"
61 #include "core/event/key_event.h"
62 #include "core/event/touch_event.h"
63 #include "core/pipeline/base/element_register.h"
64 #include "core/pipeline_ng/pipeline_context.h"
65 
66 namespace OHOS::Ace::NG {
67 
68 namespace {
69 constexpr int32_t SHEET_INFO_IDX = -2;
70 constexpr Dimension SHEET_IMAGE_MARGIN = 16.0_vp;
71 constexpr Dimension SHEET_DIVIDER_WIDTH = 1.0_px;
72 constexpr Dimension SHEET_LIST_PADDING = 24.0_vp;
73 constexpr Dimension DIALOG_BUTTON_TEXT_SIZE = 16.0_fp;
74 constexpr Color DEFAULT_BUTTON_COLOR = Color(0xff007dff);
75 const CalcLength SHEET_IMAGE_SIZE(40.0_vp);
76 constexpr int32_t TWO_BUTTON_MODE = 2;
77 constexpr int32_t ONE_BUTTON_MODE = 1;
78 constexpr int32_t START_CHILD_INDEX = 0;
79 constexpr uint32_t DIALOG_TITLE_MAXLINES = 1;
80 constexpr Dimension DIALOG_ONE_TITLE_ALL_HEIGHT = 56.0_vp;
81 constexpr Dimension DIALOG_TITLE_CONTENT_HEIGHT = 35.0_px;
82 constexpr int32_t DIALOG_TITLE_AVE_BY_2 = 2;
83 constexpr Dimension DIALOG_CONTENT_PADDING_TOP = 0.0_vp;
84 constexpr Dimension DIALOG_SUBTITLE_PADDING_LEFT = 24.0_vp;
85 constexpr Dimension DIALOG_SUBTITLE_PADDING_RIGHT = 24.0_vp;
86 constexpr Dimension DIALOG_TWO_TITLE_ZERO_SPACE = 0.0_vp;
87 constexpr Dimension DIALOG_TWO_TITLE_SPACE = 16.0_vp;
88 constexpr float BUTTON_TEXT_OPACITY = 0.6f; // [Button Component Defect]
89 } // namespace
90 
OnModifyDone()91 void DialogPattern::OnModifyDone()
92 {
93     Pattern::OnModifyDone();
94     auto host = GetHost();
95     CHECK_NULL_VOID(host);
96     auto gestureHub = host->GetOrCreateGestureEventHub();
97     CHECK_NULL_VOID(gestureHub);
98 
99     if (!onClick_) {
100         InitClickEvent(gestureHub);
101     }
102     auto focusHub = host->GetOrCreateFocusHub();
103     CHECK_NULL_VOID(focusHub);
104     RegisterOnKeyEvent(focusHub);
105 }
106 
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)107 void DialogPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
108 {
109     GestureEventFunc task = [weak = WeakClaim(this)](const GestureEvent& info) {
110         auto pattern = weak.Upgrade();
111         CHECK_NULL_VOID_NOLOG(pattern);
112         pattern->HandleClick(info);
113     };
114     onClick_ = MakeRefPtr<ClickEvent>(std::move(task));
115     gestureHub->AddClickEvent(onClick_);
116 }
117 
HandleClick(const GestureEvent & info)118 void DialogPattern::HandleClick(const GestureEvent& info)
119 {
120     if (info.GetSourceDevice() == SourceType::KEYBOARD) {
121         return;
122     }
123     auto host = GetHost();
124     CHECK_NULL_VOID(host);
125     auto props = host->GetLayoutProperty<DialogLayoutProperty>();
126     CHECK_NULL_VOID(props);
127     auto globalOffset = host->GetPaintRectOffset();
128     auto autoCancel = props->GetAutoCancel().value_or(true);
129     if (autoCancel) {
130         auto content = DynamicCast<FrameNode>(host->GetChildAtIndex(0));
131         CHECK_NULL_VOID(content);
132         auto contentRect = content->GetGeometryNode()->GetFrameRect();
133         // close dialog if clicked outside content rect
134         auto&& clickPosition = info.GetGlobalLocation();
135         if (!contentRect.IsInRegion(
136                 PointF(clickPosition.GetX() - globalOffset.GetX(), clickPosition.GetY() - globalOffset.GetY()))) {
137             PopDialog(-1);
138         }
139     }
140 }
141 
PopDialog(int32_t buttonIdx=-1)142 void DialogPattern::PopDialog(int32_t buttonIdx = -1)
143 {
144     LOGI("DialogPattern::PopDialog from click");
145     auto pipeline = PipelineContext::GetCurrentContext();
146     CHECK_NULL_VOID(pipeline);
147     auto overlayManager = pipeline->GetOverlayManager();
148     CHECK_NULL_VOID(overlayManager);
149     auto host = GetHost();
150     CHECK_NULL_VOID(host);
151     if (host->IsRemoving()) {
152         LOGI("Dialog already in close animation, no need to fire event again.");
153         return;
154     }
155 
156     auto hub = host->GetEventHub<DialogEventHub>();
157     if (buttonIdx != -1) {
158         hub->FireSuccessEvent(buttonIdx);
159     } else {
160         // trigger onCancel callback
161         hub->FireCancelEvent();
162     }
163 
164     overlayManager->CloseDialog(host);
165 }
166 
167 // set render context properties of content frame
UpdateContentRenderContext(const RefPtr<FrameNode> & contentNode,const DialogProperties & props)168 void DialogPattern::UpdateContentRenderContext(const RefPtr<FrameNode>& contentNode, const DialogProperties& props)
169 {
170     auto contentRenderContext = contentNode->GetRenderContext();
171     CHECK_NULL_VOID(contentRenderContext);
172     contentRenderContext->UpdateBackgroundColor(props.backgroundColor.value_or(dialogTheme_->GetBackgroundColor()));
173 
174     if (props.borderRadius.has_value()) {
175         contentRenderContext->UpdateBorderRadius(props.borderRadius.value());
176     } else {
177         BorderRadiusProperty radius;
178         radius.SetRadius(dialogTheme_->GetRadius().GetX());
179         contentRenderContext->UpdateBorderRadius(radius);
180     }
181     contentRenderContext->SetClipToBounds(true);
182 }
183 
CreateDialogScroll(const DialogProperties & dialogProps)184 RefPtr<FrameNode> DialogPattern::CreateDialogScroll(const DialogProperties& dialogProps)
185 {
186     auto scroll = FrameNode::CreateFrameNode(
187         V2::SCROLL_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ScrollPattern>());
188     CHECK_NULL_RETURN(scroll, nullptr);
189     auto props = scroll->GetLayoutProperty<ScrollLayoutProperty>();
190     props->UpdateAxis(Axis::VERTICAL);
191     props->UpdateAlignment(Alignment::CENTER_LEFT);
192     // If title not exist, set scroll align center so that text align center.
193     if ((dialogProps.title.empty() && dialogProps.subtitle.empty()) ||
194         SystemProperties::GetDeviceType() == DeviceType::WATCH) {
195         props->UpdateAlignSelf(FlexAlign::CENTER);
196     } else {
197         props->UpdateAlignSelf(FlexAlign::FLEX_START);
198     }
199     return scroll;
200 }
201 
BuildChild(const DialogProperties & props)202 void DialogPattern::BuildChild(const DialogProperties& props)
203 {
204     LOGI("build dialog child");
205     // append customNode
206     if (customNode_) {
207         // wrap custom node to set background color and round corner
208         auto contentWrapper = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG,
209             ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LinearLayoutPattern>(true));
210         CHECK_NULL_VOID(contentWrapper);
211         if (!props.customStyle) {
212             UpdateContentRenderContext(contentWrapper, props);
213         }
214         customNode_->MountToParent(contentWrapper);
215         auto dialog = GetHost();
216         contentWrapper->MountToParent(dialog);
217         return;
218     }
219 
220     // Make dialog Content Column
221     auto contentColumn = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
222         AceType::MakeRefPtr<LinearLayoutPattern>(true));
223     CHECK_NULL_VOID(contentColumn);
224 
225     if (!props.title.empty() || !props.subtitle.empty()) {
226         auto title = BuildTitle(props);
227         CHECK_NULL_VOID(title);
228         contentColumn->AddChild(title);
229     }
230 
231     if (!props.content.empty()) {
232         auto content = BuildContent(props);
233         CHECK_NULL_VOID(content);
234         // create a scroll
235         auto scroll = CreateDialogScroll(props);
236         CHECK_NULL_VOID(scroll);
237         content->MountToParent(scroll);
238         scroll->MountToParent(contentColumn);
239         scroll->MarkModifyDone();
240     }
241 
242     if (!props.customStyle) {
243         UpdateContentRenderContext(contentColumn, props);
244     }
245 
246     auto columnProp = AceType::DynamicCast<LinearLayoutProperty>(contentColumn->GetLayoutProperty());
247     CHECK_NULL_VOID(columnProp);
248     // content is full screen in Watch mode
249     auto deviceType = SystemProperties::GetDeviceType();
250     if (deviceType == DeviceType::WATCH) {
251         columnProp->UpdateMeasureType(MeasureType::MATCH_PARENT);
252     } else {
253         columnProp->UpdateMeasureType(MeasureType::MATCH_CONTENT);
254     }
255 
256     // build ActionSheet child
257     if (props.type == DialogType::ACTION_SHEET && !props.sheetsInfo.empty()) {
258         auto sheetContainer = BuildSheet(props.sheetsInfo);
259         CHECK_NULL_VOID(sheetContainer);
260         sheetContainer->MountToParent(contentColumn);
261         // scrollable
262         sheetContainer->MarkModifyDone();
263     }
264 
265     // Make Menu node if hasMenu (actionMenu)
266     if (props.isMenu) {
267         auto menu = BuildMenu(props.buttons);
268         CHECK_NULL_VOID(menu);
269         menu->MountToParent(contentColumn);
270     } else {
271         // build buttons
272         if (!props.buttons.empty()) {
273             auto buttonContainer = BuildButtons(props.buttons, props.buttonDirection);
274             CHECK_NULL_VOID(buttonContainer);
275             buttonContainer->MountToParent(contentColumn);
276         }
277     }
278 
279     auto dialog = GetHost();
280     contentColumn->MountToParent(dialog);
281 }
282 
BuildMainTitle(const DialogProperties & dialogProperties)283 RefPtr<FrameNode> DialogPattern::BuildMainTitle(const DialogProperties& dialogProperties)
284 {
285     auto title = FrameNode::CreateFrameNode(
286         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
287     auto titleProp = AceType::DynamicCast<TextLayoutProperty>(title->GetLayoutProperty());
288     CHECK_NULL_RETURN(titleProp, nullptr);
289     titleProp->UpdateMaxLines(DIALOG_TITLE_MAXLINES);
290     titleProp->UpdateTextOverflow(TextOverflow::ELLIPSIS);
291     titleProp->UpdateAdaptMaxFontSize(dialogTheme_->GetTitleTextStyle().GetFontSize());
292     titleProp->UpdateAdaptMinFontSize(dialogTheme_->GetTitleTextStyle().GetFontSize());
293     std::string titleContent = dialogProperties.title.empty() ? dialogProperties.subtitle : dialogProperties.title;
294     titleProp->UpdateContent(titleContent);
295     auto titleStyle = dialogTheme_->GetTitleTextStyle();
296     titleProp->UpdateFontSize(titleStyle.GetFontSize());
297     titleProp->UpdateFontWeight(titleStyle.GetFontWeight());
298     titleProp->UpdateTextColor(titleStyle.GetTextColor());
299     PaddingProperty titlePadding;
300     auto paddingInTheme = (dialogProperties.content.empty() && dialogProperties.buttons.empty())
301                               ? dialogTheme_->GetTitleDefaultPadding()
302                               : dialogTheme_->GetTitleAdjustPadding();
303     titlePadding.left = CalcLength(paddingInTheme.Left());
304     titlePadding.right = CalcLength(paddingInTheme.Right());
305     if (!dialogProperties.title.empty() && !dialogProperties.subtitle.empty()) {
306         titlePadding.top = CalcLength(DIALOG_TWO_TITLE_SPACE);
307         titlePadding.bottom = CalcLength(DIALOG_TWO_TITLE_ZERO_SPACE);
308     } else {
309         titlePadding.top = CalcLength(
310             (DIALOG_ONE_TITLE_ALL_HEIGHT - Dimension(DIALOG_TITLE_CONTENT_HEIGHT.ConvertToVp(), DimensionUnit::VP)) /
311             DIALOG_TITLE_AVE_BY_2);
312         titlePadding.bottom = CalcLength(
313             (DIALOG_ONE_TITLE_ALL_HEIGHT - Dimension(DIALOG_TITLE_CONTENT_HEIGHT.ConvertToVp(), DimensionUnit::VP)) /
314             DIALOG_TITLE_AVE_BY_2);
315     }
316     titleProp->UpdatePadding(titlePadding);
317 
318     // XTS inspector value
319     title_ = dialogProperties.title;
320     subtitle_ = dialogProperties.subtitle;
321 
322     auto titleRow = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
323         AceType::MakeRefPtr<LinearLayoutPattern>(false));
324     CHECK_NULL_RETURN(titleRow, nullptr);
325     auto titleRowProps = titleRow->GetLayoutProperty<LinearLayoutProperty>();
326     CHECK_NULL_RETURN(titleRowProps, nullptr);
327     titleRowProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
328     titleRowProps->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
329     title->MountToParent(titleRow);
330     title->MarkModifyDone();
331     return titleRow;
332 }
333 
BuildSubTitle(const DialogProperties & dialogProperties)334 RefPtr<FrameNode> DialogPattern::BuildSubTitle(const DialogProperties& dialogProperties)
335 {
336     auto subtitle = FrameNode::CreateFrameNode(
337         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
338     auto titleProp = AceType::DynamicCast<TextLayoutProperty>(subtitle->GetLayoutProperty());
339     CHECK_NULL_RETURN(titleProp, nullptr);
340     auto titleStyle = dialogTheme_->GetSubTitleTextStyle();
341     titleProp->UpdateMaxLines(DIALOG_TITLE_MAXLINES);
342     titleProp->UpdateTextOverflow(TextOverflow::ELLIPSIS);
343     titleProp->UpdateAdaptMaxFontSize(titleStyle.GetFontSize());
344     titleProp->UpdateAdaptMinFontSize(titleStyle.GetFontSize());
345     titleProp->UpdateContent(dialogProperties.subtitle);
346     titleProp->UpdateFontSize(titleStyle.GetFontSize());
347     titleProp->UpdateTextColor(titleStyle.GetTextColor());
348     PaddingProperty titlePadding;
349     titlePadding.left = CalcLength(DIALOG_SUBTITLE_PADDING_LEFT);
350     titlePadding.right = CalcLength(DIALOG_SUBTITLE_PADDING_RIGHT);
351     titlePadding.top = CalcLength(DIALOG_TWO_TITLE_ZERO_SPACE);
352     titlePadding.bottom = CalcLength(DIALOG_TWO_TITLE_SPACE);
353     titleProp->UpdatePadding(titlePadding);
354 
355     // XTS inspector value
356     subtitle_ = dialogProperties.subtitle;
357 
358     auto subtitleRow = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
359         AceType::MakeRefPtr<LinearLayoutPattern>(false));
360     CHECK_NULL_RETURN(subtitleRow, nullptr);
361     auto subtitleRowProps = subtitleRow->GetLayoutProperty<LinearLayoutProperty>();
362     CHECK_NULL_RETURN(subtitleRowProps, nullptr);
363     subtitleRowProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
364     subtitleRowProps->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
365     subtitle->MountToParent(subtitleRow);
366     subtitle->MarkModifyDone();
367     return subtitleRow;
368 }
369 
BuildTitle(const DialogProperties & dialogProperties)370 RefPtr<FrameNode> DialogPattern::BuildTitle(const DialogProperties& dialogProperties)
371 {
372     auto titleRow = BuildMainTitle(dialogProperties);
373     if (!dialogProperties.title.empty() && !dialogProperties.subtitle.empty()) {
374         auto titleColumn = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG,
375             ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LinearLayoutPattern>(true));
376         CHECK_NULL_RETURN(titleColumn, nullptr);
377         auto columnProps = titleColumn->GetLayoutProperty<LinearLayoutProperty>();
378         CHECK_NULL_RETURN(columnProps, nullptr);
379         columnProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
380         columnProps->UpdateMeasureType(MeasureType::MATCH_CONTENT);
381         auto subtitleRow = BuildSubTitle(dialogProperties);
382         titleColumn->AddChild(titleRow);
383         titleColumn->AddChild(subtitleRow);
384         return titleColumn;
385     }
386     return titleRow;
387 }
388 
BuildContent(const DialogProperties & props)389 RefPtr<FrameNode> DialogPattern::BuildContent(const DialogProperties& props)
390 {
391     // Make Content node
392     auto contentNode = FrameNode::CreateFrameNode(
393         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
394     auto contentProp = AceType::DynamicCast<TextLayoutProperty>(contentNode->GetLayoutProperty());
395     CHECK_NULL_RETURN(contentProp, nullptr);
396     // textAlign always align start. When text line count 1 and title doesn't exist, set text center position.
397     contentProp->UpdateTextAlign(TextAlign::START);
398     contentProp->UpdateContent(props.content);
399     auto contentStyle = dialogTheme_->GetContentTextStyle();
400     contentProp->UpdateFontSize(contentStyle.GetFontSize());
401     contentProp->UpdateTextColor(contentStyle.GetTextColor());
402     LOGD("content = %s", props.content.c_str());
403     // update padding
404     Edge contentPaddingInTheme;
405     PaddingProperty contentPadding;
406     if (!props.title.empty() || !props.subtitle.empty()) {
407         contentPaddingInTheme =
408             props.buttons.empty() ? dialogTheme_->GetDefaultPadding() : dialogTheme_->GetAdjustPadding();
409         contentPadding.top = CalcLength(DIALOG_CONTENT_PADDING_TOP);
410     } else {
411         contentPaddingInTheme =
412             props.buttons.empty() ? dialogTheme_->GetContentDefaultPadding() : dialogTheme_->GetContentAdjustPadding();
413         contentPadding.top = CalcLength(contentPaddingInTheme.Top());
414     }
415     contentPadding.left = CalcLength(contentPaddingInTheme.Left());
416     contentPadding.right = CalcLength(contentPaddingInTheme.Right());
417     contentPadding.bottom = CalcLength(contentPaddingInTheme.Bottom());
418     contentProp->UpdatePadding(contentPadding);
419 
420     // XTS inspector value
421     message_ = props.content;
422     contentNode->MarkModifyDone();
423     return contentNode;
424 }
425 
426 // to close dialog when clicked, use button index in Prompt to trigger success callback
BindCloseCallBack(const RefPtr<GestureEventHub> & hub,int32_t buttonIdx)427 void DialogPattern::BindCloseCallBack(const RefPtr<GestureEventHub>& hub, int32_t buttonIdx)
428 {
429     auto host = GetHost();
430     auto closeCallback = [weak = WeakClaim(RawPtr(host)), buttonIdx](GestureEvent& /*info*/) {
431         auto dialog = weak.Upgrade();
432         CHECK_NULL_VOID(dialog);
433         dialog->GetPattern<DialogPattern>()->PopDialog(buttonIdx);
434     };
435 
436     hub->AddClickEvent(AceType::MakeRefPtr<ClickEvent>(closeCallback));
437 }
438 
ParseButtonFontColorAndBgColor(const ButtonInfo & params,std::string & textColor,std::optional<Color> & bgColor)439 void DialogPattern::ParseButtonFontColorAndBgColor(
440     const ButtonInfo& params, std::string& textColor, std::optional<Color>& bgColor)
441 {
442     // Parse Button Style
443     if (params.dlgButtonStyle.has_value()) {
444         switch (params.dlgButtonStyle.value()) {
445             case DialogButtonStyle::DEFAULT:
446                 textColor = dialogTheme_->GetButtonDefaultFontColor().ColorToString();
447                 bgColor = dialogTheme_->GetButtonDefaultBgColor();
448                 break;
449             case DialogButtonStyle::HIGHTLIGHT:
450                 textColor = dialogTheme_->GetButtonHighlightFontColor().ColorToString();
451                 bgColor = dialogTheme_->GetButtonHighlightBgColor();
452                 break;
453             default:
454                 break;
455         }
456     }
457 
458     // font color and background color
459     if (!params.textColor.empty()) {
460         textColor = params.textColor;
461     }
462     if (params.isBgColorSetted) {
463         bgColor = params.bgColor;
464     }
465 
466     // Parse default focus
467     if (textColor.empty()) {
468         if (params.defaultFocus && isFirstDefaultFocus_) {
469             textColor = dialogTheme_->GetButtonHighlightFontColor().ColorToString();
470         } else {
471             textColor = dialogTheme_->GetButtonDefaultFontColor().ColorToString();
472         }
473     }
474     if (!bgColor.has_value()) {
475         if (params.defaultFocus && isFirstDefaultFocus_) {
476             bgColor = dialogTheme_->GetButtonHighlightBgColor();
477             isFirstDefaultFocus_ = false;
478         } else {
479             bgColor = dialogTheme_->GetButtonDefaultBgColor();
480         }
481     }
482 }
483 
CreateButton(const ButtonInfo & params,int32_t index,bool isCancel,bool isVertical,int32_t length)484 RefPtr<FrameNode> DialogPattern::CreateButton(
485     const ButtonInfo& params, int32_t index, bool isCancel, bool isVertical, int32_t length)
486 {
487     auto buttonNode = FrameNode::CreateFrameNode(
488         V2::BUTTON_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), MakeRefPtr<ButtonPattern>());
489     CHECK_NULL_RETURN(buttonNode, nullptr);
490     UpdateDialogButtonProperty(buttonNode, index, isVertical, length);
491 
492     // parse button text color and background color
493     std::string textColor;
494     std::optional<Color> bgColor;
495     ParseButtonFontColorAndBgColor(params, textColor, bgColor);
496 
497     // append text inside button
498     auto textNode = CreateButtonText(params.text, textColor);
499     CHECK_NULL_RETURN(textNode, nullptr);
500     SetButtonTextOpacity(textNode, params.enabled);
501     textNode->MountToParent(buttonNode);
502     textNode->MarkModifyDone();
503 
504     SetButtonEnabled(buttonNode, params.enabled);
505 
506     auto hub = buttonNode->GetOrCreateGestureEventHub();
507     CHECK_NULL_RETURN(hub, nullptr);
508     // bind click event
509     if (params.action) {
510         hub->AddClickEvent(params.action);
511     }
512 
513     // to close dialog when clicked inside button rect
514     if (!isCancel) {
515         BindCloseCallBack(hub, index);
516     } else {
517         BindCloseCallBack(hub, -1);
518     }
519 
520     // add scale animation
521     auto inputHub = buttonNode->GetOrCreateInputEventHub();
522     CHECK_NULL_RETURN(inputHub, nullptr);
523     inputHub->SetHoverEffect(params.defaultFocus ? HoverEffectType::SCALE : HoverEffectType::NONE);
524 
525     // update background color
526     auto renderContext = buttonNode->GetRenderContext();
527     CHECK_NULL_RETURN(renderContext, nullptr);
528     renderContext->UpdateBackgroundColor(bgColor.value());
529 
530     // set button default height
531     auto layoutProps = buttonNode->GetLayoutProperty();
532     CHECK_NULL_RETURN(layoutProps, nullptr);
533     auto pipeline = PipelineContext::GetCurrentContext();
534     CHECK_NULL_RETURN(pipeline, nullptr);
535     auto theme = pipeline->GetTheme<ButtonTheme>();
536     CHECK_NULL_RETURN(theme, nullptr);
537     layoutProps->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(theme->GetHeight())));
538 
539     return buttonNode;
540 }
541 
UpdateDialogButtonProperty(RefPtr<FrameNode> & buttonNode,int32_t index,bool isVertical,int32_t length)542 void DialogPattern::UpdateDialogButtonProperty(
543     RefPtr<FrameNode>& buttonNode, int32_t index, bool isVertical, int32_t length)
544 {
545     // update button padding
546     auto buttonProp = AceType::DynamicCast<ButtonLayoutProperty>(buttonNode->GetLayoutProperty());
547     PaddingProperty buttonPadding;
548     buttonPadding.left = CalcLength(SHEET_LIST_PADDING);
549     buttonPadding.right = CalcLength(SHEET_LIST_PADDING);
550     buttonProp->UpdatePadding(buttonPadding);
551 
552     if (!isVertical) {
553         // set flex grow to fill horizontal space
554         buttonProp->UpdateLayoutWeight(1);
555         buttonProp->UpdateFlexGrow(1.0);
556         buttonProp->UpdateFlexShrink(1.0);
557     } else if (isVertical && index != (length - 1)) {
558         // update button space in vertical
559         auto buttonSpace = dialogTheme_->GetMutiButtonPaddingVertical();
560         MarginProperty margin = {
561             .bottom = CalcLength(buttonSpace),
562         };
563         buttonProp->UpdateMargin(margin);
564     }
565 }
566 
CreateDivider(const Dimension & dividerLength,const Dimension & dividerWidth,const Color & color,const Dimension & space)567 RefPtr<FrameNode> DialogPattern::CreateDivider(
568     const Dimension& dividerLength, const Dimension& dividerWidth, const Color& color, const Dimension& space)
569 {
570     auto dividerNode = FrameNode::CreateFrameNode(
571         V2::DIVIDER_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<DividerPattern>());
572     CHECK_NULL_RETURN(dividerNode, nullptr);
573     auto dividerProps = dividerNode->GetLayoutProperty<DividerLayoutProperty>();
574     CHECK_NULL_RETURN(dividerProps, nullptr);
575     dividerProps->UpdateVertical(true);
576     dividerProps->UpdateStrokeWidth(dividerWidth);
577     dividerProps->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(dividerLength)));
578     auto dividerPaintProps = dividerNode->GetPaintProperty<DividerRenderProperty>();
579     CHECK_NULL_RETURN(dividerPaintProps, nullptr);
580     dividerPaintProps->UpdateDividerColor(color);
581 
582     // add divider margin
583     MarginProperty margin = {
584         .left = CalcLength((space - dividerWidth) / 2),
585         .right = CalcLength((space - dividerWidth) / 2),
586     };
587     dividerProps->UpdateMargin(margin);
588     return dividerNode;
589 }
590 
591 // alert dialog buttons
BuildButtons(const std::vector<ButtonInfo> & buttons,const DialogButtonDirection & direction)592 RefPtr<FrameNode> DialogPattern::BuildButtons(
593     const std::vector<ButtonInfo>& buttons, const DialogButtonDirection& direction)
594 {
595     auto Id = ElementRegister::GetInstance()->MakeUniqueId();
596     RefPtr<FrameNode> container;
597     bool isVertical;
598     if (direction == DialogButtonDirection::HORIZONTAL ||
599         (direction == DialogButtonDirection::AUTO && buttons.size() == TWO_BUTTON_MODE)) {
600         // use horizontal layout
601         isVertical = false;
602         container = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, Id, AceType::MakeRefPtr<LinearLayoutPattern>(false));
603         CHECK_NULL_RETURN(container, nullptr);
604         auto layoutProps = container->GetLayoutProperty<LinearLayoutProperty>();
605         layoutProps->UpdateMainAxisAlign(FlexAlign::SPACE_BETWEEN);
606         layoutProps->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
607     } else {
608         // use vertical layout
609         isVertical = true;
610         container = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, Id, AceType::MakeRefPtr<LinearLayoutPattern>(true));
611         auto layoutProps = container->GetLayoutProperty<LinearLayoutProperty>();
612         layoutProps->UpdateCrossAxisAlign(FlexAlign::STRETCH);
613         layoutProps->UpdateMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS);
614     }
615     CHECK_NULL_RETURN(container, nullptr);
616     // set action's padding
617     PaddingProperty actionPadding;
618     if (buttons.size() == ONE_BUTTON_MODE || isVertical) {
619         actionPadding.left = CalcLength(dialogTheme_->GetSingleButtonPaddingStart());
620         actionPadding.right = CalcLength(dialogTheme_->GetSingleButtonPaddingEnd());
621     } else {
622         actionPadding.left = CalcLength(dialogTheme_->GetMutiButtonPaddingStart());
623         actionPadding.right = CalcLength(dialogTheme_->GetMutiButtonPaddingEnd());
624     }
625     auto padding = dialogTheme_->GetActionsPadding();
626     actionPadding.top = CalcLength(padding.Top());
627     actionPadding.bottom = CalcLength(dialogTheme_->GetButtonPaddingBottom());
628     container->GetLayoutProperty()->UpdatePadding(actionPadding);
629 
630     AddButtonAndDivider(buttons, container, isVertical);
631     container->MarkModifyDone();
632 
633     return container;
634 }
635 
AddButtonAndDivider(const std::vector<ButtonInfo> & buttons,const RefPtr<NG::FrameNode> & container,bool isVertical)636 void DialogPattern::AddButtonAndDivider(
637     const std::vector<ButtonInfo>& buttons, const RefPtr<NG::FrameNode>& container, bool isVertical)
638 {
639     auto dividerLength = dialogTheme_->GetDividerLength();
640     auto dividerWidth = dialogTheme_->GetDividerBetweenButtonWidth_();
641     auto dividerColor = dialogTheme_->GetDividerColor();
642     auto buttonSpace = dialogTheme_->GetMutiButtonPaddingHorizontal();
643     auto length = buttons.size();
644     for (size_t i = 0; i < length; ++i) {
645         if (i != 0 && !isVertical) {
646             auto dividerNode = CreateDivider(
647                 dividerLength, dividerWidth, dividerColor, buttonSpace);
648             CHECK_NULL_VOID(dividerNode);
649             container->AddChild(dividerNode);
650         }
651         auto buttonNode = CreateButton(buttons[i], i, false, isVertical, length);
652         CHECK_NULL_VOID(buttonNode);
653         auto buttonPattern = buttonNode->GetPattern<ButtonPattern>();
654         CHECK_NULL_VOID(buttonPattern);
655         buttonPattern->SetSkipColorConfigurationUpdate();
656         buttonNode->MountToParent(container);
657         buttonNode->MarkModifyDone();
658     }
659 }
660 
CreateButtonText(const std::string & text,const std::string & colorStr)661 RefPtr<FrameNode> DialogPattern::CreateButtonText(const std::string& text, const std::string& colorStr)
662 {
663     auto textNode = FrameNode::CreateFrameNode(
664         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
665     CHECK_NULL_RETURN(textNode, nullptr);
666     textNode->GetOrCreateFocusHub()->SetFocusable(true);
667     auto textProps = textNode->GetLayoutProperty<TextLayoutProperty>();
668     CHECK_NULL_RETURN(textProps, nullptr);
669     textProps->UpdateContent(text);
670     textProps->UpdateMaxLines(1);
671     textProps->UpdateTextOverflow(TextOverflow::ELLIPSIS);
672     Dimension buttonTextSize =
673         dialogTheme_->GetButtonTextSize().IsValid() ? dialogTheme_->GetButtonTextSize() : DIALOG_BUTTON_TEXT_SIZE;
674     textProps->UpdateFontSize(buttonTextSize);
675     // update text color
676     Color color;
677     if (Color::ParseColorString(colorStr, color)) {
678         textProps->UpdateTextColor(color);
679     } else {
680         textProps->UpdateTextColor(DEFAULT_BUTTON_COLOR);
681     }
682     return textNode;
683 }
684 
BuildSheetItem(const ActionSheetInfo & item)685 RefPtr<FrameNode> DialogPattern::BuildSheetItem(const ActionSheetInfo& item)
686 {
687     // ListItem -> Row -> title + icon
688     auto Id = ElementRegister::GetInstance()->MakeUniqueId();
689     RefPtr<FrameNode> itemNode = FrameNode::CreateFrameNode(
690         V2::LIST_ITEM_ETS_TAG, Id, AceType::MakeRefPtr<ListItemPattern>(nullptr, V2::ListItemStyle::NONE));
691     CHECK_NULL_RETURN(itemNode, nullptr);
692 
693     // update sheet row flex align
694     auto rowId = ElementRegister::GetInstance()->MakeUniqueId();
695     auto itemRow = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, rowId, AceType::MakeRefPtr<LinearLayoutPattern>(false));
696     CHECK_NULL_RETURN(itemRow, nullptr);
697     auto layoutProps = itemRow->GetLayoutProperty<LinearLayoutProperty>();
698     layoutProps->UpdateMainAxisAlign(FlexAlign::FLEX_START);
699     layoutProps->UpdateMeasureType(MeasureType::MATCH_PARENT_MAIN_AXIS);
700 
701     // mount icon
702     if (!item.icon.empty()) {
703         auto iconNode = BuildSheetInfoIcon(item.icon);
704         iconNode->MountToParent(itemRow);
705         iconNode->MarkModifyDone();
706     }
707 
708     // mount title
709     if (!item.title.empty()) {
710         auto titleNode = BuildSheetInfoTitle(item.title);
711         titleNode->MountToParent(itemRow);
712         titleNode->MarkModifyDone();
713     }
714 
715     // set sheetItem action
716     auto hub = itemRow->GetOrCreateGestureEventHub();
717     if (item.action) {
718         hub->AddClickEvent(item.action);
719     }
720 
721     // close dialog when clicked
722     BindCloseCallBack(hub, SHEET_INFO_IDX);
723     itemRow->MountToParent(itemNode);
724     return itemNode;
725 }
726 
BuildSheetInfoTitle(const std::string & title)727 RefPtr<FrameNode> DialogPattern::BuildSheetInfoTitle(const std::string& title)
728 {
729     auto titleId = ElementRegister::GetInstance()->MakeUniqueId();
730     auto titleNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, titleId, AceType::MakeRefPtr<TextPattern>());
731     CHECK_NULL_RETURN(titleNode, nullptr);
732     // update text style
733     auto style = dialogTheme_->GetContentTextStyle();
734     auto props = titleNode->GetLayoutProperty<TextLayoutProperty>();
735     props->UpdateContent(title);
736     props->UpdateTextOverflow(TextOverflow::ELLIPSIS);
737     props->UpdateAdaptMaxFontSize(style.GetFontSize());
738     props->UpdateAdaptMinFontSize(dialogTheme_->GetTitleMinFontSize());
739     props->UpdateMaxLines(style.GetMaxLines());
740     props->UpdateFlexGrow(1.0f);
741     props->UpdateFlexShrink(1.0f);
742     return titleNode;
743 }
744 
BuildSheetInfoIcon(const std::string & icon)745 RefPtr<FrameNode> DialogPattern::BuildSheetInfoIcon(const std::string& icon)
746 {
747     auto iconId = ElementRegister::GetInstance()->MakeUniqueId();
748     auto iconNode = FrameNode::CreateFrameNode(V2::IMAGE_ETS_TAG, iconId, AceType::MakeRefPtr<ImagePattern>());
749     CHECK_NULL_RETURN(iconNode, nullptr);
750     // add image margin
751     MarginProperty margin = {
752         .left = CalcLength(SHEET_IMAGE_MARGIN),
753         .right = CalcLength(SHEET_IMAGE_MARGIN),
754         .top = CalcLength(SHEET_IMAGE_MARGIN),
755         .bottom = CalcLength(SHEET_IMAGE_MARGIN),
756     };
757     auto iconProps = iconNode->GetLayoutProperty<ImageLayoutProperty>();
758     iconProps->UpdateMargin(margin);
759     LOGD("item icon src = %s", icon.c_str());
760     auto imageSrc = ImageSourceInfo(icon);
761     iconProps->UpdateImageSourceInfo(imageSrc);
762     iconProps->UpdateUserDefinedIdealSize(CalcSize(SHEET_IMAGE_SIZE, SHEET_IMAGE_SIZE));
763     return iconNode;
764 }
765 
BuildSheet(const std::vector<ActionSheetInfo> & sheets)766 RefPtr<FrameNode> DialogPattern::BuildSheet(const std::vector<ActionSheetInfo>& sheets)
767 {
768     LOGI("start building action sheet items");
769     auto listId = ElementRegister::GetInstance()->MakeUniqueId();
770     auto list = FrameNode::CreateFrameNode(V2::LIST_ETS_TAG, listId, AceType::MakeRefPtr<ListPattern>());
771     CHECK_NULL_RETURN(list, nullptr);
772 
773     // set sheet padding
774     CalcLength padding(SHEET_LIST_PADDING.ConvertToPx());
775     PaddingProperty sheetPadding = {
776         .left = padding,
777         .right = padding,
778         .top = padding,
779         .bottom = padding,
780     };
781     list->GetLayoutProperty()->UpdatePadding(sheetPadding);
782     list->GetPaintProperty<ListPaintProperty>()->UpdateBarDisplayMode(DisplayMode::OFF);
783 
784     for (auto&& item : sheets) {
785         auto itemNode = BuildSheetItem(item);
786         CHECK_NULL_RETURN(itemNode, nullptr);
787         list->AddChild(itemNode);
788     }
789 
790     // set list divider
791     auto divider = V2::ItemDivider {
792         .strokeWidth = SHEET_DIVIDER_WIDTH,
793         .color = Color::GRAY,
794     };
795     auto props = list->GetLayoutProperty<ListLayoutProperty>();
796     props->UpdateDivider(divider);
797     props->UpdateListDirection(Axis::VERTICAL);
798     return list;
799 }
800 
BuildMenu(const std::vector<ButtonInfo> & buttons)801 RefPtr<FrameNode> DialogPattern::BuildMenu(const std::vector<ButtonInfo>& buttons)
802 {
803     auto menu = FrameNode::CreateFrameNode(
804         V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), MakeRefPtr<LinearLayoutPattern>(true));
805     menuNode_ = menu;
806     // column -> button
807     for (size_t i = 0; i < buttons.size(); ++i) {
808         RefPtr<FrameNode> button;
809         if (i != (buttons.size() - 1)) {
810             button = CreateButton(buttons[i], i);
811         } else {
812             button = CreateButton(buttons[i], i, true);
813         }
814         CHECK_NULL_RETURN(button, nullptr);
815         auto props = DynamicCast<FrameNode>(button)->GetLayoutProperty();
816         props->UpdateFlexGrow(1);
817         props->UpdateFlexShrink(1);
818 
819         button->MountToParent(menu);
820         button->MarkModifyDone();
821     }
822     auto menuProps = menu->GetLayoutProperty<LinearLayoutProperty>();
823     CHECK_NULL_RETURN(menuProps, nullptr);
824     menuProps->UpdateCrossAxisAlign(FlexAlign::STRETCH);
825     menuProps->UpdateMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS);
826     return menu;
827 }
828 
RegisterOnKeyEvent(const RefPtr<FocusHub> & focusHub)829 void DialogPattern::RegisterOnKeyEvent(const RefPtr<FocusHub>& focusHub)
830 {
831     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
832         auto pattern = wp.Upgrade();
833         CHECK_NULL_RETURN_NOLOG(pattern, false);
834         return pattern->OnKeyEvent(event);
835     };
836     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
837 }
838 
OnKeyEvent(const KeyEvent & event)839 bool DialogPattern::OnKeyEvent(const KeyEvent& event)
840 {
841     if (event.action != KeyAction::DOWN) {
842         return false;
843     }
844     return false;
845 }
846 
847 // XTS inspector
ToJsonValue(std::unique_ptr<JsonValue> & json) const848 void DialogPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
849 {
850     auto host = GetHost();
851     CHECK_NULL_VOID(host);
852     if (host->GetTag() == V2::ALERT_DIALOG_ETS_TAG || host->GetTag() == V2::ACTION_SHEET_DIALOG_ETS_TAG) {
853         json->Put("title", title_.c_str());
854         json->Put("subtitle", subtitle_.c_str());
855         json->Put("message", message_.c_str());
856     }
857 }
858 
OnColorConfigurationUpdate()859 void DialogPattern::OnColorConfigurationUpdate()
860 {
861     auto host = GetHost();
862     CHECK_NULL_VOID(host);
863     if (!GetDialogProperties().customStyle) {
864         auto context = host->GetContext();
865         CHECK_NULL_VOID_NOLOG(context);
866         auto dialogTheme = context->GetTheme<DialogTheme>();
867         CHECK_NULL_VOID_NOLOG(dialogTheme);
868         auto col = DynamicCast<FrameNode>(host->GetChildAtIndex(START_CHILD_INDEX));
869         CHECK_NULL_VOID_NOLOG(col);
870         auto colContext = col->GetContext();
871         CHECK_NULL_VOID_NOLOG(colContext);
872         auto colRenderContext = col->GetRenderContext();
873         CHECK_NULL_VOID_NOLOG(colRenderContext);
874         colRenderContext->UpdateBackgroundColor(dialogTheme->GetBackgroundColor());
875     }
876     CHECK_NULL_VOID_NOLOG(menuNode_);
877     for (const auto& buttonNode : menuNode_->GetChildren()) {
878         if (buttonNode->GetTag() != V2::BUTTON_ETS_TAG) {
879             continue;
880         }
881         auto buttonFrameNode = DynamicCast<FrameNode>(buttonNode);
882         CHECK_NULL_VOID_NOLOG(buttonFrameNode);
883         auto pattern = buttonFrameNode->GetPattern<ButtonPattern>();
884         CHECK_NULL_VOID_NOLOG(pattern);
885         pattern->SetSkipColorConfigurationUpdate();
886     }
887     OnModifyDone();
888     host->MarkDirtyNode();
889 }
890 
SetButtonTextOpacity(const RefPtr<FrameNode> & textNode,bool enabled)891 void DialogPattern::SetButtonTextOpacity(const RefPtr<FrameNode>& textNode, bool enabled)
892 {
893     auto textNodeRenderContext = textNode->GetRenderContext();
894     CHECK_NULL_VOID(textNodeRenderContext);
895     // [Button Component Defect] Button text color is no set while disabled status.
896     if (!enabled) {
897         textNodeRenderContext->UpdateOpacity(BUTTON_TEXT_OPACITY);
898     }
899 }
900 
SetButtonEnabled(const RefPtr<FrameNode> & buttonNode,bool enabled)901 void DialogPattern::SetButtonEnabled(const RefPtr<FrameNode>& buttonNode, bool enabled)
902 {
903     // set Enabled and Focusable
904     auto buttonButtonEvent = buttonNode->GetEventHub<ButtonEventHub>();
905     CHECK_NULL_VOID(buttonButtonEvent);
906     buttonButtonEvent->SetEnabled(enabled);
907     buttonNode->GetOrCreateFocusHub()->SetFocusable(enabled);
908 }
909 } // namespace OHOS::Ace::NG
910