1 /*
2 * Copyright (c) 2021-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
16 #include "core/components/dialog/dialog_component.h"
17
18 #include <atomic>
19
20 #include "base/geometry/dimension.h"
21 #include "base/i18n/localization.h"
22 #include "base/log/log.h"
23 #include "base/resource/internal_resource.h"
24 #include "base/utils/system_properties.h"
25 #include "core/animation/curve_animation.h"
26 #include "core/animation/curves.h"
27 #include "core/animation/keyframe.h"
28 #include "core/animation/keyframe_animation.h"
29 #include "core/components/box/box_component.h"
30 #include "core/components/button/button_theme.h"
31 #include "core/components/common/layout/constants.h"
32 #include "core/components/common/properties/color.h"
33 #include "core/components/common/properties/decoration.h"
34 #include "core/components/dialog/action_sheet/action_sheet_component.h"
35 #include "core/components/dialog/alert_dialog_component.h"
36 #include "core/components/dialog/dialog_element.h"
37 #include "core/components/dialog/render_dialog.h"
38 #include "core/components/drag_bar/drag_bar_component.h"
39 #include "core/components/focus_collaboration/focus_collaboration_component.h"
40 #include "core/components/focusable/focusable_component.h"
41 #include "core/components/image/image_component.h"
42 #include "core/components/scroll/scroll_component.h"
43 #include "core/components/theme/theme_manager.h"
44 #include "core/components/wrap/wrap_component.h"
45 #include "core/pipeline/pipeline_context.h"
46
47 namespace OHOS::Ace {
48 namespace {
49
50 constexpr double PHONE_ENTER_CURVE_X0 = 0.38;
51 constexpr double PHONE_ENTER_CURVE_Y0 = 1.33;
52 constexpr double PHONE_ENTER_CURVE_X1 = 0.60;
53 constexpr double PHONE_ENTER_CURVE_Y1 = 1.0;
54 constexpr double PHONE_OPACITY_MIDDLE_IN = 0.375;
55 constexpr Dimension CAR_TITLE_MIN_HEIGHT = 64.0_vp;
56
57 } // namespace
58
59 static std::atomic<int32_t> g_dialogId(0);
60
61 const char CALLBACK_SUCCESS[] = "success";
62 const char CALLBACK_CANCEL[] = "cancel";
63 const char CALLBACK_COMPLETE[] = "complete";
64 const char DIALOG_TWEEN_NAME[] = "tween";
65 const int32_t DIALOG_BUTTONS_COUNT_WATCH = 2;
66 const char DIALOG_OK[] = "common.ok";
67 const char DIALOG_CANCEL[] = "common.cancel";
68 const char SEPARATE[] = " ";
69
DialogComponent()70 DialogComponent::DialogComponent()
71 {
72 dialogId_ = GenerateDialogId();
73 }
74
CreateElement()75 RefPtr<Element> DialogComponent::CreateElement()
76 {
77 return AceType::MakeRefPtr<DialogElement>();
78 }
79
CreateRenderNode()80 RefPtr<RenderNode> DialogComponent::CreateRenderNode()
81 {
82 return RenderDialog::Create();
83 }
84
GenerateDialogId()85 int32_t DialogComponent::GenerateDialogId()
86 {
87 return g_dialogId.fetch_add(1, std::memory_order_relaxed);
88 }
89
BuildChild(const RefPtr<ThemeManager> & themeManager)90 void DialogComponent::BuildChild(const RefPtr<ThemeManager>& themeManager)
91 {
92 if (!themeManager) {
93 return;
94 }
95 dialogTheme_ = AceType::DynamicCast<DialogTheme>(themeManager->GetTheme(DialogTheme::TypeId()));
96 if (!dialogTheme_) {
97 return;
98 }
99 if (!isDeviceTypeSet_) {
100 deviceType_ = SystemProperties::GetDeviceType();
101 }
102 bool isLimit = true;
103 auto box = BuildBox(isLimit);
104 auto transition = BuildAnimation(box);
105 BuildDialogTween(transition, isLimit, margin_);
106
107 auto focusCollaboration = AceType::MakeRefPtr<FocusCollaborationComponent>();
108 if (!HasCustomChild()) {
109 std::list<RefPtr<Component>> columnChildren;
110 auto column = AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, columnChildren);
111 column->SetMainAxisSize(MainAxisSize::MIN);
112 BuildTitle(column);
113 BuildContent(column);
114 if (isMenu_) {
115 BuildMenu(column);
116 } else {
117 BuildActions(themeManager, column);
118 }
119 BuildFocusChild(column, focusCollaboration);
120 } else {
121 // build custom child
122 BuildFocusChild(customComponent_, focusCollaboration);
123 if (IsDragable()) {
124 BuildDragBar(focusCollaboration);
125 }
126 }
127 if (deviceType_ == DeviceType::WATCH) {
128 auto scroll = AceType::MakeRefPtr<ScrollComponent>(focusCollaboration);
129 box->SetChild(scroll);
130 } else {
131 box->SetChild(focusCollaboration);
132 }
133 box->SetTextDirection(GetTextDirection());
134 }
135
BuildBox(bool & isLimit)136 RefPtr<BoxComponent> DialogComponent::BuildBox(bool& isLimit)
137 {
138 auto box = AceType::MakeRefPtr<BoxComponent>();
139 // If use custom style, don't set default style.
140 if (properties_.customStyle) {
141 isLimit = false;
142 return box;
143 }
144
145 auto backDecoration = AceType::MakeRefPtr<Decoration>();
146 backDecoration->SetBackgroundColor(backgroundColor_);
147 Border border;
148 border.SetBorderRadius(dialogTheme_->GetRadius());
149 backDecoration->SetBorder(border);
150
151 if (deviceType_ == DeviceType::WATCH) {
152 box->SetFlex(BoxFlex::FLEX_XY);
153 } else {
154 box->SetFlex(BoxFlex::FLEX_X);
155 }
156 box->SetBackDecoration(backDecoration);
157 if (height_.IsValid()) {
158 box->SetHeight(height_.Value(), height_.Unit());
159 isLimit = false;
160 }
161 if (width_.IsValid()) {
162 box->SetWidth(width_.Value(), width_.Unit());
163 isLimit = false;
164 }
165 if (isSetMargin_) {
166 box->SetMargin(margin_);
167 }
168 return box;
169 }
170
BuildDialogTween(const RefPtr<TransitionComponent> & transition,bool isLimit,Edge margin)171 void DialogComponent::BuildDialogTween(const RefPtr<TransitionComponent>& transition, bool isLimit, Edge margin)
172 {
173 auto dialogTween = AceType::MakeRefPtr<DialogTweenComponent>();
174 auto controller = AceType::MakeRefPtr<Animator>(context_);
175 dialogTween->SetAnimator(controller);
176 if (animator_) {
177 animator_->AddProxyController(controller);
178 }
179 dialogTween->SetParentAnimator(animator_);
180 dialogTween->SetAutoCancel(autoCancel_);
181 dialogTween->SetChild(transition);
182 dialogTween->SetTextDirection(GetTextDirection());
183 dialogTween->SetOnSuccessId(onSuccessId_);
184 dialogTween->SetOnCancelId(onCancelId_);
185 dialogTween->SetOnCompleteId(onCompleteId_);
186 dialogTween->SetOnStatusChanged(properties_.onStatusChanged);
187 dialogTween->SetDialogId(dialogId_);
188 if (isMenu_) {
189 dialogTween->SetIsMenu(true);
190 dialogTween->SetMenuSuccessId(menuSuccessId_);
191 }
192 dialogTween->SetOnPositiveSuccessId(onPositiveSuccessId_);
193 dialogTween->SetOnNegativeSuccessId(onNegativeSuccessId_);
194 dialogTween->SetOnNeutralSuccessId(onNeutralSuccessId_);
195 dialogTween->SetData(data_);
196 dialogTween->SetDialogLimit(isLimit);
197 dialogTween->SetDragable(dragable_);
198 if (isSetMargin_) {
199 dialogTween->SetMargin(margin);
200 }
201 dialogTween->SetAlignment(properties_.alignment);
202 dialogTween->SetOffset(properties_.offset);
203 dialogTween->SetGridCount(properties_.gridCount);
204 if (dialogTweenBox_) {
205 const auto& dialogComposed = GenerateComposed("dialog", dialogTween, false);
206 dialogTween->SetComposedId(dialogTweenComposedId_);
207 dialogTween->SetCustomDialogId(customDialogId_);
208 dialogTweenBox_->SetChild(dialogComposed);
209 }
210 }
211
BuildFocusChild(const RefPtr<Component> & child,const RefPtr<FocusCollaborationComponent> & collaboration)212 void DialogComponent::BuildFocusChild(
213 const RefPtr<Component>& child, const RefPtr<FocusCollaborationComponent>& collaboration)
214 {
215 if (HasCustomChild()) {
216 // for custom child
217 collaboration->InsertChild(0, child);
218 } else if (actions_.empty()) {
219 auto focusable = AceType::MakeRefPtr<FocusableComponent>(child);
220 focusable->SetFocusable(true);
221 focusable->SetFocusNode(true);
222 collaboration->InsertChild(0, focusable);
223 } else {
224 collaboration->InsertChild(0, child);
225 }
226 }
227
BuildDragBar(const RefPtr<FocusCollaborationComponent> & collaboration)228 void DialogComponent::BuildDragBar(const RefPtr<FocusCollaborationComponent>& collaboration)
229 {
230 auto dragBar = AceType::MakeRefPtr<DragBarComponent>();
231 auto boxForContent = AceType::MakeRefPtr<BoxComponent>();
232 boxForContent->SetFlex(BoxFlex::FLEX_X);
233 auto column =
234 AceType::MakeRefPtr<ColumnComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, std::list<RefPtr<Component>>());
235 column->SetCrossAxisSize(CrossAxisSize::MAX);
236 auto mode = PanelMode::HALF;
237 dragBar->SetPanelMode(mode);
238 dragBar->SetHasDragBar(dragable_);
239 column->AppendChild(dragBar);
240 collaboration->InsertChild(1, column);
241 }
242
BuildTitle(const RefPtr<ColumnComponent> & column)243 void DialogComponent::BuildTitle(const RefPtr<ColumnComponent>& column)
244 {
245 if (!title_) {
246 return;
247 }
248 auto titlePadding = AceType::MakeRefPtr<PaddingComponent>();
249 if (titlePadding_ == Edge::NONE) {
250 titlePadding_ = (!content_ && actions_.empty()) ? dialogTheme_->GetTitleDefaultPadding()
251 : dialogTheme_->GetTitleAdjustPadding();
252 }
253 auto textBox = AceType::MakeRefPtr<BoxComponent>();
254 textBox->SetDeliverMinToChild(false);
255 textBox->SetChild(title_);
256 titlePadding->SetPadding(std::move(titlePadding_));
257 titlePadding->SetChild(textBox);
258 std::list<RefPtr<Component>> rowChildren;
259 RefPtr<RowComponent> row;
260 if (deviceType_ == DeviceType::PHONE && !isMenu_) {
261 row = AceType::MakeRefPtr<RowComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, rowChildren);
262 } else {
263 row = AceType::MakeRefPtr<RowComponent>(FlexAlign::CENTER, FlexAlign::CENTER, rowChildren);
264 }
265 row->SetStretchToParent(true);
266 if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
267 auto box = AceType::MakeRefPtr<BoxComponent>();
268 box->SetMinHeight(CAR_TITLE_MIN_HEIGHT);
269 box->SetChild(titlePadding);
270 row->AppendChild(box);
271 } else {
272 row->AppendChild(titlePadding);
273 }
274
275 auto titleFlex = AceType::MakeRefPtr<FlexItemComponent>(0, 0, 0.0, row);
276 column->AppendChild(GenerateComposed("dialogTitle", titleFlex, true));
277 }
278
BuildContent(const RefPtr<ColumnComponent> & column)279 void DialogComponent::BuildContent(const RefPtr<ColumnComponent>& column)
280 {
281 if (!content_) {
282 return;
283 }
284 auto contentPadding = AceType::MakeRefPtr<PaddingComponent>();
285 if (contentPadding_ == Edge::NONE) {
286 if (!title_) {
287 contentPadding_ = actions_.empty() ? dialogTheme_->GetDefaultPadding() : dialogTheme_->GetAdjustPadding();
288 } else {
289 contentPadding_ =
290 actions_.empty() ? dialogTheme_->GetContentDefaultPadding() : dialogTheme_->GetContentAdjustPadding();
291 }
292 }
293 contentPadding->SetPadding(std::move(contentPadding_));
294 RefPtr<FlexItemComponent> contentFlex;
295 if (deviceType_ == DeviceType::WATCH) {
296 contentPadding->SetChild(content_);
297 contentFlex = AceType::MakeRefPtr<FlexItemComponent>(0, 0, 0.0, contentPadding);
298 } else {
299 auto scroll = AceType::MakeRefPtr<ScrollComponent>(content_);
300 contentPadding->SetChild(scroll);
301 contentFlex = AceType::MakeRefPtr<FlexItemComponent>(0, 1, 0.0, contentPadding);
302 }
303 column->AppendChild(GenerateComposed("dialogContent", contentFlex, true));
304 }
305
BuildMenu(const RefPtr<ColumnComponent> & column)306 void DialogComponent::BuildMenu(const RefPtr<ColumnComponent>& column)
307 {
308 if (actions_.empty()) {
309 LOGW("the action is empty");
310 return;
311 }
312
313 std::list<RefPtr<Component>> columnChildren;
314 auto actionIter = actions_.begin();
315 for (size_t index = 0; index < actions_.size(); ++index) {
316 std::list<RefPtr<Component>> rowChildren;
317 auto buttonRow = AceType::MakeRefPtr<RowComponent>(FlexAlign::CENTER, FlexAlign::CENTER, rowChildren);
318 auto rowItem = AceType::MakeRefPtr<FlexItemComponent>(
319 1, 1, 0.0, BuildButton(*actionIter, menuSuccessId_[index], Edge::NONE, false));
320 buttonRow->AppendChild(rowItem);
321 auto columnItem = AceType::MakeRefPtr<FlexItemComponent>(1, 1, 0.0, buttonRow);
322 column->AppendChild(columnItem);
323 ++actionIter;
324 }
325 }
326
BuildActions(const RefPtr<ThemeManager> & themeManager,const RefPtr<ColumnComponent> & column)327 void DialogComponent::BuildActions(const RefPtr<ThemeManager>& themeManager, const RefPtr<ColumnComponent>& column)
328 {
329 if (deviceType_ == DeviceType::WATCH) {
330 BuildActionsForWatch(column);
331 return;
332 }
333
334 if (actions_.empty()) {
335 LOGW("the action is empty");
336 return;
337 }
338
339 auto actionsPadding = AceType::MakeRefPtr<PaddingComponent>();
340 actionsPadding->SetPadding(dialogTheme_->GetActionsPadding());
341 if (actions_.size() == 1) { // the button in dialog is one.
342 std::list<RefPtr<Component>> rowChildren;
343 auto row = AceType::MakeRefPtr<RowComponent>(FlexAlign::SPACE_AROUND, FlexAlign::FLEX_START, rowChildren);
344 row->SetStretchToParent(true);
345 row->AppendChild(AceType::MakeRefPtr<FlexItemComponent>(
346 1, 1, 0.0, BuildButton(actions_.front(), onPositiveSuccessId_, Edge::NONE, true)));
347 actionsPadding->SetChild(row);
348 } else if (actions_.size() == 2) { // the button in dialog is two.
349 std::list<RefPtr<Component>> rowChildren;
350 auto row = AceType::MakeRefPtr<RowComponent>(FlexAlign::SPACE_AROUND, FlexAlign::CENTER, rowChildren);
351 row->SetStretchToParent(true);
352 row->AppendChild(AceType::MakeRefPtr<FlexItemComponent>(
353 1, 1, 0.0, BuildButton(actions_.front(), onPositiveSuccessId_, Edge::NONE)));
354 row->AppendChild(AceType::MakeRefPtr<FlexItemComponent>(0, 0, 0.0, BuildDivider(themeManager)));
355 row->AppendChild(AceType::MakeRefPtr<FlexItemComponent>(
356 1, 1, 0.0, BuildButton(actions_.back(), onNegativeSuccessId_, Edge::NONE, true)));
357 actionsPadding->SetChild(row);
358 } else { // the button in dialog is more than two.
359 std::list<RefPtr<Component>> wrapChildren;
360 auto wrap = AceType::MakeRefPtr<WrapComponent>(wrapChildren);
361 wrap->SetDialogStretch(true);
362 wrap->SetMainAlignment(WrapAlignment::CENTER);
363 wrap->SetSpacing(dialogTheme_->GetButtonSpacingHorizontal());
364 wrap->SetContentSpacing(dialogTheme_->GetButtonSpacingVertical());
365 int32_t num = 0;
366 for (const auto& action : actions_) {
367 ++num;
368 if (num == 1) {
369 wrap->AppendChild(BuildButton(action, onPositiveSuccessId_, Edge::NONE));
370 } else if (num == 2) {
371 wrap->AppendChild(BuildButton(action, onNegativeSuccessId_, Edge::NONE, true));
372 } else if (num == 3) {
373 wrap->AppendChild(BuildButton(action, onNeutralSuccessId_, Edge::NONE));
374 } else {
375 break;
376 }
377 }
378 actionsPadding->SetChild(wrap);
379 }
380 auto actionsFlex = AceType::MakeRefPtr<FlexItemComponent>(0, 0, 0.0, actionsPadding);
381 column->AppendChild(actionsFlex);
382 }
383
BuildActionsForWatch(const OHOS::Ace::RefPtr<OHOS::Ace::ColumnComponent> & column)384 void DialogComponent::BuildActionsForWatch(const OHOS::Ace::RefPtr<OHOS::Ace::ColumnComponent>& column)
385 {
386 if (actions_.empty() || actions_.size() != DIALOG_BUTTONS_COUNT_WATCH) {
387 return;
388 }
389
390 std::list<RefPtr<Component>> rowChildren;
391 auto row = AceType::MakeRefPtr<RowComponent>(FlexAlign::SPACE_BETWEEN, FlexAlign::FLEX_START, rowChildren);
392 row->SetStretchToParent(true);
393 row->AppendChild(BuildButton(actions_.front(), onPositiveSuccessId_, dialogTheme_->GetButtonPaddingRight()));
394 row->AppendChild(BuildButton(actions_.back(), onNegativeSuccessId_, dialogTheme_->GetButtonPaddingLeft(), true));
395 auto actionsPadding = AceType::MakeRefPtr<PaddingComponent>();
396 actionsPadding->SetPadding(dialogTheme_->GetDefaultPadding());
397 actionsPadding->SetChild(row);
398 auto actionsFlex = AceType::MakeRefPtr<FlexItemComponent>(0, 0, 0.0, actionsPadding);
399 column->AppendChild(actionsFlex);
400 }
401
BuildButton(const RefPtr<ButtonComponent> & button,const EventMarker & callbackId,const Edge & edge,bool isAutoFocus)402 RefPtr<Component> DialogComponent::BuildButton(
403 const RefPtr<ButtonComponent>& button, const EventMarker& callbackId, const Edge& edge, bool isAutoFocus)
404 {
405 button->SetClickedEventId(callbackId);
406 button->SetAutoFocusState(isAutoFocus);
407 auto buttonPadding = AceType::MakeRefPtr<PaddingComponent>();
408 buttonPadding->SetPadding(edge);
409 buttonPadding->SetChild(button);
410 return GenerateComposed("dialogButton", buttonPadding, true);
411 }
412
BuildAnimation(const RefPtr<BoxComponent> & child)413 RefPtr<TransitionComponent> DialogComponent::BuildAnimation(const RefPtr<BoxComponent>& child)
414 {
415 if (deviceType_ == DeviceType::PHONE) {
416 return BuildAnimationForPhone(child);
417 }
418 // Build scale animation for in.
419 auto scaleFrameStart =
420 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetScaleStart());
421 auto scaleFrameEnd = AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetScaleEnd());
422 auto scaleAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
423 scaleAnimationIn->AddKeyframe(scaleFrameStart);
424 scaleAnimationIn->AddKeyframe(scaleFrameEnd);
425 scaleAnimationIn->SetCurve(Curves::FRICTION);
426 // Build opacity animation for in.
427 auto opacityKeyframeStart =
428 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetOpacityStart());
429 auto opacityKeyframeEnd =
430 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetOpacityEnd());
431 auto opacityAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
432 opacityAnimationIn->AddKeyframe(opacityKeyframeStart);
433 opacityAnimationIn->AddKeyframe(opacityKeyframeEnd);
434 opacityAnimationIn->SetCurve(Curves::FRICTION);
435 // Build tween option for in
436 TweenOption tweenOptionIn;
437 tweenOptionIn.SetTransformFloatAnimation(AnimationType::SCALE, scaleAnimationIn);
438 tweenOptionIn.SetOpacityAnimation(opacityAnimationIn);
439 tweenOptionIn.SetDuration(dialogTheme_->GetAnimationDurationIn());
440 tweenOptionIn.SetFillMode(FillMode::FORWARDS);
441
442 // Build scale animation for out.
443 auto scaleFrameStartOut =
444 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetScaleEnd());
445 auto scaleFrameEndOut =
446 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetScaleStart());
447 auto scaleAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
448 scaleAnimationOut->AddKeyframe(scaleFrameStartOut);
449 scaleAnimationOut->AddKeyframe(scaleFrameEndOut);
450 scaleAnimationOut->SetCurve(Curves::SMOOTH);
451 // Build opacity animation for out.
452 auto opacityKeyframeStartOut =
453 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetOpacityEnd());
454 auto opacityKeyframeEndOut =
455 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetOpacityStart());
456 auto opacityAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
457 opacityAnimationOut->AddKeyframe(opacityKeyframeStartOut);
458 opacityAnimationOut->AddKeyframe(opacityKeyframeEndOut);
459 opacityAnimationOut->SetCurve(Curves::SMOOTH);
460 // Build tween option for out
461 TweenOption tweenOptionOut;
462 tweenOptionOut.SetTransformFloatAnimation(AnimationType::SCALE, scaleAnimationOut);
463 tweenOptionOut.SetOpacityAnimation(opacityAnimationOut);
464 tweenOptionOut.SetDuration(dialogTheme_->GetAnimationDurationOut());
465 tweenOptionOut.SetFillMode(FillMode::FORWARDS);
466
467 // Build transition
468 auto transition =
469 AceType::MakeRefPtr<TransitionComponent>(TweenComponent::AllocTweenComponentId(), DIALOG_TWEEN_NAME, child);
470 transition->SetIsFirstFrameShow(false);
471 transition->SetTransitionOption(tweenOptionIn, tweenOptionOut);
472 return transition;
473 }
474
BuildAnimationForPhone(const RefPtr<Component> & child)475 RefPtr<TransitionComponent> DialogComponent::BuildAnimationForPhone(const RefPtr<Component>& child)
476 {
477 // Build scale animation for in.
478 auto scaleFrameStart =
479 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetScaleStart());
480 auto scaleFrameEnd = AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetScaleEnd());
481 auto scaleAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
482 scaleAnimationIn->AddKeyframe(scaleFrameStart);
483 scaleAnimationIn->AddKeyframe(scaleFrameEnd);
484 auto dialogCurve = AceType::MakeRefPtr<CubicCurve>(
485 PHONE_ENTER_CURVE_X0, PHONE_ENTER_CURVE_Y0, PHONE_ENTER_CURVE_X1, PHONE_ENTER_CURVE_Y1);
486 scaleAnimationIn->SetCurve(dialogCurve);
487 // Build opacity animation for in.
488 auto opacityKeyframeStart =
489 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetOpacityStart());
490 auto opacityKeyframeMiddle =
491 AceType::MakeRefPtr<Keyframe<float>>(PHONE_OPACITY_MIDDLE_IN, dialogTheme_->GetOpacityEnd());
492 auto opacityKeyframeEnd =
493 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetOpacityEnd());
494 auto opacityAnimationIn = AceType::MakeRefPtr<KeyframeAnimation<float>>();
495 opacityAnimationIn->AddKeyframe(opacityKeyframeStart);
496 opacityAnimationIn->AddKeyframe(opacityKeyframeMiddle);
497 opacityAnimationIn->AddKeyframe(opacityKeyframeEnd);
498 opacityAnimationIn->SetCurve(Curves::SHARP);
499 // Build tween option for in
500 TweenOption tweenOptionIn;
501 tweenOptionIn.SetTransformFloatAnimation(AnimationType::SCALE, scaleAnimationIn);
502 tweenOptionIn.SetOpacityAnimation(opacityAnimationIn);
503 tweenOptionIn.SetDuration(dialogTheme_->GetAnimationDurationIn());
504 tweenOptionIn.SetFillMode(FillMode::FORWARDS);
505 // Build scale animation for out.
506 auto scaleFrameStartOut =
507 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetScaleEnd());
508 auto scaleFrameEndOut =
509 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetScaleStart());
510 auto scaleAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
511 scaleAnimationOut->AddKeyframe(scaleFrameStartOut);
512 scaleAnimationOut->AddKeyframe(scaleFrameEndOut);
513 scaleAnimationOut->SetCurve(Curves::SMOOTH);
514 // Build opacity animation for out.
515 auto opacityKeyframeStartOut =
516 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameStart(), dialogTheme_->GetOpacityEnd());
517 auto opacityKeyframeEndOut =
518 AceType::MakeRefPtr<Keyframe<float>>(dialogTheme_->GetFrameEnd(), dialogTheme_->GetOpacityStart());
519 auto opacityAnimationOut = AceType::MakeRefPtr<KeyframeAnimation<float>>();
520 opacityAnimationOut->AddKeyframe(opacityKeyframeStartOut);
521 opacityAnimationOut->AddKeyframe(opacityKeyframeEndOut);
522 opacityAnimationOut->SetCurve(Curves::SMOOTH);
523 // Build tween option for out
524 TweenOption tweenOptionOut;
525 tweenOptionOut.SetTransformFloatAnimation(AnimationType::SCALE, scaleAnimationOut);
526 tweenOptionOut.SetOpacityAnimation(opacityAnimationOut);
527 tweenOptionOut.SetDuration(dialogTheme_->GetAnimationDurationOut());
528 tweenOptionOut.SetFillMode(FillMode::FORWARDS);
529 auto transition =
530 AceType::MakeRefPtr<TransitionComponent>(TweenComponent::AllocTweenComponentId(), DIALOG_TWEEN_NAME, child);
531 transition->SetIsFirstFrameShow(false);
532 transition->SetTransitionOption(tweenOptionIn, tweenOptionOut);
533 return transition;
534 }
535
GenerateComposed(const std::string & name,const RefPtr<Component> & child,bool isDialogTweenChild)536 RefPtr<Component> DialogComponent::GenerateComposed(
537 const std::string& name, const RefPtr<Component>& child, bool isDialogTweenChild)
538 {
539 const auto& pipelineContext = context_.Upgrade();
540 if (pipelineContext) {
541 const auto& accessibilityManager = pipelineContext->GetAccessibilityManager();
542 if (accessibilityManager) {
543 // use accessibility node already created with dom node in JS app
544 int32_t composedId = customDialogId_;
545 if (composedId == -1) {
546 composedId = accessibilityManager->GenerateNextAccessibilityId();
547 }
548 const auto& composed = AceType::MakeRefPtr<ComposedComponent>(std::to_string(composedId), name, child);
549 if (isDialogTweenChild) {
550 accessibilityManager->CreateSpecializedNode(name, composedId, dialogTweenComposedId_);
551 } else {
552 dialogTweenComposedId_ = composedId;
553 }
554 return composed;
555 }
556 }
557 return child;
558 }
559
BuildDivider(const RefPtr<ThemeManager> & themeManager)560 RefPtr<Component> DialogComponent::BuildDivider(const RefPtr<ThemeManager>& themeManager)
561 {
562 if (!themeManager) {
563 return nullptr;
564 }
565 auto padding = AceType::MakeRefPtr<PaddingComponent>();
566 auto dialogTheme = AceType::DynamicCast<DialogTheme>(themeManager->GetTheme(DialogTheme::TypeId()));
567 if (!dialogTheme) {
568 return nullptr;
569 }
570 if (SystemProperties::GetDeviceType() == DeviceType::TV) {
571 padding->SetPadding(Edge(dialogTheme->GetButtonSpacingHorizontal(), Dimension(0.0, DimensionUnit::VP),
572 Dimension(0.0, DimensionUnit::VP), Dimension(0.0, DimensionUnit::VP)));
573 return padding;
574 }
575 padding->SetPadding(dialogTheme->GetDividerPadding());
576 auto dividerBox = AceType::MakeRefPtr<BoxComponent>();
577 dividerBox->SetWidth(dialogTheme->GetDividerWidth().Value(), dialogTheme->GetDividerWidth().Unit());
578 dividerBox->SetHeight(dialogTheme->GetDividerHeight().Value(), dialogTheme->GetDividerHeight().Unit());
579 auto backDecoration = AceType::MakeRefPtr<Decoration>();
580 backDecoration->SetBackgroundColor(dialogTheme->GetDividerColor());
581 dividerBox->SetBackDecoration(backDecoration);
582 padding->SetChild(dividerBox);
583 return padding;
584 }
585
Build(const DialogProperties & dialogProperties,const WeakPtr<PipelineContext> & context)586 RefPtr<DialogComponent> DialogBuilder::Build(
587 const DialogProperties& dialogProperties, const WeakPtr<PipelineContext>& context)
588 {
589 auto dialog = BuildDialogWithType(dialogProperties.type);
590 dialog->SetDialogProperties(dialogProperties);
591 auto pipelineContext = context.Upgrade();
592 if (!pipelineContext) {
593 return dialog;
594 }
595 auto themeManager = pipelineContext->GetThemeManager();
596 if (!themeManager) {
597 return dialog;
598 }
599 auto dialogTheme = AceType::DynamicCast<DialogTheme>(themeManager->GetTheme(DialogTheme::TypeId()));
600 if (!dialogTheme) {
601 return dialog;
602 }
603 std::string data;
604 dialog->SetContext(context);
605 dialog->SetBackgroundColor(dialogTheme->GetBackgroundColor());
606 // Set title and content of dialog
607 BuildTitleAndContent(dialog, dialogProperties, dialogTheme, data);
608 // Set buttons of dialog
609 BuildButtons(themeManager, dialog, dialogProperties.buttons, dialogTheme, data);
610 // Build DialogTween
611 auto controller = AceType::MakeRefPtr<Animator>(context);
612 dialog->SetAnimator(controller);
613 dialog->SetAutoCancel(dialogProperties.autoCancel);
614 dialog->SetData(data);
615 // Set eventMarker of dialog component
616 if (!dialogProperties.callbacks.empty()) {
617 for (const auto& callback : dialogProperties.callbacks) {
618 if (callback.first == CALLBACK_SUCCESS) {
619 dialog->SetOnSuccessId(callback.second);
620 }
621 if (callback.first == CALLBACK_CANCEL) {
622 dialog->SetOnCancelId(callback.second);
623 }
624 if (callback.first == CALLBACK_COMPLETE) {
625 dialog->SetOnCompleteId(callback.second);
626 }
627 }
628 }
629 // Set menu evenMarker
630 if (dialogProperties.isMenu) {
631 dialog->SetIsMenu(true);
632 for (size_t index = 0; index < dialogProperties.buttons.size(); ++index) {
633 dialog->GetMenuSuccessId().emplace_back(BackEndEventManager<void()>::GetInstance().GetAvailableMarker());
634 }
635 }
636 return BuildAnimation(dialog, dialogTheme);
637 }
638
BuildDialogWithType(DialogType type)639 RefPtr<DialogComponent> DialogBuilder::BuildDialogWithType(DialogType type)
640 {
641 RefPtr<DialogComponent> dialog;
642 // Create different dialog according to type.
643 switch (type) {
644 case DialogType::ALERT_DIALOG: {
645 dialog = AceType::MakeRefPtr<AlertDialogComponent>();
646 dialog->SetOnSuccessId(BackEndEventManager<void(int32_t)>::GetInstance().GetAvailableMarker());
647 break;
648 }
649 case DialogType::ACTION_SHEET: {
650 dialog = AceType::MakeRefPtr<ActionSheetComponent>();
651 dialog->SetIsMenu(true);
652 dialog->SetOnSuccessId(BackEndEventManager<void(int32_t)>::GetInstance().GetAvailableMarker());
653 break;
654 }
655 default:
656 dialog = AceType::MakeRefPtr<DialogComponent>();
657 break;
658 }
659 return dialog;
660 }
661
BuildTitleAndContent(const RefPtr<DialogComponent> & dialog,const DialogProperties & dialogProperties,const RefPtr<DialogTheme> & dialogTheme,std::string & data)662 void DialogBuilder::BuildTitleAndContent(const RefPtr<DialogComponent>& dialog,
663 const DialogProperties& dialogProperties, const RefPtr<DialogTheme>& dialogTheme, std::string& data)
664 {
665 auto deviceType = SystemProperties::GetDeviceType();
666 if ((deviceType != DeviceType::WATCH) && (!dialogProperties.title.empty())) {
667 auto titleComponent = AceType::MakeRefPtr<TextComponent>(dialogProperties.title);
668 auto style = dialogTheme->GetTitleTextStyle();
669 style.SetMaxLines(dialogTheme->GetTitleMaxLines());
670 style.SetTextOverflow(TextOverflow::ELLIPSIS);
671 style.SetAdaptTextSize(style.GetFontSize(), dialogTheme->GetTitleMinFontSize());
672 titleComponent->SetTextStyle(style);
673 titleComponent->SetFocusColor(style.GetTextColor());
674 dialog->SetTitle(titleComponent);
675 data += dialogProperties.title + SEPARATE;
676 }
677 if (!dialogProperties.content.empty()) {
678 auto contentComponent = AceType::MakeRefPtr<TextComponent>(dialogProperties.content);
679 auto contentStyle = dialogTheme->GetContentTextStyle();
680 if (deviceType == DeviceType::WATCH) {
681 std::vector<TextSizeGroup> preferTextSizeGroups;
682 preferTextSizeGroups.push_back({ contentStyle.GetFontSize(), 1 });
683 preferTextSizeGroups.push_back({ dialogTheme->GetContentMinFontSize(), UINT32_MAX, TextOverflow::NONE });
684 contentStyle.SetPreferTextSizeGroups(preferTextSizeGroups);
685 contentStyle.SetTextAlign(TextAlign::CENTER);
686 }
687 contentComponent->SetTextStyle(contentStyle);
688 contentComponent->SetFocusColor(dialogTheme->GetContentTextStyle().GetTextColor());
689 dialog->SetContent(contentComponent);
690 data += dialogProperties.content + SEPARATE;
691 }
692 }
693
BuildButtons(const RefPtr<ThemeManager> & themeManager,const RefPtr<DialogComponent> & dialog,const std::vector<ButtonInfo> & buttons,const RefPtr<DialogTheme> & dialogTheme,std::string & data)694 void DialogBuilder::BuildButtons(const RefPtr<ThemeManager>& themeManager, const RefPtr<DialogComponent>& dialog,
695 const std::vector<ButtonInfo>& buttons, const RefPtr<DialogTheme>& dialogTheme,
696 std::string& data)
697 {
698 if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
699 BuildButtonsForWatch(themeManager, dialog, data);
700 return;
701 }
702 if (buttons.empty()) {
703 return;
704 }
705 auto buttonTheme = AceType::DynamicCast<ButtonTheme>(themeManager->GetTheme(ButtonTheme::TypeId()));
706 if (!buttonTheme) {
707 return;
708 }
709 int32_t buttonIndex = 0;
710 std::list<RefPtr<ButtonComponent>> buttonComponents;
711 for (const auto& button : buttons) {
712 if (button.text.empty()) {
713 continue;
714 }
715 data += button.text + SEPARATE;
716
717 // Init text style in button.
718 TextStyle buttonTextStyle = buttonTheme->GetTextStyle();
719 const Color TEXT_COLOR = Color::FromString("#0a59f4");
720 buttonTextStyle.SetTextColor(TEXT_COLOR);
721 buttonTextStyle.SetAdaptTextSize(buttonTheme->GetMaxFontSize(), buttonTheme->GetMinFontSize());
722 buttonTextStyle.SetMaxLines(1);
723 buttonTextStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
724 if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
725 buttonTextStyle.SetAdaptTextSize(dialogTheme->GetButtonTextSize(), dialogTheme->GetMinButtonTextSize());
726 buttonTextStyle.SetFontWeight(FontWeight::MEDIUM);
727 buttonTextStyle.SetFontSize(dialogTheme->GetButtonTextSize());
728 buttonTextStyle.SetAdaptTextSize(dialogTheme->GetButtonTextSize(), dialogTheme->GetMinButtonTextSize());
729 if (buttonIndex != static_cast<int32_t>(buttons.size()) - 1) {
730 buttonTextStyle.SetTextColor(dialogTheme->GetCommonButtonTextColor());
731 } else {
732 buttonTextStyle.SetTextColor(dialogTheme->GetEmphasizeButtonTextColor());
733 }
734 }
735
736 RefPtr<ButtonComponent> buttonComponent;
737 if (!button.textColor.empty()) {
738 buttonTextStyle.SetTextColor(Color::FromString(button.textColor));
739 buttonComponent = ButtonBuilder::Build(
740 themeManager, button.text, buttonTextStyle, Color::FromString(button.textColor), true);
741 } else {
742 buttonComponent =
743 ButtonBuilder::Build(themeManager, button.text, buttonTextStyle, buttonTextStyle.GetTextColor(), true);
744 }
745 buttonComponent->SetBackgroundColor(dialogTheme->GetButtonBackgroundColor());
746 if (SystemProperties::GetDeviceType() == DeviceType::CAR) {
747 buttonComponent->SetHeight(dialogTheme->GetButtonHeight());
748 buttonComponent->SetRectRadius(dialogTheme->GetButtonHeight() / 2.0);
749 if (buttonIndex != static_cast<int32_t>(buttons.size()) - 1) {
750 buttonComponent->SetBackgroundColor(dialogTheme->GetCommonButtonBgColor());
751 } else {
752 buttonComponent->SetBackgroundColor(dialogTheme->GetEmphasizeButtonBgColor());
753 }
754 }
755 buttonComponent->SetHoverColor(Color::FromString("#0C000000"));
756 buttonComponent->SetClickedColor(dialogTheme->GetButtonClickedColor());
757 // If background color of button is setted by developer, use it.
758 if (button.isBgColorSetted) {
759 buttonComponent->SetBackgroundColor(button.bgColor);
760 buttonComponent->SetHoverColor(button.bgColor.BlendColorWithAlpha(dialogTheme->GetButtonClickedColor()));
761 buttonComponent->SetClickedColor(button.bgColor.BlendColorWithAlpha(dialogTheme->GetButtonClickedColor()));
762 }
763 buttonComponent->SetType(ButtonType::TEXT);
764 buttonComponents.emplace_back(buttonComponent);
765 ++buttonIndex;
766 }
767 dialog->SetActions(buttonComponents);
768 }
769
BuildButtonsForWatch(const RefPtr<ThemeManager> & themeManager,const RefPtr<DialogComponent> & dialog,std::string & data)770 void DialogBuilder::BuildButtonsForWatch(
771 const RefPtr<ThemeManager>& themeManager, const RefPtr<DialogComponent>& dialog, std::string& data)
772 {
773 auto buttonTheme = AceType::DynamicCast<ButtonTheme>(themeManager->GetTheme(ButtonTheme::TypeId()));
774 auto dialogTheme = AceType::DynamicCast<DialogTheme>(themeManager->GetTheme(DialogTheme::TypeId()));
775 if (!buttonTheme || !dialogTheme) {
776 return;
777 }
778 std::string buttonText;
779 std::list<RefPtr<ButtonComponent>> buttonComponents;
780 for (int32_t i = 1; i <= DIALOG_BUTTONS_COUNT_WATCH; ++i) {
781 auto buttonPadding = AceType::MakeRefPtr<PaddingComponent>();
782 buttonPadding->SetPadding(buttonTheme->GetMinCircleButtonPadding());
783 RefPtr<ImageComponent> buttonIcon;
784 if (i == 1) {
785 buttonText = Localization::GetInstance()->GetEntryLetters(DIALOG_CANCEL);
786 buttonIcon = AceType::MakeRefPtr<ImageComponent>(InternalResource::ResourceId::WRONG_SVG);
787 } else {
788 buttonText = Localization::GetInstance()->GetEntryLetters(DIALOG_OK);
789 buttonIcon = AceType::MakeRefPtr<ImageComponent>(InternalResource::ResourceId::CORRECT_SVG);
790 }
791 data += buttonText + SEPARATE;
792 buttonIcon->SetWidth(buttonTheme->GetMinCircleButtonIcon());
793 buttonIcon->SetHeight(buttonTheme->GetMinCircleButtonIcon());
794 buttonPadding->SetChild(buttonIcon);
795 std::list<RefPtr<Component>> buttonChildren;
796 buttonChildren.emplace_back(buttonPadding);
797 auto buttonComponent = AceType::MakeRefPtr<ButtonComponent>(buttonChildren);
798 buttonComponent->SetWidth(buttonTheme->GetMinCircleButtonDiameter());
799 buttonComponent->SetHeight(buttonTheme->GetMinCircleButtonDiameter());
800 buttonComponent->SetRectRadius(buttonTheme->GetMinCircleButtonDiameter() / 2.0);
801 buttonComponent->SetBackgroundColor(buttonTheme->GetBgColor());
802 buttonComponent->SetClickedColor(buttonTheme->GetClickedColor());
803 if (i == 2) {
804 buttonComponent->SetBackgroundColor(dialogTheme->GetButtonBackgroundColor());
805 buttonComponent->SetClickedColor(dialogTheme->GetButtonClickedColor());
806 }
807 buttonComponent->SetFocusColor(buttonTheme->GetBgFocusColor());
808 buttonComponent->SetFocusAnimationColor(buttonTheme->GetBgFocusColor());
809 buttonComponent->SetAccessibilityText(buttonText);
810 buttonComponents.emplace_back(buttonComponent);
811 }
812 dialog->SetActions(buttonComponents);
813 }
814
BuildAnimation(const RefPtr<DialogComponent> & dialogChild,const RefPtr<DialogTheme> & dialogTheme)815 RefPtr<DialogComponent> DialogBuilder::BuildAnimation(
816 const RefPtr<DialogComponent>& dialogChild, const RefPtr<DialogTheme>& dialogTheme)
817 {
818 auto tweenBox = AceType::MakeRefPtr<BoxComponent>();
819 auto decoration = AceType::MakeRefPtr<Decoration>();
820 decoration->SetBackgroundColor(Color(dialogTheme->GetMaskColorEnd()));
821 tweenBox->SetBackDecoration(decoration);
822 const auto& colorAnimation = AceType::MakeRefPtr<CurveAnimation<Color>>(
823 dialogTheme->GetMaskColorStart(), dialogTheme->GetMaskColorEnd(), Curves::LINEAR);
824 // Build tween option of in
825 TweenOption tweenOptionIn;
826 tweenOptionIn.SetColorAnimation(colorAnimation);
827 tweenOptionIn.SetDuration(dialogTheme->GetAnimationDurationIn());
828 tweenOptionIn.SetFillMode(FillMode::FORWARDS);
829 // Build tween option of out
830 const auto& colorAnimationOut = AceType::MakeRefPtr<CurveAnimation<Color>>(
831 dialogTheme->GetMaskColorEnd(), dialogTheme->GetMaskColorStart(), Curves::LINEAR);
832 TweenOption tweenOptionOut;
833 tweenOptionOut.SetColorAnimation(colorAnimationOut);
834 tweenOptionOut.SetDuration(dialogTheme->GetAnimationDurationOut());
835 tweenOptionOut.SetFillMode(FillMode::FORWARDS);
836 // Build transition
837 auto transition =
838 AceType::MakeRefPtr<TransitionComponent>(TweenComponent::AllocTweenComponentId(), "transition", tweenBox);
839 transition->SetIsFirstFrameShow(false);
840 transition->SetTransitionOption(tweenOptionIn, tweenOptionOut);
841
842 dialogChild->SetChild(transition);
843 dialogChild->SetDialogTweenBox(tweenBox);
844 return dialogChild;
845 }
846
847 } // namespace OHOS::Ace