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