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