• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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_ng/pattern/stage/stage_manager.h"
17 
18 #include "base/geometry/ng/size_t.h"
19 #include "base/memory/referenced.h"
20 #include "base/utils/utils.h"
21 #include "core/animation/page_transition_common.h"
22 #include "core/common/container.h"
23 #include "core/components/common/layout/constants.h"
24 #include "core/components_ng/base/frame_node.h"
25 #include "core/components_ng/base/ui_node.h"
26 #include "core/components_ng/manager/shared_overlay/shared_overlay_manager.h"
27 #include "core/components_ng/pattern/overlay/overlay_manager.h"
28 #include "core/components_ng/pattern/stage/page_pattern.h"
29 #include "core/components_ng/pattern/stage/stage_pattern.h"
30 #include "core/components_ng/property/property.h"
31 #include "core/components_v2/inspector/inspector_constants.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 #include "core/pipeline_ng/ui_task_scheduler.h"
34 
35 namespace OHOS::Ace::NG {
36 
37 namespace {
FirePageTransition(const RefPtr<FrameNode> & page,PageTransitionType transitionType)38 void FirePageTransition(const RefPtr<FrameNode>& page, PageTransitionType transitionType)
39 {
40     CHECK_NULL_VOID(page);
41     auto pagePattern = page->GetPattern<PagePattern>();
42     CHECK_NULL_VOID(pagePattern);
43     page->GetEventHub<EventHub>()->SetEnabled(false);
44     pagePattern->SetPageInTransition(true);
45     if (transitionType == PageTransitionType::EXIT_PUSH || transitionType == PageTransitionType::EXIT_POP) {
46         pagePattern->TriggerPageTransition(
47             transitionType, [weak = WeakPtr<FrameNode>(page), transitionType, instanceId = Container::CurrentId()]() {
48                 ContainerScope scope(instanceId);
49                 LOGI("pageTransition exit finish");
50                 auto page = weak.Upgrade();
51                 CHECK_NULL_VOID(page);
52                 auto context = PipelineContext::GetCurrentContext();
53                 CHECK_NULL_VOID(context);
54                 context->SetIsNeedShowFocus(false);
55                 auto pageFocusHub = page->GetFocusHub();
56                 CHECK_NULL_VOID(pageFocusHub);
57                 pageFocusHub->SetParentFocusable(false);
58                 pageFocusHub->LostFocus();
59                 if (transitionType == PageTransitionType::EXIT_POP && page->GetParent()) {
60                     auto stageNode = page->GetParent();
61                     stageNode->RemoveChild(page);
62                     stageNode->RebuildRenderContextTree();
63                     context->RequestFrame();
64                     return;
65                 }
66                 page->GetEventHub<EventHub>()->SetEnabled(true);
67                 auto pattern = page->GetPattern<PagePattern>();
68                 CHECK_NULL_VOID(pattern);
69                 pattern->SetPageInTransition(false);
70                 pattern->ProcessHideState();
71             });
72         return;
73     }
74     pagePattern->TriggerPageTransition(
75         transitionType, [weak = WeakPtr<FrameNode>(page), instanceId = Container::CurrentId()]() {
76             ContainerScope scope(instanceId);
77             LOGI("pageTransition in finish");
78             auto page = weak.Upgrade();
79             CHECK_NULL_VOID(page);
80             page->GetEventHub<EventHub>()->SetEnabled(true);
81             auto pattern = page->GetPattern<PagePattern>();
82             CHECK_NULL_VOID(pattern);
83             pattern->SetPageInTransition(false);
84 
85             auto pageFocusHub = page->GetFocusHub();
86             CHECK_NULL_VOID(pageFocusHub);
87             pageFocusHub->SetParentFocusable(true);
88             pageFocusHub->RequestFocus();
89             auto context = PipelineContext::GetCurrentContext();
90             CHECK_NULL_VOID(context);
91             context->SetIsNeedShowFocus(false);
92         });
93 }
94 } // namespace
95 
StartTransition(const RefPtr<FrameNode> & srcPage,const RefPtr<FrameNode> & destPage,RouteType type)96 void StageManager::StartTransition(const RefPtr<FrameNode>& srcPage, const RefPtr<FrameNode>& destPage, RouteType type)
97 {
98     auto pipeline = PipelineContext::GetCurrentContext();
99     CHECK_NULL_VOID(pipeline);
100     auto sharedManager = pipeline->GetSharedOverlayManager();
101     CHECK_NULL_VOID(sharedManager);
102     sharedManager->StartSharedTransition(srcPage, destPage);
103     srcPageNode_ = srcPage;
104     destPageNode_ = destPage;
105     if (type == RouteType::PUSH) {
106         FirePageTransition(srcPage, PageTransitionType::EXIT_PUSH);
107         FirePageTransition(destPage, PageTransitionType::ENTER_PUSH);
108     } else if (type == RouteType::POP) {
109         FirePageTransition(srcPage, PageTransitionType::EXIT_POP);
110         FirePageTransition(destPage, PageTransitionType::ENTER_POP);
111     }
112 }
113 
StageManager(const RefPtr<FrameNode> & stage)114 StageManager::StageManager(const RefPtr<FrameNode>& stage) : stageNode_(stage)
115 {
116     CHECK_NULL_VOID(stageNode_);
117     stagePattern_ = DynamicCast<StagePattern>(stageNode_->GetPattern());
118 }
119 
StopPageTransition()120 void StageManager::StopPageTransition()
121 {
122     auto srcNode = srcPageNode_.Upgrade();
123     if (srcNode) {
124         auto pattern = srcNode->GetPattern<PagePattern>();
125         pattern->StopPageTransition();
126         srcPageNode_ = nullptr;
127     }
128     auto destNode = destPageNode_.Upgrade();
129     if (destNode) {
130         auto pattern = destNode->GetPattern<PagePattern>();
131         pattern->StopPageTransition();
132         destPageNode_ = nullptr;
133     }
134 }
135 
PushPage(const RefPtr<FrameNode> & node,bool needHideLast,bool needTransition)136 bool StageManager::PushPage(const RefPtr<FrameNode>& node, bool needHideLast, bool needTransition)
137 {
138     CHECK_NULL_RETURN(stageNode_, false);
139     CHECK_NULL_RETURN(node, false);
140     auto pipeline = AceType::DynamicCast<NG::PipelineContext>(PipelineBase::GetCurrentContext());
141     CHECK_NULL_RETURN(pipeline, false);
142     StopPageTransition();
143 
144     const auto& children = stageNode_->GetChildren();
145     RefPtr<FrameNode> outPageNode;
146     needTransition &= !children.empty();
147     if (needTransition) {
148         pipeline->FlushPipelineImmediately();
149     }
150     if (!children.empty() && needHideLast) {
151         FirePageHide(children.back(), needTransition ? PageTransitionType::EXIT_PUSH : PageTransitionType::NONE);
152         outPageNode = AceType::DynamicCast<FrameNode>(children.back());
153     }
154     auto rect = stageNode_->GetGeometryNode()->GetFrameRect();
155     rect.SetOffset({});
156     node->GetRenderContext()->SyncGeometryProperties(rect);
157     // mount to parent and mark build render tree.
158     node->MountToParent(stageNode_);
159     // then build the total child.
160     node->Build();
161     stageNode_->RebuildRenderContextTree();
162     FirePageShow(node, needTransition ? PageTransitionType::ENTER_PUSH : PageTransitionType::NONE);
163 
164     auto pagePattern = node->GetPattern<PagePattern>();
165     CHECK_NULL_RETURN(pagePattern, false);
166     stagePattern_->currentPageIndex_ = pagePattern->GetPageInfo()->GetPageId();
167     if (needTransition) {
168         pipeline->AddAfterLayoutTask([weakStage = WeakClaim(this), weakIn = WeakPtr<FrameNode>(node),
169                                                weakOut = WeakPtr<FrameNode>(outPageNode)]() {
170             auto stage = weakStage.Upgrade();
171             CHECK_NULL_VOID(stage);
172             auto inPageNode = weakIn.Upgrade();
173             auto outPageNode = weakOut.Upgrade();
174             stage->StartTransition(outPageNode, inPageNode, RouteType::PUSH);
175         });
176     }
177 
178     // flush layout task.
179     if (!stageNode_->GetGeometryNode()->GetMarginFrameSize().IsPositive()) {
180         // in first load case, wait for window size.
181         LOGI("waiting for window size");
182         return true;
183     }
184     stageNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
185     return true;
186 }
187 
PopPage(bool needShowNext,bool needTransition)188 bool StageManager::PopPage(bool needShowNext, bool needTransition)
189 {
190     auto pipeline = PipelineContext::GetCurrentContext();
191     CHECK_NULL_RETURN(pipeline, false);
192     CHECK_NULL_RETURN(stageNode_, false);
193     StopPageTransition();
194     const auto& children = stageNode_->GetChildren();
195     if (children.empty()) {
196         LOGE("fail to pop page due to children is null");
197         return false;
198     }
199     auto pageNode = children.back();
200     const size_t transitionPageSize = 2;
201     needTransition &= (children.size() >= transitionPageSize);
202     if (needTransition) {
203         pipeline->FlushPipelineImmediately();
204     }
205     FirePageHide(pageNode, needTransition ? PageTransitionType::EXIT_POP : PageTransitionType::NONE);
206 
207     RefPtr<FrameNode> inPageNode;
208     if (needShowNext && children.size() >= transitionPageSize) {
209         auto newPageNode = *(++children.rbegin());
210         FirePageShow(newPageNode, needTransition ? PageTransitionType::ENTER_POP : PageTransitionType::NONE);
211         inPageNode = AceType::DynamicCast<FrameNode>(newPageNode);
212     }
213 
214     auto outPageNode = AceType::DynamicCast<FrameNode>(pageNode);
215     if (needTransition) {
216         StartTransition(outPageNode, inPageNode, RouteType::POP);
217         return true;
218     }
219     stageNode_->RemoveChild(pageNode);
220     pageNode->SetChildrenInDestroying();
221     stageNode_->RebuildRenderContextTree();
222     pipeline->RequestFrame();
223     return true;
224 }
225 
PopPageToIndex(int32_t index,bool needShowNext,bool needTransition)226 bool StageManager::PopPageToIndex(int32_t index, bool needShowNext, bool needTransition)
227 {
228     auto pipeline = PipelineContext::GetCurrentContext();
229     CHECK_NULL_RETURN(pipeline, false);
230     CHECK_NULL_RETURN(stageNode_, false);
231     StopPageTransition();
232     const auto& children = stageNode_->GetChildren();
233     if (children.empty()) {
234         LOGE("fail to pop page due to children is null");
235         return false;
236     }
237     int32_t popSize = static_cast<int32_t>(children.size()) - index - 1;
238     if (popSize < 0) {
239         LOGE("fail to pop page due to index is out of range");
240         return false;
241     }
242     if (popSize == 0) {
243         LOGD("already here");
244         return true;
245     }
246 
247     if (needTransition) {
248         pipeline->FlushPipelineImmediately();
249     }
250     bool firstPageTransition = true;
251     auto outPageNode = AceType::DynamicCast<FrameNode>(children.back());
252     auto iter = children.rbegin();
253     for (int32_t current = 0; current < popSize; ++current) {
254         auto pageNode = *iter;
255         FirePageHide(
256             pageNode, firstPageTransition && needTransition ? PageTransitionType::EXIT_POP : PageTransitionType::NONE);
257         firstPageTransition = false;
258         ++iter;
259     }
260 
261     RefPtr<FrameNode> inPageNode;
262     if (needShowNext) {
263         const auto& newPageNode = *iter;
264         FirePageShow(newPageNode, needTransition ? PageTransitionType::ENTER_POP : PageTransitionType::NONE);
265         inPageNode = AceType::DynamicCast<FrameNode>(newPageNode);
266     }
267 
268     if (needTransition) {
269         // from the penultimate node, (popSize - 1) nodes are deleted.
270         // the last node will be deleted after pageTransition
271         LOGI("PopPageToIndex, before pageTransition, to index:%{public}d, children size:%{public}zu, "
272              "stage children size:%{public}zu",
273             index, children.size(), stageNode_->GetChildren().size());
274         for (int32_t current = 1; current < popSize; ++current) {
275             auto pageNode = *(++children.rbegin());
276             stageNode_->RemoveChild(pageNode);
277         }
278         stageNode_->RebuildRenderContextTree();
279         StartTransition(outPageNode, inPageNode, RouteType::POP);
280         return true;
281     }
282     for (int32_t current = 0; current < popSize; ++current) {
283         auto pageNode = children.back();
284         stageNode_->RemoveChild(pageNode);
285     }
286     stageNode_->RebuildRenderContextTree();
287     pipeline->RequestFrame();
288     return true;
289 }
290 
CleanPageStack()291 bool StageManager::CleanPageStack()
292 {
293     auto pipeline = PipelineContext::GetCurrentContext();
294     CHECK_NULL_RETURN(pipeline, false);
295     CHECK_NULL_RETURN(stageNode_, false);
296     const auto& children = stageNode_->GetChildren();
297     if (children.size() <= 1) {
298         LOGE("fail to clean page stack due to children size is illegal");
299         return false;
300     }
301     auto popSize = static_cast<int32_t>(children.size() - 1);
302     for (int32_t count = 1; count <= popSize; ++count) {
303         auto pageNode = children.front();
304         // mark pageNode child as destroying
305         pageNode->SetChildrenInDestroying();
306         stageNode_->RemoveChild(pageNode);
307     }
308     stageNode_->RebuildRenderContextTree();
309     pipeline->RequestFrame();
310     return true;
311 }
312 
MovePageToFront(const RefPtr<FrameNode> & node,bool needHideLast,bool needTransition)313 bool StageManager::MovePageToFront(const RefPtr<FrameNode>& node, bool needHideLast, bool needTransition)
314 {
315     auto pipeline = PipelineContext::GetCurrentContext();
316     CHECK_NULL_RETURN(pipeline, false);
317     CHECK_NULL_RETURN(stageNode_, false);
318     StopPageTransition();
319     const auto& children = stageNode_->GetChildren();
320     if (children.empty()) {
321         LOGE("child is empty");
322         return false;
323     }
324     const auto& lastPage = children.back();
325     if (lastPage == node) {
326         LOGD("page already on the top");
327         return true;
328     }
329     if (needTransition) {
330         pipeline->FlushPipelineImmediately();
331     }
332     if (needHideLast) {
333         FirePageHide(lastPage, needTransition ? PageTransitionType::EXIT_PUSH : PageTransitionType::NONE);
334     }
335     node->MovePosition(static_cast<int32_t>(stageNode_->GetChildren().size() - 1));
336     node->GetRenderContext()->ResetPageTransitionEffect();
337     FirePageShow(node, needTransition ? PageTransitionType::ENTER_PUSH : PageTransitionType::NONE);
338 
339     stageNode_->RebuildRenderContextTree();
340     if (needTransition) {
341         auto outPageNode = AceType::DynamicCast<FrameNode>(lastPage);
342         StartTransition(outPageNode, node, RouteType::PUSH);
343     }
344     pipeline->RequestFrame();
345     return true;
346 }
347 
FirePageHide(const RefPtr<UINode> & node,PageTransitionType transitionType)348 void StageManager::FirePageHide(const RefPtr<UINode>& node, PageTransitionType transitionType)
349 {
350     auto pageNode = DynamicCast<FrameNode>(node);
351     CHECK_NULL_VOID(pageNode);
352     auto pagePattern = pageNode->GetPattern<PagePattern>();
353     CHECK_NULL_VOID(pagePattern);
354     pagePattern->OnHide();
355     if (transitionType == PageTransitionType::NONE) {
356         // If there is a page transition, this function should execute after page transition,
357         // otherwise the page will not be visible
358         pagePattern->ProcessHideState();
359     }
360 
361     auto pageFocusHub = pageNode->GetFocusHub();
362     CHECK_NULL_VOID(pageFocusHub);
363     pageFocusHub->SetParentFocusable(false);
364 
365     auto context = PipelineContext::GetCurrentContext();
366     CHECK_NULL_VOID_NOLOG(context);
367     context->SetIsNeedShowFocus(false);
368 }
369 
FirePageShow(const RefPtr<UINode> & node,PageTransitionType transitionType)370 void StageManager::FirePageShow(const RefPtr<UINode>& node, PageTransitionType transitionType)
371 {
372     auto pageNode = DynamicCast<FrameNode>(node);
373     CHECK_NULL_VOID(pageNode);
374     auto pagePattern = pageNode->GetPattern<PagePattern>();
375     CHECK_NULL_VOID(pagePattern);
376     pagePattern->OnShow();
377     // With or without a page transition, we need to make the coming page visible first
378     pagePattern->ProcessShowState();
379 
380     auto pageFocusHub = pageNode->GetFocusHub();
381     CHECK_NULL_VOID(pageFocusHub);
382     pageFocusHub->SetParentFocusable(true);
383     pageFocusHub->RequestFocus();
384 
385     auto context = PipelineContext::GetCurrentContext();
386     CHECK_NULL_VOID_NOLOG(context);
387     context->SetIsNeedShowFocus(false);
388 }
389 
GetLastPage()390 RefPtr<FrameNode> StageManager::GetLastPage()
391 {
392     CHECK_NULL_RETURN(stageNode_, nullptr);
393     const auto& children = stageNode_->GetChildren();
394     if (children.empty()) {
395         LOGE("fail to return page due to children is null");
396         return nullptr;
397     }
398     return DynamicCast<FrameNode>(children.back());
399 }
400 
ReloadStage()401 void StageManager::ReloadStage()
402 {
403     CHECK_NULL_VOID(stageNode_);
404     const auto& children = stageNode_->GetChildren();
405     for (const auto& child : children) {
406         auto frameNode = DynamicCast<FrameNode>(child);
407         if (!frameNode) {
408             continue;
409         }
410         auto pagePattern = frameNode->GetPattern<PagePattern>();
411         if (!pagePattern) {
412             continue;
413         }
414         pagePattern->ReloadPage();
415     }
416 }
417 
418 } // namespace OHOS::Ace::NG
419