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