• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
PopToPage(int32_t pageId)183 void StageElement::PopToPage(int32_t pageId)
184 {
185     if (!CanPopPage()) {
186         return;
187     }
188     operation_ = StackOperation::POP_TO_PAGE;
189     directedPageId_ = pageId;
190     MarkDirty();
191 }
192 
RestorePopPage(const RefPtr<Component> & newComponent)193 void StageElement::RestorePopPage(const RefPtr<Component>& newComponent)
194 {
195     operation_ = StackOperation::RESTORE;
196     newComponent_ = newComponent;
197     MarkDirty();
198 }
199 
CanReplacePage()200 bool StageElement::CanReplacePage()
201 {
202     if (!CanRouterPage()) {
203         return false;
204     }
205     if (children_.empty()) {
206         LOGE("StageElement: no extra page for replace.");
207         return false;
208     }
209     return true;
210 }
211 
Replace(const RefPtr<Component> & newComponent)212 void StageElement::Replace(const RefPtr<Component>& newComponent)
213 {
214     if (!CanReplacePage()) {
215         return;
216     }
217     operation_ = StackOperation::REPLACE;
218     newComponent_ = newComponent;
219     MarkDirty();
220 }
221 
ClearOffStage()222 bool StageElement::ClearOffStage()
223 {
224     if (!CanPopPage()) {
225         return false;
226     }
227     operation_ = StackOperation::CLEAR;
228     MarkDirty();
229     return true;
230 }
231 
PerformBuild()232 void StageElement::PerformBuild()
233 {
234     LOGD("StageElement: PerformBuild, operation: %{public}d", operation_);
235     switch (operation_) {
236         case StackOperation::NONE:
237             break;
238         case StackOperation::RESTORE: {
239             RestorePop();
240             break;
241         }
242         case StackOperation::PUSH_PAGE: {
243             PerformPushPage();
244             auto render = GetRenderNode();
245             if (render) {
246                 render->MarkNeedLayout();
247             }
248             break;
249         }
250         case StackOperation::POP:
251             PerformPop();
252             break;
253         case StackOperation::REPLACE: {
254             PerformReplace();
255             auto render = GetRenderNode();
256             if (render) {
257                 render->MarkNeedLayout();
258             }
259             break;
260         }
261         case StackOperation::POP_TO_PAGE:
262             PerformPopToPage();
263             break;
264         case StackOperation::CLEAR:
265             PerformClear();
266             break;
267         default:
268             break;
269     }
270     isWaitingForBuild_ = false;
271 }
272 
RefreshFocus()273 void StageElement::RefreshFocus()
274 {
275     // Process focus logic on the current top page when page changes
276     if (IsFocusable()) {
277         focusNodes_.back()->RequestFocus();
278     } else {
279         auto context = context_.Upgrade();
280         if (!context) {
281             LOGE("Pipeline context is nullptr");
282             return;
283         }
284         context->RootLostFocus();
285     }
286 }
287 
CheckPageTransitionElement(const RefPtr<PageTransitionElement> & transitionIn,const RefPtr<PageTransitionElement> & transitionOut)288 bool StageElement::CheckPageTransitionElement(
289     const RefPtr<PageTransitionElement>& transitionIn, const RefPtr<PageTransitionElement>& transitionOut)
290 {
291     if (!transitionIn) {
292         LOGW("transition in is empty, skip transition.");
293         return false;
294     }
295     if (!transitionOut) {
296         LOGW("transition out is empty, skip transition.");
297         return false;
298     }
299     auto controllerIn = transitionIn->GetTransitionController();
300     if (!controllerIn) {
301         LOGW("controller in is null, skip transition.");
302         return false;
303     }
304     auto controllerOut = transitionOut->GetTransitionController();
305     if (!controllerOut) {
306         LOGW("controller out is null, skip transition.");
307         return false;
308     }
309     return true;
310 }
311 
PerformPushPageTransition(const RefPtr<Element> & elementIn,const RefPtr<Element> & elementOut)312 bool StageElement::PerformPushPageTransition(const RefPtr<Element>& elementIn, const RefPtr<Element>& elementOut)
313 {
314     auto transitionIn = PageTransitionElement::GetTransitionElement(elementIn);
315     auto transitionOut = PageTransitionElement::GetTransitionElement(elementOut);
316     auto pageIn = AceType::DynamicCast<PageElement>(elementIn);
317     auto pageOut = AceType::DynamicCast<PageElement>(elementOut);
318     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
319         LOGW("check page transition failed, skip push transition.");
320         return false;
321     }
322     auto context = GetContext().Upgrade();
323     if (context && context->GetIsDeclarative()) {
324         transitionIn->SetDeclarativeDirection(TransitionDirection::TRANSITION_IN);
325         transitionOut->SetDeclarativeDirection(TransitionDirection::TRANSITION_OUT);
326     }
327     LOGD("notify push page event. page id: in: %{public}d, out: %{public}d", pageIn->GetPageId(), pageOut->GetPageId());
328     NotifyPageTransitionListeners(TransitionEvent::PUSH_START, pageIn, pageOut);
329     ACE_SCOPED_TRACE("PUSH_START");
330     if (!InitTransition(transitionIn, transitionOut, TransitionEvent::PUSH_START)) {
331         LOGW("init transition failed, skip push transition.");
332         return false;
333     }
334     if (!pageIn || !pageOut) {
335         LOGE("push page failed. page in / out is null.");
336         return false;
337     }
338     if ((!controllerIn_) || (!controllerOut_)) {
339         LOGE("push page failed. controller in / out is null.");
340         return false;
341     }
342     auto weak = AceType::WeakClaim(this);
343     WeakPtr<PageElement> pageInWeak = pageIn;
344     WeakPtr<PageElement> pageOutWeak = pageOut;
345     controllerIn_->AddStopListener([weak, pageInWeak, pageOutWeak]() {
346         auto stage = weak.Upgrade();
347         if (stage) {
348             stage->NotifyPageTransitionListeners(TransitionEvent::PUSH_END, pageInWeak, pageOutWeak);
349             ACE_SCOPED_TRACE("PUSH_END");
350             auto context = stage->context_.Upgrade();
351             if (context) {
352                 context->OnPageShow();
353             }
354         }
355     });
356     // make stage untouchable when push page.
357     PerformPushPageInStage(pageOut);
358     LOGD("start push transition.");
359     controllerIn_->Forward();
360     controllerOut_->Forward();
361     StartSharedController(context_, TransitionEvent::PUSH_START, controllerIn_->GetDuration());
362     auto cardController = GetCardController(context_, pageIn->GetCardComposeId());
363     if (cardController) {
364         cardController->SetDuration(controllerIn_->GetDuration());
365         cardController->Forward();
366     }
367     return true;
368 }
369 
AddListenerForPopPage(const WeakPtr<PageElement> & pageInWeak,const WeakPtr<PageElement> & pageOutWeak)370 void StageElement::AddListenerForPopPage(
371     const WeakPtr<PageElement>& pageInWeak, const WeakPtr<PageElement>& pageOutWeak)
372 {
373     auto weak = AceType::WeakClaim(this);
374     // Add stop listener to remove top page when transition done.
375     controllerIn_->AddStopListener([weak, pageInWeak, pageOutWeak]() {
376         auto stage = weak.Upgrade();
377         auto elementIn = DynamicCast<Element>(pageInWeak.Upgrade());
378         if (stage && elementIn) {
379             // Remove top page.
380             stage->UpdateChild(elementIn, nullptr);
381             stage->MakeTopPageTouchable();
382             stage->NotifyPageTransitionListeners(TransitionEvent::POP_END, pageInWeak, pageOutWeak);
383             ACE_SCOPED_TRACE("POP_END");
384             stage->RefreshFocus();
385         }
386     });
387 }
388 
PerformPopPageTransition(const RefPtr<Element> & elementIn,const RefPtr<Element> & elementOut)389 bool StageElement::PerformPopPageTransition(const RefPtr<Element>& elementIn, const RefPtr<Element>& elementOut)
390 {
391     auto transitionIn = PageTransitionElement::GetTransitionElement(elementIn);
392     auto transitionOut = PageTransitionElement::GetTransitionElement(elementOut);
393     auto pageIn = AceType::DynamicCast<PageElement>(elementIn);
394     auto pageOut = AceType::DynamicCast<PageElement>(elementOut);
395     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
396         LOGW("check page transition failed, skip pop transition.");
397         return false;
398     }
399     auto context = GetContext().Upgrade();
400     if (context && context->GetIsDeclarative()) {
401         transitionIn->SetDeclarativeDirection(TransitionDirection::TRANSITION_OUT);
402         transitionOut->SetDeclarativeDirection(TransitionDirection::TRANSITION_IN);
403     }
404     NotifyPageTransitionListeners(TransitionEvent::POP_START, pageIn, pageOut);
405     ACE_SCOPED_TRACE("POP_START");
406     if (!InitTransition(transitionIn, transitionOut, TransitionEvent::POP_START)) {
407         LOGW("init transition failed, skip pop transition.");
408         return false;
409     }
410     if (!pageIn || !pageOut) {
411         LOGE("pop page failed. page in / out is null.");
412         return false;
413     }
414     if ((!controllerIn_) || (!controllerOut_)) {
415         LOGE("pop page failed. controller in / out is null.");
416         return false;
417     }
418     AddListenerForPopPage(pageIn, pageOut);
419     PerformPopPageInStage(pageOut, transitionOut);
420 #ifndef WEARABLE_PRODUCT
421     PerformPopMultimodalScene(pageIn->GetPageId(), pageOut->GetPageId());
422 #endif
423     LOGD("start pop transition.");
424     RRect cardRRect = GetCardRect(context_, pageIn->GetCardComposeId());
425     if (cardRRect.GetRect().IsValid()) {
426         controllerIn_->Forward();
427         controllerOut_->Forward();
428     } else {
429         if (context && context->GetIsDeclarative()) {
430             if (transitionIn->GetIsCustomOption()) {
431                 controllerIn_->Forward();
432             } else {
433                 controllerIn_->Backward();
434             }
435             if (transitionOut->GetIsCustomOption()) {
436                 controllerOut_->Forward();
437             } else {
438                 controllerOut_->Backward();
439             }
440         } else {
441             controllerIn_->Backward();
442             controllerOut_->Backward();
443         }
444     }
445     StartSharedController(context_, TransitionEvent::POP_START, controllerIn_->GetDuration());
446     auto cardController = GetCardController(context_, pageIn->GetCardComposeId());
447     if (cardController) {
448         cardController->SetDuration(controllerIn_->GetDuration());
449         cardController->Forward();
450     }
451     return true;
452 }
453 
PerformPushPage()454 void StageElement::PerformPushPage()
455 {
456     LOGD("start to push page.");
457 #ifndef WEARABLE_PRODUCT
458     auto pageComponent = DynamicCast<PageComponent>(newComponent_);
459     if (pageComponent) {
460         PerformPushMultimodalScene(pageComponent->GetPageId());
461     } else {
462         LOGW("fail to perform push scene due to page component is null");
463     }
464 #endif
465     RefPtr<Element> topElement;
466     if (children_.empty()) {
467         LOGD("push first page, just update child, no transition.");
468         NotifyPageTransitionListeners(TransitionEvent::PUSH_START, nullptr, nullptr);
469         auto newElement = UpdateChild(nullptr, newComponent_);
470         auto pageIn = AceType::DynamicCast<PageElement>(newElement);
471         if (!pageIn) {
472             LOGE("no page element found, do not notify page transition event.");
473             return;
474         }
475         LOGD("notify push first page event. page id: in: %{public}d.", pageIn->GetPageId());
476         NotifyPageTransitionListeners(TransitionEvent::PUSH_END, pageIn, nullptr);
477         return;
478     } else {
479         topElement = children_.back();
480     }
481     auto pushedElement = UpdateChild(nullptr, newComponent_);
482     MakeTopPageTouchable();
483     auto transitionIn = PageTransitionElement::GetTransitionElement(pushedElement);
484     auto transitionOut = PageTransitionElement::GetTransitionElement(topElement);
485     if (!CheckPageTransitionElement(transitionIn, transitionOut)) {
486         LOGE("check page transition failed, skip push transition.");
487         return;
488     }
489     LOGD("set transition in hidden.");
490     transitionIn->SetWrapHidden(true);
491     transitionIn->SkipPostFlush();
492     transitionOut->SkipPostFlush();
493     auto context = context_.Upgrade();
494     if (context) {
495         if (!postponePageTransition_) {
496             context->AddPostFlushListener(AceType::Claim(this));
497         }
498         pendingOperation_ = operation_;
499     }
500     RefreshFocus();
501 }
502 
PerformPop()503 void StageElement::PerformPop()
504 {
505     if (children_.size() <= 1) {
506         LOGD("no enough element left in stage.");
507         return;
508     }
509     auto context = context_.Upgrade();
510     if (context) {
511         if (!postponePageTransition_) {
512             context->AddPostFlushListener(Claim(this));
513         }
514         pendingOperation_ = operation_;
515     }
516 }
517 
RestorePop()518 void StageElement::RestorePop()
519 {
520     // add page before current page and run pop
521     auto newChild = UpdateChildWithSlot(nullptr, newComponent_, -2, GetRenderSlot());
522     operation_ = StackOperation::POP;
523     PerformPop();
524 }
525 
PerformReplace()526 void StageElement::PerformReplace()
527 {
528     if (children_.empty()) {
529         LOGE("replace page failed. no page in stage now.");
530         return;
531     }
532     auto newPage = DynamicCast<PageComponent>(newComponent_);
533     auto oldElement = children_.back();
534 #ifndef WEARABLE_PRODUCT
535     auto oldPage = DynamicCast<PageElement>(oldElement);
536     if (newPage && oldPage) {
537         PerformReplaceActiveScene(newPage->GetPageId(), oldPage->GetPageId());
538     }
539 #endif
540     UpdateChild(oldElement, nullptr);
541     UpdateChild(nullptr, newComponent_);
542     auto context = context_.Upgrade();
543     if (context) {
544         context->OnPageShow();
545     }
546     RefreshFocus();
547 }
548 
PerformPopToPage()549 void StageElement::PerformPopToPage()
550 {
551     if (children_.empty()) {
552         LOGE("pop page failed. no page in stage now.");
553         return;
554     }
555     // check if top page matches
556     auto topElement = *children_.rbegin();
557     if (topElement) {
558         auto topPage = AceType::DynamicCast<PageElement>(topElement);
559         if (topPage && directedPageId_ == topPage->GetPageId()) {
560             LOGW("already in target page. do not need to jump.");
561             return;
562         }
563     }
564     // skip top page and remove others. At least, need Top and End child to perform pop.
565     for (auto iter = (++children_.rbegin()); children_.size() > POP_TO_LEAST_COUNT; iter = (++children_.rbegin())) {
566         auto child = *iter;
567         RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(child);
568         if (!page) {
569             LOGW("try pop to page with wrapped display");
570             RefPtr<DisplayElement> display = AceType::DynamicCast<DisplayElement>(child);
571             if (display) {
572                 page = AceType::DynamicCast<PageElement>(display->GetFirstChild());
573             }
574         } else {
575             if (directedPageId_ == page->GetPageId()) {
576                 break;
577             }
578 #ifndef WEARABLE_PRODUCT
579             PerformRemoveInactiveScene(page->GetPageId());
580 #endif
581         }
582         // remove child.
583         UpdateChild(child, nullptr);
584     }
585     PerformPop();
586 }
587 
PerformClear()588 void StageElement::PerformClear()
589 {
590     if (children_.empty()) {
591         LOGE("clear page failed. no page in stage now.");
592         return;
593     }
594     for (auto iter = (++children_.rbegin()); iter != children_.rend(); iter = (++children_.rbegin())) {
595         auto page = AceType::DynamicCast<PageElement>(*iter);
596 #ifndef WEARABLE_PRODUCT
597         if (page) {
598             PerformRemoveInactiveScene(page->GetPageId());
599         }
600 #endif
601         UpdateChild(*iter, nullptr);
602     }
603     RefreshFocus();
604 }
605 
IsTransitionStop() const606 bool StageElement::IsTransitionStop() const
607 {
608     if ((!controllerIn_) || (!controllerOut_)) {
609         LOGD("controllerIn or controllerOut are null.");
610         return true;
611     }
612     return ((controllerIn_->IsStopped()) && (controllerOut_->IsStopped()));
613 }
614 
InitTransition(const RefPtr<PageTransitionElement> & transition,TransitionDirection direction,TransitionEvent event,const RRect & cardRRect)615 bool StageElement::InitTransition(const RefPtr<PageTransitionElement>& transition, TransitionDirection direction,
616     TransitionEvent event, const RRect& cardRRect)
617 {
618     if (!transition) {
619         LOGE("init transition failed. transition is null. direction: %{public}d", direction);
620         return false;
621     }
622 
623     auto controller = transition->GetTransitionController();
624     if (!controller) {
625         LOGE("init transition failed. transition controller is null. direction: %{public}d", direction);
626         return false;
627     }
628     if (!controller->IsStopped()) {
629         controller->Stop();
630     }
631 
632     auto deviceType = SystemProperties::GetDeviceType();
633     // Reset status listener.
634     controller->ClearAllListeners();
635     auto context = GetContext().Upgrade();
636     if (context && context->GetIsDeclarative()) {
637         transition->LoadTransition();
638         transition->ResetPageTransitionAnimation();
639     }
640     transition->SetTransition(deviceType, event, direction, cardRRect);
641     transition->SetTransitionDirection(event, direction);
642     controller->SetFillMode(FillMode::FORWARDS);
643     transition->InitController(direction, event);
644     return true;
645 }
646 
InitTransition(const RefPtr<PageTransitionElement> & transitionIn,const RefPtr<PageTransitionElement> & transitionOut,TransitionEvent event)647 bool StageElement::InitTransition(const RefPtr<PageTransitionElement>& transitionIn,
648     const RefPtr<PageTransitionElement>& transitionOut, TransitionEvent event)
649 {
650     auto parentElement = transitionIn->GetElementParent();
651     RefPtr<PageElement> page = AceType::DynamicCast<PageElement>(parentElement.Upgrade());
652     if (!page) {
653         return false;
654     }
655     RRect cardRRect = GetCardRect(context_, page->GetCardComposeId());
656     if (!InitTransition(transitionIn, TransitionDirection::TRANSITION_IN, event, cardRRect)) {
657         LOGE("init transition in failed.");
658         return false;
659     }
660     if (!InitTransition(transitionOut, TransitionDirection::TRANSITION_OUT, event, cardRRect)) {
661         LOGE("init transition out failed.");
662         return false;
663     }
664     transitionIn->AddPreFlush();
665     transitionOut->AddPreFlush();
666     controllerIn_ = transitionIn->GetTransitionController();
667     controllerOut_ = transitionOut->GetTransitionController();
668     return true;
669 }
670 
IsFocusable() const671 bool StageElement::IsFocusable() const
672 {
673     if (!FocusNode::IsFocusable()) {
674         return false;
675     }
676 
677     if (focusNodes_.empty()) {
678         return false;
679     }
680 
681     return focusNodes_.back()->IsFocusable();
682 }
683 #ifndef WEARABLE_PRODUCT
PerformPushMultimodalScene(int32_t pageId)684 void StageElement::PerformPushMultimodalScene(int32_t pageId)
685 {
686     auto context = GetContext().Upgrade();
687     if (!context || !context->GetMultiModalManager()) {
688         LOGE("fail to push multimodal scene due to manager get failed");
689         return;
690     }
691     context->GetMultiModalManager()->PushActiveScene(pageId);
692 }
693 
PerformPopMultimodalScene(int32_t poppedPageId,int32_t incomingPageId)694 void StageElement::PerformPopMultimodalScene(int32_t poppedPageId, int32_t incomingPageId)
695 {
696     auto context = GetContext().Upgrade();
697     if (!context || !context->GetMultiModalManager()) {
698         LOGE("fail to pop multimodal scene due to manager get failed");
699         return;
700     }
701     context->GetMultiModalManager()->PopActiveScene(poppedPageId, incomingPageId);
702 }
703 
PerformReplaceActiveScene(int32_t newPageId,int32_t replaceId)704 void StageElement::PerformReplaceActiveScene(int32_t newPageId, int32_t replaceId)
705 {
706     auto context = GetContext().Upgrade();
707     if (!context || !context->GetMultiModalManager()) {
708         LOGE("fail to replace multimodal scene due to manager get failed");
709         return;
710     }
711     context->GetMultiModalManager()->ReplaceActiveScene(newPageId, replaceId);
712 }
713 
PerformRemoveInactiveScene(int32_t pageId)714 void StageElement::PerformRemoveInactiveScene(int32_t pageId)
715 {
716     auto context = GetContext().Upgrade();
717     if (!context || !context->GetMultiModalManager()) {
718         LOGE("fail to remove inactive multimodal scene due to manager get failed");
719         return;
720     }
721     context->GetMultiModalManager()->RemoveInactiveScene(pageId);
722 }
723 #endif
724 
ProcessStageInPageTransition()725 void StageElement::ProcessStageInPageTransition()
726 {
727     auto renderStage = GetRenderNode();
728     if (renderStage && controllerIn_ && controllerOut_) {
729         renderStage->SetDisableTouchEvent(true);
730         auto weakRenderStage = WeakClaim(RawPtr(renderStage));
731         auto lastStopController =
732             controllerIn_->GetDuration() > controllerOut_->GetDuration() ? controllerIn_ : controllerOut_;
733         lastStopController->AddStopListener([weakRenderStage] {
734             auto stage = weakRenderStage.Upgrade();
735             if (stage) {
736                 stage->SetDisableTouchEvent(false);
737             }
738         });
739     }
740 }
741 
PerformPushPageInStage(const WeakPtr<PageElement> & pageOutWeak)742 void StageElement::PerformPushPageInStage(const WeakPtr<PageElement>& pageOutWeak)
743 {
744     ProcessStageInPageTransition();
745     controllerOut_->AddStopListener([pageOutWeak] {
746         auto pageOut = pageOutWeak.Upgrade();
747         if (pageOut) {
748             pageOut->SetHidden(true);
749         }
750     });
751 }
752 
PerformPopPageInStage(const RefPtr<PageElement> & pageOut,const RefPtr<PageTransitionElement> & transitionOut)753 void StageElement::PerformPopPageInStage(
754     const RefPtr<PageElement>& pageOut, const RefPtr<PageTransitionElement>& transitionOut)
755 {
756     ProcessStageInPageTransition();
757     pageOut->SetHidden(false);
758 }
759 
OnPostFlush()760 void StageElement::OnPostFlush()
761 {
762     LOGD("StageElement: PostFlush, pending operation: %{public}d", pendingOperation_);
763     if (children_.size() < POP_TO_LEAST_COUNT) {
764         LOGE("Can not handle less than two pages.");
765         return;
766     }
767     auto elementIter = children_.rbegin();
768     auto topElement = *(elementIter++);
769     auto nextTopElement = *(elementIter++);
770 
771     switch (pendingOperation_) {
772         case StackOperation::PUSH_PAGE:
773             PerformPushPageTransition(topElement, nextTopElement);
774             break;
775         case StackOperation::POP:
776         case StackOperation::POP_TO_PAGE:
777             PerformPopPageTransition(topElement, nextTopElement);
778             break;
779         default:
780             break;
781     }
782     pendingOperation_ = StackOperation::NONE;
783 }
784 
MakeTopPageTouchable()785 void StageElement::MakeTopPageTouchable()
786 {
787     auto renderStage = GetRenderNode();
788     if (!renderStage) {
789         return;
790     }
791     const auto& children = renderStage->GetChildren();
792     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
793         auto& child = *iter;
794         // only top page can touch
795         child->SetDisableTouchEvent(!(iter == children.rbegin()));
796     }
797 }
798 
PushPage(const RefPtr<Component> & newComponent)799 void SectionStageElement::PushPage(const RefPtr<Component>& newComponent)
800 {
801     AddAsOnlyPage(newComponent);
802 }
803 
Replace(const RefPtr<Component> & newComponent)804 void SectionStageElement::Replace(const RefPtr<Component>& newComponent)
805 {
806     AddAsOnlyPage(newComponent);
807 }
808 
AddAsOnlyPage(const RefPtr<Component> & newComponent)809 void SectionStageElement::AddAsOnlyPage(const RefPtr<Component>& newComponent)
810 {
811     if (children_.empty()) {
812         StageElement::PushPage(newComponent);
813     } else {
814         StageElement::Replace(newComponent);
815     }
816 }
817 
818 } // namespace OHOS::Ace
819