1 /*
2  * Copyright (c) 2022-2024 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_ng/pattern/navrouter/navdestination_pattern.h"
17 
18 #include <atomic>
19 
20 #include "base/log/dump_log.h"
21 #include "core/common/agingadapation/aging_adapation_dialog_theme.h"
22 #include "core/common/agingadapation/aging_adapation_dialog_util.h"
23 #include "core/common/container.h"
24 #include "core/components/theme/app_theme.h"
25 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
26 #include "core/components_ng/pattern/navigation/title_bar_layout_property.h"
27 #include "core/components_ng/pattern/navigation/title_bar_node.h"
28 #include "core/components_ng/pattern/navigation/navigation_title_util.h"
29 #include "core/components_ng/pattern/navigation/navigation_toolbar_util.h"
30 #include "core/components_ng/pattern/navigation/title_bar_pattern.h"
31 #include "core/components_ng/pattern/text/text_layout_property.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 std::atomic<uint64_t> g_navDestinationPatternNextAutoGenId = 0;
36 // titlebar ZINDEX
37 constexpr static int32_t DEFAULT_TITLEBAR_ZINDEX = 2;
38 constexpr float TRANSLATE_THRESHOLD = 26.0f;
39 const auto TRANSLATE_CURVE = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 228.0f, 30.0f);
40 const auto TRANSLATE_DELAY = 2000;
41 
BuildMenu(const RefPtr<NavDestinationGroupNode> & navDestinationGroupNode,const RefPtr<TitleBarNode> & titleBarNode)42 void BuildMenu(const RefPtr<NavDestinationGroupNode>& navDestinationGroupNode, const RefPtr<TitleBarNode>& titleBarNode)
43 {
44     if (navDestinationGroupNode->GetMenuNodeOperationValue(ChildNodeOperation::NONE) == ChildNodeOperation::REPLACE) {
45         titleBarNode->RemoveChild(titleBarNode->GetMenu());
46         titleBarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
47     }
48     if (navDestinationGroupNode->GetPrevMenuIsCustomValue(false)) {
49         if (navDestinationGroupNode->GetMenuNodeOperationValue(ChildNodeOperation::NONE) == ChildNodeOperation::NONE) {
50             return;
51         }
52         titleBarNode->SetMenu(navDestinationGroupNode->GetMenu());
53         titleBarNode->AddChild(titleBarNode->GetMenu());
54         titleBarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
55         navDestinationGroupNode->UpdateMenuNodeOperation(ChildNodeOperation::NONE);
56     } else {
57         navDestinationGroupNode->UpdateMenuNodeOperation(ChildNodeOperation::NONE);
58         auto navDestinationPattern = navDestinationGroupNode->GetPattern<NavDestinationPattern>();
59         CHECK_NULL_VOID(navDestinationPattern);
60         auto titleBarMenuItems = navDestinationPattern->GetTitleBarMenuItems();
61         auto toolBarMenuItems = navDestinationPattern->GetToolBarMenuItems();
62 
63         bool isButtonEnabled = false;
64         auto hub = navDestinationGroupNode->GetEventHub<EventHub>();
65         if (hub) {
66             isButtonEnabled = hub->IsEnabled();
67         }
68         if (navDestinationPattern->HasMenuNodeId()) {
69             auto menuNode = NavigationTitleUtil::CreateMenuItems(navDestinationPattern->GetMenuNodeId(),
70                 titleBarMenuItems, navDestinationGroupNode, isButtonEnabled, DES_FIELD,
71                 titleBarNode->GetInnerParentId(), false);
72             CHECK_NULL_VOID(menuNode);
73             navDestinationGroupNode->SetMenu(menuNode);
74         }
75 
76         titleBarMenuItems.insert(titleBarMenuItems.end(), toolBarMenuItems.begin(), toolBarMenuItems.end());
77         auto landscapeMenuNode = NavigationTitleUtil::CreateMenuItems(navDestinationPattern->GetLandscapeMenuNodeId(),
78             titleBarMenuItems, navDestinationGroupNode, isButtonEnabled, DES_FIELD, titleBarNode->GetInnerParentId(),
79             true);
80         CHECK_NULL_VOID(landscapeMenuNode);
81         navDestinationGroupNode->SetLandscapeMenu(landscapeMenuNode);
82     }
83 }
84 
GetTitleOrToolBarTranslateAndHeight(const RefPtr<FrameNode> & barNode,float & translate,float & height)85 bool GetTitleOrToolBarTranslateAndHeight(const RefPtr<FrameNode>& barNode, float& translate, float& height)
86 {
87     CHECK_NULL_RETURN(barNode, false);
88     auto renderContext = barNode->GetRenderContext();
89     CHECK_NULL_RETURN(renderContext, false);
90     auto options = renderContext->GetTransformTranslateValue(TranslateOptions(0.0f, 0.0f, 0.0f));
91     translate = options.y.ConvertToPx();
92     height = renderContext->GetPaintRectWithoutTransform().Height();
93     return true;
94 }
95 }
96 
NavDestinationPattern(const RefPtr<ShallowBuilder> & shallowBuilder)97 NavDestinationPattern::NavDestinationPattern(const RefPtr<ShallowBuilder>& shallowBuilder)
98     : shallowBuilder_(shallowBuilder)
99 {
100     navDestinationId_ = g_navDestinationPatternNextAutoGenId.fetch_add(1);
101 }
102 
NavDestinationPattern()103 NavDestinationPattern::NavDestinationPattern()
104 {
105     navDestinationId_ = g_navDestinationPatternNextAutoGenId.fetch_add(1);
106 }
107 
~NavDestinationPattern()108 NavDestinationPattern::~NavDestinationPattern()
109 {
110     customNode_ = nullptr;
111     if (scrollableProcessor_) {
112         scrollableProcessor_->UnbindAllScrollers();
113     }
114 }
115 
OnActive()116 void NavDestinationPattern::OnActive()
117 {
118     Pattern::OnActive();
119     auto hostNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
120     CHECK_NULL_VOID(hostNode);
121     auto navDestinationContext = hostNode->GetRenderContext();
122     CHECK_NULL_VOID(navDestinationContext);
123     auto navDestinationLayoutProperty = hostNode->GetLayoutProperty<NavDestinationLayoutProperty>();
124     CHECK_NULL_VOID(navDestinationLayoutProperty);
125     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(hostNode->GetTitleBarNode());
126     CHECK_NULL_VOID(titleBarNode);
127     auto titleBarLayoutProperty = titleBarNode->GetLayoutProperty<TitleBarLayoutProperty>();
128     CHECK_NULL_VOID(titleBarLayoutProperty);
129     if (navDestinationLayoutProperty->GetHideTitleBar().value_or(false)) {
130         titleBarLayoutProperty->UpdateVisibility(VisibleType::GONE);
131     } else {
132         titleBarLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
133     }
134     titleBarNode->MarkModifyDone();
135 }
136 
OnModifyDone()137 void NavDestinationPattern::OnModifyDone()
138 {
139     Pattern::OnModifyDone();
140     auto hostNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
141     CHECK_NULL_VOID(hostNode);
142     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(hostNode->GetTitleBarNode());
143     CHECK_NULL_VOID(titleBarNode);
144     auto titleBarRenderContext = titleBarNode->GetRenderContext();
145     CHECK_NULL_VOID(titleBarRenderContext);
146     titleBarNode->SetInnerParentId(hostNode->GetInspectorId().value_or(""));
147     // set the titlebar to float on the top
148     titleBarRenderContext->UpdateZIndex(DEFAULT_TITLEBAR_ZINDEX);
149     auto navDestinationLayoutProperty = hostNode->GetLayoutProperty<NavDestinationLayoutProperty>();
150     CHECK_NULL_VOID(navDestinationLayoutProperty);
151     UpdateHideBarProperty();
152     ExpandContentSafeAreaIfNeeded();
153     UpdateNameIfNeeded(hostNode);
154     UpdateBackgroundColorIfNeeded(hostNode);
155     bool needRunTitleBarAnimation = false;
156     MountTitleBar(hostNode, needRunTitleBarAnimation);
157     bool needRunToolBarAnimation = false;
158     NavigationToolbarUtil::MountToolBar(hostNode, needRunToolBarAnimation);
159     HandleTitleBarAndToolBarAnimation(hostNode, needRunTitleBarAnimation, needRunToolBarAnimation);
160     auto pipeline = hostNode->GetContext();
161     CHECK_NULL_VOID(pipeline);
162     if (GreatOrEqual(pipeline->GetFontScale(), AgingAdapationDialogUtil::GetDialogBigFontSizeScale())) {
163         auto titleBarPattern = titleBarNode->GetPattern<TitleBarPattern>();
164         CHECK_NULL_VOID(titleBarPattern);
165         auto backButtonNode = AceType::DynamicCast<FrameNode>(titleBarNode->GetBackButton());
166         CHECK_NULL_VOID(backButtonNode);
167         titleBarPattern->InitBackButtonLongPressEvent(backButtonNode);
168     }
169     if (scrollableProcessor_) {
170         scrollableProcessor_->UpdateBindingRelation();
171     }
172 }
173 
OnLanguageConfigurationUpdate()174 void NavDestinationPattern::OnLanguageConfigurationUpdate()
175 {
176     if (isRightToLeft_ == AceApplicationInfo::GetInstance().IsRightToLeft()) {
177         return;
178     }
179     isRightToLeft_ = AceApplicationInfo::GetInstance().IsRightToLeft();
180     auto hostNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
181     CHECK_NULL_VOID(hostNode);
182     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(hostNode->GetTitleBarNode());
183     CHECK_NULL_VOID(titleBarNode);
184     titleBarNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
185 }
186 
UpdateNameIfNeeded(RefPtr<NavDestinationGroupNode> & hostNode)187 void NavDestinationPattern::UpdateNameIfNeeded(RefPtr<NavDestinationGroupNode>& hostNode)
188 {
189     if (!name_.empty()) {
190         return;
191     }
192 
193     if (hostNode->GetInspectorId().has_value()) {
194         name_ = hostNode->GetInspectorIdValue();
195     } else {
196         name_ = std::to_string(GetHost()->GetId());
197     }
198     auto pathInfo = GetNavPathInfo();
199     if (pathInfo) {
200         pathInfo->SetName(name_);
201     }
202 }
203 
UpdateBackgroundColorIfNeeded(RefPtr<NavDestinationGroupNode> & hostNode)204 void NavDestinationPattern::UpdateBackgroundColorIfNeeded(RefPtr<NavDestinationGroupNode>& hostNode)
205 {
206     auto renderContext = hostNode->GetRenderContext();
207     CHECK_NULL_VOID(renderContext);
208     if (IsUserDefinedBgColor()) {
209         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "User defined Background color: %{public}s",
210             renderContext->GetBackgroundColor()->ColorToString().c_str());
211         return;
212     }
213     if (hostNode->GetNavDestinationMode() == NavDestinationMode::DIALOG) {
214         renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
215         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Set dialog background color: %{public}s",
216             renderContext->GetBackgroundColor()->ColorToString().c_str());
217         return;
218     }
219     auto pipelineContext = PipelineContext::GetCurrentContext();
220     if (!pipelineContext) {
221         return;
222     }
223     auto theme = pipelineContext->GetTheme<AppTheme>();
224     if (!theme) {
225         return;
226     }
227     renderContext->UpdateBackgroundColor(theme->GetBackgroundColor());
228     TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Default background color: %{public}s",
229         renderContext->GetBackgroundColor()->ColorToString().c_str());
230 }
231 
MountTitleBar(RefPtr<NavDestinationGroupNode> & hostNode,bool & needRunTitleBarAnimation)232 void NavDestinationPattern::MountTitleBar(
233     RefPtr<NavDestinationGroupNode>& hostNode, bool& needRunTitleBarAnimation)
234 {
235     needRunTitleBarAnimation = false;
236     auto navDestinationLayoutProperty = hostNode->GetLayoutProperty<NavDestinationLayoutProperty>();
237     CHECK_NULL_VOID(navDestinationLayoutProperty);
238     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(hostNode->GetTitleBarNode());
239     CHECK_NULL_VOID(titleBarNode);
240     auto titleBarLayoutProperty = titleBarNode->GetLayoutProperty<TitleBarLayoutProperty>();
241     CHECK_NULL_VOID(titleBarLayoutProperty);
242 
243     if (navDestinationLayoutProperty->HasNoPixMap()) {
244         if (navDestinationLayoutProperty->HasImageSource()) {
245             titleBarLayoutProperty->UpdateImageSource(navDestinationLayoutProperty->GetImageSourceValue());
246         }
247         if (navDestinationLayoutProperty->HasPixelMap()) {
248             titleBarLayoutProperty->UpdatePixelMap(navDestinationLayoutProperty->GetPixelMapValue());
249         }
250         titleBarLayoutProperty->UpdateNoPixMap(navDestinationLayoutProperty->GetNoPixMapValue());
251     }
252     bool hideTitleBar = navDestinationLayoutProperty->GetHideTitleBarValue(false);
253     BuildMenu(hostNode, titleBarNode);
254 
255     auto navDesIndex = hostNode->GetIndex();
256     if (navDesIndex == 0) {
257         navDestinationLayoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
258         titleBarLayoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
259     }
260 
261     if (currHideTitleBar_.has_value() && currHideTitleBar_.value() != hideTitleBar && hideTitleBar) {
262         /**
263          * we need reset translate&opacity of titleBar when state change from show to hide. @sa EnableTitleBarSwipe
264          */
265         NavigationTitleUtil::UpdateTitleOrToolBarTranslateYAndOpacity(hostNode, titleBarNode, 0.0f, true);
266     }
267     // At the initial state, animation is not required.
268     if (!currHideTitleBar_.has_value() || !navDestinationLayoutProperty->GetIsAnimatedTitleBarValue(false)) {
269         currHideTitleBar_ = hideTitleBar;
270         HideOrShowTitleBarImmediately(hostNode, hideTitleBar);
271         return;
272     }
273 
274     titleBarNode->MarkModifyDone();
275     titleBarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
276 
277     // Animation is needed only when the status changed.
278     needRunTitleBarAnimation = currHideTitleBar_.value() != hideTitleBar;
279     currHideTitleBar_ = hideTitleBar;
280 }
281 
GetBackButtonState()282 bool NavDestinationPattern::GetBackButtonState()
283 {
284     auto hostNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
285     CHECK_NULL_RETURN(hostNode, false);
286     auto navDestinationLayoutProperty = hostNode->GetLayoutProperty<NavDestinationLayoutProperty>();
287     CHECK_NULL_RETURN(navDestinationLayoutProperty, false);
288 
289     auto translateState = navDestinationLayoutProperty->GetTitleBarTranslateStateValue(BarTranslateState::NONE);
290     if (navDestinationLayoutProperty->GetHideTitleBarValue(false) && translateState == BarTranslateState::NONE) {
291         return false;
292     }
293     // get navigation node
294     auto parent = AceType::DynamicCast<FrameNode>(hostNode->GetParent());
295     RefPtr<NavigationGroupNode> navigationNode;
296     while (parent && !parent->IsRootNode()) {
297         navigationNode = AceType::DynamicCast<NavigationGroupNode>(parent);
298         if (navigationNode) {
299             break;
300         }
301         parent = AceType::DynamicCast<FrameNode>(parent->GetParent());
302     }
303     if (!navigationNode) {
304         TAG_LOGW(AceLogTag::ACE_NAVIGATION, "can't find navigation node");
305         return false;
306     }
307     auto navigationLayoutProperty = navigationNode->GetLayoutProperty<NavigationLayoutProperty>();
308     CHECK_NULL_RETURN(navigationLayoutProperty, false);
309     auto pattern = navigationNode->GetPattern<NavigationPattern>();
310     auto stack = pattern->GetNavigationStack();
311     auto index = stack->FindIndex(name_, customNode_, true);
312     bool showBackButton = true;
313     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(hostNode->GetTitleBarNode());
314     if (index == 0 && (pattern->GetNavigationMode() == NavigationMode::SPLIT ||
315         navigationLayoutProperty->GetHideNavBarValue(false))) {
316         showBackButton = false;
317     }
318     auto isCustomTitle = hostNode->GetPrevTitleIsCustomValue(false);
319     if (isCustomTitle) {
320         return showBackButton;
321     }
322     auto titleBarPattern = titleBarNode->GetPattern<TitleBarPattern>();
323     CHECK_NULL_RETURN(titleBarPattern, showBackButton);
324     if (titleBarPattern->IsFontSizeSettedByDeveloper()) {
325         return showBackButton;
326     }
327     auto titleNode = AceType::DynamicCast<FrameNode>(titleBarNode->GetTitle());
328     CHECK_NULL_RETURN(titleNode, showBackButton);
329     auto theme = NavigationGetTheme();
330     CHECK_NULL_RETURN(theme, showBackButton);
331     auto textLayoutProperty = titleNode->GetLayoutProperty<TextLayoutProperty>();
332     auto currentFontSize = textLayoutProperty->GetAdaptMaxFontSizeValue(Dimension(0.0, DimensionUnit::FP));
333     auto targetFontSize = showBackButton ? theme->GetTitleFontSizeMin() : theme->GetTitleFontSize();
334     if (targetFontSize == currentFontSize) {
335         return showBackButton;
336     }
337     textLayoutProperty->UpdateAdaptMaxFontSize(targetFontSize);
338     textLayoutProperty->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
339     return showBackButton;
340 }
341 
OnAttachToFrameNode()342 void NavDestinationPattern::OnAttachToFrameNode()
343 {
344     auto host = GetHost();
345     CHECK_NULL_VOID(host);
346     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
347         SafeAreaExpandOpts opts = { .type = SAFE_AREA_TYPE_SYSTEM | SAFE_AREA_TYPE_CUTOUT,
348             .edges = SAFE_AREA_EDGE_ALL };
349         host->GetLayoutProperty()->UpdateSafeAreaExpandOpts(opts);
350     }
351     isRightToLeft_ = AceApplicationInfo::GetInstance().IsRightToLeft();
352     auto id = host->GetId();
353     auto pipeline = host->GetContext();
354     CHECK_NULL_VOID(pipeline);
355     pipeline->AddWindowStateChangedCallback(id);
356     pipeline->AddWindowSizeChangeCallback(id);
357 }
358 
OnDetachFromFrameNode(FrameNode * frameNode)359 void NavDestinationPattern::OnDetachFromFrameNode(FrameNode* frameNode)
360 {
361     CHECK_NULL_VOID(frameNode);
362     auto id = frameNode->GetId();
363     auto pipeline = frameNode->GetContext();
364     CHECK_NULL_VOID(pipeline);
365     pipeline->RemoveWindowStateChangedCallback(id);
366     pipeline->RemoveWindowSizeChangeCallback(id);
367 }
368 
DumpInfo()369 void NavDestinationPattern::DumpInfo()
370 {
371     DumpLog::GetInstance().AddDesc(std::string("name: ").append(name_));
372 }
373 
OverlayOnBackPressed()374 bool NavDestinationPattern::OverlayOnBackPressed()
375 {
376     CHECK_NULL_RETURN(overlayManager_, false);
377     CHECK_EQUAL_RETURN(overlayManager_->IsModalEmpty(), true, false);
378     return overlayManager_->RemoveOverlay(true);
379 }
380 
NeedIgnoreKeyboard()381 bool NavDestinationPattern::NeedIgnoreKeyboard()
382 {
383     auto layoutProperty = GetLayoutProperty<NavDestinationLayoutProperty>();
384     CHECK_NULL_RETURN(layoutProperty, false);
385     auto& opts = layoutProperty->GetSafeAreaExpandOpts();
386     if (opts && (opts->type & SAFE_AREA_TYPE_KEYBOARD) && (opts->edges & SAFE_AREA_EDGE_BOTTOM)) {
387         return true;
388     }
389     return false;
390 }
391 
OnFontScaleConfigurationUpdate()392 void NavDestinationPattern::OnFontScaleConfigurationUpdate()
393 {
394     auto hostNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
395     CHECK_NULL_VOID(hostNode);
396     auto pipeline = hostNode->GetContext();
397     CHECK_NULL_VOID(pipeline);
398     auto titleBarUINode = hostNode->GetTitleBarNode();
399     auto titleBarNode = AceType::DynamicCast<TitleBarNode>(titleBarUINode);
400     CHECK_NULL_VOID(titleBarNode);
401     auto backButtonNode = AceType::DynamicCast<FrameNode>(titleBarNode->GetBackButton());
402     CHECK_NULL_VOID(backButtonNode);
403     if (LessNotEqual(pipeline->GetFontScale(), AgingAdapationDialogUtil::GetDialogBigFontSizeScale())) {
404         auto gestureHub = backButtonNode->GetOrCreateGestureEventHub();
405         CHECK_NULL_VOID(gestureHub);
406         gestureHub->SetLongPressEvent(nullptr);
407         auto longPressRecognizer = gestureHub->GetLongPressRecognizer();
408         CHECK_NULL_VOID(longPressRecognizer);
409         longPressRecognizer->SetOnActionEnd(nullptr);
410         return;
411     }
412     auto titleBarPattern = titleBarNode->GetPattern<TitleBarPattern>();
413     CHECK_NULL_VOID(titleBarPattern);
414     titleBarPattern->InitBackButtonLongPressEvent(backButtonNode);
415 }
416 
SetSystemBarStyle(const RefPtr<SystemBarStyle> & style)417 void NavDestinationPattern::SetSystemBarStyle(const RefPtr<SystemBarStyle>& style)
418 {
419     auto host = GetHost();
420     CHECK_NULL_VOID(host);
421     auto pipeline = host->GetContext();
422     CHECK_NULL_VOID(pipeline);
423     auto windowManager = pipeline->GetWindowManager();
424     CHECK_NULL_VOID(windowManager);
425     if (!backupStyle_.has_value()) {
426         backupStyle_ = windowManager->GetSystemBarStyle();
427     }
428     currStyle_ = style;
429     auto navigationNode = AceType::DynamicCast<NavigationGroupNode>(navigationNode_.Upgrade());
430     CHECK_NULL_VOID(navigationNode);
431     auto navigationPattern = navigationNode->GetPattern<NavigationPattern>();
432     if (navigationPattern->IsFullPageNavigation() && navigationPattern->IsTopNavDestination(host)) {
433         if (currStyle_.value() != nullptr) {
434             windowManager->SetSystemBarStyle(currStyle_.value());
435         } else {
436             navigationPattern->TryRestoreSystemBarStyle(windowManager);
437         }
438     }
439 }
440 
GetTitlebarZIndex() const441 int32_t NavDestinationPattern::GetTitlebarZIndex() const
442 {
443     return DEFAULT_TITLEBAR_ZINDEX;
444 }
445 
OnWindowHide()446 void NavDestinationPattern::OnWindowHide()
447 {
448     CHECK_NULL_VOID(navDestinationContext_);
449     auto navPathInfo = navDestinationContext_->GetNavPathInfo();
450     CHECK_NULL_VOID(navPathInfo);
451     if (!navPathInfo->GetIsEntry()) {
452         return;
453     }
454     TAG_LOGI(AceLogTag::ACE_NAVIGATION, "window lifecycle change to hide, clear navDestination entry tag");
455     navPathInfo->SetIsEntry(false);
456     auto stack = GetNavigationStack().Upgrade();
457     CHECK_NULL_VOID(stack);
458     auto index = navDestinationContext_->GetIndex();
459     stack->SetIsEntryByIndex(index, false);
460 }
461 
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)462 void NavDestinationPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
463 {
464     auto navDestinationGroupNode = AceType::DynamicCast<NavDestinationGroupNode>(GetHost());
465     CHECK_NULL_VOID(navDestinationGroupNode);
466     if (preWidth_.has_value() && preWidth_.value() != width) {
467         AbortBarAnimation();
468     }
469     preWidth_ = width;
470     // change menu num in landscape and orientation
471     do {
472         if (navDestinationGroupNode->GetPrevMenuIsCustomValue(false)) {
473             break;
474         }
475         auto targetNum = SystemProperties::GetDeviceOrientation() == DeviceOrientation::LANDSCAPE ? MAX_MENU_NUM_LARGE
476                                                                                                   : MAX_MENU_NUM_SMALL;
477         if (targetNum == maxMenuNums_) {
478             break;
479         }
480         maxMenuNums_ = targetNum;
481         auto titleBarNode = AceType::DynamicCast<TitleBarNode>(navDestinationGroupNode->GetTitleBarNode());
482         CHECK_NULL_VOID(titleBarNode);
483         BuildMenu(navDestinationGroupNode, titleBarNode);
484         titleBarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
485     } while (0);
486 }
487 
UpdateTitleAndToolBarHiddenOffset(float offset)488 void NavDestinationPattern::UpdateTitleAndToolBarHiddenOffset(float offset)
489 {
490     CancelShowTitleAndToolBarTask();
491     auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(GetHost());
492     CHECK_NULL_VOID(nodeBase);
493     if (EnableTitleBarSwipe(nodeBase)) {
494         auto titleBarNode = AceType::DynamicCast<TitleBarNode>(nodeBase->GetTitleBarNode());
495         UpdateBarHiddenOffset(nodeBase, titleBarNode, offset, true);
496     }
497     if (EnableToolBarSwipe(nodeBase)) {
498         auto toolBarNode = AceType::DynamicCast<NavToolbarNode>(nodeBase->GetToolBarNode());
499         UpdateBarHiddenOffset(nodeBase, toolBarNode, offset, false);
500     }
501 }
502 
CancelShowTitleAndToolBarTask()503 void NavDestinationPattern::CancelShowTitleAndToolBarTask()
504 {
505     if (titleBarSwipeContext_.showBarTask) {
506         titleBarSwipeContext_.showBarTask.Cancel();
507         titleBarSwipeContext_.showBarTask.Reset(nullptr);
508     }
509     if (toolBarSwipeContext_.showBarTask) {
510         toolBarSwipeContext_.showBarTask.Cancel();
511         toolBarSwipeContext_.showBarTask.Reset(nullptr);
512     }
513 }
514 
ResetTitleAndToolBarState()515 void NavDestinationPattern::ResetTitleAndToolBarState()
516 {
517     auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(GetHost());
518     CHECK_NULL_VOID(nodeBase);
519     if (EnableTitleBarSwipe(nodeBase)) {
520         auto titleBarNode = AceType::DynamicCast<TitleBarNode>(nodeBase->GetTitleBarNode());
521         ResetBarState(nodeBase, titleBarNode, true);
522     }
523     if (EnableToolBarSwipe(nodeBase)) {
524         auto toolBarNode = AceType::DynamicCast<NavToolbarNode>(nodeBase->GetToolBarNode());
525         ResetBarState(nodeBase, toolBarNode, false);
526     }
527 }
528 
ResetBarState(const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<FrameNode> & barNode,bool isTitle)529 void NavDestinationPattern::ResetBarState(const RefPtr<NavDestinationNodeBase>& nodeBase,
530     const RefPtr<FrameNode>& barNode, bool isTitle)
531 {
532     CHECK_NULL_VOID(nodeBase);
533     CHECK_NULL_VOID(barNode);
534     auto& ctx = GetSwipeContext(isTitle);
535     if (ctx.isBarHiding || ctx.isBarShowing) {
536         return;
537     }
538 
539     float translate = 0.0f;
540     float barHeight = 0.0f;
541     if (!GetTitleOrToolBarTranslateAndHeight(barNode, translate, barHeight) || NearZero(barHeight)) {
542         return;
543     }
544 
545     auto threshold = Dimension(TRANSLATE_THRESHOLD, DimensionUnit::VP).ConvertToPx();
546     float halfBarHeight = barHeight / 2.0f;
547     if (GreatOrEqual(threshold, halfBarHeight)) {
548         threshold = halfBarHeight;
549     }
550     float showAreaHeight = barHeight - std::abs(translate);
551     if (GreatNotEqual(showAreaHeight, 0.0f) && LessNotEqual(showAreaHeight, threshold)) {
552         /**
553          * Scroll to show a small portion of the titleBar&toolBar,
554          * but the height of shownArea is less than the threshold,
555          * it needs to be restored to the hidden state.
556          */
557         StartHideOrShowBarInner(nodeBase, barHeight, translate, isTitle, true);
558     } else if (GreatOrEqual(showAreaHeight, barHeight - threshold) && LessNotEqual(showAreaHeight, barHeight)) {
559         /**
560          * Scroll to hide a small portion of the titleBar&toolBar,
561          * but the height of hiddenArea is less than the threshold,
562          * it needs to be restored to the shown state.
563          */
564         StartHideOrShowBarInner(nodeBase, barHeight, translate, isTitle, false);
565     } else {
566         // After a period of inactivity, the titleBar&toolBar needs to be shown again.
567         PostShowBarDelayedTask(isTitle);
568     }
569 }
570 
EnableTitleBarSwipe(const RefPtr<NavDestinationNodeBase> & nodeBase)571 bool NavDestinationPattern::EnableTitleBarSwipe(const RefPtr<NavDestinationNodeBase>& nodeBase)
572 {
573     CHECK_NULL_RETURN(nodeBase, false);
574     auto property = nodeBase->GetLayoutProperty<NavDestinationLayoutPropertyBase>();
575     CHECK_NULL_RETURN(property, false);
576     return !property->GetHideTitleBarValue(false);
577 }
578 
EnableToolBarSwipe(const RefPtr<NavDestinationNodeBase> & nodeBase)579 bool NavDestinationPattern::EnableToolBarSwipe(const RefPtr<NavDestinationNodeBase>& nodeBase)
580 {
581     CHECK_NULL_RETURN(nodeBase, false);
582     auto property = nodeBase->GetLayoutProperty<NavDestinationLayoutPropertyBase>();
583     CHECK_NULL_RETURN(property, false);
584     return !property->GetHideToolBarValue(false);
585 }
586 
UpdateBarHiddenOffset(const RefPtr<NavDestinationNodeBase> & nodeBase,const RefPtr<FrameNode> & barNode,float offset,bool isTitle)587 void NavDestinationPattern::UpdateBarHiddenOffset(
588     const RefPtr<NavDestinationNodeBase>& nodeBase, const RefPtr<FrameNode>& barNode, float offset, bool isTitle)
589 {
590     CHECK_NULL_VOID(nodeBase);
591     CHECK_NULL_VOID(barNode);
592     auto& ctx = GetSwipeContext(isTitle);
593     if (ctx.isBarShowing || ctx.isBarHiding) {
594         return;
595     }
596 
597     float preTranslate = 0.0f;
598     float barHeight = 0.0f;
599     if (!GetTitleOrToolBarTranslateAndHeight(barNode, preTranslate, barHeight) || NearZero(barHeight)) {
600         return;
601     }
602 
603     float newTranslate = 0.0f;
604     if (isTitle) {
605         newTranslate = std::clamp(preTranslate - offset, -barHeight, 0.0f);
606     } else {
607         newTranslate = std::clamp(preTranslate + offset, 0.0f, barHeight);
608     }
609     NavigationTitleUtil::UpdateTitleOrToolBarTranslateYAndOpacity(nodeBase, barNode, newTranslate, isTitle);
610 
611     auto threshold = Dimension(TRANSLATE_THRESHOLD, DimensionUnit::VP).ConvertToPx();
612     float halfBarHeight = barHeight / 2.0f;
613     if (GreatOrEqual(threshold, halfBarHeight)) {
614         threshold = halfBarHeight;
615     }
616     if (Positive(offset) && LessNotEqual(std::abs(preTranslate), threshold) &&
617         GreatOrEqual(std::abs(newTranslate), threshold)) {
618         // When the scrolling up distance exceeds the threshold, it is necessary to start the hide animation.
619         StartHideOrShowBarInner(nodeBase, barHeight, newTranslate, isTitle, true);
620     } else if (Negative(offset) && LessNotEqual(barHeight - std::abs(preTranslate), threshold) &&
621         GreatOrEqual(barHeight - std::abs(newTranslate), threshold)) {
622         // When the scrolling down distance exceeds the threshold, it is necessary to start the show animation.
623         StartHideOrShowBarInner(nodeBase, barHeight, newTranslate, isTitle, false);
624     }
625 }
626 
ShowTitleAndToolBar()627 void NavDestinationPattern::ShowTitleAndToolBar()
628 {
629     auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(GetHost());
630     CHECK_NULL_VOID(nodeBase);
631     if (EnableTitleBarSwipe(nodeBase)) {
632         auto titleBarNode = AceType::DynamicCast<TitleBarNode>(nodeBase->GetTitleBarNode());
633         float translate = 0.0f;
634         float barHeight = 0.0f;
635         if (GetTitleOrToolBarTranslateAndHeight(titleBarNode, translate, barHeight)) {
636             if (titleBarSwipeContext_.showBarTask) {
637                 titleBarSwipeContext_.showBarTask.Cancel();
638                 titleBarSwipeContext_.showBarTask.Reset(nullptr);
639             }
640             StopHideBarIfNeeded(translate, true);
641             StartHideOrShowBarInner(nodeBase, barHeight, translate, true, false);
642         }
643     }
644     if (EnableToolBarSwipe(nodeBase)) {
645         auto toolBarNode = AceType::DynamicCast<NavToolbarNode>(nodeBase->GetToolBarNode());
646         float translate = 0.0f;
647         float barHeight = 0.0f;
648         if (GetTitleOrToolBarTranslateAndHeight(toolBarNode, translate, barHeight)) {
649             if (toolBarSwipeContext_.showBarTask) {
650                 toolBarSwipeContext_.showBarTask.Cancel();
651                 toolBarSwipeContext_.showBarTask.Reset(nullptr);
652             }
653             StopHideBarIfNeeded(translate, false);
654             StartHideOrShowBarInner(nodeBase, barHeight, translate, false, false);
655         }
656     }
657 }
658 
StartHideOrShowBarInner(const RefPtr<NavDestinationNodeBase> & nodeBase,float barHeight,float curTranslate,bool isTitle,bool isHide)659 void NavDestinationPattern::StartHideOrShowBarInner(
660     const RefPtr<NavDestinationNodeBase>& nodeBase, float barHeight, float curTranslate, bool isTitle, bool isHide)
661 {
662     CHECK_NULL_VOID(nodeBase);
663     auto barNode = GetBarNode(nodeBase, isTitle);
664     CHECK_NULL_VOID(barNode);
665 
666     auto propertyCallback = [weak = WeakClaim(this), barHeight, isTitle, isHide]() {
667         auto pattern = weak.Upgrade();
668         CHECK_NULL_VOID(pattern);
669         auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(pattern->GetHost());
670         CHECK_NULL_VOID(nodeBase);
671         auto barNode = pattern->GetBarNode(nodeBase, isTitle);
672         CHECK_NULL_VOID(barNode);
673         float translate = isHide ? (isTitle ? -barHeight : barHeight) : 0.0f;
674         NavigationTitleUtil::UpdateTitleOrToolBarTranslateYAndOpacity(nodeBase, barNode, translate, isTitle);
675     };
676     auto finishCallback = [weak = WeakClaim(this), isTitle, isHide]() {
677         auto pattern = weak.Upgrade();
678         CHECK_NULL_VOID(pattern);
679         auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(pattern->GetHost());
680         CHECK_NULL_VOID(nodeBase);
681         auto barNode = pattern->GetBarNode(nodeBase, isTitle);
682         CHECK_NULL_VOID(barNode);
683         auto& ctx = pattern->GetSwipeContext(isTitle);
684         if (isHide) {
685             ctx.isBarHiding = false;
686         } else {
687             ctx.isBarShowing = false;
688         }
689         if (isHide) {
690             pattern->PostShowBarDelayedTask(isTitle);
691         }
692     };
693     AnimationOption option;
694     option.SetCurve(TRANSLATE_CURVE);
695     auto& ctx = GetSwipeContext(isTitle);
696     if (isHide) {
697         ctx.isBarHiding = true;
698     } else {
699         ctx.isBarShowing = true;
700     }
701     NavigationTitleUtil::UpdateTitleOrToolBarTranslateYAndOpacity(nodeBase, barNode, curTranslate, isTitle);
702     AnimationUtils::Animate(option, propertyCallback, finishCallback);
703 }
704 
StopHideBarIfNeeded(float curTranslate,bool isTitle)705 void NavDestinationPattern::StopHideBarIfNeeded(float curTranslate, bool isTitle)
706 {
707     auto& ctx = GetSwipeContext(isTitle);
708     if (!ctx.isBarHiding) {
709         return;
710     }
711 
712     auto propertyCallback = [weak = WeakClaim(this), isTitle, curTranslate]() {
713         auto pattern = weak.Upgrade();
714         CHECK_NULL_VOID(pattern);
715         auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(pattern->GetHost());
716         CHECK_NULL_VOID(nodeBase);
717         auto barNode = pattern->GetBarNode(nodeBase, isTitle);
718         CHECK_NULL_VOID(barNode);
719         NavigationTitleUtil::UpdateTitleOrToolBarTranslateYAndOpacity(nodeBase, barNode, curTranslate, isTitle);
720     };
721     AnimationOption option;
722     option.SetDuration(0);
723     option.SetCurve(Curves::LINEAR);
724     AnimationUtils::Animate(option, propertyCallback);
725     ctx.isBarHiding = false;
726 }
727 
PostShowBarDelayedTask(bool isTitle)728 void NavDestinationPattern::PostShowBarDelayedTask(bool isTitle)
729 {
730     auto pipeline = GetContext();
731     CHECK_NULL_VOID(pipeline);
732     auto taskExecutor = pipeline->GetTaskExecutor();
733     CHECK_NULL_VOID(taskExecutor);
734     auto& ctx = GetSwipeContext(isTitle);
735     if (ctx.showBarTask) {
736         ctx.showBarTask.Cancel();
737     }
738 
739     ctx.showBarTask.Reset([weak = WeakClaim(this), isTitle]() {
740         auto pattern = weak.Upgrade();
741         CHECK_NULL_VOID(pattern);
742         auto nodeBase = AceType::DynamicCast<NavDestinationNodeBase>(pattern->GetHost());
743         CHECK_NULL_VOID(nodeBase);
744         auto pipeline = nodeBase->GetContext();
745         CHECK_NULL_VOID(pipeline);
746         auto barNode = pattern->GetBarNode(nodeBase, isTitle);
747         CHECK_NULL_VOID(barNode);
748         float translate = 0.0f;
749         float barHeight = 0.0f;
750         if (!GetTitleOrToolBarTranslateAndHeight(barNode, translate, barHeight)) {
751             return;
752         }
753 
754         pattern->StartHideOrShowBarInner(nodeBase, barHeight, translate, isTitle, false);
755         pipeline->RequestFrame();
756     });
757 
758     taskExecutor->PostDelayedTask(ctx.showBarTask, TaskExecutor::TaskType::UI, TRANSLATE_DELAY,
759         isTitle ? "ArkUINavDestinationShowTitleBar" : "ArkUINavDestinationShowToolBar");
760 }
761 
GetBarNode(const RefPtr<NavDestinationNodeBase> & nodeBase,bool isTitle)762 RefPtr<FrameNode> NavDestinationPattern::GetBarNode(const RefPtr<NavDestinationNodeBase>& nodeBase, bool isTitle)
763 {
764     CHECK_NULL_RETURN(nodeBase, nullptr);
765     return isTitle ? AceType::DynamicCast<FrameNode>(nodeBase->GetTitleBarNode())
766                    : AceType::DynamicCast<FrameNode>(nodeBase->GetToolBarNode());
767 }
768 } // namespace OHOS::Ace::NG
769