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 24namespace OHOS { 25static constexpr uint16_t DEFAULT_ARC_LABEL_ROLL_COUNT = 1; 26static constexpr uint16_t DEFAULT_ARC_LABEL_ANIMATOR_SPEED = 10; 27 28class ArcLabelAnimator : public Animator, public AnimatorCallback { 29public: 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 40 virtual ~ArcLabelAnimator() {} 41 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 71 void SetRollSpeed(uint16_t speed) 72 { 73 speed_ = speed; 74 } 75 76 int16_t GetRollSpeed() const 77 { 78 return speed_; 79 } 80 81 void SetRollCount(uint16_t rollCount) 82 { 83 rollCount_ = rollCount; 84 } 85 86 void RegisterScrollListener(ArcLabelScrollListener* scrollListener) 87 { 88 scrollListener_ = scrollListener; 89 } 90 91private: 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 125private: 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 137UIArcLabel::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 161UIArcLabel::~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 175void UIArcLabel::SetStyle(uint8_t key, int64_t value) 176{ 177 UIView::SetStyle(key, value); 178 RefreshArcLabel(); 179} 180 181void 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 193const char* UIArcLabel::GetText() const 194{ 195 return (arcLabelText_ == nullptr) ? nullptr : arcLabelText_->GetText(); 196} 197 198void UIArcLabel::SetAlign(UITextLanguageAlignment horizontalAlign) 199{ 200 InitArcLabelText(); 201 arcLabelText_->SetAlign(horizontalAlign, TEXT_ALIGNMENT_TOP); 202 if (arcLabelText_->IsNeedRefresh()) { 203 RefreshArcLabel(); 204 } 205} 206 207UITextLanguageAlignment UIArcLabel::GetHorAlign() 208{ 209 InitArcLabelText(); 210 return arcLabelText_->GetHorAlign(); 211} 212 213UITextLanguageDirect UIArcLabel::GetDirect() 214{ 215 InitArcLabelText(); 216 return arcLabelText_->GetDirect(); 217} 218 219void UIArcLabel::SetFontId(uint16_t fontId) 220{ 221 InitArcLabelText(); 222 arcLabelText_->SetFontId(fontId); 223 if (arcLabelText_->IsNeedRefresh()) { 224 RefreshArcLabel(); 225 } 226} 227 228uint16_t UIArcLabel::GetFontId() 229{ 230 InitArcLabelText(); 231 return arcLabelText_->GetFontId(); 232} 233 234void 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 246void 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 258void 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 281Rect 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 287void UIArcLabel::RefreshArcLabel() 288{ 289 Invalidate(); 290 if (!needRefresh_) { 291 needRefresh_ = true; 292 } 293} 294 295void 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 325uint32_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 336void 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 366void 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 413uint16_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 425void 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 448void UIArcLabel::Stop() 449{ 450 if (hasAnimator_) { 451 static_cast<ArcLabelAnimator*>(animator_.animator)->Stop(); 452 } 453} 454 455void 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 464void 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 473void 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 482uint16_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