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/page_pattern.h"
17
18 #include "base/utils/utils.h"
19 #include "core/animation/animator.h"
20 #include "core/components/common/properties/alignment.h"
21 #include "core/pipeline_ng/pipeline_context.h"
22
23 namespace OHOS::Ace::NG {
24
25 namespace {
IterativeAddToSharedMap(const RefPtr<UINode> & node,SharedTransitionMap & map)26 void IterativeAddToSharedMap(const RefPtr<UINode>& node, SharedTransitionMap& map)
27 {
28 const auto& children = node->GetChildren();
29 for (const auto& child : children) {
30 auto frameChild = AceType::DynamicCast<FrameNode>(child);
31 if (!frameChild) {
32 IterativeAddToSharedMap(child, map);
33 continue;
34 }
35 auto id = frameChild->GetRenderContext()->GetShareId();
36 if (!id.empty()) {
37 LOGD("add id:%{public}s", id.c_str());
38 map[id] = frameChild;
39 }
40 IterativeAddToSharedMap(frameChild, map);
41 }
42 }
43 } // namespace
44
OnAttachToFrameNode()45 void PagePattern::OnAttachToFrameNode()
46 {
47 auto host = GetHost();
48 CHECK_NULL_VOID(host);
49 host->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
50 host->GetLayoutProperty()->UpdateAlignment(Alignment::TOP_LEFT);
51 }
52
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> &,const DirtySwapConfig &)53 bool PagePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& /*wrapper*/, const DirtySwapConfig& /*config*/)
54 {
55 if (isFirstLoad_) {
56 isFirstLoad_ = false;
57 if (firstBuildCallback_) {
58 firstBuildCallback_();
59 firstBuildCallback_ = nullptr;
60 }
61 }
62 return false;
63 }
64
TriggerPageTransition(PageTransitionType type,const std::function<void ()> & onFinish)65 bool PagePattern::TriggerPageTransition(PageTransitionType type, const std::function<void()>& onFinish)
66 {
67 auto host = GetHost();
68 CHECK_NULL_RETURN(host, false);
69 auto renderContext = host->GetRenderContext();
70 CHECK_NULL_RETURN(renderContext, false);
71 if (pageTransitionFunc_) {
72 pageTransitionFunc_();
73 }
74 auto effect = FindPageTransitionEffect(type);
75 pageTransitionFinish_ = std::make_shared<std::function<void()>>(onFinish);
76 auto wrappedOnFinish = [weak = WeakClaim(this), sharedFinish = pageTransitionFinish_]() {
77 auto pattern = weak.Upgrade();
78 CHECK_NULL_VOID_NOLOG(pattern);
79 if (sharedFinish == pattern->pageTransitionFinish_) {
80 // ensure this is exactly the finish callback saved in pagePattern,
81 // otherwise means new pageTransition started
82 pattern->FirePageTransitionFinish();
83 }
84 };
85 if (effect && effect->GetUserCallback()) {
86 if (!controller_) {
87 controller_ = AceType::MakeRefPtr<Animator>(PipelineContext::GetCurrentContext());
88 }
89 if (!controller_->IsStopped()) {
90 controller_->Finish();
91 }
92 controller_->ClearInterpolators();
93 RouteType routeType = (type == PageTransitionType::ENTER_POP || type == PageTransitionType::EXIT_POP)
94 ? RouteType::POP
95 : RouteType::PUSH;
96 auto floatAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 1.0f, effect->GetCurve());
97 floatAnimation->AddListener(
98 [routeType, handler = effect->GetUserCallback(), weak = WeakClaim(this)](const float& progress) {
99 auto pattern = weak.Upgrade();
100 CHECK_NULL_VOID(pattern);
101 handler(routeType, progress);
102 });
103 if (effect->GetDelay() >= 0) {
104 controller_->SetStartDelay(effect->GetDelay());
105 }
106 controller_->SetDuration(effect->GetDuration());
107 controller_->AddInterpolator(floatAnimation);
108 controller_->AddStopListener(wrappedOnFinish);
109 controller_->Forward();
110 return renderContext->TriggerPageTransition(type, nullptr);
111 }
112 return renderContext->TriggerPageTransition(type, wrappedOnFinish);
113 }
114
ProcessHideState()115 void PagePattern::ProcessHideState()
116 {
117 auto host = GetHost();
118 CHECK_NULL_VOID(host);
119 host->SetActive(false);
120 host->OnVisibleChange(false);
121 host->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
122 auto parent = host->GetAncestorNodeOfFrame();
123 CHECK_NULL_VOID(parent);
124 parent->MarkNeedSyncRenderTree();
125 parent->RebuildRenderContextTree();
126 }
127
ProcessShowState()128 void PagePattern::ProcessShowState()
129 {
130 auto host = GetHost();
131 CHECK_NULL_VOID(host);
132 host->SetActive(true);
133 host->OnVisibleChange(true);
134 host->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
135 auto parent = host->GetAncestorNodeOfFrame();
136 CHECK_NULL_VOID(parent);
137 parent->MarkNeedSyncRenderTree();
138 parent->RebuildRenderContextTree();
139 }
140
OnShow()141 void PagePattern::OnShow()
142 {
143 // Do not invoke onPageShow unless the initialRender function has been executed.
144 CHECK_NULL_VOID_NOLOG(isRenderDone_);
145 CHECK_NULL_VOID_NOLOG(!isOnShow_);
146 isOnShow_ = true;
147 auto context = PipelineContext::GetCurrentContext();
148 CHECK_NULL_VOID(context);
149 if (onPageShow_) {
150 context->PostAsyncEvent([onPageShow = onPageShow_]() { onPageShow(); });
151 }
152 }
153
OnHide()154 void PagePattern::OnHide()
155 {
156 CHECK_NULL_VOID_NOLOG(isOnShow_);
157 isOnShow_ = false;
158 auto context = PipelineContext::GetCurrentContext();
159 CHECK_NULL_VOID(context);
160 if (onPageHide_) {
161 context->PostAsyncEvent([onPageHide = onPageHide_]() { onPageHide(); });
162 }
163 }
164
BuildSharedTransitionMap()165 void PagePattern::BuildSharedTransitionMap()
166 {
167 auto host = GetHost();
168 CHECK_NULL_VOID(host);
169 sharedTransitionMap_.clear();
170 IterativeAddToSharedMap(host, sharedTransitionMap_);
171 }
172
ReloadPage()173 void PagePattern::ReloadPage()
174 {
175 auto host = GetHost();
176 CHECK_NULL_VOID(host);
177 auto customNode = DynamicCast<CustomNodeBase>(host->GetFirstChild());
178 CHECK_NULL_VOID(customNode);
179 customNode->FireReloadFunction(true);
180 }
181
FindPageTransitionEffect(PageTransitionType type)182 RefPtr<PageTransitionEffect> PagePattern::FindPageTransitionEffect(PageTransitionType type)
183 {
184 RefPtr<PageTransitionEffect> result;
185 for (auto iter = pageTransitionEffects_.rbegin(); iter != pageTransitionEffects_.rend(); ++iter) {
186 auto effect = *iter;
187 if (effect->CanFit(type)) {
188 result = effect;
189 break;
190 }
191 }
192 return result;
193 }
194
ClearPageTransitionEffect()195 void PagePattern::ClearPageTransitionEffect()
196 {
197 pageTransitionEffects_.clear();
198 }
199
GetTopTransition() const200 RefPtr<PageTransitionEffect> PagePattern::GetTopTransition() const
201 {
202 return pageTransitionEffects_.empty() ? nullptr : pageTransitionEffects_.back();
203 }
204
AddPageTransition(const RefPtr<PageTransitionEffect> & effect)205 void PagePattern::AddPageTransition(const RefPtr<PageTransitionEffect>& effect)
206 {
207 pageTransitionEffects_.emplace_back(effect);
208 }
209
AddJsAnimator(const std::string & animatorId,const RefPtr<Framework::AnimatorInfo> & animatorInfo)210 void PagePattern::AddJsAnimator(const std::string& animatorId, const RefPtr<Framework::AnimatorInfo>& animatorInfo)
211 {
212 CHECK_NULL_VOID(animatorInfo);
213 auto animator = animatorInfo->GetAnimator();
214 CHECK_NULL_VOID(animator);
215 animator->AttachScheduler(PipelineContext::GetCurrentContext());
216 jsAnimatorMap_[animatorId] = animatorInfo;
217 }
218
GetJsAnimator(const std::string & animatorId)219 RefPtr<Framework::AnimatorInfo> PagePattern::GetJsAnimator(const std::string& animatorId)
220 {
221 auto iter = jsAnimatorMap_.find(animatorId);
222 if (iter != jsAnimatorMap_.end()) {
223 return iter->second;
224 }
225 return nullptr;
226 }
227
SetFirstBuildCallback(std::function<void ()> && buildCallback)228 void PagePattern::SetFirstBuildCallback(std::function<void()>&& buildCallback)
229 {
230 if (isFirstLoad_) {
231 firstBuildCallback_ = std::move(buildCallback);
232 } else if (buildCallback) {
233 buildCallback();
234 }
235 }
236
FirePageTransitionFinish()237 void PagePattern::FirePageTransitionFinish()
238 {
239 if (pageTransitionFinish_) {
240 auto onFinish = *pageTransitionFinish_;
241 pageTransitionFinish_ = nullptr;
242 if (onFinish) {
243 onFinish();
244 }
245 }
246 }
247
StopPageTransition()248 void PagePattern::StopPageTransition()
249 {
250 if (controller_ && !controller_->IsStopped()) {
251 controller_->Finish();
252 return;
253 }
254 FirePageTransitionFinish();
255 }
256
257 } // namespace OHOS::Ace::NG