1/*
2 * Copyright (c) 2020-2022 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_label.h"
17#include "font/ui_font.h"
18#include "gfx_utils/graphic_log.h"
19#include "themes/theme_manager.h"
20
21namespace OHOS {
22class LabelAnimator : public Animator, public AnimatorCallback {
23public:
24    LabelAnimator(uint16_t textX, uint16_t labelX, int16_t startPos, UIView* view)
25        : Animator(this, view, 0, true),
26          startPos_(startPos),
27          textX_(textX),
28          labelX_(labelX),
29          offsetX_(startPos),
30          waitCount_(ANIM_WAIT_COUNT),
31          speed_(0),
32          preRunTime_(0),
33          decimal_(0)
34    {
35    }
36
37    virtual ~LabelAnimator() {}
38
39    int16_t GetStartPos() const
40    {
41        return startPos_;
42    }
43
44    void SetStartPos(int16_t pos)
45    {
46        startPos_ = pos;
47    }
48
49    void UpdateWidth(uint16_t textWidth, uint16_t labelWidth)
50    {
51        textX_ = textWidth;
52        labelX_ = labelWidth;
53        waitCount_ = ANIM_WAIT_COUNT;
54        preRunTime_ = 0;
55        decimal_ = 0;
56        offsetX_ = startPos_;
57        static_cast<UILabel*>(view_)->offsetX_ = offsetX_;
58        view_->Invalidate();
59    }
60
61    void Callback(UIView* view) override
62    {
63        if (view == nullptr) {
64            return;
65        }
66
67        uint32_t curTime = GetRunTime();
68        if (waitCount_ > 0) {
69            waitCount_--;
70            preRunTime_ = curTime;
71            return;
72        }
73        if (curTime == preRunTime_) {
74            return;
75        }
76        uint32_t time = (curTime > preRunTime_) ? (curTime - preRunTime_) : (UINT32_MAX - preRunTime_ + curTime);
77        // 1000: 1000 milliseconds is 1 second
78        float floatStep = (static_cast<float>(time * speed_) / 1000) + decimal_;
79        uint16_t integerStep = static_cast<uint16_t>(floatStep);
80        decimal_ = floatStep - integerStep;
81        preRunTime_ = curTime;
82
83        if (integerStep != 0) {
84            offsetX_ -= integerStep;
85        } else {
86            return;
87        }
88        offsetX_ = ((offsetX_ - labelX_) % (textX_ + labelX_)) + labelX_;
89        static_cast<UILabel*>(view)->offsetX_ = offsetX_;
90        view->Invalidate();
91    }
92
93    void SetAnimatorSpeed(uint16_t animSpeed)
94    {
95        speed_ = animSpeed;
96        decimal_ = 0;
97    }
98
99    int16_t GetAnimatorSpeed() const
100    {
101        return speed_;
102    }
103
104private:
105    static constexpr uint8_t ANIM_WAIT_COUNT = 50;
106    int16_t startPos_;
107    uint16_t textX_;
108    uint16_t labelX_;
109    int16_t offsetX_;
110    uint16_t waitCount_;
111    uint16_t speed_;
112    uint32_t preRunTime_;
113    float decimal_;
114};
115
116UILabel::UILabel()
117    : labelText_(nullptr),
118      needRefresh_(false),
119      useTextColor_(false),
120      hasAnimator_(false),
121      lineBreakMode_(LINE_BREAK_ELLIPSIS),
122      ellipsisIndex_(Text::TEXT_ELLIPSIS_END_INV),
123      offsetX_(0),
124      textColor_(Color::White()),
125      animator_{nullptr}
126{
127    Theme* theme = ThemeManager::GetInstance().GetCurrent();
128    Style& style = (theme != nullptr) ? (theme->GetLabelStyle()) : (StyleDefault::GetLabelStyle());
129    UIView::SetStyle(style);
130    animator_.speed = DEFAULT_ANIMATOR_SPEED;
131}
132
133UILabel::~UILabel()
134{
135    if (hasAnimator_) {
136        delete animator_.animator;
137        animator_.animator = nullptr;
138        hasAnimator_ = false;
139    }
140    if (labelText_ != nullptr) {
141        delete labelText_;
142        labelText_ = nullptr;
143    }
144}
145
146void UILabel::InitLabelText()
147{
148    if (labelText_ == nullptr) {
149        labelText_ = new Text();
150    }
151}
152
153int16_t UILabel::GetWidth()
154{
155    InitLabelText();
156    if (needRefresh_ && labelText_->IsExpandWidth()) {
157        ReMeasure();
158    }
159    return UIView::GetWidth();
160}
161
162int16_t UILabel::GetHeight()
163{
164    InitLabelText();
165    if (needRefresh_ && labelText_->IsExpandHeight()) {
166        ReMeasure();
167    }
168    return UIView::GetHeight();
169}
170
171void UILabel::SetStyle(uint8_t key, int64_t value)
172{
173    UIView::SetStyle(key, value);
174    RefreshLabel();
175}
176
177void UILabel::SetText(const char* text)
178{
179    InitLabelText();
180    labelText_->SetText(text);
181    if (labelText_->IsNeedRefresh()) {
182        RefreshLabel();
183    }
184}
185
186
187void UILabel::SetText(const SpannableString* text)
188{
189    InitLabelText();
190    labelText_->SetSpannableString(text);
191    if (labelText_->IsNeedRefresh()) {
192        RefreshLabel();
193    }
194}
195
196
197void UILabel::SetAbsoluteSizeSpan(uint16_t start, uint16_t end, uint8_t size)
198{
199    if (labelText_ == nullptr) {
200        return;
201    }
202    labelText_->SetAbsoluteSizeSpan(start, end, size);
203    if (labelText_->IsNeedRefresh()) {
204        RefreshLabel();
205    }
206}
207
208void UILabel::SetRelativeSizeSpan(uint16_t start, uint16_t end, float size)
209{
210    if (labelText_ == nullptr) {
211        return;
212    }
213    labelText_->SetRelativeSizeSpan(start, end, size);
214    if (labelText_->IsNeedRefresh()) {
215        RefreshLabel();
216    }
217}
218
219uint8_t UILabel::GetFontSize()
220{
221    InitLabelText();
222    return labelText_->GetFontSize();
223}
224
225void UILabel::SetLineBreakMode(const uint8_t lineBreakMode)
226{
227    InitLabelText();
228    if ((lineBreakMode >= LINE_BREAK_MAX) || (lineBreakMode_ == lineBreakMode)) {
229        return;
230    }
231    lineBreakMode_ = lineBreakMode;
232    if ((lineBreakMode_ == LINE_BREAK_ADAPT) || (lineBreakMode_ == LINE_BREAK_STRETCH) ||
233        (lineBreakMode_ == LINE_BREAK_MARQUEE)) {
234        labelText_->SetExpandWidth(true);
235    } else {
236        labelText_->SetExpandWidth(false);
237    }
238    if ((lineBreakMode_ == LINE_BREAK_ADAPT) || (lineBreakMode_ == LINE_BREAK_WRAP)) {
239        labelText_->SetExpandHeight(true);
240    } else {
241        labelText_->SetExpandHeight(false);
242    }
243    if (lineBreakMode_ != LINE_BREAK_MARQUEE) {
244        offsetX_ = 0;
245        if (hasAnimator_) {
246            animator_.animator->Stop();
247        }
248    }
249    RefreshLabel();
250}
251
252void UILabel::SetAlign(UITextLanguageAlignment horizontalAlign, UITextLanguageAlignment verticalAlign)
253{
254    InitLabelText();
255    labelText_->SetAlign(horizontalAlign, verticalAlign);
256    if (labelText_->IsNeedRefresh()) {
257        RefreshLabel();
258    }
259}
260
261void UILabel::SetFontId(uint16_t fontId)
262{
263    InitLabelText();
264    labelText_->SetFontId(fontId);
265    if (labelText_->IsNeedRefresh()) {
266        RefreshLabel();
267    }
268}
269
270void UILabel::SetFont(const char* name, uint8_t size)
271{
272    InitLabelText();
273    labelText_->SetFont(name, size);
274    if (labelText_->IsNeedRefresh()) {
275        RefreshLabel();
276    }
277}
278
279uint16_t UILabel::GetTextWidth()
280{
281    InitLabelText();
282    if (labelText_->IsNeedRefresh()) {
283        ReMeasure();
284    }
285    return labelText_->GetTextSize().x;
286}
287
288uint16_t UILabel::GetTextHeight()
289{
290    InitLabelText();
291    if (labelText_->IsNeedRefresh()) {
292        ReMeasure();
293    }
294    return labelText_->GetTextSize().y;
295}
296
297void UILabel::SetWidth(int16_t width)
298{
299    if (GetWidth() != width) {
300        UIView::SetWidth(width);
301        RefreshLabel();
302    }
303}
304
305void UILabel::SetHeight(int16_t height)
306{
307    if (GetHeight() != height) {
308        UIView::SetHeight(height);
309        RefreshLabel();
310    }
311}
312
313void UILabel::RefreshLabel()
314{
315    Invalidate();
316    ellipsisIndex_ = Text::TEXT_ELLIPSIS_END_INV;
317    if (!needRefresh_) {
318        needRefresh_ = true;
319    }
320}
321
322void UILabel::ReMeasure()
323{
324    if (!needRefresh_) {
325        return;
326    }
327    needRefresh_ = false;
328    InitLabelText();
329    Style style = GetStyleConst();
330    style.textColor_ = GetTextColor();
331    bool flag = false;
332    if ((transMap_ != nullptr) && !transMap_->IsInvalid()) {
333        transMap_->SetInvalid(true);
334        flag = true;
335    }
336    labelText_->ReMeasureTextSize(GetContentRect(), style);
337    Point textSize = labelText_->GetTextSize();
338    switch (lineBreakMode_) {
339        case LINE_BREAK_ADAPT:
340            Resize(textSize.x, textSize.y);
341            break;
342        case LINE_BREAK_STRETCH:
343            SetWidth(textSize.x);
344            break;
345        case LINE_BREAK_WRAP:
346            SetHeight(textSize.y);
347            break;
348        case LINE_BREAK_ELLIPSIS:
349            ellipsisIndex_ = labelText_->GetEllipsisIndex(GetContentRect(), style);
350            labelText_->ReMeasureTextWidthInEllipsisMode(GetContentRect(), style, ellipsisIndex_);
351            break;
352        case LINE_BREAK_MARQUEE:
353            RemeasureForMarquee(textSize.x);
354            break;
355        default:
356            break;
357    }
358    if ((transMap_ != nullptr) && flag) {
359        transMap_->SetInvalid(false);
360    }
361}
362
363void UILabel::RemeasureForMarquee(int16_t textWidth)
364{
365    int16_t rectWidth = GetWidth();
366    if (textWidth > rectWidth) {
367        offsetX_ = GetRollStartPos();
368        if (labelText_->GetDirect() == TEXT_DIRECT_RTL) {
369            labelText_->SetAlign(TEXT_ALIGNMENT_RIGHT, GetVerAlign());
370        } else {
371            labelText_->SetAlign(TEXT_ALIGNMENT_LEFT, GetVerAlign());
372        }
373        if (hasAnimator_) {
374            static_cast<LabelAnimator*>(animator_.animator)->UpdateWidth(textWidth, rectWidth);
375        } else {
376            LabelAnimator* animator = new LabelAnimator(textWidth, rectWidth, offsetX_, this);
377            if (animator == nullptr) {
378                GRAPHIC_LOGE("new LabelAnimator fail");
379                return;
380            }
381            animator->SetAnimatorSpeed(animator_.speed);
382            animator_.animator = animator;
383            hasAnimator_ = true;
384        }
385        animator_.animator->Start();
386    } else {
387        offsetX_ = 0;
388        if (hasAnimator_) {
389            animator_.animator->Stop();
390        }
391    }
392}
393
394void UILabel::SetRollStartPos(int16_t pos)
395{
396    if (hasAnimator_) {
397        static_cast<LabelAnimator*>(animator_.animator)->SetStartPos(pos);
398    } else {
399        animator_.pos = pos;
400    }
401}
402
403int16_t UILabel::GetRollStartPos() const
404{
405    return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetStartPos() : animator_.pos;
406}
407
408void UILabel::SetRollSpeed(uint16_t speed)
409{
410    if (hasAnimator_) {
411        static_cast<LabelAnimator*>(animator_.animator)->SetAnimatorSpeed(speed);
412    } else {
413        animator_.speed = speed;
414    }
415}
416
417uint16_t UILabel::GetRollSpeed() const
418{
419    return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetAnimatorSpeed() : animator_.speed;
420}
421
422void UILabel::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
423{
424    InitLabelText();
425    UIView::OnDraw(gfxDstBuffer, invalidatedArea);
426    Style style = GetStyleConst();
427    style.textColor_ = GetTextColor();
428    OpacityType opa = GetMixOpaScale();
429    labelText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(),
430                       GetContentRect(), offsetX_, style, ellipsisIndex_, opa);
431}
432} // namespace OHOS
433