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