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