1 /*
2  * Copyright (c) 2021-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/stage/stage_element.h"
17 
18 #include "core/animation/card_transition_controller.h"
19 #include "core/animation/shared_transition_controller.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 constexpr int32_t POP_TO_LEAST_COUNT = 2;
25 
StartSharedController(WeakPtr<PipelineContext> contextWeak,TransitionEvent event,int32_t duration)26 void StartSharedController(WeakPtr<PipelineContext> contextWeak, TransitionEvent event, int32_t duration)
27 {
28     auto context = contextWeak.Upgrade();
29     if (!context) {
30         LOGE("Get Shared Controller failed. context is null.");
31         return;
32     }
33     auto sharedTransitionController = context->GetSharedTransitionController();
34     if (!sharedTransitionController) {
35         LOGE("Get Shared Controller failed. shared transition controller is null.");
36         return;
37     }
38     if (!sharedTransitionController->HasSharedTransition(event)) {
39         return;
40     }
41 
42     for (const auto& controller : sharedTransitionController->GetAnimators()) {
43         controller->AttachScheduler(context);
44         if (controller->GetDuration() == 0) {
45             controller->SetDuration(duration);
46         }
47         controller->Forward();
48     }
49 }
50 
GetCardController(WeakPtr<PipelineContext> contextWeak,const ComposeId & composeId)51 RefPtr<Animator> GetCardController(WeakPtr<PipelineContext> contextWeak, const ComposeId& composeId)
52 {
53     auto context = contextWeak.Upgrade();
54     if (!context) {
55         LOGE("Get card controller failed. context is null.");
56         return nullptr;
57     }
58     auto cardTransitionController = context->GetCardTransitionController();
59     if (!cardTransitionController) {
60         LOGE("Get card controller failed. shared transition controller is null.");
61         return nullptr;
62     }
63     if (!cardTransitionController->GetCardRect(composeId).GetRect().IsValid()) {
64         return nullptr;
65     }
66     return cardTransitionController->GetAnimator();
67 }
68 
GetCardRect(WeakPtr<PipelineContext> contextWeak,const ComposeId & composeId)69 RRect GetCardRect(WeakPtr<PipelineContext> contextWeak, const ComposeId& composeId)
70 {
71     auto context = contextWeak.Upgrade();
72     if (!context) {
73         LOGE("Get card controller failed. context is null.");
74         return RRect();
75     }
76     auto cardTransitionController = context->GetCardTransitionController();
77     if (!cardTransitionController) {
78         LOGE("Get card controller failed. shared transition controller is null.");
79         return RRect();
80     }
81     return cardTransitionController->GetCardRect(composeId);
82 }
83 
84 } // namespace
85 
OnKeyEvent(const KeyEvent & keyEvent)86 bool StageElement::OnKeyEvent(const KeyEvent& keyEvent)
87 {
88     if (!IsCurrentFocus()) {
89         LOGE("stage is not current focus.");
90         return false;
91     }
92     if (itLastFocusNode_ != focusNodes_.end() && (*itLastFocusNode_)->HandleKeyEvent(keyEvent)) {
93         return true;
94     }
95     if (FocusNode::OnKeyEvent(keyEvent)) {
96         return true;
97     }
98 
99     return false;
100 }
101 
MarkDirty()102 void StageElement::MarkDirty()
103 {
104     isWaitingForBuild_ = true;
105     StackElement::MarkDirty();
106 }
107 
CanRouterPage()108 bool StageElement::CanRouterPage()
109 {
110     if (isWaitingForBuild_) {
111         LOGE("StageElement: waiting for performBuild. Not ready for router page.");
112         return false;
113     }
114     if (pendingOperation_ != StackOperation::NONE) {
115         LOGE("StageElement: waiting for pending operation: %{public}d. Not ready for router page.", pendingOperation_);
116         return false;
117     }
118     if (!IsTransitionStop()) {
119         LOGE("router page failed. transition animation is not complete.");
120         return false;
121     }
122     return true;
123 }
124 
PostponePageTransition()125 void StageElement::PostponePageTransition()
126 {
127     postponePageTransition_ = true;
128 }
129 
LaunchPageTransition()130 void StageElement::LaunchPageTransition()
131 {
132     postponePageTransition_ = false;
133     auto context = context_.Upgrade();
134     if (pendingOperation_ != StackOperation::NONE && context) {
135         context->AddPostFlushListener(AceType::Claim(this));
136     }
137 }
138 
CanPushPage()139 bool StageElement::CanPushPage()
140 {
141     return CanRouterPage();
142 }
143 
PushPage(const RefPtr<Component> & newComponent)144 void StageElement::PushPage(const RefPtr<Component>& newComponent)
145 {
146     if (!CanPushPage()) {
147         return;
148     }
149     operation_ = StackOperation::PUSH_PAGE;
150     newComponent_ = newComponent;
151     MarkDirty();
152 }
153 
CanPopPage()154 bool StageElement::CanPopPage()
155 {
156     if (!CanRouterPage()) {
157         return false;
158     }
159 
160     if (children_.empty() || children_.size() == 1) {
161         LOGE("StageElement: no extra page for pop.");
162         return false;
163     }
164     return true;
165 }
166 
Pop()167 void StageElement::Pop()
168 {
169     if (!CanPopPage()) {
170         return;
171     }
172     operation_ = StackOperation::POP;
173     MarkDirty();
174 }
175 
GetTopPage() const176 RefPtr<PageElement> StageElement::GetTopPage() const
177 {
178     if (children_.empty()) {
179         LOGE("Can not deal with empty children_.");
180         return nullptr;
181     }
182     auto elementIter = children_.rbegin();
183     auto topElement = *elementIter;
184     if (!topElement) {
185         LOGE("top element is null, invalid data.");
186         return nullptr;
187     }
188     return topElement->GetPageElement();
189 }
190 
PopToPage(int32_t pageId)191 void StageElement::PopToPage(int32_t pageId)
192 {
193     if (!CanPopPage()) {
194         return;
195     }
196     operation_ = StackOperation::POP_TO_PAGE;
197     directedPageId_ = pageId;
198     MarkDirty();
199 }
200 
RestorePopPage(const RefPtr<Component> & newComponent)201 void StageElement::RestorePopPage(const RefPtr<Component>& newComponent)
202 {
203     operation_ = StackOperation::RESTORE;
204     newComponent_ = newComponent;
205     MarkDirty();
206 }
207 
CanReplacePage()208 bool StageElement::CanReplacePage()
209 {
210     if (!CanRouterPage()) {
211         return false;
212     }
213     if (children_.empty()) {
214         LOGE("StageElement: no extra page for replace.");
215         return false;
216     }
217     return true;
218 }
219 
Replace(const RefPtr<Component> & newComponent)220 void StageElement::Replace(const RefPtr<Component>& newComponent)
221 {
222     Replace(newComponent, nullptr);
223 }
224 
Replace(const RefPtr<Component> & newComponent,const std::function<void ()> & listener)225 void StageElement::Replace(const RefPtr<Component>& newComponent, const std::function<void()>& listener)
226 {
227     if (!CanReplacePage()) {
228         return;
229     }
230     operation_ = StackOperation::REPLACE;
231     newComponent_ = newComponent;
232     AddListener(listener);
233     MarkDirty();
234 }
235 
ClearOffStage(const std::function<void ()> & listener)236 bool StageElement::ClearOffStage(const std::function<void()>& listener)
237 {
238     if (!CanPopPage()) {
239         return false;
240     }
241     operation_ = StackOperation::CLEAR;
242     AddListener(listener);
243     MarkDirty();
244     return true;
245 }
246 
SetSinglePageId(int32_t pageId)247 void StageElement::SetSinglePageId(int32_t pageId)
248 {
249     singlePageId_ = pageId;
250 }
251 
PerformBuild()252 void StageElement::PerformBuild()
253 {
254     switch (operation_) {
255         case StackOperation::NONE:
256             break;
257         case StackOperation::RESTORE: {
258             RestorePop();
259             break;
260         }
261         case StackOperation::PUSH_PAGE: {
262             PerformPushPage();
263             auto render = GetRenderNode();
264             if (render) {
265                 render->MarkNeedLayout();
266             }
267             break;
268         }
269         case StackOperation::POP:
270             PerformPop();
271             break;
272         case StackOperation::REPLACE: {
273             PerformReplace();
274             auto render = GetRenderNode();
275             if (render) {
276                 render->MarkNeedLayout();
277             }
278             break;
279         }
280         case StackOperation::POP_TO_PAGE:
281             PerformPopToPage();
282             break;
283         case StackOperation::CLEAR:
284             PerformClear();
285             break;
286         default:
287             break;
288     }
289     isWaitingForBuild_ = false;
290     if (routerListener_ && (operation_ == StackOperation::REPLACE || operation_ == StackOperation::CLEAR)) {
291         routerListener_();
292         routerListener_ = nullptr;
293     }
294 }
295 
RefreshFocus()296 void StageElement::RefreshFocus()
297 {
298     // Process focus logic on the current top page when page changes
299     if (IsFocusable()) {
300         focusNodes_.back()->RequestFocus();
301         auto iter = focusNodes_.rbegin();
302         ++iter;
303         while (iter != focusNodes_.rend()) {
304             (*iter)->SetParentFocusable(false);
305             ++iter;
306         }
307     } else {
308         auto context = context_.Upgrade();
309         if (!context) {
310             LOGE("Pipeline context is nullptr");
311             return;
312         }
313         context->RootLostFocus();
314     }
315 }
316 
CheckPageTransitionElement(const RefPtr<PageTransitionElement> & transitionIn,const RefPtr<PageTransitionElement> & transitionOut)317 bool StageElement::CheckPageTransitionElement(
318     const RefPtr<PageTransitionElement>& transitionIn, const RefPtr<PageTransitionElement>& transitionOut)
319 {
320     if (!transitionIn) {
321         LOGW("transition in is empty, skip transition.");
322         return false;
323     }
324     if (!transitionOut) {
325         LOGW("transition out is empty, skip transition.");
326         return false;
327     }
328     auto controllerIn = transitionIn->GetTransitionController();
329     if (!controllerIn) {
330         LOGW("controller in is null, skip transition.");
331         return false;
332     }
333     auto controllerOut = transitionOut->GetTransitionController();
334     if (!controllerOut) {
335         LOGW("controller out is null, skip transition.");
336         return false;
337     }
338     return true;
339 }
340 
PerformPushPageTransition(const RefPtr<Element> & elementIn,const RefPtr<Element> & elementOut)341 bool StageElement::PerformPushPageTransition(const RefPtr<Element>& elementIn, const RefPtr<Element>& elementOut)
342 {
343     auto transitionIn = PageTransitionElement::GetTransitionElement(elementIn);
344     auto transitionOut = PageTransitionElement::GetTransitionElement(elementOut);
345     auto pageIn = AceType::DynamicCast<PageElement>(elementIn);
346     auto pageOut = AceType::DynamicCast<PageElement>(elementOut);
347     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
348         LOGW("check page transition failed, skip push transition.");
349         return false;
350     }
351     auto context = GetContext().Upgrade();
352     if (context && context->GetIsDeclarative()) {
353         transitionIn->SetDeclarativeDirection(TransitionDirection::TRANSITION_IN);
354         transitionOut->SetDeclarativeDirection(TransitionDirection::TRANSITION_OUT);
355     }
356     if (!pageIn || !pageOut) {
357         LOGE("push page failed. page in / out is null.");
358         return false;
359     }
360     NotifyPageTransitionListeners(TransitionEvent::PUSH_START, pageIn, pageOut);
361     ACE_SCOPED_TRACE("PUSH_START");
362     if (!InitTransition(transitionIn, transitionOut, TransitionEvent::PUSH_START)) {
363         LOGW("init transition failed, skip push transition.");
364         return false;
365     }
366     if ((!controllerIn_) || (!controllerOut_)) {
367         LOGE("push page failed. controller in / out is null.");
368         return false;
369     }
370     auto weak = AceType::WeakClaim(this);
371     WeakPtr<PageElement> pageInWeak = pageIn;
372     WeakPtr<PageElement> pageOutWeak = pageOut;
373     controllerIn_->AddStopListener([weak, pageInWeak, pageOutWeak]() {
374         auto stage = weak.Upgrade();
375         if (stage) {
376             stage->NotifyPageTransitionListeners(TransitionEvent::PUSH_END, pageInWeak, pageOutWeak);
377             ACE_SCOPED_TRACE("PUSH_END");
378             if (stage->singlePageId_ != -1) {
379                 stage->RecycleSinglePage();
380             }
381         }
382     });
383     // make stage untouchable when push page.
384     PerformPushPageInStage(pageOut);
385     controllerIn_->Forward();
386     controllerOut_->Forward();
387     StartSharedController(context_, TransitionEvent::PUSH_START, controllerIn_->GetDuration());
388     auto cardController = GetCardController(context_, pageIn->GetCardComposeId());
389     if (cardController) {
390         cardController->SetDuration(controllerIn_->GetDuration());
391         cardController->Forward();
392     }
393     return true;
394 }
395 
AddListenerForPopPage(const WeakPtr<PageElement> & pageInWeak,const WeakPtr<PageElement> & pageOutWeak)396 void StageElement::AddListenerForPopPage(
397     const WeakPtr<PageElement>& pageInWeak, const WeakPtr<PageElement>& pageOutWeak)
398 {
399     auto weak = AceType::WeakClaim(this);
400     // Add stop listener to remove top page when transition done.
401     controllerIn_->AddStopListener([weak, pageInWeak, pageOutWeak]() {
402         auto stage = weak.Upgrade();
403         auto elementIn = DynamicCast<Element>(pageInWeak.Upgrade());
404         if (stage && elementIn) {
405             // Remove top page.
406             stage->UpdateChild(elementIn, nullptr);
407             stage->MakeTopPageTouchable();
408             stage->NotifyPageTransitionListeners(TransitionEvent::POP_END, pageInWeak, pageOutWeak);
409             ACE_SCOPED_TRACE("POP_END");
410             stage->RefreshFocus();
411         }
412     });
413 }
414 
PerformPopPageTransition(const RefPtr<Element> & elementIn,const RefPtr<Element> & elementOut)415 bool StageElement::PerformPopPageTransition(const RefPtr<Element>& elementIn, const RefPtr<Element>& elementOut)
416 {
417     auto transitionIn = PageTransitionElement::GetTransitionElement(elementIn);
418     auto transitionOut = PageTransitionElement::GetTransitionElement(elementOut);
419     auto pageIn = AceType::DynamicCast<PageElement>(elementIn);
420     auto pageOut = AceType::DynamicCast<PageElement>(elementOut);
421     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
422         LOGW("check page transition failed, skip pop transition.");
423         return false;
424     }
425     auto context = GetContext().Upgrade();
426     if (context && context->GetIsDeclarative()) {
427         transitionIn->SetDeclarativeDirection(TransitionDirection::TRANSITION_OUT);
428         transitionOut->SetDeclarativeDirection(TransitionDirection::TRANSITION_IN);
429     }
430     NotifyPageTransitionListeners(TransitionEvent::POP_START, pageIn, pageOut);
431     ACE_SCOPED_TRACE("POP_START");
432     if (!InitTransition(transitionIn, transitionOut, TransitionEvent::POP_START)) {
433         LOGW("init transition failed, skip pop transition.");
434         return false;
435     }
436     if (!pageIn || !pageOut) {
437         LOGE("pop page failed. page in / out is null.");
438         return false;
439     }
440     if ((!controllerIn_) || (!controllerOut_)) {
441         LOGE("pop page failed. controller in / out is null.");
442         return false;
443     }
444     AddListenerForPopPage(pageIn, pageOut);
445     PerformPopPageInStage(pageOut, transitionOut);
446 #ifndef WEARABLE_PRODUCT
447     PerformPopMultimodalScene(pageIn->GetPageId(), pageOut->GetPageId());
448 #endif
449     RRect cardRRect = GetCardRect(context_, pageIn->GetCardComposeId());
450     if (cardRRect.GetRect().IsValid()) {
451         controllerIn_->Forward();
452         controllerOut_->Forward();
453     } else {
454         if (context && context->GetIsDeclarative()) {
455             if (transitionIn->GetIsCustomOption()) {
456                 controllerIn_->Forward();
457             } else {
458                 controllerIn_->Backward();
459             }
460             if (transitionOut->GetIsCustomOption()) {
461                 controllerOut_->Forward();
462             } else {
463                 controllerOut_->Backward();
464             }
465         } else {
466             controllerIn_->Backward();
467             controllerOut_->Backward();
468         }
469     }
470     StartSharedController(context_, TransitionEvent::POP_START, controllerIn_->GetDuration());
471     auto cardController = GetCardController(context_, pageIn->GetCardComposeId());
472     if (cardController) {
473         cardController->SetDuration(controllerIn_->GetDuration());
474         cardController->Forward();
475     }
476     return true;
477 }
478 
PerformPushPage()479 void StageElement::PerformPushPage()
480 {
481 #ifndef WEARABLE_PRODUCT
482     auto pageComponent = DynamicCast<PageComponent>(newComponent_);
483     if (pageComponent) {
484         PerformPushMultimodalScene(pageComponent->GetPageId());
485     } else {
486         LOGW("fail to perform push scene due to page component is null");
487     }
488 #endif
489     RefPtr<Element> topElement;
490     if (children_.empty()) {
491         NotifyPageTransitionListeners(TransitionEvent::PUSH_START, nullptr, nullptr);
492         auto newElement = UpdateChild(nullptr, newComponent_);
493         auto pageIn = AceType::DynamicCast<PageElement>(newElement);
494         if (!pageIn) {
495             LOGE("no page element found, do not notify page transition event.");
496             return;
497         }
498         NotifyPageTransitionListeners(TransitionEvent::PUSH_END, pageIn, nullptr);
499         return;
500     } else {
501         topElement = children_.back();
502     }
503     auto pushedElement = UpdateChild(nullptr, newComponent_);
504     MakeTopPageTouchable();
505     auto transitionIn = PageTransitionElement::GetTransitionElement(pushedElement);
506     auto transitionOut = PageTransitionElement::GetTransitionElement(topElement);
507     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
508         LOGE("check page transition failed, skip push transition.");
509         return;
510     }
511     transitionIn->SetWrapHidden(true);
512     transitionIn->SkipPostFlush();
513     transitionOut->SkipPostFlush();
514     auto context = context_.Upgrade();
515     if (context) {
516         if (!postponePageTransition_) {
517             context->AddPostFlushListener(AceType::Claim(this));
518         }
519         pendingOperation_ = operation_;
520     }
521     RefreshFocus();
522 }
523 
PerformPop()524 void StageElement::PerformPop()
525 {
526     if (children_.size() <= 1) {
527         return;
528     }
529     auto context = context_.Upgrade();
530     if (context) {
531         if (!postponePageTransition_) {
532             context->AddPostFlushListener(Claim(this));
533         }
534         pendingOperation_ = operation_;
535     }
536 }
537 
RestorePop()538 void StageElement::RestorePop()
539 {
540     operation_ = StackOperation::PUSH_PAGE;
541     UpdateChild(children_.back(), nullptr);
542     PerformPushPage();
543 }
544 
PerformReplace()545 void StageElement::PerformReplace()
546 {
547     if (children_.empty()) {
548         LOGE("replace page failed. no page in stage now.");
549         return;
550     }
551     auto newPage = DynamicCast<PageComponent>(newComponent_);
552     auto oldElement = children_.back();
553 #ifndef WEARABLE_PRODUCT
554     auto oldPage = DynamicCast<PageElement>(oldElement);
555     if (newPage && oldPage) {
556         PerformReplaceActiveScene(newPage->GetPageId(), oldPage->GetPageId());
557     }
558 #endif
559     UpdateChild(oldElement, nullptr);
560     UpdateChild(nullptr, newComponent_);
561     RefreshFocus();
562     if (singlePageId_ != -1) {
563         RecycleSinglePage();
564     }
565 }
566 
PerformPopToPage()567 void StageElement::PerformPopToPage()
568 {
569     if (children_.empty()) {
570         LOGE("pop page failed. no page in stage now.");
571         return;
572     }
573     // check if top page matches
574     auto topElement = *children_.rbegin();
575     if (topElement) {
576         auto topPage = AceType::DynamicCast<PageElement>(topElement);
577         if (topPage && directedPageId_ == topPage->GetPageId()) {
578             LOGW("already in target page. do not need to jump.");
579             return;
580         }
581     }
582     // skip top page and remove others. At least, need Top and End child to perform pop.
583     for (auto iter = (++children_.rbegin()); children_.size() > POP_TO_LEAST_COUNT; iter = (++children_.rbegin())) {
584         auto child = *iter;
585         RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(child);
586         if (!page) {
587             LOGW("try pop to page with wrapped display");
588             RefPtr<DisplayElement> display = AceType::DynamicCast<DisplayElement>(child);
589             if (display) {
590                 page = AceType::DynamicCast<PageElement>(display->GetFirstChild());
591             }
592         } else {
593             if (directedPageId_ == page->GetPageId()) {
594                 break;
595             }
596 #ifndef WEARABLE_PRODUCT
597             PerformRemoveInactiveScene(page->GetPageId());
598 #endif
599         }
600         // remove child.
601         UpdateChild(child, nullptr);
602     }
603     PerformPop();
604 }
605 
PerformClear()606 void StageElement::PerformClear()
607 {
608     if (children_.empty()) {
609         LOGE("clear page failed. no page in stage now.");
610         return;
611     }
612     for (auto iter = (++children_.rbegin()); iter != children_.rend(); iter = (++children_.rbegin())) {
613         auto page = AceType::DynamicCast<PageElement>(*iter);
614 #ifndef WEARABLE_PRODUCT
615         if (page) {
616             PerformRemoveInactiveScene(page->GetPageId());
617         }
618 #endif
619         UpdateChild(*iter, nullptr);
620     }
621     RefreshFocus();
622 }
623 
IsTransitionStop() const624 bool StageElement::IsTransitionStop() const
625 {
626     if ((!controllerIn_) || (!controllerOut_)) {
627         return true;
628     }
629     return ((controllerIn_->IsStopped()) && (controllerOut_->IsStopped()));
630 }
631 
InitTransition(const RefPtr<PageTransitionElement> & transition,TransitionDirection direction,TransitionEvent event,const RRect & cardRRect)632 bool StageElement::InitTransition(const RefPtr<PageTransitionElement>& transition, TransitionDirection direction,
633     TransitionEvent event, const RRect& cardRRect)
634 {
635     if (!transition) {
636         LOGE("init transition failed. transition is null. direction: %{public}d", direction);
637         return false;
638     }
639 
640     auto controller = transition->GetTransitionController();
641     if (!controller) {
642         LOGE("init transition failed. transition controller is null. direction: %{public}d", direction);
643         return false;
644     }
645     if (!controller->IsStopped()) {
646         controller->Stop();
647     }
648 
649     auto deviceType = SystemProperties::GetDeviceType();
650     // Reset status listener.
651     controller->ClearAllListeners();
652     auto context = GetContext().Upgrade();
653     if (context && context->GetIsDeclarative()) {
654         transition->LoadTransition();
655         transition->ResetPageTransitionAnimation();
656     }
657     transition->SetTransition(deviceType, event, direction, cardRRect);
658     transition->SetTransitionDirection(event, direction);
659     controller->SetFillMode(FillMode::FORWARDS);
660     transition->InitController(direction, event);
661     return true;
662 }
663 
InitTransition(const RefPtr<PageTransitionElement> & transitionIn,const RefPtr<PageTransitionElement> & transitionOut,TransitionEvent event)664 bool StageElement::InitTransition(const RefPtr<PageTransitionElement>& transitionIn,
665     const RefPtr<PageTransitionElement>& transitionOut, TransitionEvent event)
666 {
667     auto parentElement = transitionIn->GetElementParent();
668     RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(parentElement.Upgrade());
669     if (!page) {
670         return false;
671     }
672     RRect cardRRect = GetCardRect(context_, page->GetCardComposeId());
673     if (!InitTransition(transitionIn, TransitionDirection::TRANSITION_IN, event, cardRRect)) {
674         LOGE("init transition in failed.");
675         return false;
676     }
677     if (!InitTransition(transitionOut, TransitionDirection::TRANSITION_OUT, event, cardRRect)) {
678         LOGE("init transition out failed.");
679         return false;
680     }
681     transitionIn->AddPreFlush();
682     transitionOut->AddPreFlush();
683     controllerIn_ = transitionIn->GetTransitionController();
684     controllerOut_ = transitionOut->GetTransitionController();
685     return true;
686 }
687 
IsFocusable() const688 bool StageElement::IsFocusable() const
689 {
690     if (!FocusNode::IsFocusable()) {
691         return false;
692     }
693 
694     if (focusNodes_.empty()) {
695         return false;
696     }
697 
698     return focusNodes_.back()->IsFocusable();
699 }
700 #ifndef WEARABLE_PRODUCT
PerformPushMultimodalScene(int32_t pageId)701 void StageElement::PerformPushMultimodalScene(int32_t pageId)
702 {
703     auto context = GetContext().Upgrade();
704     if (!context || !context->GetMultiModalManager()) {
705         LOGE("fail to push multimodal scene due to manager get failed");
706         return;
707     }
708     context->GetMultiModalManager()->PushActiveScene(pageId);
709 }
710 
PerformPopMultimodalScene(int32_t poppedPageId,int32_t incomingPageId)711 void StageElement::PerformPopMultimodalScene(int32_t poppedPageId, int32_t incomingPageId)
712 {
713     auto context = GetContext().Upgrade();
714     if (!context || !context->GetMultiModalManager()) {
715         LOGE("fail to pop multimodal scene due to manager get failed");
716         return;
717     }
718     context->GetMultiModalManager()->PopActiveScene(poppedPageId, incomingPageId);
719 }
720 
PerformReplaceActiveScene(int32_t newPageId,int32_t replaceId)721 void StageElement::PerformReplaceActiveScene(int32_t newPageId, int32_t replaceId)
722 {
723     auto context = GetContext().Upgrade();
724     if (!context || !context->GetMultiModalManager()) {
725         LOGE("fail to replace multimodal scene due to manager get failed");
726         return;
727     }
728     context->GetMultiModalManager()->ReplaceActiveScene(newPageId, replaceId);
729 }
730 
PerformRemoveInactiveScene(int32_t pageId)731 void StageElement::PerformRemoveInactiveScene(int32_t pageId)
732 {
733     auto context = GetContext().Upgrade();
734     if (!context || !context->GetMultiModalManager()) {
735         LOGE("fail to remove inactive multimodal scene due to manager get failed");
736         return;
737     }
738     context->GetMultiModalManager()->RemoveInactiveScene(pageId);
739 }
740 #endif
741 
ProcessStageInPageTransition()742 void StageElement::ProcessStageInPageTransition()
743 {
744     auto renderStage = GetRenderNode();
745     if (renderStage && controllerIn_ && controllerOut_) {
746         renderStage->SetDisableTouchEvent(true);
747         auto weakRenderStage = WeakClaim(RawPtr(renderStage));
748         auto lastStopController =
749             controllerIn_->GetDuration() > controllerOut_->GetDuration() ? controllerIn_ : controllerOut_;
750         lastStopController->AddStopListener([weakRenderStage] {
751             auto stage = weakRenderStage.Upgrade();
752             if (stage) {
753                 stage->SetDisableTouchEvent(false);
754             }
755         });
756     }
757 }
758 
PerformPushPageInStage(const WeakPtr<PageElement> & pageOutWeak)759 void StageElement::PerformPushPageInStage(const WeakPtr<PageElement>& pageOutWeak)
760 {
761     ProcessStageInPageTransition();
762     controllerOut_->AddStopListener([pageOutWeak] {
763         auto pageOut = pageOutWeak.Upgrade();
764         if (pageOut) {
765             pageOut->SetHidden(true);
766         }
767     });
768 }
769 
PerformPopPageInStage(const RefPtr<PageElement> & pageOut,const RefPtr<PageTransitionElement> & transitionOut)770 void StageElement::PerformPopPageInStage(
771     const RefPtr<PageElement>& pageOut, const RefPtr<PageTransitionElement>& transitionOut)
772 {
773     ProcessStageInPageTransition();
774     pageOut->SetHidden(false);
775 }
776 
OnPostFlush()777 void StageElement::OnPostFlush()
778 {
779     if (children_.size() < POP_TO_LEAST_COUNT) {
780         LOGE("Can not handle less than two pages.");
781         return;
782     }
783     auto elementIter = children_.rbegin();
784     auto topElement = *(elementIter++);
785     auto nextTopElement = *(elementIter++);
786 
787     switch (pendingOperation_) {
788         case StackOperation::PUSH_PAGE:
789             PerformPushPageTransition(topElement, nextTopElement);
790             break;
791         case StackOperation::POP:
792         case StackOperation::POP_TO_PAGE:
793             PerformPopPageTransition(topElement, nextTopElement);
794             break;
795         default:
796             break;
797     }
798     pendingOperation_ = StackOperation::NONE;
799 }
800 
MakeTopPageTouchable()801 void StageElement::MakeTopPageTouchable()
802 {
803     auto renderStage = GetRenderNode();
804     if (!renderStage) {
805         return;
806     }
807     const auto& children = renderStage->GetChildren();
808     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
809         auto& child = *iter;
810         // only top page can touch
811         child->SetDisableTouchEvent(!(iter == children.rbegin()));
812     }
813 }
814 
RecycleSinglePage()815 void StageElement::RecycleSinglePage()
816 {
817     LOGI("single page recycle");
818     auto iter = find_if(children_.begin(), children_.end(), [&](const RefPtr<Element>& item) {
819         RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(item);
820         return page && singlePageId_ == page->GetPageId();
821     });
822     if (iter != children_.end()) {
823         UpdateChild(*iter, nullptr);
824     }
825     singlePageId_ = -1;
826 }
827 
PushPage(const RefPtr<Component> & newComponent)828 void SectionStageElement::PushPage(const RefPtr<Component>& newComponent)
829 {
830     AddAsOnlyPage(newComponent);
831 }
832 
Replace(const RefPtr<Component> & newComponent)833 void SectionStageElement::Replace(const RefPtr<Component>& newComponent)
834 {
835     AddAsOnlyPage(newComponent);
836 }
837 
AddAsOnlyPage(const RefPtr<Component> & newComponent)838 void SectionStageElement::AddAsOnlyPage(const RefPtr<Component>& newComponent)
839 {
840     if (children_.empty()) {
841         StageElement::PushPage(newComponent);
842     } else {
843         StageElement::Replace(newComponent);
844     }
845 }
846 
847 } // namespace OHOS::Ace
848