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