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