1 /*
2 * Copyright (c) 2021 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 } else {
173 // Do Nothing for other events.
174 }
175 });
176 }
177
PerformBuild()178 void DialogModalElement::PerformBuild()
179 {
180 SoleChildElement::PerformBuild();
181 if (!controller_) {
182 controller_ = AceType::MakeRefPtr<Animator>(GetContext());
183 controller_->SetDuration(APP_TRANSITION_DURATION);
184 controller_->SetFillMode(FillMode::FORWARDS);
185 auto scale = MakeRefPtr<CurveAnimation<float>>(SCALE_BEGIN, SCALE_END, Curves::FRICTION);
186 scale->SetReverseCurve(Curves::FAST_OUT_SLOW_IN);
187 auto opacity = MakeRefPtr<CurveAnimation<float>>(OPACITY_BEGIN, OPACITY_END, Curves::FAST_OUT_SLOW_IN);
188 option_.SetTransformFloatAnimation(AnimationType::SCALE, scale);
189 option_.SetOpacityAnimation(opacity);
190
191 auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
192 if (!tween) {
193 LOGE("Dialog Modal perform build failed. tween is null.");
194 return;
195 }
196 tween->SetController(controller_);
197 tween->SetOption(option_);
198 tween->ApplyKeyframes();
199 tween->SetOpacity(OPACITY_BEGIN);
200 }
201 }
202
CreateOriginAnimation()203 void DialogModalElement::CreateOriginAnimation()
204 {
205 auto dialogRender = AceType::DynamicCast<RenderDialogModal>(GetRenderNode());
206 if (!dialogRender) {
207 LOGE("Create origin animation failed. dialog Render is null.");
208 return;
209 }
210 const auto& clip = dialogRender->GetRenderClip();
211 if (!clip) {
212 LOGE("Create origin animation failed. clip is null.");
213 return;
214 }
215 option_.ClearListeners();
216 if (controller_) {
217 controller_->ClearInterpolators();
218 }
219 const auto& clipRect = clip->GetClipRect(clip->GetPaintRect().GetOffset());
220 double centerX = clipRect.GetOffset().GetX() + clipRect.Width() / 2.0;
221 double centerY = clipRect.GetOffset().GetY() + clipRect.Height() / 2.0;
222 option_.SetTransformOrigin(Dimension(centerX), Dimension(centerY));
223 auto tween = AceType::DynamicCast<TweenElement>(GetFirstChild());
224 if (!tween) {
225 LOGE("Create origin animation failed. tween is null.");
226 return;
227 }
228 tween->SetOption(option_);
229 tween->ApplyKeyframes();
230 }
231
AnimateToEnterApp()232 void DialogModalElement::AnimateToEnterApp()
233 {
234 if (!controller_) {
235 LOGE("Animate To Enter App failed. controller is null.");
236 return;
237 }
238 controller_->RemoveStopListener(stopCallbackId_);
239 CreateOriginAnimation();
240 controller_->Forward();
241 auto rootElement = GetElementParent().Upgrade();
242 if (!rootElement) {
243 LOGE("Animate To Enter App failed. root element is null.");
244 return;
245 }
246 auto root = AceType::DynamicCast<RenderRoot>(rootElement->GetRenderNode());
247 if (!root) {
248 LOGE("Animate To Enter App failed. render root is null.");
249 return;
250 }
251 root->AnimateToShow(APP_TRANSITION_DURATION);
252 }
253
AnimateToExitApp()254 void DialogModalElement::AnimateToExitApp()
255 {
256 if (!controller_) {
257 LOGE("Animate To Exit App failed. controller is null.");
258 return;
259 }
260 controller_->RemoveStopListener(stopCallbackId_);
261 CreateOriginAnimation();
262 controller_->Backward();
263 stopCallbackId_ = controller_->AddStopListener([contextWeak = context_]() {
264 auto context = contextWeak.Upgrade();
265 if (!context) {
266 LOGE("Handle dialog modal exit transition failed. context is null.");
267 return;
268 }
269 // force finish
270 context->Finish(false);
271 });
272 auto rootElement = GetElementParent().Upgrade();
273 if (!rootElement) {
274 LOGE("Animate To Exit App failed. root element is null.");
275 return;
276 }
277 auto root = AceType::DynamicCast<RenderRoot>(rootElement->GetRenderNode());
278 if (!root) {
279 LOGE("Animate To Exit App failed. render root is null.");
280 return;
281 }
282 root->AnimateToHide(APP_TRANSITION_DURATION);
283 }
284
285 } // namespace OHOS::Ace
286