1 /*
2 * Copyright (c) 2022 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/navigation/navigation_group_node.h"
17
18 #include "base/memory/ace_type.h"
19 #include "base/memory/referenced.h"
20 #include "base/perfmonitor/perf_constants.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/page_transition_common.h"
24 #include "core/common/container.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components/theme/app_theme.h"
27 #include "core/components_ng/base/view_stack_processor.h"
28 #include "core/components_ng/event/focus_hub.h"
29 #include "core/components_ng/pattern/button/button_layout_property.h"
30 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
31 #include "core/components_ng/pattern/navigation/nav_bar_node.h"
32 #include "core/components_ng/pattern/navigation/navigation_declaration.h"
33 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
34 #include "core/components_ng/pattern/navigation/title_bar_layout_property.h"
35 #include "core/components_ng/pattern/navigation/title_bar_node.h"
36 #include "core/components_ng/pattern/navrouter/navdestination_event_hub.h"
37 #include "core/components_ng/pattern/navrouter/navdestination_group_node.h"
38 #include "core/components_ng/pattern/navrouter/navdestination_layout_property.h"
39 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
40 #include "core/components_ng/pattern/navrouter/navrouter_event_hub.h"
41 #include "core/components_ng/pattern/navrouter/navrouter_group_node.h"
42 #include "core/components_ng/pattern/stack/stack_layout_property.h"
43 #include "core/components_ng/pattern/stack/stack_model_ng.h"
44 #include "core/components_ng/pattern/stack/stack_pattern.h"
45 #include "core/components_ng/pattern/stage/page_pattern.h"
46 #include "core/components_ng/property/measure_property.h"
47 #include "core/components_ng/property/property.h"
48 #include "core/components_ng/render/render_context.h"
49 #include "core/components_v2/inspector/inspector_constants.h"
50
51 namespace OHOS::Ace::NG {
52 namespace {
53 constexpr double HALF = 0.5;
54 constexpr double PARENT_PAGE_OFFSET = 0.2;
55 constexpr double PARENT_TITLE_OFFSET = 0.02;
56 constexpr float REMOVE_CLIP_SIZE = 10000.0f;
57 constexpr int32_t MASK_DURATION = 350;
58 constexpr int32_t OPACITY_TITLE_OUT_DELAY = 17;
59 constexpr int32_t OPACITY_TITLE_IN_DELAY = 33;
60 constexpr int32_t OPACITY_TITLE_DURATION = 150;
61 constexpr int32_t OPACITY_BACKBUTTON_IN_DELAY = 150;
62 constexpr int32_t OPACITY_BACKBUTTON_IN_DURATION = 200;
63 constexpr int32_t OPACITY_BACKBUTTON_OUT_DURATION = 67;
64 constexpr int32_t DEFAULT_ANIMATION_DURATION = 450;
65 constexpr int32_t DEFAULT_REPLACE_DURATION = 150;
66 const Color MASK_COLOR = Color::FromARGB(25, 0, 0, 0);
67 const RefPtr<InterpolatingSpring> springCurve = AceType::MakeRefPtr<InterpolatingSpring>(0.0f, 1.0f, 342.0f, 37.0f);
68 const RefPtr<CubicCurve> replaceCurve = AceType::MakeRefPtr<CubicCurve>(0.33, 0.0, 0.67, 1.0);
69 } // namespace
GetOrCreateGroupNode(const std::string & tag,int32_t nodeId,const std::function<RefPtr<Pattern> (void)> & patternCreator)70 RefPtr<NavigationGroupNode> NavigationGroupNode::GetOrCreateGroupNode(
71 const std::string& tag, int32_t nodeId, const std::function<RefPtr<Pattern>(void)>& patternCreator)
72 {
73 auto frameNode = GetFrameNode(tag, nodeId);
74 CHECK_NULL_RETURN(!frameNode, AceType::DynamicCast<NavigationGroupNode>(frameNode));
75 auto pattern = patternCreator ? patternCreator() : MakeRefPtr<Pattern>();
76 auto navigationGroupNode = AceType::MakeRefPtr<NavigationGroupNode>(tag, nodeId, pattern);
77 navigationGroupNode->InitializePatternAndContext();
78 ElementRegister::GetInstance()->AddUINode(navigationGroupNode);
79 return navigationGroupNode;
80 }
81
~NavigationGroupNode()82 NavigationGroupNode::~NavigationGroupNode()
83 {
84 auto context = PipelineContext::GetCurrentContext();
85 CHECK_NULL_VOID(context);
86 auto stageManager = context->GetStageManager();
87 CHECK_NULL_VOID(stageManager);
88 RefPtr<FrameNode> pageNode = stageManager->GetLastPage();
89 CHECK_NULL_VOID(pageNode);
90 auto pagePattern = pageNode->GetPattern<PagePattern>();
91 CHECK_NULL_VOID(pagePattern);
92 CHECK_NULL_VOID(pagePattern->GetPageInfo());
93 int32_t pageId = pagePattern->GetPageInfo()->GetPageId();
94 context->RemoveNavigationStateCallback(pageId, GetId());
95 context->DeleteNavigationNode(curId_);
96 }
97
AddChildToGroup(const RefPtr<UINode> & child,int32_t slot)98 void NavigationGroupNode::AddChildToGroup(const RefPtr<UINode>& child, int32_t slot)
99 {
100 auto pattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
101 CHECK_NULL_VOID(pattern);
102 auto navBar = AceType::DynamicCast<NavBarNode>(GetNavBarNode());
103 CHECK_NULL_VOID(navBar);
104 auto contentNode = navBar->GetNavBarContentNode();
105 if (!contentNode) {
106 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
107 contentNode = FrameNode::GetOrCreateFrameNode(
108 V2::NAVBAR_CONTENT_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
109 navBar->SetNavBarContentNode(contentNode);
110 navBar->AddChild(contentNode);
111
112 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
113 auto navBarContentNode = AceType::DynamicCast<FrameNode>(contentNode);
114 SafeAreaExpandOpts opts = {.type = SAFE_AREA_TYPE_SYSTEM, .edges = SAFE_AREA_EDGE_ALL};
115 navBarContentNode->GetLayoutProperty()->UpdateSafeAreaExpandOpts(opts);
116 }
117 }
118 contentNode->AddChild(child);
119 }
120
UpdateNavDestinationNodeWithoutMarkDirty(const RefPtr<UINode> & remainChild,bool modeChange)121 void NavigationGroupNode::UpdateNavDestinationNodeWithoutMarkDirty(const RefPtr<UINode>& remainChild, bool modeChange)
122 {
123 auto pattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
124 CHECK_NULL_VOID(pattern);
125 const auto& navDestinationNodes = pattern->GetAllNavDestinationNodes();
126
127 auto navigationContentNode = AceType::DynamicCast<FrameNode>(GetContentNode());
128 CHECK_NULL_VOID(navigationContentNode);
129 bool hasChanged = false;
130 int32_t slot = 0;
131 UpdateLastStandardIndex();
132 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "last standard page index is %{public}d", lastStandardIndex_);
133 for (uint32_t i = 0; i != navDestinationNodes.size(); ++i) {
134 const auto& childNode = navDestinationNodes[i];
135 const auto& uiNode = childNode.second;
136 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(GetNavDestinationNode(uiNode));
137 if (navDestination == nullptr) {
138 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "get destination node failed");
139 return;
140 }
141 auto navDestinationPattern = navDestination->GetPattern<NavDestinationPattern>();
142 CHECK_NULL_VOID(navDestinationPattern);
143 navDestinationPattern->SetName(childNode.first);
144 navDestinationPattern->SetCustomNode(uiNode);
145 SetBackButtonEvent(navDestination);
146 navDestination->SetIndex(i);
147 auto eventHub = navDestination->GetEventHub<NavDestinationEventHub>();
148 CHECK_NULL_VOID(eventHub);
149 if (!eventHub->GetOnStateChange()) {
150 auto onStateChangeMap = pattern->GetOnStateChangeMap();
151 auto iter = onStateChangeMap.find(uiNode->GetId());
152 if (iter != onStateChangeMap.end()) {
153 eventHub->SetOnStateChange(iter->second);
154 pattern->DeleteOnStateChangeItem(iter->first);
155 }
156 }
157 int32_t childIndex = navigationContentNode->GetChildIndex(navDestination);
158 if (childIndex < 0) {
159 navigationContentNode->AddChild(navDestination, slot);
160 hasChanged = true;
161 } else if (childIndex != slot) {
162 navDestination->MovePosition(slot);
163 hasChanged = true;
164 }
165 slot++;
166 }
167
168 for (int32_t i = static_cast<int32_t>(navDestinationNodes.size()) - 1; i >= 0; --i) {
169 const auto& childNode = navDestinationNodes[i];
170 const auto& uiNode = childNode.second;
171 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(GetNavDestinationNode(uiNode));
172 hasChanged = (UpdateNavDestinationVisibility(navDestination, remainChild, i, navDestinationNodes.size())
173 || hasChanged);
174 }
175
176 while (static_cast<size_t>(slot) < navigationContentNode->GetChildren().size()) {
177 // delete useless nodes that are not at the top
178 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(navigationContentNode->GetLastChild());
179 if (!navDestination) {
180 navigationContentNode->RemoveChild(navigationContentNode->GetLastChild());
181 hasChanged = true;
182 continue;
183 }
184 auto eventHub = navDestination->GetEventHub<NavDestinationEventHub>();
185 if (eventHub) {
186 eventHub->FireChangeEvent(false);
187 }
188 auto uiNode = navDestination->GetPattern<NavDestinationPattern>()->GetCustomNode();
189 if (uiNode != remainChild) {
190 if (navDestination->IsOnAnimation()) {
191 // The NavDestination in the animation needs to be cleaned in the animation onfinish callback.
192 ++slot;
193 continue;
194 }
195 // remove content child
196 auto navDestinationPattern = navDestination->GetPattern<NavDestinationPattern>();
197 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "remove child: %{public}s", navDestinationPattern->GetName().c_str());
198 if (navDestinationPattern->GetIsOnShow()) {
199 eventHub->FireOnHiddenEvent(navDestinationPattern->GetName());
200 navDestinationPattern->SetIsOnShow(false);
201 NavigationPattern::FireNavigationStateChange(navDestination, false);
202 }
203 auto shallowBuilder = navDestinationPattern->GetShallowBuilder();
204 if (shallowBuilder) {
205 shallowBuilder->MarkIsExecuteDeepRenderDone(false);
206 }
207 if (navDestination->GetContentNode()) {
208 navDestination->GetContentNode()->Clean();
209 }
210 navigationContentNode->RemoveChild(navDestination, true);
211 navDestinationPattern->SetCustomNode(nullptr);
212 hasChanged = true;
213 } else {
214 // remain the last child for pop animation
215 navDestination->MovePosition(slot);
216 ++slot;
217 }
218 }
219 if (modeChange) {
220 navigationContentNode->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
221 } else if (hasChanged) {
222 navigationContentNode->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
223 }
224 }
225
ToJsonValue(std::unique_ptr<JsonValue> & json) const226 void NavigationGroupNode::ToJsonValue(std::unique_ptr<JsonValue>& json) const
227 {
228 FrameNode::ToJsonValue(json);
229 auto navBarNode = AceType::DynamicCast<NavBarNode>(GetNavBarNode());
230 CHECK_NULL_VOID(navBarNode);
231 navBarNode->ToJsonValue(json);
232 }
233
GetNavDestinationNode(RefPtr<UINode> uiNode)234 RefPtr<UINode> NavigationGroupNode::GetNavDestinationNode(RefPtr<UINode> uiNode)
235 {
236 if (!uiNode) {
237 return nullptr;
238 }
239 // create NavDestinationNode from uiNode stored in NavigationStack
240 while (uiNode) {
241 if (uiNode->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG) {
242 // this is a navDestination node
243 return uiNode;
244 }
245 if (AceType::DynamicCast<UINode>(uiNode)) {
246 // this is an UINode, go deep further for navDestination node
247 auto children = uiNode->GetChildren();
248 uiNode = children.front();
249 continue;
250 }
251 }
252 CHECK_NULL_RETURN(uiNode, nullptr);
253 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "get navDestination node failed: id: %{public}d, %{public}s",
254 uiNode->GetId(), uiNode->GetTag().c_str());
255 return nullptr;
256 }
257
SetBackButtonEvent(const RefPtr<NavDestinationGroupNode> & navDestination)258 void NavigationGroupNode::SetBackButtonEvent(const RefPtr<NavDestinationGroupNode>& navDestination)
259 {
260 auto titleBarNode = AceType::DynamicCast<TitleBarNode>(navDestination->GetTitleBarNode());
261 CHECK_NULL_VOID(titleBarNode);
262 auto backButtonNode = AceType::DynamicCast<FrameNode>(titleBarNode->GetBackButton());
263 CHECK_NULL_VOID(backButtonNode);
264 auto backButtonEventHub = backButtonNode->GetEventHub<EventHub>();
265 CHECK_NULL_VOID(backButtonEventHub);
266 auto onBackButtonEvent = [navDestinationWeak = WeakPtr<NavDestinationGroupNode>(navDestination),
267 navigationWeak = WeakClaim(this)](GestureEvent& /*info*/) -> bool {
268 auto navDestination = navDestinationWeak.Upgrade();
269 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "click navigation back button");
270 CHECK_NULL_RETURN(navDestination, false);
271 auto eventHub = navDestination->GetEventHub<NavDestinationEventHub>();
272 CHECK_NULL_RETURN(eventHub, false);
273 auto isOverride = eventHub->GetOnBackPressedEvent();
274 auto result = false;
275 if (isOverride) {
276 result = eventHub->FireOnBackPressedEvent();
277 }
278 if (result) {
279 return true;
280 }
281 auto navigation = navigationWeak.Upgrade();
282 CHECK_NULL_RETURN(navigation, false);
283 // if set hideNavBar and stack size is one, return false
284 auto navigationLayoutProperty = AceType::DynamicCast<NavigationLayoutProperty>(navigation->GetLayoutProperty());
285 auto pattern = AceType::DynamicCast<NavigationPattern>(navigation->GetPattern());
286 auto stack = pattern->GetNavigationStack();
287 CHECK_NULL_RETURN(stack, false);
288 if (navigationLayoutProperty->GetHideNavBarValue(false) && stack->Size() <= 1) {
289 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "set hideNavBar and stack size is no more than one");
290 return false;
291 }
292 const auto& children = navigation->GetContentNode()->GetChildren();
293 auto isLastChild = children.size() == 1;
294 if (isOverride) {
295 result = navigation->HandleBack(navDestination, isLastChild, true);
296 } else {
297 result = navigation->HandleBack(navDestination, isLastChild, false);
298 }
299 // when js navigationStack is provided, modifyDone will be called by state manager.
300 auto navigationPattern = navigation->GetPattern<NavigationPattern>();
301 CHECK_NULL_RETURN(navigationPattern, false);
302 if (!navigationPattern->GetNavigationStackProvided()) {
303 navigation->MarkModifyDone();
304 navigation->MarkDirtyNode();
305 }
306
307 return result;
308 }; // backButton event
309
310 navDestination->SetNavDestinationBackButtonEvent(onBackButtonEvent);
311 backButtonEventHub->GetOrCreateGestureEventHub()->SetUserOnClick(onBackButtonEvent);
312 }
313
CheckCanHandleBack()314 bool NavigationGroupNode::CheckCanHandleBack()
315 {
316 auto navigation = AceType::WeakClaim(this).Upgrade();
317 CHECK_NULL_RETURN(navigation, false);
318 auto navigationPattern = GetPattern<NavigationPattern>();
319 CHECK_NULL_RETURN(navigationPattern, false);
320
321 auto navigationStack = navigationPattern->GetNavigationStack();
322 CHECK_NULL_RETURN(navigationStack, false);
323 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(
324 NavigationGroupNode::GetNavDestinationNode(navigationStack->Get()));
325 if (!navDestination) {
326 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "can't find destination node to process back press");
327 return false;
328 }
329 auto navDestinationPattern = AceType::DynamicCast<NavDestinationPattern>(navDestination->GetPattern());
330 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navDestination consume back button event: %{public}s",
331 navDestinationPattern->GetName().c_str());
332 GestureEvent gestureEvent;
333 return navDestination->GetNavDestinationBackButtonEvent()(gestureEvent);
334 }
335
HandleBack(const RefPtr<FrameNode> & node,bool isLastChild,bool isOverride)336 bool NavigationGroupNode::HandleBack(const RefPtr<FrameNode>& node, bool isLastChild, bool isOverride)
337 {
338 auto navigationPattern = GetPattern<NavigationPattern>();
339 if (!isOverride && !isLastChild) {
340 navigationPattern->RemoveNavDestination();
341 return true;
342 }
343 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(node);
344 CHECK_NULL_RETURN(navDestination, false);
345
346 auto mode = navigationPattern->GetNavigationMode();
347 auto layoutProperty = GetLayoutProperty<NavigationLayoutProperty>();
348 if (isLastChild && (mode == NavigationMode::SPLIT ||
349 (mode == NavigationMode::STACK && layoutProperty->GetHideNavBar().value_or(false)))) {
350 return false;
351 }
352
353 navigationPattern->RemoveNavDestination();
354 return true;
355 }
356
TransitionWithPop(const RefPtr<FrameNode> & preNode,const RefPtr<FrameNode> & curNode,bool isNavBar)357 void NavigationGroupNode::TransitionWithPop(const RefPtr<FrameNode>& preNode, const RefPtr<FrameNode>& curNode,
358 bool isNavBar)
359 {
360 CHECK_NULL_VOID(preNode);
361
362 auto preNavDestination = AceType::DynamicCast<NavDestinationGroupNode>(preNode);
363 CHECK_NULL_VOID(preNavDestination);
364 preNavDestination->SetTransitionType(PageTransitionType::EXIT_POP);
365
366 /* obtain preTitle, preBackIcon and preFrameSize of preNavDestination */
367 auto preTitleNode = AceType::DynamicCast<TitleBarNode>(preNavDestination->GetTitleBarNode());
368 CHECK_NULL_VOID(preTitleNode);
369 auto preBackIcon = AceType::DynamicCast<FrameNode>(preTitleNode->GetBackButton());
370 CHECK_NULL_VOID(preBackIcon);
371
372 auto preFrameSize = preNode->GetGeometryNode()->GetFrameSize();
373 RefPtr<TitleBarNode> curTitleNode;
374 if (curNode) {
375 if (isNavBar) {
376 auto navBarNode = AceType::DynamicCast<NavBarNode>(curNode);
377 CHECK_NULL_VOID(navBarNode);
378 navBarNode->SetTransitionType(PageTransitionType::ENTER_POP);
379 curTitleNode = navBarNode ? AceType::DynamicCast<TitleBarNode>(navBarNode->GetTitleBarNode()) : nullptr;
380 } else {
381 auto curNavDestination = AceType::DynamicCast<NavDestinationGroupNode>(curNode);
382 CHECK_NULL_VOID(curNavDestination);
383 curNavDestination->SetTransitionType(PageTransitionType::ENTER_POP);
384 curTitleNode =
385 curNavDestination ? AceType::DynamicCast<TitleBarNode>(curNavDestination->GetTitleBarNode()) : nullptr;
386 }
387 CHECK_NULL_VOID(curTitleNode);
388 }
389
390 /* create animation finish callback */
391 AnimationFinishCallback callback = [weakPreNode = WeakPtr<NavDestinationGroupNode>(preNavDestination),
392 weakPreTitle = WeakPtr<TitleBarNode>(preTitleNode),
393 weakPreBackIcon = WeakPtr<FrameNode>(preBackIcon),
394 weakNavigation = WeakClaim(this)] {
395 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navigation pop animation end");
396 PerfMonitor::GetPerfMonitor()->End(PerfConstants::ABILITY_OR_PAGE_SWITCH, true);
397 auto navigation = weakNavigation.Upgrade();
398 if (navigation) {
399 navigation->isOnAnimation_ = false;
400 navigation->OnAccessibilityEvent(AccessibilityEventType::PAGE_CHANGE);
401 }
402 auto preNavDesNode = weakPreNode.Upgrade();
403 CHECK_NULL_VOID(preNavDesNode);
404 if (preNavDesNode->GetTransitionType() != PageTransitionType::EXIT_POP) {
405 // has another transition, just return
406 return;
407 }
408 auto preNavDesPattern = preNavDesNode->GetPattern<NavDestinationPattern>();
409 CHECK_NULL_VOID(preNavDesPattern);
410 // NavRouter will restore the preNavDesNode and needs to set the initial state after the animation ends.
411 auto shallowBuilder = preNavDesPattern->GetShallowBuilder();
412 if (shallowBuilder) {
413 shallowBuilder->MarkIsExecuteDeepRenderDone(false);
414 }
415 preNavDesNode->SetIsOnAnimation(false);
416 preNavDesNode->GetEventHub<EventHub>()->SetEnabledInternal(true);
417 preNavDesNode->GetRenderContext()->ClipWithRRect(RectF(0.0f, 0.0f, REMOVE_CLIP_SIZE, REMOVE_CLIP_SIZE),
418 RadiusF(EdgeF(0.0f, 0.0f)));
419 preNavDesNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
420 auto preTitleNode = weakPreTitle.Upgrade();
421 if (preTitleNode) {
422 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
423 }
424
425 if (!preNavDesNode->IsCacheNode() && preNavDesNode->GetContentNode()) {
426 preNavDesNode->GetContentNode()->Clean();
427 }
428
429 auto parent = preNavDesNode->GetParent();
430 CHECK_NULL_VOID(parent);
431 parent->RemoveChild(preNavDesNode);
432 preNavDesPattern->SetCustomNode(nullptr);
433 parent->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
434 auto context = PipelineContext::GetCurrentContext();
435 CHECK_NULL_VOID(context);
436 context->MarkNeedFlushMouseEvent();
437 };
438
439 /* set initial status of animation */
440 preNode->GetEventHub<EventHub>()->SetEnabledInternal(false);
441 preNode->GetRenderContext()->ClipWithRRect(RectF(0.0f, 0.0f, preFrameSize.Width(), REMOVE_CLIP_SIZE),
442 RadiusF(EdgeF(0.0f, 0.0f)));
443 preNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
444 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
445 preNavDestination->SetIsOnAnimation(true);
446
447 if (curNode) {
448 auto curFrameSize = curNode->GetGeometryNode()->GetFrameSize();
449 curNode->GetRenderContext()->ClipWithRRect(
450 RectF(0.0f, 0.0f, REMOVE_CLIP_SIZE, REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
451 curNode->GetRenderContext()->UpdateTranslateInXY({ -curFrameSize.Width() * PARENT_PAGE_OFFSET, 0.0f });
452 curTitleNode->GetRenderContext()->UpdateTranslateInXY({ curFrameSize.Width() * PARENT_TITLE_OFFSET, 0.0f });
453 }
454
455 /* start transition animation */
456 AnimationOption option = CreateAnimationOption(springCurve, FillMode::FORWARDS, DEFAULT_ANIMATION_DURATION,
457 callback);
458 AnimationUtils::Animate(option, [preNode, preTitleNode, preFrameSize, curNode, curTitleNode]() {
459 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::ABILITY_OR_PAGE_SWITCH, PerfActionType::LAST_UP, "");
460 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navigation pop animation start");
461 /* preNode */
462 preNode->GetRenderContext()->ClipWithRRect(
463 RectF(preFrameSize.Width() * HALF, 0.0f, preFrameSize.Width(), REMOVE_CLIP_SIZE),
464 RadiusF(EdgeF(0.0f, 0.0f)));
465 preNode->GetRenderContext()->UpdateTranslateInXY({ preFrameSize.Width() * HALF, 0.0f });
466 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ preFrameSize.Width() * HALF, 0.0f });
467
468 /* curNode */
469 if (curNode) {
470 curNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
471 curTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
472 }
473 }, option.GetOnFinishEvent());
474
475 /* opacity for title and backIcon */
476 TitleOpacityAnimation(preTitleNode, true);
477 BackButtonAnimation(preBackIcon, false);
478
479 /* mask animation */
480 if (curNode) {
481 AnimationOption maskOption = CreateAnimationOption(Curves::FRICTION, FillMode::FORWARDS, MASK_DURATION,
482 nullptr);
483 curNode->GetRenderContext()->SetActualForegroundColor(MASK_COLOR);
484 AnimationUtils::Animate(
485 maskOption, [curNode]() { curNode->GetRenderContext()->SetActualForegroundColor(Color::TRANSPARENT); });
486 }
487
488 // clear this flag for navBar layout only
489 if (isNavBar) {
490 SetNeedSetInvisible(false);
491 }
492 isOnAnimation_ = true;
493 }
494
TransitionWithPush(const RefPtr<FrameNode> & preNode,const RefPtr<FrameNode> & curNode,bool isNavBar)495 void NavigationGroupNode::TransitionWithPush(const RefPtr<FrameNode>& preNode, const RefPtr<FrameNode>& curNode,
496 bool isNavBar)
497 {
498 CHECK_NULL_VOID(preNode);
499 CHECK_NULL_VOID(curNode);
500
501 RefPtr<FrameNode> preTitleNode;
502 if (isNavBar) {
503 auto navBarNode = AceType::DynamicCast<NavBarNode>(preNode);
504 navBarNode->SetTransitionType(PageTransitionType::EXIT_PUSH);
505 preTitleNode = navBarNode ? AceType::DynamicCast<TitleBarNode>(navBarNode->GetTitleBarNode()) : nullptr;
506 } else {
507 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(preNode);
508 navDestination->SetTransitionType(PageTransitionType::EXIT_PUSH);
509 preTitleNode = navDestination ? AceType::DynamicCast<TitleBarNode>(navDestination->GetTitleBarNode()) : nullptr;
510 }
511 CHECK_NULL_VOID(preTitleNode);
512
513 auto mode = GetNavigationMode();
514 auto curNavDestination = AceType::DynamicCast<NavDestinationGroupNode>(curNode);
515 CHECK_NULL_VOID(curNavDestination);
516 curNavDestination->SetTransitionType(PageTransitionType::ENTER_PUSH);
517 auto curTitleNode = AceType::DynamicCast<TitleBarNode>(curNavDestination->GetTitleBarNode());
518 CHECK_NULL_VOID(curTitleNode);
519
520 auto preFrameSize = preNode->GetGeometryNode()->GetFrameSize();
521 auto curFrameSize = curNode->GetGeometryNode()->GetFrameSize();
522
523 /* Create animation callback */
524 AnimationFinishCallback callback = [weakPreNode = WeakPtr<FrameNode>(preNode),
525 weakPreTitle = WeakPtr<FrameNode>(preTitleNode),
526 weakNavigation = WeakClaim(this),
527 weakCurNode = WeakPtr<FrameNode>(curNode),
528 isNavBar] {
529 PerfMonitor::GetPerfMonitor()->End(PerfConstants::ABILITY_OR_PAGE_SWITCH, true);
530 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navigation push animation end");
531 auto navigation = weakNavigation.Upgrade();
532 CHECK_NULL_VOID(navigation);
533 auto preNode = weakPreNode.Upgrade();
534 CHECK_NULL_VOID(preNode);
535 auto preTitle = weakPreTitle.Upgrade();
536 if (preTitle) {
537 preTitle->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
538 }
539 preNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
540 preNode->GetRenderContext()->SetActualForegroundColor(Color::TRANSPARENT);
541 bool needSetInvisible = false;
542 if (isNavBar) {
543 needSetInvisible = AceType::DynamicCast<NavBarNode>(preNode)->GetTransitionType() ==
544 PageTransitionType::EXIT_PUSH;
545 // store this flag for navBar layout only
546 navigation->SetNeedSetInvisible(needSetInvisible);
547 } else {
548 needSetInvisible = AceType::DynamicCast<NavDestinationGroupNode>(preNode)->GetTransitionType() ==
549 PageTransitionType::EXIT_PUSH;
550 }
551 // for the case, the navBar form EXIT_PUSH to push during animation
552 if (needSetInvisible) {
553 if (!isNavBar) {
554 preNode->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
555 preNode->SetJSViewActive(false);
556 } else {
557 // navigation mode could be transformed to split mode in the process of animation and
558 // navBar will be invisible only under the stack mode
559 if (navigation->GetNavigationMode() == NavigationMode::STACK) {
560 preNode->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
561 preNode->SetJSViewActive(false);
562 navigation->NotifyPageHide();
563 }
564 }
565 }
566
567 navigation->OnAccessibilityEvent(AccessibilityEventType::PAGE_CHANGE);
568 auto curNode = weakCurNode.Upgrade();
569 CHECK_NULL_VOID(curNode);
570 if (AceType::DynamicCast<NavDestinationGroupNode>(curNode)->GetTransitionType() !=
571 PageTransitionType::ENTER_PUSH) {
572 return;
573 }
574 navigation->isOnAnimation_ = false;
575 curNode->GetRenderContext()->ClipWithRRect(
576 RectF(0.0f, 0.0f, REMOVE_CLIP_SIZE, REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
577 };
578
579 /* set initial status of animation */
580 /* preNode */
581 preNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
582 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
583 /* curNode */
584 curNode->GetRenderContext()->ClipWithRRect(
585 RectF(curFrameSize.Width() * HALF, 0.0f, curFrameSize.Width(), REMOVE_CLIP_SIZE),
586 RadiusF(EdgeF(0.0f, 0.0f)));
587 curNode->GetRenderContext()->UpdateTranslateInXY({ curFrameSize.Width() * HALF, 0.0f });
588 curTitleNode->GetRenderContext()->UpdateTranslateInXY({ curFrameSize.Width() * HALF, 0.0f });
589
590 /* start transition animation */
591 AnimationOption option = CreateAnimationOption(springCurve, FillMode::FORWARDS, DEFAULT_ANIMATION_DURATION,
592 callback);
593 AnimationUtils::Animate(option, [preNode, preTitleNode, curNode, curTitleNode, preFrameSize, curFrameSize,
594 mode]() {
595 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::ABILITY_OR_PAGE_SWITCH, PerfActionType::LAST_UP, "");
596 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navigation push animation start");
597 // preNode
598 preNode->GetRenderContext()->UpdateTranslateInXY({ -preFrameSize.Width() * PARENT_PAGE_OFFSET, 0.0f });
599 preTitleNode->GetRenderContext()->UpdateTranslateInXY({ preFrameSize.Width() * PARENT_TITLE_OFFSET, 0.0f });
600 // curNode
601 curNode->GetRenderContext()->ClipWithRRect(
602 RectF(0.0f, 0.0f, curFrameSize.Width(), REMOVE_CLIP_SIZE), RadiusF(EdgeF(0.0f, 0.0f)));
603 curNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
604 curTitleNode->GetRenderContext()->UpdateTranslateInXY({ 0.0f, 0.0f });
605 }, option.GetOnFinishEvent());
606 MaskAnimation(preNode->GetRenderContext());
607
608 // title opacity
609 TitleOpacityAnimation(curTitleNode, false);
610 // backIcon opacity
611 auto backIcon = AceType::DynamicCast<FrameNode>(curTitleNode->GetBackButton());
612 BackButtonAnimation(backIcon, true);
613 isOnAnimation_ = true;
614 }
615
BackButtonAnimation(const RefPtr<FrameNode> & backButtonNode,bool isTransitionIn)616 void NavigationGroupNode::BackButtonAnimation(const RefPtr<FrameNode>& backButtonNode, bool isTransitionIn)
617 {
618 CHECK_NULL_VOID(backButtonNode);
619 AnimationOption transitionOption;
620 transitionOption.SetCurve(Curves::SHARP);
621 auto backButtonNodeContext = backButtonNode->GetRenderContext();
622 CHECK_NULL_VOID(backButtonNodeContext);
623 if (isTransitionIn) {
624 transitionOption.SetDelay(OPACITY_BACKBUTTON_IN_DELAY);
625 transitionOption.SetDuration(OPACITY_BACKBUTTON_IN_DURATION);
626 backButtonNodeContext->OpacityAnimation(transitionOption, 0.0, 1.0);
627 } else {
628 transitionOption.SetDuration(OPACITY_BACKBUTTON_OUT_DURATION);
629 // recover after transition animation.
630 transitionOption.SetOnFinishEvent([backButtonNodeContext]() {
631 backButtonNodeContext->UpdateOpacity(1.0);
632 backButtonNodeContext->SetOpacity(1.0f);
633 });
634 backButtonNodeContext->OpacityAnimation(transitionOption, 1.0, 0.0);
635 }
636 }
637
MaskAnimation(const RefPtr<RenderContext> & transitionOutNodeContext)638 void NavigationGroupNode::MaskAnimation(const RefPtr<RenderContext>& transitionOutNodeContext)
639 {
640 AnimationOption maskOption;
641 maskOption.SetCurve(Curves::FRICTION);
642 maskOption.SetDuration(MASK_DURATION);
643 maskOption.SetFillMode(FillMode::FORWARDS);
644 transitionOutNodeContext->SetActualForegroundColor(Color::TRANSPARENT);
645 AnimationUtils::Animate(
646 maskOption, [transitionOutNodeContext]() { transitionOutNodeContext->SetActualForegroundColor(MASK_COLOR); },
647 maskOption.GetOnFinishEvent());
648 }
649
TitleOpacityAnimation(const RefPtr<FrameNode> & node,bool isTransitionOut)650 void NavigationGroupNode::TitleOpacityAnimation(const RefPtr<FrameNode>& node, bool isTransitionOut)
651 {
652 auto titleNode = AceType::DynamicCast<TitleBarNode>(node);
653 CHECK_NULL_VOID(titleNode);
654 auto titleRenderContext = titleNode->GetRenderContext();
655 CHECK_NULL_VOID(titleRenderContext);
656 AnimationOption opacityOption;
657 opacityOption.SetCurve(Curves::SHARP);
658 opacityOption.SetDuration(OPACITY_TITLE_DURATION);
659 if (isTransitionOut) {
660 opacityOption.SetDelay(OPACITY_TITLE_OUT_DELAY);
661 // recover after transition animation.
662 opacityOption.SetOnFinishEvent([titleRenderContext]() {
663 titleRenderContext->UpdateOpacity(1.0);
664 titleRenderContext->SetOpacity(1.0f);
665 });
666 titleRenderContext->OpacityAnimation(opacityOption, 1.0, 0.0);
667 } else {
668 opacityOption.SetDelay(OPACITY_TITLE_IN_DELAY);
669 titleRenderContext->OpacityAnimation(opacityOption, 0.0, 1.0);
670 }
671 }
672
TransitionWithReplace(const RefPtr<FrameNode> & preNode,const RefPtr<FrameNode> & curNode,bool isNavBar)673 void NavigationGroupNode::TransitionWithReplace(
674 const RefPtr<FrameNode>& preNode, const RefPtr<FrameNode>& curNode, bool isNavBar)
675 {
676 CHECK_NULL_VOID(preNode);
677 CHECK_NULL_VOID(curNode);
678 AnimationOption option;
679 option.SetCurve(replaceCurve);
680 option.SetFillMode(FillMode::FORWARDS);
681 option.SetDuration(DEFAULT_REPLACE_DURATION);
682 option.SetOnFinishEvent([weakPreNode = WeakPtr<FrameNode>(preNode), weakNavigation = WeakClaim(this),
683 isNavBar]() {
684 PerfMonitor::GetPerfMonitor()->End(PerfConstants::ABILITY_OR_PAGE_SWITCH, true);
685 auto preNode = weakPreNode.Upgrade();
686 CHECK_NULL_VOID(preNode);
687 auto navigationNode = weakNavigation.Upgrade();
688 CHECK_NULL_VOID(navigationNode);
689 navigationNode->isOnAnimation_ = false;
690 navigationNode->OnAccessibilityEvent(AccessibilityEventType::PAGE_CHANGE);
691 navigationNode->DealNavigationExit(preNode, isNavBar);
692 auto context = PipelineContext::GetCurrentContext();
693 CHECK_NULL_VOID(context);
694 context->MarkNeedFlushMouseEvent();
695 });
696 preNode->GetEventHub<EventHub>()->SetEnabledInternal(false);
697 curNode->GetRenderContext()->UpdateOpacity(0.0f);
698 if (!isNavBar) {
699 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(preNode);
700 if (navDestination) {
701 navDestination->SetIsOnAnimation(true);
702 }
703 }
704 AnimationUtils::Animate(
705 option,
706 [curNode]() {
707 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::ABILITY_OR_PAGE_SWITCH, PerfActionType::LAST_UP, "");
708 curNode->GetRenderContext()->UpdateOpacity(1.0f);
709 },
710 option.GetOnFinishEvent());
711 isOnAnimation_ = true;
712 }
713
OnInspectorIdUpdate(const std::string & id)714 void NavigationGroupNode::OnInspectorIdUpdate(const std::string& id)
715 {
716 auto context = PipelineContext::GetCurrentContext();
717 CHECK_NULL_VOID(context);
718 context->AddOrReplaceNavigationNode(id, WeakClaim(this));
719 curId_ = id;
720 }
721
DealNavigationExit(const RefPtr<FrameNode> & preNode,bool isNavBar,bool isAnimated)722 void NavigationGroupNode::DealNavigationExit(const RefPtr<FrameNode>& preNode, bool isNavBar, bool isAnimated)
723 {
724 CHECK_NULL_VOID(preNode);
725 if (preNode->GetEventHub<EventHub>()) {
726 preNode->GetEventHub<EventHub>()->SetEnabledInternal(true);
727 }
728 if (isNavBar && isAnimated) {
729 SetNeedSetInvisible(true);
730 return;
731 }
732 auto navDestinationNode = AceType::DynamicCast<NavDestinationGroupNode>(preNode);
733 CHECK_NULL_VOID(navDestinationNode);
734 navDestinationNode->SetIsOnAnimation(false);
735 auto navDestinationPattern = navDestinationNode->GetPattern<NavDestinationPattern>();
736 auto shallowBuilder = navDestinationPattern->GetShallowBuilder();
737 if (shallowBuilder) {
738 shallowBuilder->MarkIsExecuteDeepRenderDone(false);
739 }
740 // remove old navdestination node
741 if (navDestinationNode->GetContentNode()) {
742 navDestinationNode->GetContentNode()->Clean();
743 }
744 auto parent = AceType::DynamicCast<FrameNode>(preNode->GetParent());
745 CHECK_NULL_VOID(parent);
746 parent->RemoveChild(preNode);
747 navDestinationPattern->SetCustomNode(nullptr);
748 parent->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
749 }
750
NotifyPageHide()751 void NavigationGroupNode::NotifyPageHide()
752 {
753 auto context = PipelineContext::GetCurrentContext();
754 CHECK_NULL_VOID(context);
755 auto stageManager = context->GetStageManager();
756 CHECK_NULL_VOID(stageManager);
757 auto container = Container::Current();
758 CHECK_NULL_VOID(container);
759 auto pageUrlChecker = container->GetPageUrlChecker();
760 CHECK_NULL_VOID(pageUrlChecker);
761 RefPtr<FrameNode> pageNode = stageManager->GetLastPage();
762 CHECK_NULL_VOID(pageNode);
763 auto pagePattern = pageNode->GetPattern<NG::PagePattern>();
764 CHECK_NULL_VOID(pagePattern);
765 auto pageInfo = pagePattern->GetPageInfo();
766 CHECK_NULL_VOID(pageInfo);
767 pageUrlChecker->NotifyPageHide(pageInfo->GetPageUrl());
768 }
769
UpdateLastStandardIndex()770 void NavigationGroupNode::UpdateLastStandardIndex()
771 {
772 // remove the impact of last standard index
773 lastStandardIndex_ = -1;
774 auto navigationPattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
775 CHECK_NULL_VOID(navigationPattern);
776 auto navigationStack = navigationPattern->GetNavigationStack();
777 CHECK_NULL_VOID(navigationStack);
778 const auto& navDestinationNodes = navigationStack->GetAllNavDestinationNodes();
779 if (navDestinationNodes.size() == 0) {
780 return;
781 }
782 for (int32_t index = static_cast<int32_t>(navDestinationNodes.size()) - 1; index >= 0; index--) {
783 const auto& curPath = navDestinationNodes[index];
784 const auto& uiNode = curPath.second;
785 if (!uiNode) {
786 continue;
787 }
788 auto navDestinationNode = AceType::DynamicCast<NavDestinationGroupNode>(GetNavDestinationNode(uiNode));
789 if (navDestinationNode && navDestinationNode->GetNavDestinationMode() == NavDestinationMode::STANDARD) {
790 lastStandardIndex_ = index;
791 return;
792 }
793 }
794 }
795
UpdateNavDestinationVisibility(const RefPtr<NavDestinationGroupNode> & navDestination,const RefPtr<UINode> & remainChild,int32_t index,size_t destinationSize)796 bool NavigationGroupNode::UpdateNavDestinationVisibility(const RefPtr<NavDestinationGroupNode>& navDestination,
797 const RefPtr<UINode>& remainChild, int32_t index, size_t destinationSize)
798 {
799 auto eventHub = navDestination->GetEventHub<NavDestinationEventHub>();
800 CHECK_NULL_RETURN(eventHub, false);
801 if (index == static_cast<int32_t>(destinationSize) - 1) {
802 // process shallow builder
803 navDestination->ProcessShallowBuilder();
804 navDestination->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE, true);
805 navDestination->SetJSViewActive(true);
806 navDestination->GetEventHub<EventHub>()->SetEnabledInternal(true);
807 // for the navDestination at the top, FireChangeEvent
808 eventHub->FireChangeEvent(true);
809 bool hasChanged = CheckNeedMeasure(navDestination->GetLayoutProperty()->GetPropertyChangeFlag());
810 if (!hasChanged && NavigationLayoutAlgorithm::IsAutoHeight(GetLayoutProperty<NavigationLayoutProperty>())) {
811 hasChanged = true;
812 }
813 return hasChanged;
814 }
815 if (index < lastStandardIndex_) {
816 auto pattern = AceType::DynamicCast<NavDestinationPattern>(navDestination->GetPattern());
817 if (!navDestination->IsOnAnimation()) {
818 if (pattern && pattern->GetIsOnShow()) {
819 // fire hidden event
820 eventHub->FireOnHiddenEvent(pattern->GetName());
821 eventHub->FireChangeEvent(false);
822 pattern->SetIsOnShow(false);
823 NavigationPattern::FireNavigationStateChange(navDestination, false);
824 }
825 if (navDestination->GetPattern<NavDestinationPattern>()->GetCustomNode() != remainChild) {
826 navDestination->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
827 navDestination->SetJSViewActive(false);
828 }
829 }
830 return false;
831 }
832 auto pattern = AceType::DynamicCast<NavDestinationPattern>(navDestination->GetPattern());
833 if (navDestination->GetPattern<NavDestinationPattern>()->GetCustomNode() != remainChild &&
834 !navDestination->IsOnAnimation()) {
835 navDestination->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
836 navDestination->SetJSViewActive(true);
837 }
838 return false;
839 }
840
CreateAnimationOption(const RefPtr<Curve> & curve,FillMode mode,int32_t duration,const AnimationFinishCallback & callback)841 AnimationOption NavigationGroupNode::CreateAnimationOption(const RefPtr<Curve>& curve, FillMode mode,
842 int32_t duration, const AnimationFinishCallback& callback)
843 {
844 AnimationOption option;
845 option.SetCurve(curve);
846 option.SetFillMode(mode);
847 option.SetDuration(duration);
848 if (callback != nullptr) {
849 option.SetOnFinishEvent(callback);
850 }
851 return option;
852 }
853
GetNavigationMode()854 NavigationMode NavigationGroupNode::GetNavigationMode()
855 {
856 auto navigationPattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
857 CHECK_NULL_RETURN(navigationPattern, NavigationMode::AUTO);
858 return navigationPattern->GetNavigationMode();
859 }
860
OnDetachFromMainTree(bool recursive)861 void NavigationGroupNode::OnDetachFromMainTree(bool recursive)
862 {
863 auto pattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
864 if (pattern) {
865 pattern->DetachNavigationStackFromParent();
866 }
867
868 GroupNode::OnDetachFromMainTree(recursive);
869 }
870
OnAttachToMainTree(bool recursive)871 void NavigationGroupNode::OnAttachToMainTree(bool recursive)
872 {
873 GroupNode::OnAttachToMainTree(recursive);
874
875 auto pattern = AceType::DynamicCast<NavigationPattern>(GetPattern());
876 if (pattern) {
877 pattern->AttachNavigationStackToParent();
878 }
879 }
880 } // namespace OHOS::Ace::NG
881