1 /*
2 * Copyright (c) 2021 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/shared_transition/shared_transition_element.h"
17
18 #include "core/components/display/render_display.h"
19 #include "core/components/page/page_element.h"
20
21 namespace OHOS::Ace {
22 namespace {
23
24 constexpr uint8_t MAX_OPACITY = 255;
25 constexpr uint8_t MIN_OPACITY = 0;
26
27 } // namespace
28
~SharedTransitionElement()29 SharedTransitionElement::~SharedTransitionElement()
30 {
31 auto page = pageElement_.Upgrade();
32 if (!page) {
33 return;
34 }
35
36 // shared transition may be updated by other element
37 auto& sharedTransitionMap = page->GetSharedTransitionMap();
38 auto pos = sharedTransitionMap.find(shareId_);
39 if (pos != sharedTransitionMap.end()) {
40 auto ptr = pos->second.Upgrade();
41 if (ptr == this) {
42 page->RemoveSharedTransition(shareId_);
43 }
44 }
45 }
46
Update()47 void SharedTransitionElement::Update()
48 {
49 ComposedElement::Update();
50 const auto sharedComponent = AceType::DynamicCast<SharedTransitionComponent>(component_);
51 if (!sharedComponent) {
52 LOGE("Get SharedComponent failed. id: %{public}s", GetId().c_str());
53 return;
54 }
55 option_ = sharedComponent->GetOption();
56 oldShareId_ = shareId_;
57 shareId_ = sharedComponent->GetShareId();
58 effect_ = sharedComponent->GetEffect();
59 enablePopEnter_ = sharedComponent->IsEnablePopEnter();
60 enablePopExit_ = sharedComponent->IsEnablePopExit();
61 enablePushEnter_ = sharedComponent->IsEnablePushEnter();
62 enablePushExit_ = sharedComponent->IsEnablePushExit();
63 opacity_ = sharedComponent->GetOpacity();
64 zIndex_ = sharedComponent->GetZIndex();
65 }
66
GetRenderPassengerWithPajamas() const67 RefPtr<RenderBox> SharedTransitionElement::GetRenderPassengerWithPajamas() const
68 {
69 if (children_.empty()) {
70 LOGE("Get render passenger failed. no child. id: %{public}s", GetId().c_str());
71 return nullptr;
72 }
73 const auto& passenger = AceType::DynamicCast<BoxElement>(GetContentElement());
74 if (!passenger) {
75 LOGE("Get render passenger failed. passenger is null. id: %{public}s", GetId().c_str());
76 return nullptr;
77 }
78 return AceType::DynamicCast<RenderBox>(passenger->GetRenderNode());
79 }
80
GetRenderBox()81 RefPtr<RenderBox> SharedTransitionElement::GetRenderBox()
82 {
83 const auto& parent = GetElementParent().Upgrade();
84 if (!parent) {
85 LOGE("GetRenderBox failed, parent is nullptr");
86 return nullptr;
87 }
88
89 auto box = AceType::DynamicCast<BoxElement>(parent);
90 if (!box) {
91 LOGE("GetRenderBox failed, parent is not box");
92 return nullptr;
93 }
94
95 return AceType::DynamicCast<RenderBox>(box->GetRenderNode());
96 }
97
GetSuitSize() const98 Size SharedTransitionElement::GetSuitSize() const
99 {
100 const auto& renderBox = GetRenderPassengerWithPajamas();
101 if (!renderBox) {
102 LOGE("Get layout size failed. render box not found. id: %{public}s", GetId().c_str());
103 return Size();
104 }
105 return renderBox->GetLayoutSize() - renderBox->GetMarginSize();
106 }
107
GetGlobalOffset() const108 Offset SharedTransitionElement::GetGlobalOffset() const
109 {
110 const auto& renderBox = GetRenderPassengerWithPajamas();
111 if (!renderBox) {
112 LOGE("Get Global offset failed. render box not found. id: %{public}s", GetId().c_str());
113 return Offset();
114 }
115 return renderBox->GetGlobalOffset();
116 }
117
GetOpacity() const118 float SharedTransitionElement::GetOpacity() const
119 {
120 return opacity_;
121 }
122
GetZIndex() const123 int32_t SharedTransitionElement::GetZIndex() const
124 {
125 return zIndex_;
126 }
127
AboardShuttle(Offset & ticket)128 bool SharedTransitionElement::AboardShuttle(Offset& ticket)
129 {
130 if (!passengerComponent_ || passengerElement_) {
131 LOGE("Aboard Shuttle Component failed. passenger not init or already aboard. id: %{public}s", GetId().c_str());
132 return false;
133 }
134 auto passenger = GetRenderPassengerWithPajamas();
135 if (!passenger) {
136 LOGE("Aboard Shuttle Component failed. render passenger is null. id: %{public}s", GetId().c_str());
137 return false;
138 }
139 const auto& renderBox = AceType::DynamicCast<RenderBox>(passenger);
140 if (!renderBox) {
141 LOGE("Aboard Shuttle Component failed. render passenger do not have pajamas. id: %{public}s", GetId().c_str());
142 return false;
143 }
144 // save origin width/height and recover it when GetOffShuttle
145 passengerWidth_ = passengerComponent_->GetWidthDimension();
146 passengerHeight_ = passengerComponent_->GetHeightDimension();
147 auto suitSize = GetSuitSize();
148 passengerComponent_->SetWidth(suitSize.Width());
149 passengerComponent_->SetHeight(suitSize.Height());
150
151 auto parent = GetRenderBox();
152 if (parent && parent->GetBackDecoration() != nullptr) {
153 passengerComponent_->SetBackDecoration(parent->GetBackDecoration());
154 }
155 if (parent && parent->GetFrontDecoration() != nullptr) {
156 passengerComponent_->SetFrontDecoration(parent->GetFrontDecoration());
157 }
158
159 passengerElement_ = AceType::DynamicCast<BoxElement>(GetContentElement());
160 if (!passengerElement_) {
161 LOGE("Aboard Shuttle Element failed. passenger element is null.");
162 return false;
163 }
164 // update the ticket(initial offset of the sharedTransition element) earlier
165 ticket = renderBox->GetOffsetToStage();
166 // passenger goes out and comes to the shuttle port
167 // check first child not null when check passenger. no need check again here.
168 GetFirstChild()->UpdateChild(passengerElement_, nullptr);
169 passengerRender_ = passenger;
170 passengerElement_->SetRenderNode(passengerRender_);
171 auto placeHolder = AceType::MakeRefPtr<BoxComponent>();
172 // passenger measures the size
173 auto paintRect = renderBox->GetLayoutSize();
174 placeHolder->SetHeight(paintRect.Height());
175 placeHolder->SetWidth(paintRect.Width());
176 // check first child not null when check passenger. no need check again here.
177 GetFirstChild()->UpdateChild(nullptr, placeHolder);
178 return true;
179 }
180
GetOffShuttle()181 void SharedTransitionElement::GetOffShuttle()
182 {
183 auto placeHolder = GetContentElement();
184 if (!passengerElement_ || !passengerRender_ || !placeHolder) {
185 LOGE("GetOff Shuttle failed. Passenger already Get Off or place holder is null.");
186 return;
187 }
188 if (placeHolder == passengerElement_) {
189 LOGI("Passenger already takeoff shuttle. do nothing.");
190 return;
191 }
192 // check first child not null when check placeHolder. no need check again here.
193 auto placeHolderRender = placeHolder->GetRenderNode();
194 bool placeHolderHidden = placeHolderRender ? placeHolderRender->GetHidden() : true;
195 GetFirstChild()->UpdateChild(placeHolder, nullptr);
196 auto parent = passengerElement_->GetElementParent().Upgrade();
197 if (parent) {
198 // Remove passenger from overlay.
199 parent->UpdateChild(passengerElement_, nullptr);
200 }
201
202 // save origin width/height in AboardShuttle and recover it when GetOffShuttle
203 passengerComponent_->SetWidth(passengerWidth_.Value(), passengerWidth_.Unit());
204 passengerComponent_->SetHeight(passengerHeight_.Value(), passengerHeight_.Unit());
205 passengerComponent_->SetBackDecoration(nullptr);
206 passengerComponent_->SetFrontDecoration(nullptr);
207 passengerElement_->SetRenderNode(passengerRender_);
208 passengerElement_->SetNewComponent(passengerComponent_);
209 passengerElement_->Mount(GetFirstChild());
210 // Follow place holder's hidden status.
211 passengerRender_->SetHidden(placeHolderHidden);
212 // Clear RefPtr.
213 passengerRender_.Reset();
214 passengerElement_.Reset();
215 }
216
SetVisible(bool visible)217 void SharedTransitionElement::SetVisible(bool visible)
218 {
219 auto displayElement = DynamicCast<DisplayElement>(GetFirstChild());
220 if (!displayElement) {
221 LOGE("Set visible failed. display element is null. id: %{public}s", GetId().c_str());
222 return;
223 }
224 auto displayRender = DynamicCast<RenderDisplay>(displayElement->GetRenderNode());
225 if (!displayRender) {
226 LOGE("Set visible failed. display render is null. id: %{public}s", GetId().c_str());
227 return;
228 }
229 if (visible) {
230 displayRender->UpdateOpacity(MAX_OPACITY);
231 } else {
232 displayRender->UpdateOpacity(MIN_OPACITY);
233 }
234 }
235
PerformBuild()236 void SharedTransitionElement::PerformBuild()
237 {
238 Register();
239 ComposedElement::PerformBuild();
240 }
241
BuildChild()242 RefPtr<Component> SharedTransitionElement::BuildChild()
243 {
244 auto shared = AceType::DynamicCast<SharedTransitionComponent>(component_);
245 if (shared) {
246 auto passengerComponent = ComposedElement::BuildChild();
247 passengerComponent_ = AceType::DynamicCast<BoxComponent>(passengerComponent);
248 if (!passengerComponent_) {
249 passengerComponent_ = AceType::MakeRefPtr<BoxComponent>();
250 passengerComponent_->SetChild(passengerComponent);
251 }
252 auto displayComponent_ = AceType::MakeRefPtr<DisplayComponent>();
253 if (passengerElement_) {
254 displayComponent_->SetChild(AceType::MakeRefPtr<BoxComponent>());
255 } else {
256 displayComponent_->SetChild(passengerComponent_);
257 }
258 Component::MergeRSNode(displayComponent_->GetChild(), displayComponent_);
259 return displayComponent_;
260 } else {
261 LOGE("Build child failed. no shared transition component found. id: %{public}s", GetId().c_str());
262 return ComposedElement::BuildChild();
263 }
264 }
265
Register()266 void SharedTransitionElement::Register()
267 {
268 auto page = SearchParentPage();
269 if (!page) {
270 LOGE("No parent page found.");
271 return;
272 }
273 pageElement_ = page;
274 if (!oldShareId_.empty()) {
275 auto& sharedTransitionElementMap = page->GetSharedTransitionMap();
276 auto oldSharedIter = sharedTransitionElementMap.find(oldShareId_);
277 if (oldSharedIter != sharedTransitionElementMap.end()) {
278 auto oldWeak = oldSharedIter->second;
279 auto oldShared = oldWeak.Upgrade();
280 if (oldShared == this) {
281 page->RemoveSharedTransition(oldShareId_);
282 }
283 }
284 }
285 page->AddSharedTransition(AceType::Claim(this));
286 }
287
SearchParentPage() const288 RefPtr<PageElement> SharedTransitionElement::SearchParentPage() const
289 {
290 auto parent = GetElementParent().Upgrade();
291 while (parent) {
292 auto page = AceType::DynamicCast<PageElement>(parent);
293 if (page) {
294 return page;
295 }
296 parent = parent->GetElementParent().Upgrade();
297 }
298 return nullptr;
299 }
300
GetShareId() const301 const ShareId& SharedTransitionElement::GetShareId() const
302 {
303 return shareId_;
304 }
305
GetEffect() const306 const RefPtr<SharedTransitionEffect>& SharedTransitionElement::GetEffect() const
307 {
308 return effect_;
309 }
310
IsEnablePopEnter() const311 bool SharedTransitionElement::IsEnablePopEnter() const
312 {
313 return enablePopEnter_;
314 }
315
IsEnablePushEnter() const316 bool SharedTransitionElement::IsEnablePushEnter() const
317 {
318 return enablePushEnter_;
319 }
320
IsEnablePopExit() const321 bool SharedTransitionElement::IsEnablePopExit() const
322 {
323 return enablePopExit_;
324 }
325
IsEnablePushExit() const326 bool SharedTransitionElement::IsEnablePushExit() const
327 {
328 return enablePushExit_;
329 }
330
GetContentElement() const331 RefPtr<Element> SharedTransitionElement::GetContentElement() const
332 {
333 auto display = GetFirstChild();
334 if (!display) {
335 LOGE("Get Content Element failed. display is null.");
336 return nullptr;
337 }
338 return display->GetFirstChild();
339 }
340
SetSizeModified(SizeModifiedCallback && sizeModifiedCallback)341 void SharedTransitionElement::SetSizeModified(SizeModifiedCallback&& sizeModifiedCallback)
342 {
343 auto boxBaseElement = DynamicCast<BoxBaseElement>(GetContentElement());
344 if (!boxBaseElement) {
345 LOGE("Set Size Modified failed. image element not found.");
346 return;
347 }
348 auto boxBaseRender = DynamicCast<RenderBoxBase>(boxBaseElement->GetRenderNode());
349 if (!boxBaseRender) {
350 LOGE("Set Size Modified failed. image render not found.");
351 return;
352 }
353 sizeModifiedCallback_ = sizeModifiedCallback;
354 if (!sizeModifiedCallback_) {
355 boxBaseRender->SetLayoutCallback(nullptr);
356 return;
357 }
358 boxBaseRender->SetLayoutCallback([sharedWeak = AceType::WeakClaim(this)]() {
359 auto shared = sharedWeak.Upgrade();
360 if (!shared) {
361 LOGE("Layout callback is failed. shared is null");
362 return;
363 }
364 auto context = shared->context_.Upgrade();
365 if (!context) {
366 LOGE("Layout callback is failed. context is null");
367 return;
368 }
369 // Re-create shared transition after paint.
370 context->AddPostFlushListener(shared);
371 });
372 }
373
OnPostFlush()374 void SharedTransitionElement::OnPostFlush()
375 {
376 if (sizeModifiedCallback_) {
377 sizeModifiedCallback_();
378 }
379 }
380
381 } // namespace OHOS::Ace
382