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