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/render_dialog_modal.h"
17
18 #include "base/log/event_report.h"
19 #include "core/components/stage/stage_element.h"
20
21 namespace OHOS::Ace {
22 namespace {
23
24 constexpr Dimension BG_MARGIN = 16.0_vp;
25 constexpr int32_t TRANSITION_DURATION = 350;
26 constexpr double MAX_HEIGHT_PERCENT = 0.8;
27
28 } // namespace
29
Create()30 RefPtr<RenderNode> RenderDialogModal::Create()
31 {
32 return AceType::MakeRefPtr<RenderDialogModal>();
33 }
34
Update(const RefPtr<Component> & component)35 void RenderDialogModal::Update(const RefPtr<Component>& component)
36 {
37 if (!controller_) {
38 controller_ = CREATE_ANIMATOR(GetContext());
39 }
40 MarkNeedLayout();
41 }
42
PerformLayout()43 void RenderDialogModal::PerformLayout()
44 {
45 auto child = GetFirstChild();
46 if (!child) {
47 LOGE("Child is null!");
48 return;
49 }
50 auto statusBarPx = NormalizeToPx(Dimension(statusBarHeight_, DimensionUnit::VP));
51 auto navigationBarPx = NormalizeToPx(Dimension(navigationBarHeight_, DimensionUnit::VP));
52 auto innerLayoutParam = GetLayoutParam();
53 auto maxSize = GetLayoutParam().GetMaxSize();
54 bool exceeds = false;
55 double dialogHeight = maxSize.Height() * MAX_HEIGHT_PERCENT + NormalizeToPx(BG_MARGIN) * 2;
56 if (dialogHeight + statusBarPx + navigationBarPx > maxSize.Height()) {
57 // exceeds total height.
58 exceeds = true;
59 dialogHeight = maxSize.Height() - navigationBarPx - NormalizeToPx(BG_MARGIN);
60 }
61 maxSize.SetHeight(dialogHeight);
62 // Layout as max as possible.
63 innerLayoutParam.SetMaxSize(maxSize);
64 innerLayoutParam.SetMinSize(Size(maxSize.Width(), innerLayoutParam.GetMinSize().Height()));
65 viewPort_.SetHeight(maxSize.Height() - NormalizeToPx(BG_MARGIN) * 2);
66 viewPort_.SetWidth(maxSize.Width() - NormalizeToPx(BG_MARGIN) * 2);
67 child->Layout(innerLayoutParam);
68 auto childY = GetLayoutParam().GetMaxSize().Height() - navigationBarPx - maxSize.Height();
69 child->SetPosition(Offset(0.0, childY));
70 Size selfSize;
71 if (!exceeds) {
72 selfSize = Size(GetLayoutParam().GetMaxSize().Width(),
73 GetLayoutParam().GetMaxSize().Height() - statusBarPx - navigationBarPx);
74 } else {
75 selfSize = Size(GetLayoutParam().GetMaxSize().Width(),
76 dialogHeight - statusBarPx);
77 }
78 SetLayoutSize(selfSize);
79 PerformClip();
80 if (notify_) {
81 decltype(notify_) notify = std::move(notify_);
82 notify();
83 }
84 }
85
UpdateSystemBarHeight(double statusBar,double navigationBar)86 void RenderDialogModal::UpdateSystemBarHeight(double statusBar, double navigationBar)
87 {
88 statusBarHeight_ = statusBar;
89 double delta = NormalizeToPx(Dimension(navigationBar - navigationBarHeight_, DimensionUnit::VP));
90 navigationBarHeight_ = navigationBar;
91 MovePage(delta);
92 MarkNeedLayout();
93 }
94
AnimateTo(double pageHeight,bool reverse)95 void RenderDialogModal::AnimateTo(double pageHeight, bool reverse)
96 {
97 animateTargetHeight_ = pageHeight;
98 animatingPush_ = !reverse;
99 if (controller_->IsRunning()) {
100 controller_->Stop();
101 lastAnimateFrame_ = false;
102 }
103 controller_->ClearInterpolators();
104 controller_->ClearAllListeners();
105 auto clip = GetRenderClip();
106 if (!clip) {
107 LOGE("AnimateTo failed. render clip is null.");
108 EventReport::SendRenderException(RenderExcepType::CLIP_ERR);
109 return;
110 }
111 double from = clip->GetHeight();
112 auto keyframeFrom = AceType::MakeRefPtr<Keyframe<double>>(0.0, reverse ? pageHeight : from);
113 auto keyframeTo = AceType::MakeRefPtr<Keyframe<double>>(1.0, reverse ? from : pageHeight);
114 auto heightAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
115 heightAnimation->AddKeyframe(keyframeFrom);
116 heightAnimation->AddKeyframe(keyframeTo);
117 heightAnimation->SetCurve(Curves::FRICTION);
118
119 heightAnimation->AddListener([weak = AceType::WeakClaim(this)](const double& height) {
120 auto dialogModal = weak.Upgrade();
121 if (!dialogModal) {
122 LOGE("Semi modal is null.");
123 return;
124 }
125 if (LessNotEqual(height, 0.0)) {
126 LOGE("Height less than zero, do not animate it.");
127 return;
128 }
129 dialogModal->animatingPageHeight_ = height;
130 dialogModal->MarkNeedLayout();
131 });
132 controller_->AddInterpolator(heightAnimation);
133 controller_->SetDuration(TRANSITION_DURATION);
134 controller_->SetFillMode(FillMode::FORWARDS);
135 if (reverse) {
136 controller_->Backward();
137 } else {
138 controller_->Forward();
139 }
140 controller_->AddStopListener([weak = AceType::WeakClaim(this)]() {
141 auto dialog = weak.Upgrade();
142 if (!dialog) {
143 return;
144 }
145 dialog->lastAnimateFrame_ = true;
146 });
147 }
148
GetTopPageLayoutSize() const149 Size RenderDialogModal::GetTopPageLayoutSize() const
150 {
151 auto context = context_.Upgrade();
152 if (!context) {
153 LOGE("Get Top page layout Size failed. context is null");
154 return Size();
155 }
156 auto stage = context->GetStageElement();
157 if (!stage) {
158 LOGE("Get Top page layout Size failed. stage is null");
159 return Size();
160 }
161 auto topPage = stage->GetLastChild();
162 if (!topPage) {
163 LOGE("Get Top page layout Size failed. topPage is null");
164 return Size();
165 }
166 auto render = topPage->GetRenderNode();
167 if (!render) {
168 LOGE("Get Top page layout Size failed. render is null");
169 return Size();
170 }
171 return render->GetLayoutSize();
172 }
173
CanRouterPage() const174 bool RenderDialogModal::CanRouterPage() const
175 {
176 auto context = context_.Upgrade();
177 if (!context) {
178 LOGE("Query can router page failed. context is null.");
179 return false;
180 }
181 auto stage = context->GetStageElement();
182 if (!stage) {
183 LOGE("Query can router page failed. stage is null.");
184 return false;
185 }
186 return stage->CanRouterPage();
187 }
188
PerformClip()189 void RenderDialogModal::PerformClip()
190 {
191 auto clip = GetRenderClip();
192 if (!clip) {
193 LOGE("Perform build failed. render clip is null");
194 EventReport::SendRenderException(RenderExcepType::CLIP_ERR);
195 return;
196 }
197 double pageHeight = 0.0;
198 if (IsAnimating()) {
199 pageHeight = animatingPageHeight_;
200 if (!NearEqual(GetTopPageLayoutSize().Height(), animateTargetHeight_) && animatingPush_) {
201 // Page height has changed during push page animation, change animation target height.
202 AnimateTo(GetTopPageLayoutSize().Height(), false);
203 }
204 lastAnimateFrame_ = false;
205 } else {
206 if (!CanRouterPage()) {
207 return;
208 }
209 pageHeight = GetTopPageLayoutSize().Height();
210 }
211 clip->SetOffsetY(clip->GetLayoutSize().Height() - pageHeight);
212 clip->SetHeight(pageHeight);
213 clip->MarkNeedRender();
214 }
215
GetRenderClip() const216 RefPtr<RenderClip> RenderDialogModal::GetRenderClip() const
217 {
218 auto transform = GetFirstChild();
219 if (!transform) {
220 LOGE("Get Clip Render failed. transform is null.");
221 return nullptr;
222 }
223 auto display = transform->GetFirstChild();
224 if (!display) {
225 LOGE("Get Clip Render failed. transform is null.");
226 return nullptr;
227 }
228 auto box = display->GetFirstChild();
229 if (!box) {
230 LOGE("Get Clip Render failed. box is null.");
231 return nullptr;
232 }
233 return AceType::DynamicCast<RenderClip>(box->GetFirstChild());
234 }
235
MovePage(double delta)236 void RenderDialogModal::MovePage(double delta)
237 {
238 auto context = context_.Upgrade();
239 if (!context) {
240 LOGE("Move failed, context is null");
241 return;
242 }
243 auto clip = GetRenderClip();
244 if (!clip) {
245 LOGE("Move failed, clip is null");
246 return;
247 }
248 double newHeight = clip->GetGlobalOffset().GetY() + clip->GetPaintRect().Height() - delta;
249 context->MovePage(Offset(clip->GetPaintRect().Width(), newHeight), delta);
250 }
251
252 } // namespace OHOS::Ace
253