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 
21 namespace OHOS {
22 class LabelAnimator : public Animator, public AnimatorCallback {
23 public:
LabelAnimator(uint16_t textX,uint16_t labelX,int16_t startPos,UIView * view)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 
~LabelAnimator()37     virtual ~LabelAnimator() {}
38 
GetStartPos() const39     int16_t GetStartPos() const
40     {
41         return startPos_;
42     }
43 
SetStartPos(int16_t pos)44     void SetStartPos(int16_t pos)
45     {
46         startPos_ = pos;
47     }
48 
UpdateWidth(uint16_t textWidth,uint16_t labelWidth)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 
Callback(UIView * view)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 
SetAnimatorSpeed(uint16_t animSpeed)93     void SetAnimatorSpeed(uint16_t animSpeed)
94     {
95         speed_ = animSpeed;
96         decimal_ = 0;
97     }
98 
GetAnimatorSpeed() const99     int16_t GetAnimatorSpeed() const
100     {
101         return speed_;
102     }
103 
104 private:
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 
UILabel()116 UILabel::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 
~UILabel()133 UILabel::~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 
InitLabelText()146 void UILabel::InitLabelText()
147 {
148     if (labelText_ == nullptr) {
149         labelText_ = new Text();
150     }
151 }
152 
GetWidth()153 int16_t UILabel::GetWidth()
154 {
155     InitLabelText();
156     if (needRefresh_ && labelText_->IsExpandWidth()) {
157         ReMeasure();
158     }
159     return UIView::GetWidth();
160 }
161 
GetHeight()162 int16_t UILabel::GetHeight()
163 {
164     InitLabelText();
165     if (needRefresh_ && labelText_->IsExpandHeight()) {
166         ReMeasure();
167     }
168     return UIView::GetHeight();
169 }
170 
SetStyle(uint8_t key,int64_t value)171 void UILabel::SetStyle(uint8_t key, int64_t value)
172 {
173     UIView::SetStyle(key, value);
174     RefreshLabel();
175 }
176 
SetText(const char * text)177 void UILabel::SetText(const char* text)
178 {
179     InitLabelText();
180     labelText_->SetText(text);
181     if (labelText_->IsNeedRefresh()) {
182         RefreshLabel();
183     }
184 }
185 
186 
SetText(const SpannableString * text)187 void UILabel::SetText(const SpannableString* text)
188 {
189     InitLabelText();
190     labelText_->SetSpannableString(text);
191     if (labelText_->IsNeedRefresh()) {
192         RefreshLabel();
193     }
194 }
195 
196 
SetAbsoluteSizeSpan(uint16_t start,uint16_t end,uint8_t size)197 void 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 
SetRelativeSizeSpan(uint16_t start,uint16_t end,float size)208 void 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 
GetFontSize()219 uint8_t UILabel::GetFontSize()
220 {
221     InitLabelText();
222     return labelText_->GetFontSize();
223 }
224 
SetLineBreakMode(const uint8_t lineBreakMode)225 void 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 
SetAlign(UITextLanguageAlignment horizontalAlign,UITextLanguageAlignment verticalAlign)252 void UILabel::SetAlign(UITextLanguageAlignment horizontalAlign, UITextLanguageAlignment verticalAlign)
253 {
254     InitLabelText();
255     labelText_->SetAlign(horizontalAlign, verticalAlign);
256     if (labelText_->IsNeedRefresh()) {
257         RefreshLabel();
258     }
259 }
260 
SetFontId(uint16_t fontId)261 void UILabel::SetFontId(uint16_t fontId)
262 {
263     InitLabelText();
264     labelText_->SetFontId(fontId);
265     if (labelText_->IsNeedRefresh()) {
266         RefreshLabel();
267     }
268 }
269 
SetFont(const char * name,uint8_t size)270 void 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 
GetTextWidth()279 uint16_t UILabel::GetTextWidth()
280 {
281     InitLabelText();
282     if (labelText_->IsNeedRefresh()) {
283         ReMeasure();
284     }
285     return labelText_->GetTextSize().x;
286 }
287 
GetTextHeight()288 uint16_t UILabel::GetTextHeight()
289 {
290     InitLabelText();
291     if (labelText_->IsNeedRefresh()) {
292         ReMeasure();
293     }
294     return labelText_->GetTextSize().y;
295 }
296 
SetWidth(int16_t width)297 void UILabel::SetWidth(int16_t width)
298 {
299     if (GetWidth() != width) {
300         UIView::SetWidth(width);
301         RefreshLabel();
302     }
303 }
304 
SetHeight(int16_t height)305 void UILabel::SetHeight(int16_t height)
306 {
307     if (GetHeight() != height) {
308         UIView::SetHeight(height);
309         RefreshLabel();
310     }
311 }
312 
RefreshLabel()313 void UILabel::RefreshLabel()
314 {
315     Invalidate();
316     ellipsisIndex_ = Text::TEXT_ELLIPSIS_END_INV;
317     if (!needRefresh_) {
318         needRefresh_ = true;
319     }
320 }
321 
ReMeasure()322 void 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 
RemeasureForMarquee(int16_t textWidth)363 void 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 
SetRollStartPos(int16_t pos)394 void 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 
GetRollStartPos() const403 int16_t UILabel::GetRollStartPos() const
404 {
405     return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetStartPos() : animator_.pos;
406 }
407 
SetRollSpeed(uint16_t speed)408 void 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 
GetRollSpeed() const417 uint16_t UILabel::GetRollSpeed() const
418 {
419     return hasAnimator_ ? static_cast<LabelAnimator*>(animator_.animator)->GetAnimatorSpeed() : animator_.speed;
420 }
421 
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)422 void 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