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