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