1 /*
2  * Copyright (c) 2020-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 "components/ui_arc_label.h"
17 
18 #include "common/typed_text.h"
19 #include "draw/draw_label.h"
20 #include "engines/gfx/gfx_engine_manager.h"
21 #include "font/ui_font.h"
22 #include "themes/theme_manager.h"
23 
24 namespace OHOS {
25 static constexpr uint16_t DEFAULT_ARC_LABEL_ROLL_COUNT = 1;
26 static constexpr uint16_t DEFAULT_ARC_LABEL_ANIMATOR_SPEED = 10;
27 
28 class ArcLabelAnimator : public Animator, public AnimatorCallback {
29 public:
ArcLabelAnimator(uint16_t rollCount,UIView * view)30     ArcLabelAnimator(uint16_t rollCount, UIView* view)
31         : Animator(this, view, 0, true),
32           waitCount_(ANIM_WAIT_COUNT),
33           speed_(0),
34           preRunTime_(0),
35           decimal_(0),
36           rollCount_(rollCount)
37     {
38     }
39 
~ArcLabelAnimator()40     virtual ~ArcLabelAnimator() {}
41 
Callback(UIView * view)42     void Callback(UIView* view) override
43     {
44         if (view == nullptr || rollCount_ == 0) {
45             return;
46         }
47 
48         uint32_t curTime = GetRunTime();
49         if (waitCount_ > 0) {
50             waitCount_--;
51             preRunTime_ = curTime;
52             return;
53         }
54         if (curTime == preRunTime_) {
55             return;
56         }
57         uint32_t time = (curTime > preRunTime_) ? (curTime - preRunTime_) : (UINT32_MAX - preRunTime_ + curTime);
58         // 1000: 1000 milliseconds is 1 second
59         float floatStep = (static_cast<float>(time * speed_) / 1000) + decimal_;
60         uint16_t integerStep = static_cast<uint16_t>(floatStep);
61         decimal_ = floatStep - integerStep;
62         preRunTime_ = curTime;
63 
64         if (integerStep == 0) {
65             return;
66         }
67 
68         CalculatedOffsetAngle(view);
69     }
70 
SetRollSpeed(uint16_t speed)71     void SetRollSpeed(uint16_t speed)
72     {
73         speed_ = speed;
74     }
75 
GetRollSpeed() const76     int16_t GetRollSpeed() const
77     {
78         return speed_;
79     }
80 
SetRollCount(uint16_t rollCount)81     void SetRollCount(uint16_t rollCount)
82     {
83         rollCount_ = rollCount;
84     }
85 
RegisterScrollListener(ArcLabelScrollListener * scrollListener)86     void RegisterScrollListener(ArcLabelScrollListener* scrollListener)
87     {
88         scrollListener_ = scrollListener;
89     }
90 
91 private:
CalculatedOffsetAngle(UIView * view)92     void CalculatedOffsetAngle(UIView* view)
93     {
94         if (view == nullptr) {
95             return;
96         }
97         UIArcLabel* arcLabel = static_cast<UIArcLabel*>(view);
98         if (arcLabel == nullptr) {
99             return;
100         }
101 
102         int16_t startAngle = arcLabel->GetArcTextStartAngle();
103         int16_t endAngle = arcLabel->GetArcTextEndAngle();
104         uint16_t arcAngle = (startAngle < endAngle) ? (endAngle - startAngle) :
105             (startAngle - endAngle);
106 
107         if (arcLabel->offsetAngle_ < arcAngle) {
108             arcLabel->offsetAngle_ += DEFAULT_CHANGE_ANGLE;
109         } else {
110             rollCount_--;
111             if (rollCount_ > 0) {
112                 arcLabel->offsetAngle_ = arcLabel->animator_.secondLapOffsetAngle_;
113             }
114         }
115 
116         if (rollCount_ == 0) {
117             if (scrollListener_) {
118                 scrollListener_->Finish();
119             }
120             Stop();
121         }
122         view->Invalidate();
123     }
124 
125 private:
126     static constexpr uint8_t ANIM_WAIT_COUNT = 50;
127     static constexpr float DEFAULT_CHANGE_ANGLE = 1.0f;
128     uint16_t waitCount_;
129     uint16_t speed_;
130     uint32_t preRunTime_;
131     float decimal_;
132 
133     uint16_t rollCount_;
134     ArcLabelScrollListener* scrollListener_;
135 };
136 
UIArcLabel()137 UIArcLabel::UIArcLabel()
138     : arcLabelText_(nullptr),
139       compatibilityMode_(true),
140       offsetAngle_(0.0f),
141       arcTextInfo_{0},
142       needRefresh_(false),
143       hasAnimator_(false),
144       textSize_({0, 0}),
145       radius_(0),
146       startAngle_(0),
147       endAngle_(0),
148       arcCenter_({0, 0}),
149       orientation_(TextOrientation::INSIDE)
150 {
151     Theme* theme = ThemeManager::GetInstance().GetCurrent();
152     style_ = (theme != nullptr) ? &(theme->GetLabelStyle()) : &(StyleDefault::GetLabelStyle());
153 
154     animator_.animator = nullptr;
155     animator_.scrollListener = nullptr;
156     animator_.speed = DEFAULT_ARC_LABEL_ANIMATOR_SPEED;
157     animator_.rollCount = DEFAULT_ARC_LABEL_ROLL_COUNT;
158     animator_.secondLapOffsetAngle_ = 0.0f;
159 }
160 
~UIArcLabel()161 UIArcLabel::~UIArcLabel()
162 {
163     if (arcLabelText_ != nullptr) {
164         delete arcLabelText_;
165         arcLabelText_ = nullptr;
166     }
167 
168     if (hasAnimator_) {
169         delete animator_.animator;
170         animator_.animator = nullptr;
171         hasAnimator_ = false;
172     }
173 }
174 
SetStyle(uint8_t key,int64_t value)175 void UIArcLabel::SetStyle(uint8_t key, int64_t value)
176 {
177     UIView::SetStyle(key, value);
178     RefreshArcLabel();
179 }
180 
SetText(const char * text)181 void UIArcLabel::SetText(const char* text)
182 {
183     if (text == nullptr) {
184         return;
185     }
186     InitArcLabelText();
187     arcLabelText_->SetText(text);
188     if (arcLabelText_->IsNeedRefresh()) {
189         RefreshArcLabel();
190     }
191 }
192 
GetText() const193 const char* UIArcLabel::GetText() const
194 {
195     return (arcLabelText_ == nullptr) ? nullptr : arcLabelText_->GetText();
196 }
197 
SetAlign(UITextLanguageAlignment horizontalAlign)198 void UIArcLabel::SetAlign(UITextLanguageAlignment horizontalAlign)
199 {
200     InitArcLabelText();
201     arcLabelText_->SetAlign(horizontalAlign, TEXT_ALIGNMENT_TOP);
202     if (arcLabelText_->IsNeedRefresh()) {
203         RefreshArcLabel();
204     }
205 }
206 
GetHorAlign()207 UITextLanguageAlignment UIArcLabel::GetHorAlign()
208 {
209     InitArcLabelText();
210     return arcLabelText_->GetHorAlign();
211 }
212 
GetDirect()213 UITextLanguageDirect UIArcLabel::GetDirect()
214 {
215     InitArcLabelText();
216     return arcLabelText_->GetDirect();
217 }
218 
SetFontId(uint16_t fontId)219 void UIArcLabel::SetFontId(uint16_t fontId)
220 {
221     InitArcLabelText();
222     arcLabelText_->SetFontId(fontId);
223     if (arcLabelText_->IsNeedRefresh()) {
224         RefreshArcLabel();
225     }
226 }
227 
GetFontId()228 uint16_t UIArcLabel::GetFontId()
229 {
230     InitArcLabelText();
231     return arcLabelText_->GetFontId();
232 }
233 
SetFont(const char * name,uint8_t size)234 void UIArcLabel::SetFont(const char* name, uint8_t size)
235 {
236     if (name == nullptr) {
237         return;
238     }
239     InitArcLabelText();
240     arcLabelText_->SetFont(name, size);
241     if (arcLabelText_->IsNeedRefresh()) {
242         RefreshArcLabel();
243     }
244 }
245 
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)246 void UIArcLabel::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
247 {
248     InitArcLabelText();
249     const char* text = arcLabelText_->GetText();
250     if ((text == nullptr) || (radius_ == 0)) {
251         return;
252     }
253     OpacityType opa = GetMixOpaScale();
254     UIView::OnDraw(gfxDstBuffer, invalidatedArea);
255     DrawArcText(gfxDstBuffer, invalidatedArea, opa, arcTextInfo_, orientation_);
256 }
257 
DrawArcText(BufferInfo & gfxDstBuffer,const Rect & mask,OpacityType opaScale,ArcTextInfo arcTextInfo,TextOrientation orientation)258 void UIArcLabel::DrawArcText(BufferInfo& gfxDstBuffer,
259                              const Rect& mask,
260                              OpacityType opaScale,
261                              ArcTextInfo arcTextInfo,
262                              TextOrientation orientation)
263 {
264     Point center;
265     center.x = arcTextInfo_.arcCenter.x + GetRect().GetX();
266     center.y = arcTextInfo_.arcCenter.y + GetRect().GetY();
267     Rect temp = mask;
268     if (compatibilityMode_ && hasAnimator_) {
269         temp.SetLeft(center.x - radius_);
270         temp.SetTop(center.y - radius_);
271         temp.SetWidth(radius_ * 2); // 2 mean diameter
272         temp.SetHeight(radius_ * 2);
273     }
274     arcTextInfo.hasAnimator = hasAnimator_;
275 
276     DrawLabel::DrawArcText(gfxDstBuffer, temp, arcLabelText_->GetText(), center, arcLabelText_->GetFontId(),
277                            arcLabelText_->GetFontSize(), arcTextInfo, offsetAngle_,
278                            orientation, *style_, opaScale, compatibilityMode_);
279 }
280 
GetArcTextRect(const char * text,uint16_t fontId,uint8_t fontSize,const Point & arcCenter,int16_t letterSpace,TextOrientation orientation,const ArcTextInfo & arcTextInfo)281 Rect UIArcLabel::GetArcTextRect(const char* text, uint16_t fontId, uint8_t fontSize, const Point& arcCenter,
282                                 int16_t letterSpace, TextOrientation orientation, const ArcTextInfo& arcTextInfo)
283 {
284     return TypedText::GetArcTextRect(text, fontId, fontSize, arcCenter, letterSpace, orientation, arcTextInfo);
285 }
286 
RefreshArcLabel()287 void UIArcLabel::RefreshArcLabel()
288 {
289     Invalidate();
290     if (!needRefresh_) {
291         needRefresh_ = true;
292     }
293 }
294 
ReMeasure()295 void UIArcLabel::ReMeasure()
296 {
297     if (!needRefresh_) {
298         return;
299     }
300     needRefresh_ = false;
301     InitArcLabelText();
302 
303     MeasureArcTextInfo();
304     arcTextInfo_.shapingFontId = arcLabelText_->GetShapingFontId();
305     arcTextInfo_.codePoints = arcLabelText_->GetCodePoints();
306     arcTextInfo_.codePointsNum = arcLabelText_->GetCodePointNum();
307     Rect textRect =
308         GetArcTextRect(arcLabelText_->GetText(), arcLabelText_->GetFontId(), arcLabelText_->GetFontSize(),
309                        arcCenter_, style_->letterSpace_, orientation_, arcTextInfo_);
310     int16_t arcTextWidth = textRect.GetWidth();
311     int16_t arcTextHeight = textRect.GetHeight();
312 
313     if (compatibilityMode_) {
314         SetPosition(textRect.GetX(), textRect.GetY());
315         Resize(arcTextWidth, arcTextHeight);
316     }
317 
318     arcTextInfo_.arcCenter.x = arcCenter_.x - GetX() + style_->borderWidth_ + style_->paddingLeft_;
319     arcTextInfo_.arcCenter.y = arcCenter_.y - GetY() + style_->borderWidth_ + style_->paddingTop_;
320     textSize_.x = arcTextWidth;
321     textSize_.y = arcTextHeight;
322     Invalidate();
323 }
324 
GetLineEnd(int16_t maxLength)325 uint32_t UIArcLabel::GetLineEnd(int16_t maxLength)
326 {
327     const char* text = arcLabelText_->GetText();
328     if (text == nullptr) {
329         return 0;
330     }
331 
332     return TypedText::GetNextLine(&text[arcTextInfo_.lineStart], arcLabelText_->GetFontId(),
333         arcLabelText_->GetFontSize(), style_->letterSpace_, maxLength);
334 }
335 
MeasureArcTextInfo()336 void UIArcLabel::MeasureArcTextInfo()
337 {
338     const char* text = arcLabelText_->GetText();
339     if (text == nullptr) {
340         return;
341     }
342     uint16_t letterHeight = UIFont::GetInstance()->GetHeight(arcLabelText_->GetFontId(), arcLabelText_->GetFontSize());
343     if (compatibilityMode_) {
344         arcTextInfo_.radius = ((orientation_ == TextOrientation::INSIDE) ? radius_ : (radius_ - letterHeight));
345     } else {
346         arcTextInfo_.radius = radius_;
347     }
348     if (arcTextInfo_.radius == 0) {
349         return;
350     }
351 
352     uint16_t arcAngle;
353     if (startAngle_ < endAngle_) {
354         arcAngle = endAngle_ - startAngle_;
355         arcTextInfo_.direct = TEXT_DIRECT_LTR; // Clockwise
356         arcLabelText_->SetDirect(TEXT_DIRECT_LTR);
357     } else {
358         arcAngle = startAngle_ - endAngle_;
359         arcTextInfo_.direct = TEXT_DIRECT_RTL; // Counterclockwise
360         arcLabelText_->SetDirect(TEXT_DIRECT_RTL);
361     }
362 
363     OnMeasureArcTextInfo(arcAngle, letterHeight);
364 }
365 
OnMeasureArcTextInfo(const uint16_t arcAngle,const uint16_t letterHeight)366 void UIArcLabel::OnMeasureArcTextInfo(const uint16_t arcAngle, const uint16_t letterHeight)
367 {
368     const char* text = arcLabelText_->GetText();
369     if (text == nullptr) {
370         return;
371     }
372 
373     // calculate max arc length
374     float maxLength = static_cast<float>((UI_PI * radius_ * arcAngle) / SEMICIRCLE_IN_DEGREE);
375     arcTextInfo_.lineStart = 0;
376 
377     Rect rect;
378     rect.SetWidth(static_cast<int16_t>(maxLength));
379     arcLabelText_->ReMeasureTextSize(rect, *style_);
380 
381     arcTextInfo_.lineEnd = GetLineEnd(static_cast<int16_t>(maxLength));
382     arcTextInfo_.startAngle = startAngle_ > CIRCLE_IN_DEGREE ? startAngle_ % CIRCLE_IN_DEGREE : startAngle_;
383     arcTextInfo_.endAngle = endAngle_ > CIRCLE_IN_DEGREE ? endAngle_ % CIRCLE_IN_DEGREE : endAngle_;
384 
385     int16_t actLength = GetArcLength();
386     if ((arcLabelText_->GetHorAlign() != TEXT_ALIGNMENT_LEFT) && (actLength < maxLength)) {
387         float gapLength = maxLength - actLength;
388         if (arcLabelText_->GetHorAlign() == TEXT_ALIGNMENT_CENTER) {
389             gapLength = gapLength / 2; // 2: half
390         }
391         arcTextInfo_.startAngle += TypedText::GetAngleForArcLen(gapLength, letterHeight, arcTextInfo_.radius,
392                                                                 arcTextInfo_.direct, orientation_);
393     }
394 
395     int16_t maxTextLength = arcLabelText_->GetMetaTextWidth(*style_);
396 
397     float maxTextAngle = 0.0f;
398     if (compatibilityMode_) {
399         maxTextAngle = TypedText::GetAngleForArcLen(maxTextLength, letterHeight, arcTextInfo_.radius,
400             arcTextInfo_.direct, orientation_);
401     } else {
402         maxTextAngle = TypedText::GetAngleForArcLen(maxTextLength, style_->letterSpace_, arcTextInfo_.radius);
403         maxTextAngle = arcLabelText_->GetDirect() == TEXT_DIRECT_RTL ? -maxTextAngle : maxTextAngle;
404     }
405 
406     if (arcLabelText_->GetDirect() == TEXT_DIRECT_LTR) {
407         animator_.secondLapOffsetAngle_ = -maxTextAngle;
408     } else if (arcLabelText_->GetDirect() == TEXT_DIRECT_RTL) {
409         animator_.secondLapOffsetAngle_ = maxTextAngle;
410     }
411 }
412 
GetArcLength()413 uint16_t UIArcLabel::GetArcLength()
414 {
415     const char* text = arcLabelText_->GetText();
416     if (text == nullptr) {
417         return 0;
418     }
419 
420     return TypedText::GetTextWidth(&text[arcTextInfo_.lineStart], arcLabelText_->GetFontId(),
421                                    arcLabelText_->GetFontSize(), (arcTextInfo_.lineEnd - arcTextInfo_.lineStart),
422                                    style_->letterSpace_);
423 }
424 
Start()425 void UIArcLabel::Start()
426 {
427     if (arcLabelText_->GetDirect() == TEXT_DIRECT_RTL) {
428         arcLabelText_->SetAlign(TEXT_ALIGNMENT_RIGHT, TEXT_ALIGNMENT_CENTER);
429     } else {
430         arcLabelText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
431     }
432     if (hasAnimator_) {
433         static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollCount(animator_.rollCount);
434     } else {
435         ArcLabelAnimator* animator = new ArcLabelAnimator(animator_.rollCount, this);
436         if (animator == nullptr) {
437             GRAPHIC_LOGE("new ArcLabelAnimator fail");
438             return;
439         }
440         animator->SetRollSpeed(animator_.speed);
441         animator->RegisterScrollListener(animator_.scrollListener);
442         animator_.animator = animator;
443         hasAnimator_ = true;
444     }
445     animator_.animator->Start();
446 }
447 
Stop()448 void UIArcLabel::Stop()
449 {
450     if (hasAnimator_) {
451         static_cast<ArcLabelAnimator*>(animator_.animator)->Stop();
452     }
453 }
454 
SetRollCount(const uint16_t rollCount)455 void UIArcLabel::SetRollCount(const uint16_t rollCount)
456 {
457     if (hasAnimator_) {
458         static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollCount(rollCount);
459     } else {
460         animator_.rollCount = rollCount;
461     }
462 }
463 
RegisterScrollListener(ArcLabelScrollListener * scrollListener)464 void UIArcLabel::RegisterScrollListener(ArcLabelScrollListener* scrollListener)
465 {
466     if (hasAnimator_) {
467         static_cast<ArcLabelAnimator*>(animator_.animator)->RegisterScrollListener(scrollListener);
468     } else {
469         animator_.scrollListener = scrollListener;
470     }
471 }
472 
SetRollSpeed(const uint16_t speed)473 void UIArcLabel::SetRollSpeed(const uint16_t speed)
474 {
475     if (hasAnimator_) {
476         static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollSpeed(speed);
477     } else {
478         animator_.speed = speed;
479     }
480 }
481 
GetRollSpeed() const482 uint16_t UIArcLabel::GetRollSpeed() const
483 {
484     if (hasAnimator_) {
485         return static_cast<ArcLabelAnimator*>(animator_.animator)->GetRollSpeed();
486     }
487 
488     return animator_.speed;
489 }
490 } // namespace OHOS
491