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_modal/dialog_modal_element.h"
17 
18 #include "core/components/clip/clip_element.h"
19 #include "core/components/dialog_modal/render_dialog_modal.h"
20 #include "core/components/root/render_root.h"
21 
22 namespace OHOS::Ace {
23 namespace {
24 
25 constexpr int32_t APP_TRANSITION_DURATION = 250;
26 constexpr double SCALE_BEGIN = 0.9;
27 constexpr double SCALE_END = 1.0;
28 constexpr double OPACITY_BEGIN = 0.0;
29 constexpr double OPACITY_END = 1.0;
30 
GetPageLayoutSize(const RefPtr<PageElement> & page)31 Size GetPageLayoutSize(const RefPtr<PageElement>& page)
32 {
33     if (!page) {
34         LOGE("Get page layout size failed. page is null.");
35         return Size();
36     }
37     auto render = page->GetRenderNode();
38     if (!render) {
39         LOGE("Get page layout size failed. render is null.");
40         return Size();
41     }
42     return render->GetLayoutSize();
43 }
44 
45 } // namespace
46 
GetOverlayElement() const47 RefPtr<OverlayElement> DialogModalElement::GetOverlayElement() const
48 {
49     auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
50     if (!tween) {
51         LOGE("Get overlay element failed. Tween element is null!");
52         return RefPtr<OverlayElement>();
53     }
54     auto box = AceType::DynamicCast<BoxElement>(tween->GetContentElement());
55     if (!box) {
56         LOGE("Get overlay element failed. Box element is null!");
57         return RefPtr<OverlayElement>();
58     }
59     auto clip = AceType::DynamicCast<ClipElement>(box->GetFirstChild());
60     if (!clip) {
61         LOGE("Get overlay element failed. Clip element is null!");
62         return RefPtr<OverlayElement>();
63     }
64     auto bgBox = AceType::DynamicCast<BoxElement>(clip->GetFirstChild());
65     if (!bgBox) {
66         LOGE("Get overlay element failed. bgBox element is null!");
67         return RefPtr<OverlayElement>();
68     }
69     auto stack = bgBox->GetFirstChild();
70     if (!stack) {
71         return RefPtr<OverlayElement>();
72     }
73     auto child = stack->GetChildren();
74     if (child.size() > 1) {
75         auto it = child.begin();
76         it++;
77         return AceType::DynamicCast<OverlayElement>(*it);
78     }
79     return RefPtr<OverlayElement>();
80 }
81 
GetStageElement() const82 RefPtr<StageElement> DialogModalElement::GetStageElement() const
83 {
84     auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
85     if (!tween) {
86         LOGE("Get overlay element failed. Tween element is null!");
87         return RefPtr<StageElement>();
88     }
89     auto box = AceType::DynamicCast<BoxElement>(tween->GetContentElement());
90     if (!box) {
91         LOGE("Get overlay element failed. Box element is null!");
92         return RefPtr<StageElement>();
93     }
94     auto clip = AceType::DynamicCast<ClipElement>(box->GetFirstChild());
95     if (!clip) {
96         LOGE("Get overlay element failed. Clip element is null!");
97         return RefPtr<StageElement>();
98     }
99     auto bgBox = AceType::DynamicCast<BoxElement>(clip->GetFirstChild());
100     if (!bgBox) {
101         LOGE("Get overlay element failed. bgBox element is null!");
102         return RefPtr<StageElement>();
103     }
104     auto stack = bgBox->GetFirstChild();
105     if (!stack) {
106         return RefPtr<StageElement>();
107     }
108     return AceType::DynamicCast<StageElement>(stack->GetFirstChild());
109 }
110 
UpdateSystemBarHeight(double statusBar,double navigationBar)111 void DialogModalElement::UpdateSystemBarHeight(double statusBar, double navigationBar)
112 {
113     auto renderNode = AceType::DynamicCast<RenderDialogModal>(GetRenderNode());
114     if (!renderNode) {
115         return;
116     }
117     renderNode->UpdateSystemBarHeight(statusBar, navigationBar);
118 }
119 
RegisterTransitionListener()120 void DialogModalElement::RegisterTransitionListener()
121 {
122     auto context = context_.Upgrade();
123     if (!context) {
124         LOGE("Register Transition Listener failed. context is null.");
125         return;
126     }
127     context->AddPageTransitionListener([weak = AceType::WeakClaim(this)](const TransitionEvent& event,
128                                            const WeakPtr<PageElement>& in, const WeakPtr<PageElement>& out) {
129         auto dialog = weak.Upgrade();
130         if (!dialog) {
131             LOGE("Handle transition event failed. dialog is null.");
132             return;
133         }
134         auto dialogRender = AceType::DynamicCast<RenderDialogModal>(dialog->GetRenderNode());
135         if (!dialogRender) {
136             LOGE("Handle transition event failed. dialog render is null.");
137             return;
138         }
139         if (event == TransitionEvent::POP_START) {
140             auto page = AceType::DynamicCast<PageElement>(out.Upgrade());
141             if (!page) {
142                 LOGE("Handle pop transition event failed. page out is null.");
143                 return;
144             }
145             auto pageHeight = GetPageLayoutSize(page).Height();
146             dialogRender->AnimateTo(pageHeight, true);
147         } else if (event == TransitionEvent::PUSH_START) {
148             auto page = AceType::DynamicCast<PageElement>(in.Upgrade());
149             if (!page) {
150                 // push first page.
151                 dialogRender->SetLayoutNotify([weak]() {
152                     auto dialog = weak.Upgrade();
153                     if (dialog) {
154                         dialog->AnimateToEnterApp();
155                     }
156                 });
157                 return;
158             }
159             auto render = page->GetRenderNode();
160             if (!render) {
161                 LOGE("Handle push transition event failed. page render is null.");
162                 return;
163             }
164             auto fullSize = render->GetLayoutSize();
165             auto pageHeight = fullSize.Height();
166             dialogRender->AnimateTo(pageHeight, false);
167         }
168     });
169 }
170 
PerformBuild()171 void DialogModalElement::PerformBuild()
172 {
173     SoleChildElement::PerformBuild();
174     if (!controller_) {
175         controller_ = CREATE_ANIMATOR(GetContext());
176         controller_->SetDuration(APP_TRANSITION_DURATION);
177         controller_->SetFillMode(FillMode::FORWARDS);
178         auto scale = MakeRefPtr<CurveAnimation<float>>(SCALE_BEGIN, SCALE_END, Curves::FRICTION);
179         scale->SetReverseCurve(Curves::FAST_OUT_SLOW_IN);
180         auto opacity = MakeRefPtr<CurveAnimation<float>>(OPACITY_BEGIN, OPACITY_END, Curves::FAST_OUT_SLOW_IN);
181         option_.SetTransformFloatAnimation(AnimationType::SCALE, scale);
182         option_.SetOpacityAnimation(opacity);
183 
184         auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
185         if (!tween) {
186             LOGE("Dialog Modal perform build failed. tween is null.");
187             return;
188         }
189         tween->SetController(controller_);
190         tween->SetOption(option_);
191         tween->ApplyKeyframes();
192         tween->SetOpacity(OPACITY_BEGIN);
193     }
194 }
195 
CreateOriginAnimation()196 void DialogModalElement::CreateOriginAnimation()
197 {
198     auto dialogRender = AceType::DynamicCast<RenderDialogModal>(GetRenderNode());
199     if (!dialogRender) {
200         LOGE("Create origin animation failed. dialog Render is null.");
201         return;
202     }
203     const auto& clip = dialogRender->GetRenderClip();
204     if (!clip) {
205         LOGE("Create origin animation failed. clip is null.");
206         return;
207     }
208     option_.ClearListeners();
209     if (controller_) {
210         controller_->ClearInterpolators();
211     }
212     const auto& clipRect = clip->GetClipRect(clip->GetPaintRect().GetOffset());
213     double centerX = clipRect.GetOffset().GetX() + clipRect.Width() / 2.0;
214     double centerY = clipRect.GetOffset().GetY() + clipRect.Height() / 2.0;
215     option_.SetTransformOrigin(Dimension(centerX), Dimension(centerY));
216     auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
217     if (!tween) {
218         LOGE("Create origin animation failed. tween is null.");
219         return;
220     }
221     tween->SetOption(option_);
222     tween->ApplyKeyframes();
223 }
224 
AnimateToEnterApp()225 void DialogModalElement::AnimateToEnterApp()
226 {
227     if (!controller_) {
228         LOGE("Animate To Enter App failed. controller is null.");
229         return;
230     }
231     controller_->RemoveStopListener(stopCallbackId_);
232     CreateOriginAnimation();
233     controller_->Forward();
234     auto rootElement = GetElementParent().Upgrade();
235     if (!rootElement) {
236         LOGE("Animate To Enter App failed. root element is null.");
237         return;
238     }
239     auto root = AceType::DynamicCast<RenderRoot>(rootElement->GetRenderNode());
240     if (!root) {
241         LOGE("Animate To Enter App failed. render root is null.");
242         return;
243     }
244     root->AnimateToShow(APP_TRANSITION_DURATION);
245 }
246 
AnimateToExitApp()247 void DialogModalElement::AnimateToExitApp()
248 {
249     if (!controller_) {
250         LOGE("Animate To Exit App failed. controller is null.");
251         return;
252     }
253     controller_->RemoveStopListener(stopCallbackId_);
254     CreateOriginAnimation();
255     controller_->Backward();
256     stopCallbackId_ = controller_->AddStopListener([contextWeak = context_]() {
257         auto context = contextWeak.Upgrade();
258         if (!context) {
259             LOGE("Handle dialog modal exit transition failed. context is null.");
260             return;
261         }
262         // force finish
263         context->Finish(false);
264     });
265     auto rootElement = GetElementParent().Upgrade();
266     if (!rootElement) {
267         LOGE("Animate To Exit App failed. root element is null.");
268         return;
269     }
270     auto root = AceType::DynamicCast<RenderRoot>(rootElement->GetRenderNode());
271     if (!root) {
272         LOGE("Animate To Exit App failed. render root is null.");
273         return;
274     }
275     root->AnimateToHide(APP_TRANSITION_DURATION);
276 }
277 
278 } // namespace OHOS::Ace
279