* Copyright (c) 2020-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "components/ui_arc_label.h"
#include "common/typed_text.h"
#include "draw/draw_label.h"
#include "engines/gfx/gfx_engine_manager.h"
#include "font/ui_font.h"
#include "themes/theme_manager.h"
namespace OHOS {
static constexpr uint16_t DEFAULT_ARC_LABEL_ROLL_COUNT = 1;
static constexpr uint16_t DEFAULT_ARC_LABEL_ANIMATOR_SPEED = 10;
class ArcLabelAnimator : public Animator, public AnimatorCallback {
public:
ArcLabelAnimator(uint16_t rollCount, UIView* view)
: Animator(this, view, 0, true),
waitCount_(ANIM_WAIT_COUNT),
speed_(0),
preRunTime_(0),
decimal_(0),
rollCount_(rollCount)
{
}
virtual ~ArcLabelAnimator() {}
void Callback(UIView* view) override
{
if (view == nullptr || rollCount_ == 0) {
return;
}
uint32_t curTime = GetRunTime();
if (waitCount_ > 0) {
waitCount_--;
preRunTime_ = curTime;
return;
}
if (curTime == preRunTime_) {
return;
}
uint32_t time = (curTime > preRunTime_) ? (curTime - preRunTime_) : (UINT32_MAX - preRunTime_ + curTime);
float floatStep = (static_cast<float>(time * speed_) / 1000) + decimal_;
uint16_t integerStep = static_cast<uint16_t>(floatStep);
decimal_ = floatStep - integerStep;
preRunTime_ = curTime;
if (integerStep == 0) {
return;
}
CalculatedOffsetAngle(view);
}
void SetRollSpeed(uint16_t speed)
{
speed_ = speed;
}
int16_t GetRollSpeed() const
{
return speed_;
}
void SetRollCount(uint16_t rollCount)
{
rollCount_ = rollCount;
}
void RegisterScrollListener(ArcLabelScrollListener* scrollListener)
{
scrollListener_ = scrollListener;
}
private:
void CalculatedOffsetAngle(UIView* view)
{
if (view == nullptr) {
return;
}
UIArcLabel* arcLabel = static_cast<UIArcLabel*>(view);
if (arcLabel == nullptr) {
return;
}
int16_t startAngle = arcLabel->GetArcTextStartAngle();
int16_t endAngle = arcLabel->GetArcTextEndAngle();
uint16_t arcAngle = (startAngle < endAngle) ? (endAngle - startAngle) :
(startAngle - endAngle);
if (arcLabel->offsetAngle_ < arcAngle) {
arcLabel->offsetAngle_ += DEFAULT_CHANGE_ANGLE;
} else {
rollCount_--;
if (rollCount_ > 0) {
arcLabel->offsetAngle_ = arcLabel->animator_.secondLapOffsetAngle_;
}
}
if (rollCount_ == 0) {
if (scrollListener_) {
scrollListener_->Finish();
}
Stop();
}
view->Invalidate();
}
private:
static constexpr uint8_t ANIM_WAIT_COUNT = 50;
static constexpr float DEFAULT_CHANGE_ANGLE = 1.0f;
uint16_t waitCount_;
uint16_t speed_;
uint32_t preRunTime_;
float decimal_;
uint16_t rollCount_;
ArcLabelScrollListener* scrollListener_;
};
UIArcLabel::UIArcLabel()
: arcLabelText_(nullptr),
compatibilityMode_(true),
offsetAngle_(0.0f),
arcTextInfo_{0},
needRefresh_(false),
hasAnimator_(false),
textSize_({0, 0}),
radius_(0),
startAngle_(0),
endAngle_(0),
arcCenter_({0, 0}),
orientation_(TextOrientation::INSIDE)
{
Theme* theme = ThemeManager::GetInstance().GetCurrent();
style_ = (theme != nullptr) ? &(theme->GetLabelStyle()) : &(StyleDefault::GetLabelStyle());
animator_.animator = nullptr;
animator_.scrollListener = nullptr;
animator_.speed = DEFAULT_ARC_LABEL_ANIMATOR_SPEED;
animator_.rollCount = DEFAULT_ARC_LABEL_ROLL_COUNT;
animator_.secondLapOffsetAngle_ = 0.0f;
}
UIArcLabel::~UIArcLabel()
{
if (arcLabelText_ != nullptr) {
delete arcLabelText_;
arcLabelText_ = nullptr;
}
if (hasAnimator_) {
delete animator_.animator;
animator_.animator = nullptr;
hasAnimator_ = false;
}
}
void UIArcLabel::SetStyle(uint8_t key, int64_t value)
{
UIView::SetStyle(key, value);
RefreshArcLabel();
}
void UIArcLabel::SetText(const char* text)
{
if (text == nullptr) {
return;
}
InitArcLabelText();
arcLabelText_->SetText(text);
if (arcLabelText_->IsNeedRefresh()) {
RefreshArcLabel();
}
}
const char* UIArcLabel::GetText() const
{
return (arcLabelText_ == nullptr) ? nullptr : arcLabelText_->GetText();
}
void UIArcLabel::SetAlign(UITextLanguageAlignment horizontalAlign)
{
InitArcLabelText();
arcLabelText_->SetAlign(horizontalAlign, TEXT_ALIGNMENT_TOP);
if (arcLabelText_->IsNeedRefresh()) {
RefreshArcLabel();
}
}
UITextLanguageAlignment UIArcLabel::GetHorAlign()
{
InitArcLabelText();
return arcLabelText_->GetHorAlign();
}
UITextLanguageDirect UIArcLabel::GetDirect()
{
InitArcLabelText();
return arcLabelText_->GetDirect();
}
void UIArcLabel::SetFontId(uint16_t fontId)
{
InitArcLabelText();
arcLabelText_->SetFontId(fontId);
if (arcLabelText_->IsNeedRefresh()) {
RefreshArcLabel();
}
}
uint16_t UIArcLabel::GetFontId()
{
InitArcLabelText();
return arcLabelText_->GetFontId();
}
void UIArcLabel::SetFont(const char* name, uint8_t size)
{
if (name == nullptr) {
return;
}
InitArcLabelText();
arcLabelText_->SetFont(name, size);
if (arcLabelText_->IsNeedRefresh()) {
RefreshArcLabel();
}
}
void UIArcLabel::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
InitArcLabelText();
const char* text = arcLabelText_->GetText();
if ((text == nullptr) || (radius_ == 0)) {
return;
}
OpacityType opa = GetMixOpaScale();
UIView::OnDraw(gfxDstBuffer, invalidatedArea);
DrawArcText(gfxDstBuffer, invalidatedArea, opa, arcTextInfo_, orientation_);
}
void UIArcLabel::DrawArcText(BufferInfo& gfxDstBuffer,
const Rect& mask,
OpacityType opaScale,
ArcTextInfo arcTextInfo,
TextOrientation orientation)
{
Point center;
center.x = arcTextInfo_.arcCenter.x + GetRect().GetX();
center.y = arcTextInfo_.arcCenter.y + GetRect().GetY();
Rect temp = mask;
if (compatibilityMode_ && hasAnimator_) {
temp.SetLeft(center.x - radius_);
temp.SetTop(center.y - radius_);
temp.SetWidth(radius_ * 2);
temp.SetHeight(radius_ * 2);
}
arcTextInfo.hasAnimator = hasAnimator_;
DrawLabel::DrawArcText(gfxDstBuffer, temp, arcLabelText_->GetText(), center, arcLabelText_->GetFontId(),
arcLabelText_->GetFontSize(), arcTextInfo, offsetAngle_,
orientation, *style_, opaScale, compatibilityMode_);
}
Rect UIArcLabel::GetArcTextRect(const char* text, uint16_t fontId, uint8_t fontSize, const Point& arcCenter,
int16_t letterSpace, TextOrientation orientation, const ArcTextInfo& arcTextInfo)
{
return TypedText::GetArcTextRect(text, fontId, fontSize, arcCenter, letterSpace, orientation, arcTextInfo);
}
void UIArcLabel::RefreshArcLabel()
{
Invalidate();
if (!needRefresh_) {
needRefresh_ = true;
}
}
void UIArcLabel::ReMeasure()
{
if (!needRefresh_) {
return;
}
needRefresh_ = false;
InitArcLabelText();
MeasureArcTextInfo();
arcTextInfo_.shapingFontId = arcLabelText_->GetShapingFontId();
arcTextInfo_.codePoints = arcLabelText_->GetCodePoints();
arcTextInfo_.codePointsNum = arcLabelText_->GetCodePointNum();
Rect textRect =
GetArcTextRect(arcLabelText_->GetText(), arcLabelText_->GetFontId(), arcLabelText_->GetFontSize(),
arcCenter_, style_->letterSpace_, orientation_, arcTextInfo_);
int16_t arcTextWidth = textRect.GetWidth();
int16_t arcTextHeight = textRect.GetHeight();
if (compatibilityMode_) {
SetPosition(textRect.GetX(), textRect.GetY());
Resize(arcTextWidth, arcTextHeight);
}
arcTextInfo_.arcCenter.x = arcCenter_.x - GetX() + style_->borderWidth_ + style_->paddingLeft_;
arcTextInfo_.arcCenter.y = arcCenter_.y - GetY() + style_->borderWidth_ + style_->paddingTop_;
textSize_.x = arcTextWidth;
textSize_.y = arcTextHeight;
Invalidate();
}
uint32_t UIArcLabel::GetLineEnd(int16_t maxLength)
{
const char* text = arcLabelText_->GetText();
if (text == nullptr) {
return 0;
}
return TypedText::GetNextLine(&text[arcTextInfo_.lineStart], arcLabelText_->GetFontId(),
arcLabelText_->GetFontSize(), style_->letterSpace_, maxLength);
}
void UIArcLabel::MeasureArcTextInfo()
{
const char* text = arcLabelText_->GetText();
if (text == nullptr) {
return;
}
uint16_t letterHeight = UIFont::GetInstance()->GetHeight(arcLabelText_->GetFontId(), arcLabelText_->GetFontSize());
if (compatibilityMode_) {
arcTextInfo_.radius = ((orientation_ == TextOrientation::INSIDE) ? radius_ : (radius_ - letterHeight));
} else {
arcTextInfo_.radius = radius_;
}
if (arcTextInfo_.radius == 0) {
return;
}
uint16_t arcAngle;
if (startAngle_ < endAngle_) {
arcAngle = endAngle_ - startAngle_;
arcTextInfo_.direct = TEXT_DIRECT_LTR;
arcLabelText_->SetDirect(TEXT_DIRECT_LTR);
} else {
arcAngle = startAngle_ - endAngle_;
arcTextInfo_.direct = TEXT_DIRECT_RTL;
arcLabelText_->SetDirect(TEXT_DIRECT_RTL);
}
OnMeasureArcTextInfo(arcAngle, letterHeight);
}
void UIArcLabel::OnMeasureArcTextInfo(const uint16_t arcAngle, const uint16_t letterHeight)
{
const char* text = arcLabelText_->GetText();
if (text == nullptr) {
return;
}
float maxLength = static_cast<float>((UI_PI * radius_ * arcAngle) / SEMICIRCLE_IN_DEGREE);
arcTextInfo_.lineStart = 0;
Rect rect;
rect.SetWidth(static_cast<int16_t>(maxLength));
arcLabelText_->ReMeasureTextSize(rect, *style_);
arcTextInfo_.lineEnd = GetLineEnd(static_cast<int16_t>(maxLength));
arcTextInfo_.startAngle = startAngle_ > CIRCLE_IN_DEGREE ? startAngle_ % CIRCLE_IN_DEGREE : startAngle_;
arcTextInfo_.endAngle = endAngle_ > CIRCLE_IN_DEGREE ? endAngle_ % CIRCLE_IN_DEGREE : endAngle_;
int16_t actLength = GetArcLength();
if ((arcLabelText_->GetHorAlign() != TEXT_ALIGNMENT_LEFT) && (actLength < maxLength)) {
float gapLength = maxLength - actLength;
if (arcLabelText_->GetHorAlign() == TEXT_ALIGNMENT_CENTER) {
gapLength = gapLength / 2;
}
arcTextInfo_.startAngle += TypedText::GetAngleForArcLen(gapLength, letterHeight, arcTextInfo_.radius,
arcTextInfo_.direct, orientation_);
}
int16_t maxTextLength = arcLabelText_->GetMetaTextWidth(*style_);
float maxTextAngle = 0.0f;
if (compatibilityMode_) {
maxTextAngle = TypedText::GetAngleForArcLen(maxTextLength, letterHeight, arcTextInfo_.radius,
arcTextInfo_.direct, orientation_);
} else {
maxTextAngle = TypedText::GetAngleForArcLen(maxTextLength, style_->letterSpace_, arcTextInfo_.radius);
maxTextAngle = arcLabelText_->GetDirect() == TEXT_DIRECT_RTL ? -maxTextAngle : maxTextAngle;
}
if (arcLabelText_->GetDirect() == TEXT_DIRECT_LTR) {
animator_.secondLapOffsetAngle_ = -maxTextAngle;
} else if (arcLabelText_->GetDirect() == TEXT_DIRECT_RTL) {
animator_.secondLapOffsetAngle_ = maxTextAngle;
}
}
uint16_t UIArcLabel::GetArcLength()
{
const char* text = arcLabelText_->GetText();
if (text == nullptr) {
return 0;
}
return TypedText::GetTextWidth(&text[arcTextInfo_.lineStart], arcLabelText_->GetFontId(),
arcLabelText_->GetFontSize(), (arcTextInfo_.lineEnd - arcTextInfo_.lineStart),
style_->letterSpace_);
}
void UIArcLabel::Start()
{
if (arcLabelText_->GetDirect() == TEXT_DIRECT_RTL) {
arcLabelText_->SetAlign(TEXT_ALIGNMENT_RIGHT, TEXT_ALIGNMENT_CENTER);
} else {
arcLabelText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
}
if (hasAnimator_) {
static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollCount(animator_.rollCount);
} else {
ArcLabelAnimator* animator = new ArcLabelAnimator(animator_.rollCount, this);
if (animator == nullptr) {
GRAPHIC_LOGE("new ArcLabelAnimator fail");
return;
}
animator->SetRollSpeed(animator_.speed);
animator->RegisterScrollListener(animator_.scrollListener);
animator_.animator = animator;
hasAnimator_ = true;
}
animator_.animator->Start();
}
void UIArcLabel::Stop()
{
if (hasAnimator_) {
static_cast<ArcLabelAnimator*>(animator_.animator)->Stop();
}
}
void UIArcLabel::SetRollCount(const uint16_t rollCount)
{
if (hasAnimator_) {
static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollCount(rollCount);
} else {
animator_.rollCount = rollCount;
}
}
void UIArcLabel::RegisterScrollListener(ArcLabelScrollListener* scrollListener)
{
if (hasAnimator_) {
static_cast<ArcLabelAnimator*>(animator_.animator)->RegisterScrollListener(scrollListener);
} else {
animator_.scrollListener = scrollListener;
}
}
void UIArcLabel::SetRollSpeed(const uint16_t speed)
{
if (hasAnimator_) {
static_cast<ArcLabelAnimator*>(animator_.animator)->SetRollSpeed(speed);
} else {
animator_.speed = speed;
}
}
uint16_t UIArcLabel::GetRollSpeed() const
{
if (hasAnimator_) {
return static_cast<ArcLabelAnimator*>(animator_.animator)->GetRollSpeed();
}
return animator_.speed;
}
}