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