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