1 /*
2  * Copyright (c) 2021-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/text_overlay/text_overlay_element.h"
17 
18 #include "core/components/focus_collaboration/focus_collaboration_element.h"
19 #include "core/components/text_overlay/render_text_overlay.h"
20 #include "core/components/text_overlay/text_overlay_component.h"
21 
22 namespace OHOS::Ace {
23 
PerformBuild()24 void TextOverlayElement::PerformBuild()
25 {
26     overlayComponent_ = AceType::DynamicCast<TextOverlayComponent>(component_);
27     if (!overlayComponent_) {
28         return;
29     }
30     const auto& child = children_.empty() ? nullptr : children_.front();
31     UpdateChild(child, overlayComponent_->BuildChild(overlayComponent_->GetIsSingleHandle(),
32         !overlayComponent_->GetIsSingleHandle(), false, !overlayComponent_->GetIsSingleHandle()));
33 
34     auto renderTextOverlay = AceType::DynamicCast<RenderTextOverlay>(renderNode_);
35     if (!renderTextOverlay) {
36         return;
37     }
38     auto callback = [weak = WeakClaim(this)](
39                         bool isSingleHandle, bool hasToolBar, bool hasMenu, bool hasIcon, bool hasAnimation) {
40         auto overlay = weak.Upgrade();
41         if (overlay) {
42             overlay->RebuildChild(isSingleHandle, hasToolBar, hasMenu, hasIcon, hasAnimation);
43         }
44     };
45     renderTextOverlay->SetOnRebuild(callback);
46 
47     auto startAnimationCallback = [weak = WeakClaim(this)](const TweenOption& outerOption,
48                                       const TweenOption& innerOption, bool isSingleHandle, bool isIn) {
49         auto overlay = weak.Upgrade();
50         if (overlay) {
51             overlay->OnStartAnimation(outerOption, innerOption, isSingleHandle, isIn);
52         }
53     };
54     renderTextOverlay->SetStartAnimationCallback(startAnimationCallback);
55 }
56 
OnFocus()57 void TextOverlayElement::OnFocus()
58 {
59     auto renderOverlay = AceType::DynamicCast<RenderTextOverlay>(renderNode_);
60     if (renderOverlay) {
61         renderOverlay->OnFocusChange(RenderStatus::FOCUS);
62     }
63 }
64 
OnBlur()65 void TextOverlayElement::OnBlur()
66 {
67     auto renderOverlay = AceType::DynamicCast<RenderTextOverlay>(renderNode_);
68     if (renderOverlay) {
69         renderOverlay->OnFocusChange(RenderStatus::BLUR);
70     }
71 }
72 
RebuildChild(bool isSingleHandle,bool hasToolBar,bool hasMenu,bool hasIcon,bool hasAnimation,bool buildToolBarOnly)73 void TextOverlayElement::RebuildChild(
74     bool isSingleHandle, bool hasToolBar, bool hasMenu, bool hasIcon, bool hasAnimation, bool buildToolBarOnly)
75 {
76     if (!overlayComponent_) {
77         return;
78     }
79     auto renderOverlay = AceType::DynamicCast<RenderTextOverlay>(renderNode_);
80     if (!renderOverlay) {
81         return;
82     }
83 
84     if (!buildToolBarOnly) {
85         const auto& child = children_.empty() ? nullptr : children_.front();
86         UpdateChild(child, overlayComponent_->BuildChild(isSingleHandle, hasToolBar, hasMenu, hasIcon, hasAnimation));
87         renderOverlay->MarkNeedLayout();
88         return;
89     }
90     if (GetChildren().front()) {
91         auto columnElement = GetChildren().front()->GetChildren().front();
92         if (!columnElement) {
93             return;
94         }
95         columnElement->UpdateChild(columnElement->GetFirstChild(),
96             overlayComponent_->BuildToolBar(isSingleHandle, hasToolBar, hasMenu, hasIcon, hasAnimation));
97         if (!columnElement->GetFirstChild()) {
98             return;
99         }
100         auto renderBox = columnElement->GetFirstChild()->GetRenderNode();
101         if (renderBox) {
102             renderBox->MarkNeedLayout(true, false);
103         }
104     }
105 }
106 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)107 bool TextOverlayElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
108 {
109     return false;
110 }
111 
OnStartAnimation(const TweenOption & outerOption,const TweenOption & innerOption,bool isSingleHandle,bool isIn)112 void TextOverlayElement::OnStartAnimation(
113     const TweenOption& outerOption, const TweenOption& innerOption, bool isSingleHandle, bool isIn)
114 {
115     InitTween(AceType::Claim(this));
116 
117     // Start animation of outer tween.
118     if (outerTween_) {
119         AddListener(isSingleHandle, isIn);
120         outerTween_->SetOption(outerOption);
121         outerTween_->ApplyOptions();
122         outerTween_->ApplyKeyframes();
123         auto controller = outerTween_->GetController();
124         if (controller) {
125             controller->Play();
126         }
127     }
128 
129     // Start animation of inner tween.
130     if (innerTween_) {
131         innerTween_->SetOption(innerOption);
132         innerTween_->ApplyOptions();
133         innerTween_->ApplyKeyframes();
134         if (innerTween_->GetController()) {
135             innerTween_->GetController()->Play();
136         }
137     }
138 
139     // Show animation of menu is auto play, but hide animation should manual start.
140     if (!isIn || !overlayComponent_) {
141         return;
142     }
143     auto menu = overlayComponent_->GetMenu();
144     if (menu) {
145         auto menuAnimationCallback = menu->GetRefreshAnimationCallback();
146         auto menuController = menu->GetAnimationController();
147         if (menuAnimationCallback && menuController) {
148             auto hideOption = menu->GetHideOption();
149             hideOption.SetFillMode(FillMode::FORWARDS);
150             menuAnimationCallback(hideOption, false);
151             menuController->Play();
152         }
153     }
154 }
155 
InitTween(const RefPtr<Element> & element)156 void TextOverlayElement::InitTween(const RefPtr<Element>& element)
157 {
158     if (!element || (outerTween_ && innerTween_)) {
159         return;
160     }
161 
162     if (AceType::InstanceOf<TweenElement>(element)) {
163         if (!outerTween_) {
164             outerTween_ = AceType::DynamicCast<TweenElement>(element);
165         } else if (!innerTween_) {
166             innerTween_ = AceType::DynamicCast<TweenElement>(element);
167         } else {
168             return;
169         }
170     }
171 
172     for (const auto& child : element->GetChildren()) {
173         InitTween(child);
174     }
175 }
176 
AddListener(bool isSingleHandle,bool isIn)177 void TextOverlayElement::AddListener(bool isSingleHandle, bool isIn)
178 {
179     if (!outerTween_) {
180         return;
181     }
182     auto controller = outerTween_->GetController();
183     if (!controller) {
184         return;
185     }
186     controller->AddStartListener([weak = WeakClaim(this)]() {
187         auto overlay = weak.Upgrade();
188         if (overlay) {
189             auto context = overlay->context_.Upgrade();
190             if (context) {
191                 overlay->hasFocusAnimation_ = context->IsKeyEvent();
192                 // Don't show focus animation when show tween animation.
193                 context->SetIsKeyEvent(false);
194             }
195         }
196     });
197 
198     // When animation is stop, rebuild child without animation.
199     controller->AddStopListener([weak = WeakClaim(this), isSingleHandle, isIn]() {
200         auto overlay = weak.Upgrade();
201         if (overlay) {
202             overlay->outerTween_.Reset();
203             overlay->innerTween_.Reset();
204             if (isIn) {
205                 overlay->RebuildChild(isSingleHandle, true, false, true, false, false);
206             } else {
207                 overlay->RebuildChild(isSingleHandle, false, true, true, false, true);
208             }
209             auto context = overlay->context_.Upgrade();
210             if (context && overlay->hasFocusAnimation_) {
211                 // When tween animation is stopped, restart focus animation.
212                 context->SetIsKeyEvent(true);
213             }
214         } else {
215             LOGE("overlay is null");
216             return;
217         }
218         auto renderOverlay = AceType::DynamicCast<RenderTextOverlay>(overlay->renderNode_);
219         if (renderOverlay) {
220             renderOverlay->SetIsAnimationStopped(true);
221         }
222     });
223 
224     auto renderOverlay = DynamicCast<RenderTextOverlay>(renderNode_);
225     if (renderOverlay) {
226         double offsetX = renderOverlay->GetHorizonOffsetForAnimation();
227         double from = isIn ? offsetX : 0.0;
228         double to = isIn ? 0.0 : offsetX;
229 
230         RefPtr<CurveAnimation<float>> offset = AceType::MakeRefPtr<CurveAnimation<float>>(from, to, Curves::FRICTION);
231         offset->AddListener(Animation<float>::ValueCallback([count = 0, renderOverlay](double value) mutable {
232             // Delay one frame to avoid flashing of text overlay when paintting.
233             if (count > 1 && !renderOverlay->IsAnimationStarted()) {
234                 renderOverlay->SetIsAnimationStarted(true);
235                 renderOverlay->MarkNeedRender();
236             }
237             ++count;
238         }));
239         controller->AddInterpolator(offset);
240     }
241 }
242 
243 } // namespace OHOS::Ace