1 /*
2 * Copyright (c) 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_ng/pattern/marquee/marquee_pattern.h"
17
18 #include <string>
19
20 #include "base/geometry/dimension.h"
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/geometry/offset.h"
23 #include "base/log/dump_log.h"
24 #include "base/log/log_wrapper.h"
25 #include "base/utils/utils.h"
26 #include "core/animation/curves.h"
27 #include "core/common/container_scope.h"
28 #include "core/components/common/layout/constants.h"
29 #include "core/components/common/properties/alignment.h"
30 #include "core/components/common/properties/animation_option.h"
31 #include "core/components/common/properties/color.h"
32 #include "core/components/marquee/marquee_theme.h"
33 #include "core/components_ng/pattern/text/text_layout_adapter.h"
34 #include "core/components_ng/pattern/text/text_layout_property.h"
35 #include "core/components_ng/pattern/text/text_pattern.h"
36 #include "core/components_ng/property/calc_length.h"
37 #include "core/components_ng/property/property.h"
38 #include "core/components_ng/property/transition_property.h"
39 #include "core/components_ng/render/animation_utils.h"
40 #include "core/pipeline/pipeline_base.h"
41 #include "core/pipeline_ng/pipeline_context.h"
42
43 namespace OHOS::Ace::NG {
44 namespace {
45 constexpr double DEFAULT_MARQUEE_SCROLL_DELAY = 85.0; // Delay time between each jump.
46 constexpr float HALF = 0.5f;
47 constexpr float FAKE_VALUE = 0.1f;
48 inline constexpr int32_t DEFAULT_MARQUEE_LOOP = -1;
49 constexpr uint64_t ANIMATION_INITIAL_TIME = 0;
50 } // namespace
51
OnAttachToFrameNode()52 void MarqueePattern::OnAttachToFrameNode()
53 {
54 auto host = GetHost();
55 CHECK_NULL_VOID(host);
56 host->GetRenderContext()->SetUsingContentRectForRenderFrame(true);
57 host->GetRenderContext()->SetClipToFrame(true);
58 auto pipeline = PipelineContext::GetCurrentContext();
59 CHECK_NULL_VOID(pipeline);
60 pipeline->AddWindowSizeChangeCallback(host->GetId());
61 pipeline->AddWindowStateChangedCallback(host->GetId());
62 }
63
OnDetachFromFrameNode(FrameNode * frameNode)64 void MarqueePattern::OnDetachFromFrameNode(FrameNode* frameNode)
65 {
66 auto pipeline = PipelineContext::GetCurrentContext();
67 CHECK_NULL_VOID(pipeline);
68 pipeline->RemoveWindowSizeChangeCallback(frameNode->GetId());
69 pipeline->RemoveWindowStateChangedCallback(frameNode->GetId());
70 }
71
~MarqueePattern()72 MarqueePattern::~MarqueePattern()
73 {
74 CHECK_NULL_VOID(animation_);
75 AnimationUtils::StopAnimation(animation_);
76 }
77
OnWindowHide()78 void MarqueePattern::OnWindowHide()
79 {
80 if (!playStatus_) {
81 return;
82 }
83 CHECK_NULL_VOID(animation_);
84 playStatus_ = false;
85 AnimationUtils::PauseAnimation(animation_);
86 }
87
OnWindowShow()88 void MarqueePattern::OnWindowShow()
89 {
90 if (playStatus_) {
91 return;
92 }
93 CHECK_NULL_VOID(animation_);
94 playStatus_ = true;
95 AnimationUtils::ResumeAnimation(animation_);
96 }
97
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> &,const DirtySwapConfig &)98 bool MarqueePattern::OnDirtyLayoutWrapperSwap(
99 const RefPtr<LayoutWrapper>& /* dirty */, const DirtySwapConfig& /* config */)
100 {
101 auto host = GetHost();
102 CHECK_NULL_RETURN(host, false);
103 auto geoNode = host->GetGeometryNode();
104 CHECK_NULL_RETURN(geoNode, false);
105 auto marqueeWidth = geoNode->GetFrameSize().Width();
106 if (measureChanged_ || marqueeWidth_ != marqueeWidth) {
107 measureChanged_ = false;
108 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
109 CHECK_NULL_RETURN(paintProperty, false);
110 auto playStatus = paintProperty->GetPlayerStatus().value_or(false);
111 StopMarqueeAnimation(playStatus);
112 }
113 marqueeWidth_ = marqueeWidth;
114 return false;
115 }
116
OnModifyDone()117 void MarqueePattern::OnModifyDone()
118 {
119 Pattern::OnModifyDone();
120 auto host = GetHost();
121 CHECK_NULL_VOID(host);
122 auto layoutProperty = host->GetLayoutProperty<MarqueeLayoutProperty>();
123 CHECK_NULL_VOID(layoutProperty);
124 auto textChild = DynamicCast<FrameNode>(host->GetFirstChild());
125 CHECK_NULL_VOID(textChild);
126 auto childRenderContext = textChild->GetRenderContext();
127 CHECK_NULL_VOID(childRenderContext);
128 auto textLayoutProperty = textChild->GetLayoutProperty<TextLayoutProperty>();
129 CHECK_NULL_VOID(textLayoutProperty);
130 UpdateTextDirection(layoutProperty, textLayoutProperty);
131 auto gestureHub = textChild->GetOrCreateGestureEventHub();
132 CHECK_NULL_VOID(gestureHub);
133 gestureHub->SetHitTestMode(HitTestMode::HTMNONE);
134 auto pipelineContext = PipelineContext::GetCurrentContext();
135 CHECK_NULL_VOID(pipelineContext);
136 auto theme = pipelineContext->GetTheme<TextTheme>();
137 CHECK_NULL_VOID(theme);
138 auto fontSize = layoutProperty->GetFontSize().value_or(theme->GetTextStyle().GetFontSize());
139 textLayoutProperty->UpdateFontSize(fontSize);
140 textLayoutProperty->UpdateFontWeight(layoutProperty->GetFontWeight().value_or(FontWeight::NORMAL));
141 if (layoutProperty->GetFontFamily().has_value()) {
142 textLayoutProperty->UpdateFontFamily(layoutProperty->GetFontFamily().value());
143 } else {
144 textLayoutProperty->ResetFontFamily();
145 }
146 textLayoutProperty->UpdateTextColor(layoutProperty->GetFontColor().value_or(theme->GetTextStyle().GetTextColor()));
147 textChild->MarkModifyDone();
148 childRenderContext->UpdateClipEdge(true);
149 childRenderContext->SetClipToFrame(true);
150 if (CheckMeasureFlag(layoutProperty->GetPropertyChangeFlag()) ||
151 CheckLayoutFlag(layoutProperty->GetPropertyChangeFlag())) {
152 measureChanged_ = true;
153 } else if (OnlyPlayStatusChange()) {
154 ChangeAnimationPlayStatus();
155 } else {
156 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
157 CHECK_NULL_VOID(paintProperty);
158 auto playStatus = paintProperty->GetPlayerStatus().value_or(false);
159 StopMarqueeAnimation(playStatus);
160 }
161 StoreProperties();
162 }
163
StartMarqueeAnimation()164 void MarqueePattern::StartMarqueeAnimation()
165 {
166 TAG_LOGD(AceLogTag::ACE_MARQUEE, "Start Marquee Animation.");
167 auto host = GetHost();
168 CHECK_NULL_VOID(host);
169 if (!IsRunMarquee()) {
170 return;
171 }
172 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
173 CHECK_NULL_VOID(paintProperty);
174 auto pipeline = PipelineContext::GetCurrentContext();
175 CHECK_NULL_VOID(pipeline);
176 auto repeatCount = paintProperty->GetLoop().value_or(DEFAULT_MARQUEE_LOOP);
177 if (pipeline->IsFormRender()) {
178 repeatCount = 1;
179 }
180 FireStartEvent();
181 bool needSecondPlay = repeatCount != 1;
182 auto startPosition = GetTextOffset();
183 PlayMarqueeAnimation(startPosition, repeatCount, needSecondPlay);
184 }
185
PlayMarqueeAnimation(float start,int32_t playCount,bool needSecondPlay)186 void MarqueePattern::PlayMarqueeAnimation(float start, int32_t playCount, bool needSecondPlay)
187 {
188 TAG_LOGD(AceLogTag::ACE_MARQUEE,
189 "Play Marquee Animation, startPosition is %{public}f, playCount is %{public}d, needSecondPlay is true ? "
190 "%{public}d.",
191 start, playCount, needSecondPlay);
192 auto host = GetHost();
193 CHECK_NULL_VOID(host);
194 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
195 CHECK_NULL_VOID(textNode);
196 auto textWidth = GetTextNodeWidth();
197 auto step = GetScrollAmount();
198 if (GreatNotEqual(step, textWidth)) {
199 step = DEFAULT_MARQUEE_SCROLL_AMOUNT.ConvertToPx();
200 }
201 bool isFirstStart = start == GetTextOffset() ? true : false;
202 float calculateEnd = CalculateEnd();
203 float calculateStart = CalculateStart();
204 auto direction = GetCurrentTextDirection();
205 bool isRtl = direction == TextDirection::RTL ? true : false;
206 if (isRtl) std::swap(calculateEnd, calculateStart);
207 lastAnimationParam_.lastEnd = calculateEnd;
208 lastAnimationParam_.lastStart = calculateStart;
209 if (isFirstStart) calculateStart = start;
210 auto duration = static_cast<int32_t>(std::abs(calculateEnd - calculateStart) * DEFAULT_MARQUEE_SCROLL_DELAY);
211 lastAnimationParam_.lastDistance = std::abs(calculateEnd - calculateStart);
212 lastAnimationParam_.lastStep = 1.0f;
213 if (GreatNotEqual(step, 0.0)) {
214 duration = static_cast<int32_t>(duration / step);
215 lastAnimationParam_.lastStep = step;
216 }
217 lastAnimationParam_.lastDuration = duration;
218 AnimationOption option;
219 auto iter = frameRateRange_.find(MarqueeDynamicSyncSceneType::ANIMATE);
220 if (iter != frameRateRange_.end()) {
221 option.SetFrameRateRange(iter->second);
222 }
223 option.SetCurve(Curves::LINEAR);
224 option.SetDuration(duration);
225 needSecondPlay ? option.SetIteration(1) : option.SetIteration(playCount);
226 TAG_LOGD(AceLogTag::ACE_MARQUEE,
227 "Play Marquee Animation, marqueeNodeId is %{public}d, textNodeId is %{public}d, textWidth is %{public}f, "
228 "duration is %{public}d.",
229 host->GetId(), textNode->GetId(), textWidth, duration);
230 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
231 CHECK_NULL_VOID(paintProperty);
232 auto marqueeDirection = paintProperty->GetDirection().value_or(MarqueeDirection::LEFT);
233 lastAnimationParam_.lastDirection = isRtl ?
234 (marqueeDirection == MarqueeDirection::RIGHT ? MarqueeDirection::LEFT : MarqueeDirection::RIGHT)
235 : marqueeDirection;
236 SetTextOffset(calculateStart);
237 ActionAnimation(option, calculateEnd, playCount, needSecondPlay);
238 }
239
ActionAnimation(AnimationOption & option,float end,int32_t playCount,bool needSecondPlay)240 void MarqueePattern::ActionAnimation(AnimationOption& option, float end, int32_t playCount, bool needSecondPlay)
241 {
242 animationId_++;
243 animation_ = AnimationUtils::StartAnimation(
244 option,
245 [weak = AceType::WeakClaim(this), end]() {
246 auto pattern = weak.Upgrade();
247 CHECK_NULL_VOID(pattern);
248 pattern->SetTextOffset(end);
249 },
250 [weak = AceType::WeakClaim(this), animationId = animationId_, needSecondPlay, playCount,
251 id = Container::CurrentId()]() {
252 ContainerScope scope(id);
253 auto taskExecutor = Container::CurrentTaskExecutor();
254 CHECK_NULL_VOID(taskExecutor);
255 auto onFinish = [weak, needSecondPlay, playCount, animationId]() {
256 auto pattern = weak.Upgrade();
257 CHECK_NULL_VOID(pattern);
258 if (animationId != pattern->animationId_) {
259 return;
260 }
261 if (!needSecondPlay) {
262 pattern->OnAnimationFinish();
263 return;
264 }
265 auto newPlayCount = playCount > 0 ? playCount - 1 : playCount;
266 if (newPlayCount == 0) {
267 return;
268 }
269
270 auto direction = pattern->GetCurrentTextDirection();
271 auto newStart = direction == TextDirection::RTL ? pattern->CalculateEnd() : pattern->CalculateStart();
272 pattern->lastAnimationParam_.lastAnimationPosition = newStart;
273 pattern->lastAnimationParam_.lastStartMilliseconds = GetMilliseconds();
274 pattern->PlayMarqueeAnimation(newStart, newPlayCount, false);
275 };
276 if (taskExecutor->WillRunOnCurrentThread(TaskExecutor::TaskType::UI)) {
277 onFinish();
278 return;
279 }
280 taskExecutor->PostTask([onFinish]() {onFinish();}, TaskExecutor::TaskType::UI, "ArkUIMarqueePlayAnimation");
281 },
282 [weak = AceType::WeakClaim(this)]() {
283 auto pattern = weak.Upgrade();
284 CHECK_NULL_VOID(pattern);
285 pattern->lastAnimationParam_.lastStartMilliseconds = GetMilliseconds();
286 pattern->FireBounceEvent();
287 });
288 }
289
OnAnimationFinish()290 void MarqueePattern::OnAnimationFinish()
291 {
292 FireFinishEvent();
293 SetTextOffset(0.0f);
294 }
295
StopMarqueeAnimation(bool stopAndStart)296 void MarqueePattern::StopMarqueeAnimation(bool stopAndStart)
297 {
298 TAG_LOGD(AceLogTag::ACE_MARQUEE, "Stop Marquee Animation.");
299 animation_ = nullptr;
300 animationId_++;
301 SetTextOffset(FAKE_VALUE);
302 AnimationOption option;
303 option.SetCurve(Curves::LINEAR);
304 option.SetDuration(0);
305 auto offset = stopAndStart ? GetTextOffset() : 0.0f;
306 AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), position = offset]() {
307 auto pattern = weak.Upgrade();
308 CHECK_NULL_VOID(pattern);
309 pattern->SetTextOffset(position);
310 });
311 if (stopAndStart) {
312 auto taskExecutor = Container::CurrentTaskExecutor();
313 CHECK_NULL_VOID(taskExecutor);
314 taskExecutor->PostTask(
315 [weak = AceType::WeakClaim(this), animationId = animationId_]() {
316 auto pattern = weak.Upgrade();
317 CHECK_NULL_VOID(pattern);
318 if (animationId == pattern->animationId_) {
319 pattern->StartMarqueeAnimation();
320 }
321 },
322 TaskExecutor::TaskType::UI, "ArkUIMarqueeStartAnimation");
323 } else {
324 lastAnimationParam_.lastStartMilliseconds = ANIMATION_INITIAL_TIME;
325 lastAnimationParam_.lastAnimationPosition = 0.0f;
326 }
327 }
328
FireStartEvent() const329 void MarqueePattern::FireStartEvent() const
330 {
331 auto marqueeEventHub = GetEventHub<MarqueeEventHub>();
332 CHECK_NULL_VOID(marqueeEventHub);
333 marqueeEventHub->FireStartEvent();
334 }
335
FireBounceEvent() const336 void MarqueePattern::FireBounceEvent() const
337 {
338 auto marqueeEventHub = GetEventHub<MarqueeEventHub>();
339 CHECK_NULL_VOID(marqueeEventHub);
340 marqueeEventHub->FireBounceEvent();
341 }
342
FireFinishEvent() const343 void MarqueePattern::FireFinishEvent() const
344 {
345 auto marqueeEventHub = GetEventHub<MarqueeEventHub>();
346 CHECK_NULL_VOID(marqueeEventHub);
347 marqueeEventHub->FireFinishEvent();
348 }
349
SetTextOffset(float offsetX)350 void MarqueePattern::SetTextOffset(float offsetX)
351 {
352 auto host = GetHost();
353 CHECK_NULL_VOID(host);
354 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
355 CHECK_NULL_VOID(textNode);
356 auto renderContext = textNode->GetRenderContext();
357 CHECK_NULL_VOID(renderContext);
358 TAG_LOGD(AceLogTag::ACE_MARQUEE,
359 "Marquee nodeId %{public}d, textNodeId %{public}d is setted text offsetX is %{public}f.", host->GetId(),
360 textNode->GetId(), offsetX);
361 renderContext->UpdateTransformTranslate({ offsetX, 0.0f, 0.0f });
362 }
363
GetTextOffset()364 float MarqueePattern::GetTextOffset()
365 {
366 float offsetX = 0.0f;
367 if (!IsRunMarquee()) {
368 lastAnimationParam_.lastStartMilliseconds = ANIMATION_INITIAL_TIME;
369 lastAnimationParam_.lastAnimationPosition = 0.0f;
370 return offsetX;
371 }
372 auto host = GetHost();
373 CHECK_NULL_RETURN(host, offsetX);
374 auto layoutProperty = host->GetLayoutProperty<MarqueeLayoutProperty>();
375 CHECK_NULL_RETURN(layoutProperty, offsetX);
376 auto marqueeUpdateStrategy = layoutProperty->GetMarqueeUpdateStrategy().value_or(MarqueeUpdateStrategy::DEFAULT);
377 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
378 CHECK_NULL_RETURN(paintProperty, offsetX);
379 auto playStatus = paintProperty->GetPlayerStatus().value_or(false);
380 if (playStatus && (lastAnimationParam_.lastStartMilliseconds > ANIMATION_INITIAL_TIME) &&
381 (marqueeUpdateStrategy == MarqueeUpdateStrategy::PRESERVE_POSITION)) {
382 auto currentMilliseconds = GetMilliseconds();
383 auto animationSpeed = lastAnimationParam_.lastDistance/ lastAnimationParam_.lastDuration;
384 auto diffMilliseconds =
385 std::abs(static_cast<int32_t>(currentMilliseconds - lastAnimationParam_.lastStartMilliseconds));
386 auto tempStartPosition = lastAnimationParam_.lastAnimationPosition;
387 if (NearEqual(static_cast<int32_t>(lastAnimationParam_.lastDuration), 0.0f) ||
388 (diffMilliseconds / static_cast<int32_t>(lastAnimationParam_.lastDuration)) > 0) {
389 diffMilliseconds -= lastAnimationParam_.lastDuration;
390 auto duration = static_cast<int32_t>(
391 std::abs(lastAnimationParam_.lastEnd - lastAnimationParam_.lastStart) * DEFAULT_MARQUEE_SCROLL_DELAY);
392 duration = duration / lastAnimationParam_.lastStep;
393 diffMilliseconds %= (duration + 1);
394 tempStartPosition = lastAnimationParam_.lastStart;
395 }
396 offsetX = static_cast<int32_t>(animationSpeed * diffMilliseconds);
397 auto factor = lastAnimationParam_.lastDirection == MarqueeDirection::LEFT ? -1.0f : 1.0f;
398 offsetX = offsetX * factor + tempStartPosition;
399 }
400 lastAnimationParam_.lastStartMilliseconds = GetMilliseconds();
401 lastAnimationParam_.lastAnimationPosition = offsetX;
402 return offsetX;
403 }
404
OnVisibleChange(bool isVisible)405 void MarqueePattern::OnVisibleChange(bool isVisible)
406 {
407 CHECK_NULL_VOID(!playStatus_);
408 CHECK_NULL_VOID(animation_);
409 if (isVisible) {
410 AnimationUtils::ResumeAnimation(animation_);
411 } else {
412 AnimationUtils::PauseAnimation(animation_);
413 }
414 }
415
OnlyPlayStatusChange()416 bool MarqueePattern::OnlyPlayStatusChange()
417 {
418 auto host = GetHost();
419 CHECK_NULL_RETURN(host, false);
420 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
421 CHECK_NULL_RETURN(paintProperty, false);
422 auto playStatus = paintProperty->GetPlayerStatus().value_or(false);
423 auto scrollAmount = paintProperty->GetScrollAmount().value_or(DEFAULT_MARQUEE_SCROLL_AMOUNT.ConvertToPx());
424 auto loop = paintProperty->GetLoop().value_or(DEFAULT_MARQUEE_LOOP);
425 auto direction = paintProperty->GetDirection().value_or(MarqueeDirection::LEFT);
426 if (!NearEqual(scrollAmount_, scrollAmount) || loop_ != loop || direction_ != direction) {
427 return false;
428 }
429 if (playStatus_ != playStatus) {
430 return true;
431 }
432 return false;
433 }
434
ChangeAnimationPlayStatus()435 void MarqueePattern::ChangeAnimationPlayStatus()
436 {
437 auto host = GetHost();
438 CHECK_NULL_VOID(host);
439 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
440 CHECK_NULL_VOID(paintProperty);
441 auto playStatus = paintProperty->GetPlayerStatus().value_or(false);
442 if (playStatus) {
443 if (!animation_) {
444 StartMarqueeAnimation();
445 return;
446 }
447 playStatus_ = true;
448 AnimationUtils::ResumeAnimation(animation_);
449 } else {
450 CHECK_NULL_VOID(animation_);
451 playStatus_ = false;
452 AnimationUtils::PauseAnimation(animation_);
453 }
454 }
455
StoreProperties()456 void MarqueePattern::StoreProperties()
457 {
458 auto host = GetHost();
459 CHECK_NULL_VOID(host);
460 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
461 CHECK_NULL_VOID(paintProperty);
462 playStatus_ = paintProperty->GetPlayerStatus().value_or(false);
463 scrollAmount_ = paintProperty->GetScrollAmount().value_or(DEFAULT_MARQUEE_SCROLL_AMOUNT.ConvertToPx());
464 loop_ = paintProperty->GetLoop().value_or(DEFAULT_MARQUEE_LOOP);
465 direction_ = paintProperty->GetDirection().value_or(MarqueeDirection::LEFT);
466 }
467
CalculateStart()468 float MarqueePattern::CalculateStart()
469 {
470 float start = 0.0f;
471 auto host = GetHost();
472 CHECK_NULL_RETURN(host, start);
473 auto geoNode = host->GetGeometryNode();
474 CHECK_NULL_RETURN(geoNode, start);
475 auto marqueeSize = geoNode->GetFrameSize();
476 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
477 CHECK_NULL_RETURN(textNode, start);
478 auto textGeoNode = textNode->GetGeometryNode();
479 CHECK_NULL_RETURN(textGeoNode, start);
480 auto textWidth = textGeoNode->GetFrameSize().Width();
481 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
482 CHECK_NULL_RETURN(paintProperty, start);
483 auto direction = paintProperty->GetDirection().value_or(MarqueeDirection::LEFT);
484 auto layoutProperty = host->GetLayoutProperty<MarqueeLayoutProperty>();
485 CHECK_NULL_RETURN(layoutProperty, start);
486 auto textDirection = GetCurrentTextDirection();
487 Alignment align = (textDirection == TextDirection::RTL ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT);
488
489 const auto& padding = layoutProperty->CreatePaddingAndBorder();
490 if (direction == MarqueeDirection::LEFT) {
491 if (NearEqual(align.GetHorizontal(), -1.0)) {
492 start = marqueeSize.Width() - padding.left.value_or(0);
493 } else if (NearEqual(align.GetHorizontal(), 0.0)) {
494 start = (marqueeSize.Width() + textWidth) * HALF;
495 } else {
496 start = textWidth + padding.right.value_or(0);
497 }
498 } else {
499 if (NearEqual(align.GetHorizontal(), -1.0)) {
500 start = -1 * textWidth - padding.left.value_or(0);
501 } else if (NearEqual(align.GetHorizontal(), 0.0)) {
502 start = -1 * (marqueeSize.Width() + textWidth) * HALF;
503 } else {
504 start = -1 * marqueeSize.Width() + padding.right.value_or(0);
505 }
506 }
507 return start;
508 }
509
CalculateEnd()510 float MarqueePattern::CalculateEnd()
511 {
512 float end = 0.0f;
513 auto host = GetHost();
514 CHECK_NULL_RETURN(host, end);
515 auto geoNode = host->GetGeometryNode();
516 CHECK_NULL_RETURN(geoNode, end);
517 auto marqueeSize = geoNode->GetFrameSize();
518 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
519 CHECK_NULL_RETURN(textNode, end);
520 auto textGeoNode = textNode->GetGeometryNode();
521 CHECK_NULL_RETURN(textGeoNode, end);
522 auto textWidth = textGeoNode->GetFrameSize().Width();
523 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
524 CHECK_NULL_RETURN(paintProperty, end);
525 auto layoutProperty = host->GetLayoutProperty<MarqueeLayoutProperty>();
526 CHECK_NULL_RETURN(layoutProperty, end);
527 auto direction = paintProperty->GetDirection().value_or(MarqueeDirection::LEFT);
528 const auto& padding = layoutProperty->CreatePaddingAndBorder();
529 auto textDirection = GetCurrentTextDirection();
530 Alignment align = (textDirection == TextDirection::RTL ? Alignment::CENTER_RIGHT : Alignment::CENTER_LEFT);
531
532 if (direction == MarqueeDirection::LEFT) {
533 if (NearEqual(align.GetHorizontal(), -1.0)) {
534 end = -1 * textWidth - padding.left.value_or(0);
535 } else if (NearEqual(align.GetHorizontal(), 0.0)) {
536 end = -1 * (marqueeSize.Width() + textWidth) * HALF;
537 } else {
538 end = -1 * marqueeSize.Width() + padding.right.value_or(0);
539 }
540 } else {
541 if (NearEqual(align.GetHorizontal(), -1.0)) {
542 end = marqueeSize.Width() - padding.left.value_or(0);
543 } else if (NearEqual(align.GetHorizontal(), 0.0)) {
544 end = (marqueeSize.Width() + textWidth) * HALF;
545 } else {
546 end = textWidth + padding.right.value_or(0);
547 }
548 }
549 return end;
550 }
551
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)552 void MarqueePattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
553 {
554 if (width != lastWindowWidth_ || height != lastWindowHeight_) {
555 measureChanged_ = true;
556 }
557 lastWindowHeight_ = height;
558 lastWindowWidth_ = width;
559 }
560
OnColorConfigurationUpdate()561 void MarqueePattern::OnColorConfigurationUpdate()
562 {
563 auto host = GetHost();
564 CHECK_NULL_VOID(host);
565 host->SetNeedCallChildrenUpdate(false);
566 auto textChild = host->GetChildren().front();
567 CHECK_NULL_VOID(textChild);
568 auto textChildNode = DynamicCast<FrameNode>(textChild);
569 CHECK_NULL_VOID(textChildNode);
570 auto textLayoutProperty = textChildNode->GetLayoutProperty<TextLayoutProperty>();
571 CHECK_NULL_VOID(textLayoutProperty);
572 auto pipelineContext = PipelineContext::GetCurrentContext();
573 CHECK_NULL_VOID(pipelineContext);
574 auto theme = pipelineContext->GetTheme<MarqueeTheme>();
575 CHECK_NULL_VOID(theme);
576 textLayoutProperty->UpdateTextColor(theme->GetTextColor());
577 textChildNode->MarkModifyDone();
578 textChildNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
579 }
580
DumpInfo()581 void MarqueePattern::DumpInfo()
582 {
583 auto host = GetHost();
584 CHECK_NULL_VOID(host);
585 auto textChild = AceType::DynamicCast<FrameNode>(host->GetChildren().front());
586 CHECK_NULL_VOID(textChild);
587 auto textLayoutProperty = textChild->GetLayoutProperty<TextLayoutProperty>();
588 CHECK_NULL_VOID(textLayoutProperty);
589 DumpLog::GetInstance().AddDesc(
590 std::string("Marquee text content: ").append(textLayoutProperty->GetContent().value_or("")));
591 DumpLog::GetInstance().AddDesc(std::string("Play status: ").append(std::to_string(playStatus_)));
592 DumpLog::GetInstance().AddDesc(std::string("loop: ").append(std::to_string(loop_)));
593 DumpLog::GetInstance().AddDesc(std::string("step: ").append(std::to_string(scrollAmount_)));
594 }
595
GetTextNodeWidth()596 float MarqueePattern::GetTextNodeWidth()
597 {
598 auto host = GetHost();
599 CHECK_NULL_RETURN(host, 0.0f);
600 auto geoNode = host->GetGeometryNode();
601 CHECK_NULL_RETURN(geoNode, 0.0f);
602 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
603 CHECK_NULL_RETURN(textNode, 0.0f);
604 auto textGeoNode = textNode->GetGeometryNode();
605 CHECK_NULL_RETURN(textGeoNode, 0.0f);
606 return textGeoNode->GetFrameSize().Width();
607 }
608
GetScrollAmount()609 double MarqueePattern::GetScrollAmount()
610 {
611 auto host = GetHost();
612 CHECK_NULL_RETURN(host, 0.0f);
613 auto paintProperty = host->GetPaintProperty<MarqueePaintProperty>();
614 CHECK_NULL_RETURN(paintProperty, 0.0f);
615 return paintProperty->GetScrollAmount().value_or(DEFAULT_MARQUEE_SCROLL_AMOUNT.ConvertToPx());
616 }
617
IsRunMarquee()618 bool MarqueePattern::IsRunMarquee()
619 {
620 auto host = GetHost();
621 CHECK_NULL_RETURN(host, false);
622 auto geoNode = host->GetGeometryNode();
623 CHECK_NULL_RETURN(geoNode, false);
624 auto marqueeSize = geoNode->GetFrameSize();
625 auto textNode = DynamicCast<FrameNode>(host->GetFirstChild());
626 CHECK_NULL_RETURN(textNode, false);
627 auto textGeoNode = textNode->GetGeometryNode();
628 CHECK_NULL_RETURN(textGeoNode, false);
629 auto textWidth = textGeoNode->GetFrameSize().Width();
630 auto layoutProperty = host->GetLayoutProperty<MarqueeLayoutProperty>();
631 CHECK_NULL_RETURN(layoutProperty, false);
632 float padding = 0.0f;
633 if (layoutProperty->GetPaddingProperty()) {
634 const auto& paddingProperty = layoutProperty->GetPaddingProperty();
635 padding = paddingProperty->left.value_or(CalcLength(0.0)).GetDimension().ConvertToPx() +
636 paddingProperty->right.value_or(CalcLength(0.0)).GetDimension().ConvertToPx();
637 }
638 return GreatOrEqual(textWidth + padding, marqueeSize.Width());
639 }
640
GetTextDirection(const std::string & content,TextDirection direction)641 TextDirection MarqueePattern::GetTextDirection(const std::string& content, TextDirection direction)
642 {
643 if (direction == TextDirection::LTR || direction == TextDirection::RTL) {
644 return direction;
645 }
646
647 bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
648 auto textDirection = isRTL ? TextDirection::RTL : TextDirection::LTR;
649 auto showingTextForWString = StringUtils::ToWstring(content);
650 for (const auto& charOfShowingText : showingTextForWString) {
651 if (TextLayoutadapter::IsLeftToRight(charOfShowingText)) {
652 return TextDirection::LTR;
653 }
654 if (TextLayoutadapter::IsRightToLeft(charOfShowingText) ||
655 TextLayoutadapter::IsRightTOLeftArabic(charOfShowingText)) {
656 return TextDirection::RTL;
657 }
658 }
659 return textDirection;
660 }
661
GetCurrentTextDirection()662 TextDirection MarqueePattern::GetCurrentTextDirection()
663 {
664 return currentTextDirection_;
665 }
666
CheckTextDirectionChange(TextDirection direction)667 void MarqueePattern::CheckTextDirectionChange(TextDirection direction)
668 {
669 if (direction != currentTextDirection_) {
670 lastAnimationParam_.lastStartMilliseconds = ANIMATION_INITIAL_TIME;
671 lastAnimationParam_.lastAnimationPosition = 0.0f;
672 }
673 currentTextDirection_ = direction;
674 }
675
UpdateTextDirection(const RefPtr<MarqueeLayoutProperty> & layoutProperty,const RefPtr<TextLayoutProperty> & textLayoutProperty)676 void MarqueePattern::UpdateTextDirection(
677 const RefPtr<MarqueeLayoutProperty>& layoutProperty, const RefPtr<TextLayoutProperty>& textLayoutProperty)
678 {
679 auto src = layoutProperty->GetSrc().value_or(" ");
680 textLayoutProperty->UpdateContent(src);
681 auto direction = layoutProperty->GetLayoutDirection();
682 auto textDirection = GetTextDirection(src, direction);
683 textLayoutProperty->UpdateLayoutDirection(textDirection);
684 CheckTextDirectionChange(textDirection);
685 }
686 } // namespace OHOS::Ace::NG
687