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