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