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