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