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