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/navigation_bar/render_collapsing_navigation_bar.h"
17 
18 #include "core/components/display/display_component.h"
19 #include "core/components/navigation_bar/navigation_bar_component.h"
20 #include "core/components/navigation_bar/render_navigation_container.h"
21 #include "core/components/transform/transform_component.h"
22 #include "core/components_ng/pattern/navigation/navigation_model_data.h"
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 constexpr int32_t COLLAPSING_ANIMATION_DURATION = 300;
28 constexpr double SPRING_DRAG_DELTA_OFFSET_RATIO = 7;
29 constexpr double SPRING_RESTORE_DELTA_OFFSET_RATIO = 5;
30 constexpr double FIX_TITLE_BAR_OFFSET_RATIO = 1.5;
31 constexpr double BIGGER_TITLE_SIZE = 1.0;
32 constexpr double BIGGER_TITLE_SIZE_MULTIPLE = 1.1;
33 constexpr double MAX_ALPHA_VALUE = 255.0;
34 constexpr double TRANSPARENT = 0.0;
35 constexpr Dimension HIDE_TITLE_MARGIN_TOP = 12.0_vp;
36 constexpr Dimension SHOW_TITLE_MARGIN_TOP = 8.0_vp;
37 constexpr Dimension BIGGER_TITLE_POSITION_OFFSET = 30.0_vp;
38 
39 } // namespace
40 
Create()41 RefPtr<RenderNode> RenderCollapsingNavigationBar::Create()
42 {
43     return AceType::MakeRefPtr<RenderCollapsingNavigationBar>();
44 }
45 
Update(const RefPtr<Component> & component)46 void RenderCollapsingNavigationBar::Update(const RefPtr<Component>& component)
47 {
48     Initialize(GetContext());
49 
50     auto collapsingComponent = AceType::DynamicCast<CollapsingNavigationBarComponent>(component);
51     ACE_DCHECK(collapsingComponent);
52     auto pipelineContext = GetContext();
53     auto titleComposed = collapsingComponent->GetTitleComposed();
54     if (titleComposed) {
55         titleChangedCallback_ = [titleComposed, pipelineContext, weakBar = AceType::WeakClaim(this)](double fontSize) {
56             auto boxComponent = AceType::DynamicCast<BoxComponent>(titleComposed->GetChild());
57             if (!boxComponent) {
58                 return;
59             }
60             auto transformComponent = AceType::DynamicCast<TransformComponent>(boxComponent->GetChild());
61             if (!transformComponent) {
62                 return;
63             }
64             auto bar = weakBar.Upgrade();
65             if (!bar) {
66                 return;
67             }
68             double scale = fontSize / bar->lastTitleScale_;
69             transformComponent->Scale(scale, scale);
70             bar->lastTitleScale_ = scale * bar->lastTitleScale_;
71             pipelineContext.Upgrade()->ScheduleUpdate(titleComposed);
72         };
73     }
74 
75     auto subtitleComposed = collapsingComponent->GetSubtitleComposed();
76     if (subtitleComposed) {
77         subtitleChangedCallback_ = [subtitleComposed, pipelineContext](double opacity) {
78             auto boxComponent = AceType::DynamicCast<BoxComponent>(subtitleComposed->GetChild());
79             if (!boxComponent) {
80                 return;
81             }
82             auto displayComponent = AceType::DynamicCast<DisplayComponent>(boxComponent->GetChild());
83             if (!displayComponent) {
84                 return;
85             }
86             displayComponent->SetOpacity(opacity);
87             pipelineContext.Upgrade()->ScheduleUpdate(subtitleComposed);
88         };
89     }
90     minHeight_ = collapsingComponent->GetMinHeight();
91     auto theme = GetTheme<NavigationBarTheme>();
92     titleSize_ = ChangedKeyframe(theme->GetTitleFontSize().Value() / theme->GetTitleFontSizeBig().Value(),
93         BIGGER_TITLE_SIZE, BIGGER_TITLE_SIZE_MULTIPLE);
94     double subtitleOpacity = theme->GetSubTitleColor().GetAlpha() / MAX_ALPHA_VALUE;
95     subtitleOpacity_ = ChangedKeyframe(TRANSPARENT, subtitleOpacity, subtitleOpacity);
96     changeEvent_ = AceAsyncEvent<void(const std::shared_ptr<BaseEventInfo>&)>::Create(
97         collapsingComponent->GetTitleModeChangedEvent(), context_);
98 }
99 
PerformLayout()100 void RenderCollapsingNavigationBar::PerformLayout()
101 {
102     if (!GetLayoutParam().IsValid()) {
103         return;
104     }
105     auto layoutParam = GetLayoutParam();
106     SetLayoutSize(layoutParam.GetMaxSize());
107     scrollableHeight_ = GetLayoutSize().Height() - NormalizeToPx(minHeight_);
108     auto dipScale = GetContext().Upgrade()->GetDipScale();
109     if (!NearEqual(dipScale_, dipScale)) {
110         dipScale_ = dipScale;
111         positionY_.Update(-scrollableHeight_, 0.0, BIGGER_TITLE_POSITION_OFFSET.ConvertToPx(dipScale_));
112         titlePositionY_.Update(scrollableHeight_ + NormalizeToPx(HIDE_TITLE_MARGIN_TOP),
113             NormalizeToPx(SHOW_TITLE_MARGIN_TOP + minHeight_),
114             NormalizeToPx(SHOW_TITLE_MARGIN_TOP + minHeight_ + BIGGER_TITLE_POSITION_OFFSET));
115     }
116 
117     double titlePositionY;
118     double fixedToolBarPos = -positionY_.value;
119     if (GreatNotEqual(positionY_.value, positionY_.expand)) {
120         titlePositionY = titlePositionY_.expand + positionY_.value;
121         fixedToolBarPos += positionY_.value / FIX_TITLE_BAR_OFFSET_RATIO;
122     } else {
123         titlePositionY = titlePositionY_.expand - positionY_.value * titlePositionY_.expandDis / positionY_.collapse;
124     }
125 
126     auto fixedToolBar = GetFirstChild();
127     if (fixedToolBar) {
128         fixedToolBar->Layout(layoutParam);
129         fixedToolBar->SetPosition(Offset(0.0, fixedToolBarPos));
130     }
131 
132     if (GetChildren().empty()) {
133         LOGI("Children is empty, just return.");
134         return;
135     }
136     auto titleZone = GetChildren().back();
137     titleZone->Layout(layoutParam);
138     titleZone->SetPosition(Offset(0, titlePositionY));
139 }
140 
OnRelatedStart()141 void RenderCollapsingNavigationBar::OnRelatedStart()
142 {
143     relateEvent_ = true;
144 }
145 
OnRelatedPreScroll(const Offset & delta,Offset & consumed)146 void RenderCollapsingNavigationBar::OnRelatedPreScroll(const Offset& delta, Offset& consumed)
147 {
148     double dy = delta.GetY();
149     if (!NeedHidden(dy)) {
150         return;
151     }
152 
153     ScrollBy(dy, positionY_.bigger);
154     consumed.SetY(dy);
155     if (!barIsMini_ && NearEqual(positionY_.value, positionY_.collapse)) {
156         barIsMini_ = true;
157 
158         if (changeEvent_) {
159             changeEvent_(std::make_shared<NavigationTitleModeChangeEvent>(barIsMini_));
160         }
161     }
162 }
163 
OnRelatedScroll(const Offset & delta,Offset & consumed)164 void RenderCollapsingNavigationBar::OnRelatedScroll(const Offset& delta, Offset& consumed)
165 {
166     double dy = -delta.GetY();
167     if (!NeedShow(dy)) {
168         if (!relateEvent_ && LessNotEqual(positionY_.value, positionY_.expand)) {
169             PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
170             PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
171             PreparePositionTranslate(positionY_.value, positionY_.expand);
172             controller_->Forward();
173         }
174         return;
175     }
176     if (barIsMini_) {
177         barIsMini_ = false;
178         if (changeEvent_) {
179             changeEvent_(std::make_shared<NavigationTitleModeChangeEvent>(barIsMini_));
180         }
181     }
182 
183     if (!relateEvent_ && LessNotEqual(dy, 0.0)) {
184         dy = dy / SPRING_RESTORE_DELTA_OFFSET_RATIO;
185     } else if (GreatOrEqual(positionY_.value, positionY_.expand)) {
186         dy = dy / SPRING_DRAG_DELTA_OFFSET_RATIO;
187     }
188     ScrollBy(dy, positionY_.bigger);
189     if (LessOrEqual(positionY_.value, positionY_.expand) && relateEvent_) {
190         consumed.SetY(dy);
191     }
192 }
193 
OnRelatedEnd()194 void RenderCollapsingNavigationBar::OnRelatedEnd()
195 {
196     relateEvent_ = false;
197 }
198 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)199 void RenderCollapsingNavigationBar::OnTouchTestHit(
200     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
201 {
202     dragRecognizer_->SetCoordinateOffset(coordinateOffset);
203     result.emplace_back(dragRecognizer_);
204 }
205 
Initialize(const WeakPtr<PipelineContext> & context)206 void RenderCollapsingNavigationBar::Initialize(const WeakPtr<PipelineContext>& context)
207 {
208     dragRecognizer_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
209     dragRecognizer_->SetOnDragStart([weakScroll = AceType::WeakClaim(this)](const DragStartInfo& info) {
210         auto scroll = weakScroll.Upgrade();
211         if (scroll) {
212             scroll->HandleDragStart(info);
213         }
214     });
215     dragRecognizer_->SetOnDragUpdate([weakScroll = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
216         auto scroll = weakScroll.Upgrade();
217         if (scroll) {
218             scroll->HandleDragUpdate(info);
219         }
220     });
221     dragRecognizer_->SetOnDragEnd([weakScroll = AceType::WeakClaim(this)](const DragEndInfo&) {
222         auto scroll = weakScroll.Upgrade();
223         if (scroll) {
224             scroll->HandleDragEnd();
225         }
226     });
227     controller_ = CREATE_ANIMATOR(context);
228     controller_->SetDuration(COLLAPSING_ANIMATION_DURATION);
229 
230     InitRelatedParent(GetParent());
231     if (IsRelatedEventEnable()) {
232         auto navigationContainer = AceType::DynamicCast<RenderNavigationContainer>(relatedParent_.Upgrade());
233         if (navigationContainer) {
234             navigationContainer->SetCollapsingNavigationBar(AceType::Claim(this));
235         }
236     }
237 }
238 
ScrollBy(double dy,double maxPosition)239 void RenderCollapsingNavigationBar::ScrollBy(double dy, double maxPosition)
240 {
241     lastUpScroll_ = GreatNotEqual(dy, 0.0) ? false : true;
242     positionY_.value += dy;
243     if (LessNotEqual(positionY_.value, -scrollableHeight_)) {
244         positionY_.value = -scrollableHeight_;
245     } else if (GreatNotEqual(positionY_.value, maxPosition)) {
246         positionY_.value = maxPosition;
247     }
248 
249     if (titleChangedCallback_) {
250         if (GreatNotEqual(positionY_.value, positionY_.expand)) {
251             titleSize_.value = titleSize_.expand + positionY_.value * titleSize_.biggerDis / positionY_.bigger;
252         } else {
253             titleSize_.value = titleSize_.expand - positionY_.value * titleSize_.expandDis / positionY_.collapse;
254         }
255         titleChangedCallback_(titleSize_.value);
256     }
257     if (subtitleChangedCallback_ && LessNotEqual(positionY_.value, positionY_.expand)) {
258         subtitleOpacity_.value =
259             subtitleOpacity_.expand + positionY_.value * subtitleOpacity_.expandDis / scrollableHeight_;
260         subtitleChangedCallback_(subtitleOpacity_.value);
261     }
262     MarkNeedLayout(false, true);
263 }
264 
HandleDragStart(const DragStartInfo & info)265 void RenderCollapsingNavigationBar::HandleDragStart(const DragStartInfo& info) {}
266 
HandleDragUpdate(const DragUpdateInfo & info)267 void RenderCollapsingNavigationBar::HandleDragUpdate(const DragUpdateInfo& info)
268 {
269     double mainDelta = info.GetMainDelta();
270     bool canExpand = GreatNotEqual(mainDelta, 0.0) && LessNotEqual(positionY_.value, positionY_.expand);
271     if (!NeedHidden(mainDelta) && !canExpand) {
272         return;
273     }
274     ScrollBy(mainDelta, positionY_.expand);
275 }
276 
HandleDragEnd()277 void RenderCollapsingNavigationBar::HandleDragEnd()
278 {
279     if (GreatNotEqual(positionY_.value, positionY_.expand)) {
280         PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
281         PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
282         PreparePositionTranslate(positionY_.value, positionY_.expand);
283         controller_->Forward();
284     } else if (GreatNotEqual(positionY_.value, positionY_.collapse) && !NearZero(positionY_.value)) {
285         if (lastUpScroll_) {
286             PrepareTitleSizeTranslate(titleSize_.value, titleSize_.collapse);
287             PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.collapse);
288             PreparePositionTranslate(positionY_.value, positionY_.collapse);
289             controller_->Forward();
290         } else {
291             PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
292             PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
293             PreparePositionTranslate(positionY_.value, positionY_.expand);
294             controller_->Forward();
295         }
296     }
297 }
298 
PrepareTitleSizeTranslate(double expand,double collapse)299 void RenderCollapsingNavigationBar::PrepareTitleSizeTranslate(double expand, double collapse)
300 {
301     if (!titleChangedCallback_) {
302         return;
303     }
304     titleSizeTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
305     auto weak = AceType::WeakClaim(this);
306     titleSizeTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
307         auto bar = weak.Upgrade();
308         if (bar) {
309             bar->titleChangedCallback_(value);
310         }
311     }));
312     controller_->AddInterpolator(titleSizeTranslate_);
313 }
314 
PrepareSubtitleSizeTranslate(double expand,double collapse)315 void RenderCollapsingNavigationBar::PrepareSubtitleSizeTranslate(double expand, double collapse)
316 {
317     if (!subtitleChangedCallback_) {
318         return;
319     }
320     subtitleSizeTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
321     auto weak = AceType::WeakClaim(this);
322     subtitleSizeTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
323         auto bar = weak.Upgrade();
324         if (bar) {
325             bar->subtitleChangedCallback_(value);
326         }
327     }));
328     controller_->AddInterpolator(subtitleSizeTranslate_);
329 }
330 
PreparePositionTranslate(double expand,double collapse)331 void RenderCollapsingNavigationBar::PreparePositionTranslate(double expand, double collapse)
332 {
333     positionTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
334     auto weak = AceType::WeakClaim(this);
335     positionTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
336         auto bar = weak.Upgrade();
337         if (bar) {
338             bar->positionY_.value = value;
339             bar->MarkNeedLayout();
340         }
341     }));
342     controller_->AddInterpolator(positionTranslate_);
343 }
344 
345 } // namespace OHOS::Ace
346