1 /*
2 * Copyright (c) 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_edit_text.h"
17
18 #include <codecvt>
19 #include <locale>
20
21 #include "font/ui_font.h"
22 #include "gfx_utils/graphic_log.h"
23 #include "securec.h"
24 #include "themes/theme_manager.h"
25 #include "common/typed_text.h"
26
27 namespace OHOS {
28 class CursorAnimator : public Animator, public AnimatorCallback {
29 public:
CursorAnimator(UIEditText * view)30 explicit CursorAnimator(UIEditText* view) : Animator(this, view, 0, true), editText_(view) {}
31
~CursorAnimator()32 virtual ~CursorAnimator() {}
33
Callback(UIView * view)34 void Callback(UIView* view) override
35 {
36 if ((view == nullptr) || (editText_ == nullptr)) {
37 return;
38 }
39
40 uint32_t curTime = GetRunTime();
41 if (curTime == preTime_) {
42 return;
43 }
44 uint32_t duration = (curTime > preTime_) ? (curTime - preTime_) : (UINT32_MAX - preTime_ + curTime);
45 if (duration < CURSOR_ANIMATOT_DURATION) {
46 return;
47 }
48 preTime_ = curTime;
49 editText_->drawCursor_ = !editText_->drawCursor_;
50 view->Invalidate();
51 }
52
StartAnimator()53 void StartAnimator()
54 {
55 if (editText_ == nullptr) {
56 return;
57 }
58 Start();
59 preTime_ = GetRunTime();
60 editText_->drawCursor_ = false;
61 }
62
StopAnimator()63 void StopAnimator()
64 {
65 if (editText_ == nullptr) {
66 return;
67 }
68 Stop();
69 editText_->drawCursor_ = false;
70 editText_->Invalidate();
71 }
72
73 private:
74 uint32_t preTime_ = 0;
75 UIEditText* editText_ = nullptr;
76 static constexpr uint16_t CURSOR_ANIMATOT_DURATION = 650;
77 };
78
UIEditText()79 UIEditText::UIEditText()
80 : inputText_(nullptr),
81 placeholderText_(nullptr),
82 offsetX_(DEFAULT_TEXT_OFFSET),
83 cursorIndex_(0),
84 deleteTextWidth_(0),
85 insertTextWidth_(0),
86 needRefresh_(false),
87 useTextColor_(false),
88 isFocused_(false),
89 drawCursor_(false),
90 isSetTextByInterface_(false),
91 maxLength_(MAX_TEXT_LENGTH),
92 placeholderEllipsisIndex_(Text::TEXT_ELLIPSIS_END_INV),
93 cursorPosX_(0),
94 textColor_(Color::White()),
95 placeholderColor_(Color::Gray()),
96 cursorColor_(Color::White()),
97 onChangeListener_(nullptr),
98 cursorAnimator_(nullptr)
99 {
100 touchable_ = true;
101 focusable_ = true;
102 Theme* theme = ThemeManager::GetInstance().GetCurrent();
103 Style& style = (theme != nullptr) ? (theme->GetEditTextStyle()) : (StyleDefault::GetEditTextStyle());
104 UIView::SetStyle(style);
105 InitText();
106 }
107
~UIEditText()108 UIEditText::~UIEditText()
109 {
110 if (cursorAnimator_ != nullptr) {
111 delete cursorAnimator_;
112 cursorAnimator_ = nullptr;
113 }
114 if (inputText_ != nullptr) {
115 delete inputText_;
116 inputText_ = nullptr;
117 }
118 if (placeholderText_ != nullptr) {
119 delete placeholderText_;
120 placeholderText_ = nullptr;
121 }
122 }
123
OnPressEvent(const PressEvent & event)124 bool UIEditText::OnPressEvent(const PressEvent& event)
125 {
126 DealPressEvents(false, event);
127 return UIView::OnPressEvent(event);
128 }
129
OnLongPressEvent(const LongPressEvent & event)130 bool UIEditText::OnLongPressEvent(const LongPressEvent &event)
131 {
132 DealPressEvents(true, event);
133 return UIView::OnLongPressEvent(event);
134 }
135
DealPressEvents(bool longPressEvent,const Event & event)136 void UIEditText::DealPressEvents(bool longPressEvent, const Event &event)
137 {
138 if (touchable_) {
139 if (!longPressEvent) {
140 Point pressPos = event.GetCurrentPos();
141 pressPos.x = pressPos.x - GetOrigRect().GetX();
142 pressPos.y = pressPos.y - GetOrigRect().GetY();
143 Style style = GetStyleConst();
144 cursorIndex_ = inputText_->GetLetterIndexByLinePosition(style,
145 GetContentRect().GetWidth(), pressPos.x, offsetX_);
146 UpdateOffsetX();
147 }
148 RequestFocus();
149 Invalidate();
150 }
151 }
152
Focus()153 void UIEditText::Focus()
154 {
155 if (focusable_) {
156 if (cursorAnimator_ == nullptr) {
157 cursorAnimator_ = new CursorAnimator(this);
158 }
159 static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
160 isFocused_ = true;
161 }
162 Invalidate();
163 UIView::Focus();
164 }
165
Blur()166 void UIEditText::Blur()
167 {
168 if (cursorAnimator_ != nullptr) {
169 static_cast<CursorAnimator*>(cursorAnimator_)->StopAnimator();
170 }
171 isFocused_ = false;
172 Invalidate();
173 UIView::Blur();
174 }
175
InitText()176 void UIEditText::InitText()
177 {
178 if (inputText_ == nullptr) {
179 inputText_ = new Text();
180 inputText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
181 inputText_->SetExpandWidth(true);
182 inputText_->SetExpandHeight(false);
183 }
184
185 if (placeholderText_ == nullptr) {
186 placeholderText_ = new Text();
187 placeholderText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
188 placeholderText_->SetExpandWidth(false);
189 placeholderText_->SetExpandHeight(false);
190 }
191 }
192
SetStyle(Style & style)193 void UIEditText::SetStyle(Style& style)
194 {
195 UIView::SetStyle(style);
196 RefreshText();
197 }
198
SetStyle(uint8_t key,int64_t value)199 void UIEditText::SetStyle(uint8_t key, int64_t value)
200 {
201 UIView::SetStyle(key, value);
202 RefreshText();
203 }
204
SetText(const char * text)205 void UIEditText::SetText(const char* text)
206 {
207 InitText();
208 if (text == nullptr) {
209 return;
210 }
211
212 std::string inputText = std::string(text);
213 SetText(inputText);
214 cursorIndex_ = TypedText::GetUTF8CharacterSize(text, inputText.length());
215 isSetTextByInterface_ = true;
216 }
217
SetText(std::string text)218 void UIEditText::SetText(std::string text)
219 {
220 UpdateTextString(text);
221 UpdateInnerText();
222 }
223
GetText()224 const char* UIEditText::GetText()
225 {
226 return textStr_.c_str();
227 }
228
UpdateTextString(std::string text)229 void UIEditText::UpdateTextString(std::string text)
230 {
231 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
232 std::wstring wideText = convert.from_bytes(text);
233 uint32_t textLen = wideText.length();
234 uint16_t maxLength = GetMaxLength();
235 if (textLen > maxLength) {
236 textLen = maxLength;
237 }
238 std::wstring newWideText = wideText.substr(0, textLen);
239 std::string newText = convert.to_bytes(newWideText);
240 CheckValueChange(newText);
241 textStr_ = newText;
242
243 std::wstring dotString = std::wstring(newWideText.length(), PASSWORD_DOT);
244 passwordStr_ = convert.to_bytes(dotString);
245 }
246
GetInnerText()247 std::string UIEditText::GetInnerText()
248 {
249 return textStr_;
250 }
251
GetInnerPassword()252 std::string UIEditText::GetInnerPassword()
253 {
254 return passwordStr_;
255 }
256
SetPlaceholder(const char * text)257 void UIEditText::SetPlaceholder(const char* text)
258 {
259 InitText();
260 placeholderText_->SetText(text);
261 if (placeholderText_->IsNeedRefresh()) {
262 RefreshText();
263 }
264 }
265
GetPlaceholder()266 const char* UIEditText::GetPlaceholder()
267 {
268 if ((placeholderText_ == nullptr) || placeholderText_->GetText() == nullptr) {
269 return "";
270 } else {
271 return placeholderText_->GetText();
272 }
273 }
274
SetFontId(uint16_t fontId)275 void UIEditText::SetFontId(uint16_t fontId)
276 {
277 InitText();
278 inputText_->SetFontId(fontId);
279 placeholderText_->SetFontId(fontId);
280 if (inputText_->IsNeedRefresh()) {
281 RefreshText();
282 }
283 }
284
SetFont(const char * name,uint8_t size)285 void UIEditText::SetFont(const char* name, uint8_t size)
286 {
287 InitText();
288 inputText_->SetFont(name, size);
289 placeholderText_->SetFont(name, size);
290 if (inputText_->IsNeedRefresh()) {
291 RefreshText();
292 }
293 }
294
GetTextWidth()295 uint16_t UIEditText::GetTextWidth()
296 {
297 InitText();
298 if (inputText_->IsNeedRefresh()) {
299 ReMeasure();
300 }
301 return inputText_->GetTextSize().x;
302 }
303
GetTextHeight()304 uint16_t UIEditText::GetTextHeight()
305 {
306 InitText();
307 if (inputText_->IsNeedRefresh()) {
308 ReMeasure();
309 }
310 return inputText_->GetTextSize().y;
311 }
312
RefreshText()313 void UIEditText::RefreshText()
314 {
315 Invalidate();
316 placeholderEllipsisIndex_ = Text::TEXT_ELLIPSIS_END_INV;
317 if (!needRefresh_) {
318 needRefresh_ = true;
319 }
320 }
321
ReMeasure()322 void UIEditText::ReMeasure()
323 {
324 if (!needRefresh_) {
325 return;
326 }
327 needRefresh_ = false;
328 InitText();
329 Style style = GetStyleConst();
330 style.textColor_ = GetTextColor();
331 Rect contentRect = GetContentRect();
332 int16_t width = contentRect.GetWidth() - DEFAULT_TEXT_OFFSET * 2; // 2: left and right space
333 contentRect.SetWidth(width > 0 ? width : 0);
334 inputText_->ReMeasureTextSize(contentRect, style);
335 placeholderText_->ReMeasureTextSize(contentRect, style);
336 placeholderEllipsisIndex_ = placeholderText_->GetEllipsisIndex(contentRect, style);
337 placeholderText_->ReMeasureTextWidthInEllipsisMode(contentRect, style, placeholderEllipsisIndex_);
338
339 UpdateOffsetX();
340 }
341
UpdateOffsetX()342 void UIEditText::UpdateOffsetX()
343 {
344 if (!inputText_) {
345 return;
346 }
347 uint16_t textLength = GetTextLength();
348 if (textLength == 0) {
349 return;
350 }
351
352 Rect contentRect = GetContentRect();
353 Style style = GetStyleConst();
354
355 uint16_t firstVisibleIndex = GetFirstVisibleIndex();
356 uint16_t lastVisibleIndex = GetLastVisibleIndex();
357 if ((firstVisibleIndex == 0 && lastVisibleIndex == textLength) || isSetTextByInterface_) {
358 offsetX_ = DEFAULT_TEXT_OFFSET;
359 isSetTextByInterface_ = false;
360 } else if (deleteTextWidth_ > 0 || insertTextWidth_ > 0) {
361 UpdateInsertDeletedOffset();
362 } else {
363 UpdateExtraOffsetX(firstVisibleIndex, lastVisibleIndex);
364 }
365 }
366
GetFirstVisibleIndex()367 uint16_t UIEditText::GetFirstVisibleIndex()
368 {
369 if (!inputText_) {
370 return 0;
371 }
372 Rect contentRect = GetContentRect();
373 Style style = GetStyleConst();
374 uint16_t firstVisibleIndex = 0;
375 if (inputText_->GetDirect() == UITextLanguageDirect::TEXT_DIRECT_LTR) {
376 if (offsetX_ > 0) {
377 return 0;
378 }
379 // Returns the number of characters that can be skipped by the offset.
380 firstVisibleIndex = inputText_->GetLetterIndexByLinePosition(style, contentRect.GetWidth(),
381 0, offsetX_) + 1;
382 }
383 return firstVisibleIndex;
384 }
385
GetLastVisibleIndex()386 uint16_t UIEditText::GetLastVisibleIndex()
387 {
388 if (!inputText_) {
389 return 0;
390 }
391 Rect contentRect = GetContentRect();
392 Style style = GetStyleConst();
393 uint16_t lastVisibleIndex = 0;
394 if (inputText_->GetDirect() == UITextLanguageDirect::TEXT_DIRECT_LTR) {
395 lastVisibleIndex = inputText_->GetLetterIndexByLinePosition(style, contentRect.GetWidth(),
396 contentRect.GetWidth(), offsetX_);
397 }
398 return lastVisibleIndex;
399 }
400
UpdateExtraOffsetX(const uint16_t firstVisibleIndex,const uint16_t lastVisibleIndex)401 void UIEditText::UpdateExtraOffsetX(const uint16_t firstVisibleIndex,
402 const uint16_t lastVisibleIndex)
403 {
404 if (!inputText_) {
405 return;
406 }
407 Rect contentRect = GetContentRect();
408 Style style = GetStyleConst();
409 uint16_t characterSize = 0;
410 uint16_t textLength = GetTextLength();
411 if (cursorIndex_ - firstVisibleIndex < 1 && firstVisibleIndex != 0) {
412 characterSize = inputText_->GetNextCharacterFullDispalyOffset(contentRect, style, firstVisibleIndex - 1, 1);
413 offsetX_ += characterSize;
414 if (GetFirstVisibleIndex() == 0) {
415 offsetX_ = DEFAULT_TEXT_OFFSET;
416 }
417 } else if (lastVisibleIndex - cursorIndex_ < 1 && lastVisibleIndex != textLength) {
418 characterSize = inputText_->GetNextCharacterFullDispalyOffset(contentRect, style, lastVisibleIndex, 1);
419 offsetX_ -= characterSize;
420 }
421 }
422
UpdateInsertDeletedOffset()423 void UIEditText::UpdateInsertDeletedOffset()
424 {
425 if (deleteTextWidth_ > 0) {
426 if (offsetX_ + DEFAULT_TEXT_OFFSET <= 0) {
427 offsetX_ += deleteTextWidth_;
428 }
429 deleteTextWidth_ = 0;
430 } else if (insertTextWidth_ > 0) {
431 offsetX_ -= insertTextWidth_;
432 insertTextWidth_ = 0;
433 }
434 }
435
OnDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)436 void UIEditText::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
437 {
438 InitText();
439 UIView::OnDraw(gfxDstBuffer, invalidatedArea);
440
441 bool drawPlaceholder = false;
442 if (inputText_->GetText() != nullptr && strlen(inputText_->GetText()) > 0) {
443 Style style = GetStyleConst();
444 style.textColor_ = GetTextColor();
445 OpacityType opa = GetMixOpaScale();
446 inputText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(), GetContentRect(), offsetX_, style,
447 Text::TEXT_ELLIPSIS_END_INV, opa);
448 drawPlaceholder = false;
449 } else {
450 Style style = GetStyleConst();
451 style.textColor_ = GetPlaceholderColor();
452 OpacityType opa = GetMixOpaScale();
453 placeholderText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(), GetContentRect(), DEFAULT_TEXT_OFFSET,
454 style, placeholderEllipsisIndex_, opa);
455 drawPlaceholder = true;
456 }
457
458 DrawCursor(gfxDstBuffer, invalidatedArea, drawPlaceholder);
459 }
460
DrawCursor(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea,bool drawPlaceholder)461 void UIEditText::DrawCursor(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, bool drawPlaceholder)
462 {
463 if (!(isFocused_ && drawCursor_)) {
464 return;
465 }
466
467 CalculatedCursorPos(drawPlaceholder);
468
469 Style* cursorStyle = new Style();
470 cursorStyle->SetStyle(STYLE_BACKGROUND_COLOR, cursorColor_.full);
471 cursorStyle->SetStyle(STYLE_BACKGROUND_OPA, OPA_OPAQUE);
472
473 int16_t left = 0;
474 UITextLanguageDirect direct = drawPlaceholder ? placeholderText_->GetDirect() : inputText_->GetDirect();
475 if (direct == UITextLanguageDirect::TEXT_DIRECT_LTR) {
476 left = cursorPosX_ - DEFAULT_CURSOR_OFFSET;
477 } else if (direct == UITextLanguageDirect::TEXT_DIRECT_RTL) {
478 left = cursorPosX_ + DEFAULT_CURSOR_OFFSET;
479 }
480
481 Rect viewRect;
482 viewRect.SetLeft(left);
483 viewRect.SetTop(GetOrigRect().GetTop() + (GetRect().GetHeight() - inputText_->GetFontSize()) / 2); // 2: harf size
484 viewRect.SetHeight(inputText_->GetFontSize());
485 viewRect.SetWidth(DEFAULT_CURSOR_WIDTH);
486
487 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, viewRect, invalidatedArea, *cursorStyle, OPA_OPAQUE);
488 delete cursorStyle;
489 }
490
InsertText(std::string text)491 void UIEditText::InsertText(std::string text)
492 {
493 InitText();
494 InsertTextByCursorIndex(text);
495 }
496
InsertTextByCursorIndex(std::string text)497 void UIEditText::InsertTextByCursorIndex(std::string text)
498 {
499 if (!inputText_) {
500 return;
501 }
502 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
503 std::wstring wideText = convert.from_bytes(textStr_);
504 std::wstring insertWText = convert.from_bytes(text);
505 uint32_t textLen = wideText.length();
506 uint32_t insertWTextLen = insertWText.length();
507 std::wstring newWideText = std::wstring(wideText, 0, cursorIndex_) + insertWText
508 + std::wstring(wideText, cursorIndex_, textLen);
509 std::string newText = convert.to_bytes(newWideText);
510 cursorIndex_ += insertWTextLen;
511 SetText(newText);
512
513 Style style = GetStyleConst();
514 Rect contentRect = GetContentRect();
515 cursorPosX_ = inputText_->GetPosXByLetterIndex(contentRect, style, 0, cursorIndex_);
516 if (cursorPosX_ > contentRect.GetWidth()) {
517 insertTextWidth_ = inputText_->GetNextCharacterFullDispalyOffset(contentRect,
518 style, cursorIndex_ - insertWTextLen, insertWTextLen) + style.letterSpace_;
519 }
520
521 if (isFocused_) {
522 if (cursorAnimator_ != nullptr) {
523 static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
524 }
525 }
526 }
527
CalculatedCursorPos(bool drawPlaceholder)528 void UIEditText::CalculatedCursorPos(bool drawPlaceholder)
529 {
530 if (!inputText_ || !placeholderText_) {
531 return;
532 }
533
534 Style style = GetStyleConst();
535 Rect contentRect = GetContentRect();
536 UITextLanguageDirect direct = drawPlaceholder ? placeholderText_->GetDirect() : inputText_->GetDirect();
537 if (direct == UITextLanguageDirect::TEXT_DIRECT_LTR) {
538 cursorPosX_ = contentRect.GetX() + inputText_->GetPosXByLetterIndex(contentRect,
539 style, 0, cursorIndex_) + offsetX_;
540 } else if (direct == UITextLanguageDirect::TEXT_DIRECT_RTL) {
541 cursorPosX_ = GetRect().GetRight() - style.borderWidth_ - style.paddingRight_
542 - inputText_->GetPosXByLetterIndex(contentRect, style, 0, cursorIndex_)
543 - offsetX_;
544 }
545 }
546
UpdateCursor()547 void UIEditText::UpdateCursor()
548 {
549 SetCursorIndex(cursorIndex_);
550 }
551
GetCursorIndex()552 uint16_t UIEditText::GetCursorIndex()
553 {
554 return cursorIndex_;
555 }
556
SetCursorIndex(uint16_t cursorIndex)557 void UIEditText::SetCursorIndex(uint16_t cursorIndex)
558 {
559 cursorIndex_ = cursorIndex;
560 if (cursorAnimator_ != nullptr) {
561 static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
562 }
563 }
564
DeleteBackward(uint32_t length)565 void UIEditText::DeleteBackward(uint32_t length)
566 {
567 if ((length == 0) || (textStr_.length() == 0) || (cursorIndex_ == 0)) {
568 return;
569 }
570 if (!inputText_) {
571 return;
572 }
573
574 std::string newText;
575 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
576 std::wstring wideText = convert.from_bytes(textStr_);
577 if (length > wideText.size()) {
578 cursorIndex_ = 0;
579 } else {
580 uint32_t deleteLength = cursorIndex_ >= length ? length : length - cursorIndex_;
581 if (wideText.size() >= cursorIndex_) {
582 wideText.erase(cursorIndex_ - deleteLength, deleteLength);
583 }
584 newText = convert.to_bytes(wideText);
585 cursorIndex_ -= deleteLength;
586 deleteTextWidth_ = inputText_->GetNextCharacterFullDispalyOffset(GetContentRect(),
587 GetStyleConst(), cursorIndex_, deleteLength);
588 }
589
590 SetText(newText);
591 if (cursorAnimator_ != nullptr) {
592 static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
593 }
594 }
595
UpdateInnerText()596 void UIEditText::UpdateInnerText()
597 {
598 InitText();
599 if (inputType_ == InputType::TEXT_TYPE) {
600 inputText_->SetText(GetInnerText().c_str());
601 } else {
602 inputText_->SetText(GetInnerPassword().c_str());
603 }
604 RefreshText();
605 }
606
SetMaxLength(uint16_t maxLength)607 void UIEditText::SetMaxLength(uint16_t maxLength)
608 {
609 InitText();
610 if (maxLength > MAX_TEXT_LENGTH) {
611 maxLength = MAX_TEXT_LENGTH;
612 }
613 maxLength_ = maxLength;
614 if (textStr_.length() > maxLength) {
615 SetText(textStr_.substr(0, maxLength));
616 }
617 }
618
GetMaxLength()619 uint16_t UIEditText::GetMaxLength()
620 {
621 return maxLength_;
622 }
623
SetInputType(InputType inputType)624 void UIEditText::SetInputType(InputType inputType)
625 {
626 if (inputType_ == inputType) {
627 return;
628 }
629 inputType_ = inputType;
630
631 // update view
632 UpdateInnerText();
633 }
634
CheckValueChange(std::string text)635 void UIEditText::CheckValueChange(std::string text)
636 {
637 if (onChangeListener_ == nullptr) {
638 return;
639 }
640
641 if (textStr_.compare(text) != 0) {
642 onChangeListener_->OnChange(*this, text.c_str());
643 }
644 }
645
GetTextLength()646 uint16_t UIEditText::GetTextLength()
647 {
648 if (inputText_ == nullptr) {
649 return 0;
650 }
651 return TypedText::GetUTF8CharacterSize(inputText_->GetText(),
652 std::string(inputText_->GetText()).length());
653 }
654 } // namespace OHOS
655