* Copyright (c) 2020-2022 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_label.h"
#include "font/ui_font.h"
#include "gfx_utils/graphic_log.h"
#include "themes/theme_manager.h"
namespace OHOS {
class LabelAnimator : public Animator, public AnimatorCallback {
public:
LabelAnimator(uint16_t textX, uint16_t labelX, int16_t startPos, UIView* view)
: Animator(this, view, 0, true),
startPos_(startPos),
textX_(textX),
labelX_(labelX),
offsetX_(startPos),
waitCount_(ANIM_WAIT_COUNT),
speed_(0),
preRunTime_(0),
decimal_(0)
{
}
virtual ~LabelAnimator() {}
int16_t GetStartPos() const
{
return startPos_;
}
void SetStartPos(int16_t pos)
{
startPos_ = pos;
}
void UpdateWidth(uint16_t textWidth, uint16_t labelWidth)
{
textX_ = textWidth;
labelX_ = labelWidth;
waitCount_ = ANIM_WAIT_COUNT;
preRunTime_ = 0;
decimal_ = 0;
offsetX_ = startPos_;
static_cast<UILabel*>(view_)->offsetX_ = offsetX_;
view_->Invalidate();
}
void Callback(UIView* view) override
{
if (view == nullptr) {
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) {
offsetX_ -= integerStep;
} else {
return;
}
offsetX_ = ((offsetX_ - labelX_) % (textX_ + labelX_)) + labelX_;
static_cast<UILabel*>(view)->offsetX_ = offsetX_;
view->Invalidate();
}
void SetAnimatorSpeed(uint16_t animSpeed)
{
speed_ = animSpeed;
decimal_ = 0;
}
int16_t GetAnimatorSpeed() const
{
return speed_;
}
private:
static constexpr uint8_t ANIM_WAIT_COUNT = 50;
int16_t startPos_;
uint16_t textX_;
uint16_t labelX_;
int16_t offsetX_;
uint16_t waitCount_;
uint16_t speed_;
uint32_t preRunTime_;
float decimal_;
};
UILabel::UILabel()
: labelText_(nullptr),
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
scaleRatio_(DEFAULT_SCALE_RATIO),
maxLines_(MAX_LINE_COUNT),
limitTextHeight_(0),
#endif
needRefresh_(false),
useTextColor_(false),
hasAnimator_(false),
lineBreakMode_(LINE_BREAK_ELLIPSIS),
ellipsisIndex_(Text::TEXT_ELLIPSIS_END_INV),
offsetX_(0),
textColor_(Color::White()),
animator_{nullptr}
{
Theme* theme = ThemeManager::GetInstance().GetCurrent();
Style& style = (theme != nullptr) ? (theme->GetLabelStyle()) : (StyleDefault::GetLabelStyle());
UIView::SetStyle(style);
animator_.speed = DEFAULT_ANIMATOR_SPEED;
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
scaleRatio_ = Text::GetDefaultScale();
#endif
}
UILabel::~UILabel()
{
if (hasAnimator_) {
delete animator_.animator;
animator_.animator = nullptr;
hasAnimator_ = false;
}
if (labelText_ != nullptr) {
delete labelText_;
labelText_ = nullptr;
}
}
void UILabel::InitLabelText()
{
if (labelText_ == nullptr) {
labelText_ = new Text();
}
}
int16_t UILabel::GetWidth()
{
InitLabelText();
if (needRefresh_ && labelText_->IsExpandWidth()) {
ReMeasure();
}
return UIView::GetWidth();
}
int16_t UILabel::GetHeight()
{
InitLabelText();
if (needRefresh_ && labelText_->IsExpandHeight()) {
ReMeasure();
}
return UIView::GetHeight();
}
void UILabel::SetStyle(uint8_t key, int64_t value)
{
UIView::SetStyle(key, value);
RefreshLabel();
}
void UILabel::SetText(const char* text)
{
InitLabelText();
labelText_->SetText(text);
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
void UILabel::SetText(const SpannableString* text)
{
InitLabelText();
labelText_->SetSpannableString(text);
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
void UILabel::SetAbsoluteSizeSpan(uint16_t start, uint16_t end, uint8_t size)
{
if (labelText_ == nullptr) {
return;
}
labelText_->SetAbsoluteSizeSpan(start, end, size);
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
void UILabel::SetRelativeSizeSpan(uint16_t start, uint16_t end, float size)
{
if (labelText_ == nullptr) {
return;
}
labelText_->SetRelativeSizeSpan(start, end, size);
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
uint8_t UILabel::GetFontSize()
{
InitLabelText();
return labelText_->GetFontSize();
}
void UILabel::SetLineBreakMode(const uint8_t lineBreakMode)
{
InitLabelText();
if ((lineBreakMode >= LINE_BREAK_MAX) || (lineBreakMode_ == lineBreakMode)) {
return;
}
lineBreakMode_ = lineBreakMode;
if ((lineBreakMode_ == LINE_BREAK_ADAPT) || (lineBreakMode_ == LINE_BREAK_STRETCH) ||
(lineBreakMode_ == LINE_BREAK_MARQUEE)) {
labelText_->SetExpandWidth(true);
} else {
labelText_->SetExpandWidth(false);
}
if ((lineBreakMode_ == LINE_BREAK_ADAPT) || (lineBreakMode_ == LINE_BREAK_WRAP)) {
labelText_->SetExpandHeight(true);
} else {
labelText_->SetExpandHeight(false);
}
if (lineBreakMode_ != LINE_BREAK_MARQUEE) {
offsetX_ = 0;
if (hasAnimator_) {
animator_.animator->Stop();
}
}
RefreshLabel();
}
void UILabel::SetAlign(UITextLanguageAlignment horizontalAlign, UITextLanguageAlignment verticalAlign)
{
InitLabelText();
labelText_->SetAlign(horizontalAlign, verticalAlign);
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
void UILabel::SetFontId(uint16_t fontId)
{
InitLabelText();
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
labelText_->SetFontId(fontId, scaleRatio_);
#else
labelText_->SetFontId(fontId);
#endif
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
void UILabel::SetFont(const char* name, uint8_t size)
{
InitLabelText();
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
labelText_->SetFont(name, size, scaleRatio_);
#else
labelText_->SetFont(name, size);
#endif
if (labelText_->IsNeedRefresh()) {
RefreshLabel();
}
}
uint16_t UILabel::GetTextWidth()
{
InitLabelText();
if (labelText_->IsNeedRefresh()) {
ReMeasure();
}
return labelText_->GetTextSize().x;
}
uint16_t UILabel::GetTextHeight()
{
InitLabelText();
if (labelText_->IsNeedRefresh()) {
ReMeasure();
}
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
if ((lineBreakMode_ == LINE_BREAK_ELLIPSIS) && (limitTextHeight_ > 0)) {
return limitTextHeight_;
}
#endif
return labelText_->GetTextSize().y;
}
void UILabel::SetWidth(int16_t width)
{
if (GetWidth() != width) {
UIView::SetWidth(width);
RefreshLabel();
}
}
void UILabel::SetHeight(int16_t height)
{
if (GetHeight() != height) {
UIView::SetHeight(height);
RefreshLabel();
}
}
void UILabel::RefreshLabel()
{
Invalidate();
ellipsisIndex_ = Text::TEXT_ELLIPSIS_END_INV;
if (!needRefresh_) {
needRefresh_ = true;
}
}
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
void UILabel::ReMeasure()
{
#if defined(CONFIG_DYNAMIC_LAYOUT) && (CONFIG_DYNAMIC_LAYOUT == 1)
UIView::ReMeasure();
#endif
if (!needRefresh_) {
return;
}
needRefresh_ = false;
InitLabelText();
Style style = GetStyleConst();
style.textColor_ = GetTextColor();
bool flag = false;
if ((transMap_ != nullptr) && !transMap_->IsInvalid()) {
transMap_->SetInvalid(true);
flag = true;
}
TextSizeLimitArg limit = { GetContentRect(), maxLines_ };
bool skipElps = ReMeasureLabelTextSize(limit, style);
Point textSize = labelText_->GetTextSize();
switch (lineBreakMode_) {
case LINE_BREAK_ADAPT:
Resize(textSize.x, textSize.y);
break;
case LINE_BREAK_STRETCH:
SetWidth(textSize.x);
break;
case LINE_BREAK_WRAP:
SetHeight(textSize.y);
break;
case LINE_BREAK_ELLIPSIS:
if (skipElps) {
break;
}
ellipsisIndex_ = labelText_->GetEllipsisIndex(limit.rect, style);
labelText_->ReMeasureTextWidthInEllipsisMode(limit.rect, style, ellipsisIndex_);
break;
case LINE_BREAK_MARQUEE:
RemeasureForMarquee(textSize.x);
break;
default:
break;
}
if ((transMap_ != nullptr) && flag) {
transMap_->SetInvalid(false);
}
}
#else
void UILabel::ReMeasure()
{
#if defined(CONFIG_DYNAMIC_LAYOUT) && (CONFIG_DYNAMIC_LAYOUT == 1)
UIView::ReMeasure();
#endif
if (!needRefresh_) {
return;
}
needRefresh_ = false;
InitLabelText();
Style style = GetStyleConst();
style.textColor_ = GetTextColor();
bool flag = false;
if ((transMap_ != nullptr) && !transMap_->IsInvalid()) {
transMap_->SetInvalid(true);
flag = true;
}
labelText_->ReMeasureTextSize(GetContentRect(), style);
Point textSize = labelText_->GetTextSize();
switch (lineBreakMode_) {
case LINE_BREAK_ADAPT:
Resize(textSize.x, textSize.y);
break;
case LINE_BREAK_STRETCH:
SetWidth(textSize.x);
break;
case LINE_BREAK_WRAP:
SetHeight(textSize.y);
break;
case LINE_BREAK_ELLIPSIS:
ellipsisIndex_ = labelText_->GetEllipsisIndex(GetContentRect(), style);
labelText_->ReMeasureTextWidthInEllipsisMode(GetContentRect(), style, ellipsisIndex_);
break;
case LINE_BREAK_MARQUEE:
RemeasureForMarquee(textSize.x);
break;
default:
break;
}
if ((transMap_ != nullptr) && flag) {
transMap_->SetInvalid(false);
}
}
#endif
void UILabel::RemeasureForMarquee(int16_t textWidth)
{
int16_t rectWidth = GetWidth();
if (textWidth > rectWidth) {
offsetX_ = GetRollStartPos();
if (labelText_->GetDirect() == TEXT_DIRECT_RTL) {
labelText_->SetAlign(TEXT_ALIGNMENT_RIGHT, GetVerAlign());
} else {
labelText_->SetAlign(TEXT_ALIGNMENT_LEFT, GetVerAlign());
}
if (hasAnimator_) {
static_cast<LabelAnimator*>(animator_.animator)->UpdateWidth(textWidth, rectWidth);
} else {
LabelAnimator* animator = new LabelAnimator(textWidth, rectWidth, offsetX_, this);
if (animator == nullptr) {
GRAPHIC_LOGE("new LabelAnimator fail");
return;
}
animator->SetAnimatorSpeed(animator_.speed);
animator_.animator = animator;
hasAnimator_ = true;
}
animator_.animator->Start();
} else {
offsetX_ = 0;
if (hasAnimator_) {
animator_.animator->Stop();
}
}
}
void UILabel::SetRollStartPos(int16_t pos)
{
if (hasAnimator_) {
static_cast<LabelAnimator*>(animator_.animator)->SetStartPos(pos);
} else {
animator_.pos = pos;
}
}
int16_t UILabel::GetRollStartPos() const
{
return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetStartPos() : animator_.pos;
}
void UILabel::SetRollSpeed(uint16_t speed)
{
if (hasAnimator_) {
static_cast<LabelAnimator*>(animator_.animator)->SetAnimatorSpeed(speed);
} else {
animator_.speed = speed;
}
}
uint16_t UILabel::GetRollSpeed() const
{
return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetAnimatorSpeed() : animator_.speed;
}
void UILabel::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
InitLabelText();
UIView::OnDraw(gfxDstBuffer, invalidatedArea);
Style style = GetStyleConst();
style.textColor_ = GetTextColor();
OpacityType opa = GetMixOpaScale();
labelText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(),
GetContentRect(), offsetX_, style, ellipsisIndex_, opa);
}
#if defined(CONFIG_SCALE_FONT_SIZE) && (CONFIG_SCALE_FONT_SIZE == 1)
bool IsSameScaleRatio(float ratioA, float ratioB)
{
return fabs(ratioA - ratioB) < 1e-6f;
}
uint8_t UILabel::GetOriginFontSize()
{
InitLabelText();
return labelText_->GetOriginFontSize();
}
bool UILabel::SetFontSizeScale(float ratio)
{
InitLabelText();
float realRatio = ratio;
if (realRatio > MAX_SCALE_RATIO) {
GRAPHIC_LOGW("UILabel::SetFontSizeScale ratio over max: %f", ratio);
realRatio = MAX_SCALE_RATIO;
}
if (IsSameScaleRatio(realRatio, scaleRatio_)) {
return true;
}
if (!labelText_->SetFontSizeScale(realRatio)) {
return false;
}
scaleRatio_ = realRatio;
RefreshLabel();
return true;
}
float UILabel::GetFontSizeScale() const
{
return scaleRatio_;
}
bool UILabel::HasFontSizeScale() const
{
return !IsSameScaleRatio(scaleRatio_, DEFAULT_SCALE_RATIO);
}
bool UILabel::SetMaxLines(int32_t count)
{
if (count > MAX_LINE_COUNT) {
return false;
}
maxLines_ = count;
return true;
}
int32_t UILabel::GetMaxLines() const
{
return maxLines_;
}
bool UILabel::ReMeasureLabelTextSize(TextSizeLimitArg &limit, const Style &style)
{
limitTextHeight_ = 0;
int8_t maxLines = limit.maxLines == MAX_LINE_COUNT ? MAX_LINE_COUNT : limit.maxLines + 1;
labelText_->ReMeasureTextSize(limit.rect, style, maxLines);
return AdjustBreakModeForLimitLines(limit, style);
}
bool UILabel::AdjustBreakModeForLimitLines(TextSizeLimitArg &limit, const Style &style)
{
if (maxLines_ == MAX_LINE_COUNT) {
return false;
}
if (lineBreakMode_ != LINE_BREAK_WRAP && lineBreakMode_ != LINE_BREAK_CLIP &&
lineBreakMode_ != LINE_BREAK_ELLIPSIS) {
return false;
}
int16_t totalTextHeight = labelText_->GetTextSize().y;
labelText_->ReMeasureTextSize(limit.rect, style, limit.maxLines);
limitTextHeight_ = labelText_->GetTextSize().y;
bool needEllipsis = totalTextHeight > limitTextHeight_;
if (needEllipsis && (lineBreakMode_ == LINE_BREAK_WRAP || lineBreakMode_ == LINE_BREAK_CLIP)) {
lineBreakMode_ = LINE_BREAK_ELLIPSIS;
}
UIView::SetHeight(limitTextHeight_);
if (lineBreakMode_ == LINE_BREAK_ELLIPSIS) {
labelText_->ReMeasureTextSize(limit.rect, style, limit.maxLines + 1);
limit.rect.SetHeight(limitTextHeight_);
ellipsisIndex_ = labelText_->GetEllipsisIndex(limit.rect, style);
labelText_->ReMeasureTextWidthInEllipsisMode(limit.rect, style, ellipsisIndex_);
return true;
}
return false;
}
#endif
}