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