* Copyright (c) 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_edit_text.h"
#include <codecvt>
#include <locale>
#include "font/ui_font.h"
#include "gfx_utils/graphic_log.h"
#include "securec.h"
#include "themes/theme_manager.h"
#include "common/typed_text.h"
namespace OHOS {
class CursorAnimator : public Animator, public AnimatorCallback {
public:
explicit CursorAnimator(UIEditText* view) : Animator(this, view, 0, true), editText_(view) {}
virtual ~CursorAnimator() {}
void Callback(UIView* view) override
{
if ((view == nullptr) || (editText_ == nullptr)) {
return;
}
uint32_t curTime = GetRunTime();
if (curTime == preTime_) {
return;
}
uint32_t duration = (curTime > preTime_) ? (curTime - preTime_) : (UINT32_MAX - preTime_ + curTime);
if (duration < CURSOR_ANIMATOT_DURATION) {
return;
}
preTime_ = curTime;
editText_->drawCursor_ = !editText_->drawCursor_;
view->Invalidate();
}
void StartAnimator()
{
if (editText_ == nullptr) {
return;
}
Start();
preTime_ = GetRunTime();
editText_->drawCursor_ = false;
}
void StopAnimator()
{
if (editText_ == nullptr) {
return;
}
Stop();
editText_->drawCursor_ = false;
editText_->Invalidate();
}
private:
uint32_t preTime_ = 0;
UIEditText* editText_ = nullptr;
static constexpr uint16_t CURSOR_ANIMATOT_DURATION = 650;
};
UIEditText::UIEditText()
: inputText_(nullptr),
placeholderText_(nullptr),
offsetX_(DEFAULT_TEXT_OFFSET),
cursorIndex_(0),
deleteTextWidth_(0),
insertTextWidth_(0),
offsetState_(UPDATE_OFFSET_UNKNOW),
needRefresh_(false),
useTextColor_(false),
isFocused_(false),
drawCursor_(false),
maxLength_(MAX_TEXT_LENGTH),
placeholderEllipsisIndex_(Text::TEXT_ELLIPSIS_END_INV),
cursorPosX_(0),
textColor_(Color::White()),
placeholderColor_(Color::Gray()),
cursorColor_(Color::White()),
onChangeListener_(nullptr),
cursorAnimator_(nullptr)
{
touchable_ = true;
focusable_ = true;
Theme* theme = ThemeManager::GetInstance().GetCurrent();
Style& style = (theme != nullptr) ? (theme->GetEditTextStyle()) : (StyleDefault::GetEditTextStyle());
UIView::SetStyle(style);
SetDraggable(true);
}
UIEditText::~UIEditText()
{
if (cursorAnimator_ != nullptr) {
delete cursorAnimator_;
cursorAnimator_ = nullptr;
}
if (inputText_ != nullptr) {
delete inputText_;
inputText_ = nullptr;
}
if (placeholderText_ != nullptr) {
delete placeholderText_;
placeholderText_ = nullptr;
}
}
bool UIEditText::OnPressEvent(const PressEvent& event)
{
DealPressEvents(false, event);
return UIView::OnPressEvent(event);
}
bool UIEditText::OnLongPressEvent(const LongPressEvent &event)
{
DealPressEvents(true, event);
return UIView::OnLongPressEvent(event);
}
void UIEditText::DealPressEvents(bool longPressEvent, const Event &event)
{
if (touchable_) {
if (!longPressEvent) {
Point pressPos = event.GetCurrentPos();
pressPos.x = pressPos.x - GetOrigRect().GetX();
pressPos.y = pressPos.y - GetOrigRect().GetY();
Style style = GetStyleConst();
cursorIndex_ = inputText_->GetLetterIndexByLinePosition(style,
GetContentRect().GetWidth(), pressPos.x, offsetX_);
offsetState_ = UPDATE_OFFSET_PRESS_EVENT;
UpdateOffsetX();
}
RequestFocus();
Invalidate();
}
}
bool UIEditText::OnDragEvent(const DragEvent& event)
{
DealPressEvents(false, event);
return UIView::OnDragEvent(event);
}
void UIEditText::Focus()
{
if (focusable_) {
if (cursorAnimator_ == nullptr) {
cursorAnimator_ = new CursorAnimator(this);
}
static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
isFocused_ = true;
}
Invalidate();
UIView::Focus();
}
void UIEditText::Blur()
{
if (cursorAnimator_ != nullptr) {
static_cast<CursorAnimator*>(cursorAnimator_)->StopAnimator();
}
isFocused_ = false;
Invalidate();
UIView::Blur();
}
void UIEditText::InitText()
{
if (inputText_ == nullptr) {
inputText_ = new Text();
inputText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
inputText_->SetExpandWidth(true);
inputText_->SetExpandHeight(false);
}
if (placeholderText_ == nullptr) {
placeholderText_ = new Text();
placeholderText_->SetAlign(TEXT_ALIGNMENT_LEFT, TEXT_ALIGNMENT_CENTER);
placeholderText_->SetExpandWidth(false);
placeholderText_->SetExpandHeight(false);
}
}
void UIEditText::SetStyle(Style& style)
{
UIView::SetStyle(style);
RefreshText();
}
void UIEditText::SetStyle(uint8_t key, int64_t value)
{
UIView::SetStyle(key, value);
RefreshText();
}
void UIEditText::SetText(const char* text)
{
InitText();
if (text == nullptr) {
return;
}
std::string inputText = std::string(text);
SetText(inputText);
cursorIndex_ = TypedText::GetUTF8CharacterSize(text, inputText.length());
offsetState_ = UPDATE_OFFSET_INTERFACE;
}
void UIEditText::SetText(std::string text)
{
UpdateTextString(text);
UpdateInnerText();
}
const char* UIEditText::GetText()
{
return textStr_.c_str();
}
void UIEditText::UpdateTextString(std::string text)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::wstring wideText = convert.from_bytes(text);
uint32_t textLen = wideText.length();
uint16_t maxLength = GetMaxLength();
if (textLen > maxLength) {
textLen = maxLength;
}
std::wstring newWideText = wideText.substr(0, textLen);
std::string newText = convert.to_bytes(newWideText);
CheckValueChange(newText);
textStr_ = newText;
passwordStr_ = std::string(newWideText.length(), *PASSWORD_DOT.c_str());
}
std::string UIEditText::GetInnerText()
{
return textStr_;
}
std::string UIEditText::GetInnerPassword()
{
return passwordStr_;
}
void UIEditText::SetPlaceholder(const char* text)
{
InitText();
placeholderText_->SetText(text);
if (placeholderText_->IsNeedRefresh()) {
RefreshText();
}
}
const char* UIEditText::GetPlaceholder()
{
if ((placeholderText_ == nullptr) || placeholderText_->GetText() == nullptr) {
return "";
} else {
return placeholderText_->GetText();
}
}
void UIEditText::SetFontId(uint16_t fontId)
{
InitText();
inputText_->SetFontId(fontId);
placeholderText_->SetFontId(fontId);
if (inputText_->IsNeedRefresh()) {
RefreshText();
}
}
void UIEditText::SetFont(const char* name, uint8_t size)
{
InitText();
inputText_->SetFont(name, size);
placeholderText_->SetFont(name, size);
if (inputText_->IsNeedRefresh()) {
RefreshText();
}
}
uint16_t UIEditText::GetTextWidth()
{
InitText();
if (inputText_->IsNeedRefresh()) {
ReMeasure();
}
return inputText_->GetTextSize().x;
}
uint16_t UIEditText::GetTextHeight()
{
InitText();
if (inputText_->IsNeedRefresh()) {
ReMeasure();
}
return inputText_->GetTextSize().y;
}
void UIEditText::RefreshText()
{
Invalidate();
placeholderEllipsisIndex_ = Text::TEXT_ELLIPSIS_END_INV;
if (!needRefresh_) {
needRefresh_ = true;
}
}
void UIEditText::ReMeasure()
{
if (!needRefresh_) {
return;
}
needRefresh_ = false;
InitText();
Style style = GetStyleConst();
style.textColor_ = GetTextColor();
Rect contentRect = GetContentRect();
int16_t width = contentRect.GetWidth() - BOTH_SIDE_TEXT_OFFSET;
contentRect.SetWidth(width > 0 ? width : 0);
inputText_->ReMeasureTextSize(contentRect, style);
placeholderText_->ReMeasureTextSize(contentRect, style);
placeholderEllipsisIndex_ = placeholderText_->GetEllipsisIndex(contentRect, style);
placeholderText_->ReMeasureTextWidthInEllipsisMode(contentRect, style, placeholderEllipsisIndex_);
UpdateOffsetX();
}
void UIEditText::UpdateOffsetX()
{
if (!inputText_) {
return;
}
Rect contentRect = GetContentRect();
Style style = GetStyleConst();
uint16_t textLength = GetTextLength();
uint16_t firstVisibleIndex = GetFirstVisibleIndex();
uint16_t lastVisibleIndex = GetLastVisibleIndex();
if (textLength == 0 || (firstVisibleIndex == 0 && lastVisibleIndex == textLength)) {
offsetX_ = DEFAULT_TEXT_OFFSET;
offsetState_ = UPDATE_OFFSET_UNKNOW;
deleteTextWidth_ = 0;
insertTextWidth_ = 0;
return;
}
switch (offsetState_) {
case UPDATE_OFFSET_INTERFACE:
offsetX_ = DEFAULT_TEXT_OFFSET;
break;
case UPDATE_OFFSET_PRESS_EVENT:
UpdateExtraOffsetX(firstVisibleIndex, lastVisibleIndex);
break;
case UPDATE_OFFSET_DELETE:
case UPDATE_OFFSET_INSERT:
UpdateInsertDeletedOffset();
break;
case UPDATE_OFFSET_SET_CURSOR:
UpdateOffsetBySetCursorIndex();
break;
case UPDATE_OFFSET_INPUT_TYPE:
UpdateOffsetByInputType();
break;
default:
break;
}
offsetState_ = UPDATE_OFFSET_UNKNOW;
deleteTextWidth_ = 0;
insertTextWidth_ = 0;
}
uint16_t UIEditText::GetFirstVisibleIndex()
{
if (!inputText_) {
return 0;
}
Rect contentRect = GetContentRect();
Style style = GetStyleConst();
uint16_t firstVisibleIndex = 0;
if (inputText_->GetDirect() == UITextLanguageDirect::TEXT_DIRECT_LTR) {
if (offsetX_ > 0) {
return 0;
}
firstVisibleIndex = inputText_->GetLetterIndexByLinePosition(style, contentRect.GetWidth(),
0, offsetX_) + 1;
}
return firstVisibleIndex;
}
uint16_t UIEditText::GetLastVisibleIndex()
{
if (!inputText_) {
return 0;
}
Rect contentRect = GetContentRect();
Style style = GetStyleConst();
uint16_t lastVisibleIndex = 0;
if (inputText_->GetDirect() == UITextLanguageDirect::TEXT_DIRECT_LTR) {
lastVisibleIndex = inputText_->GetLetterIndexByLinePosition(style, contentRect.GetWidth(),
contentRect.GetWidth(), offsetX_);
}
return lastVisibleIndex;
}
void UIEditText::UpdateExtraOffsetX(const uint16_t firstVisibleIndex,
const uint16_t lastVisibleIndex)
{
if (!inputText_) {
return;
}
Rect contentRect = GetContentRect();
Style style = GetStyleConst();
uint16_t characterSize = 0;
uint16_t textLength = GetTextLength();
if (cursorIndex_ - firstVisibleIndex < 1 && firstVisibleIndex != 0) {
characterSize = inputText_->GetNextCharacterFullDispalyOffset(contentRect, style, firstVisibleIndex - 1, 1);
offsetX_ += characterSize + DEFAULT_TEXT_OFFSET;
if (GetFirstVisibleIndex() == 0) {
offsetX_ = DEFAULT_TEXT_OFFSET;
}
} else if (lastVisibleIndex - cursorIndex_ < 1 && lastVisibleIndex != textLength) {
characterSize = inputText_->GetNextCharacterFullDispalyOffset(contentRect, style, lastVisibleIndex, 1);
offsetX_ -= characterSize + DEFAULT_TEXT_OFFSET;
if (GetLastVisibleIndex() == textLength) {
offsetX_ = -(GetTextWidth() - GetRect().GetWidth() + BOTH_SIDE_TEXT_OFFSET);
}
}
}
uint16_t UIEditText::GetTextWidthByCursorIndex(const uint16_t cursorIndex)
{
if (!inputText_ || cursorIndex == 0) {
return 0;
}
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::wstring wideText = convert.from_bytes(textStr_);
wideText = wideText.substr(0, cursorIndex);
std::string clipText = convert.to_bytes(wideText);
return TypedText::GetTextWidth(clipText.c_str(), GetFontId(), inputText_->GetFontSize(),
clipText.length(), GetStyleConst().letterSpace_);
}
void UIEditText::UpdateInsertDeletedOffset()
{
if (deleteTextWidth_ > 0) {
if (offsetX_ + DEFAULT_TEXT_OFFSET < 0) {
offsetX_ += deleteTextWidth_;
if (GetFirstVisibleIndex() == 0 && GetLastVisibleIndex() == GetTextLength()) {
offsetX_ = DEFAULT_TEXT_OFFSET;
}
} else {
offsetX_ = DEFAULT_TEXT_OFFSET;
}
} else if (insertTextWidth_ > 0) {
if (cursorIndex_ == GetTextLength()) {
offsetX_ = -(GetTextWidth() - GetRect().GetWidth() + BOTH_SIDE_TEXT_OFFSET);
} else {
offsetX_ -= insertTextWidth_;
}
}
}
void UIEditText::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
InitText();
UIView::OnDraw(gfxDstBuffer, invalidatedArea);
bool drawPlaceholder = false;
if (inputText_->GetText() != nullptr && strlen(inputText_->GetText()) > 0) {
Style style = GetStyleConst();
style.textColor_ = GetTextColor();
OpacityType opa = GetMixOpaScale();
inputText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(), GetContentRect(), offsetX_, style,
Text::TEXT_ELLIPSIS_END_INV, opa);
drawPlaceholder = false;
} else {
Style style = GetStyleConst();
style.textColor_ = GetPlaceholderColor();
OpacityType opa = GetMixOpaScale();
placeholderText_->OnDraw(gfxDstBuffer, invalidatedArea, GetOrigRect(), GetContentRect(), DEFAULT_TEXT_OFFSET,
style, placeholderEllipsisIndex_, opa);
drawPlaceholder = true;
}
DrawCursor(gfxDstBuffer, invalidatedArea, drawPlaceholder);
}
void UIEditText::DrawCursor(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, bool drawPlaceholder)
{
if (!(isFocused_ && drawCursor_)) {
return;
}
CalculatedCursorPos(drawPlaceholder);
Style* cursorStyle = new Style();
cursorStyle->SetStyle(STYLE_BACKGROUND_COLOR, cursorColor_.full);
cursorStyle->SetStyle(STYLE_BACKGROUND_OPA, OPA_OPAQUE);
int16_t left = 0;
UITextLanguageDirect direct = drawPlaceholder ? placeholderText_->GetDirect() : inputText_->GetDirect();
if (direct == UITextLanguageDirect::TEXT_DIRECT_LTR) {
left = cursorPosX_ - DEFAULT_CURSOR_OFFSET;
} else if (direct == UITextLanguageDirect::TEXT_DIRECT_RTL) {
left = cursorPosX_ + DEFAULT_CURSOR_OFFSET;
}
Rect viewRect;
viewRect.SetLeft(left);
viewRect.SetTop(GetOrigRect().GetTop() + (GetRect().GetHeight() - inputText_->GetFontSize()) / 2);
viewRect.SetHeight(inputText_->GetFontSize());
viewRect.SetWidth(DEFAULT_CURSOR_WIDTH);
BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, viewRect, invalidatedArea, *cursorStyle, OPA_OPAQUE);
delete cursorStyle;
}
void UIEditText::InsertText(std::string text)
{
InitText();
InsertTextByCursorIndex(text);
}
void UIEditText::InsertTextByCursorIndex(std::string text)
{
if (!inputText_) {
return;
}
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::wstring wideText = convert.from_bytes(textStr_);
std::wstring insertWText = convert.from_bytes(text);
uint32_t textLen = wideText.length();
uint32_t insertWTextLen = insertWText.length();
if (cursorIndex_ > textLen || insertWTextLen + textLen > maxLength_) {
return;
}
std::wstring newWideText = wideText.insert(cursorIndex_, insertWText);
std::string newText = convert.to_bytes(newWideText);
cursorIndex_ += insertWTextLen;
SetText(newText);
Style style = GetStyleConst();
Rect contentRect = GetContentRect();
cursorPosX_ = inputText_->GetPosXByLetterIndex(contentRect, style, 0, cursorIndex_);
if (cursorPosX_ >= contentRect.GetWidth() - DEFAULT_TEXT_OFFSET) {
insertTextWidth_ = inputText_->GetNextCharacterFullDispalyOffset(contentRect,
style, cursorIndex_ - insertWTextLen, insertWTextLen) + style.letterSpace_;
offsetState_ = UPDATE_OFFSET_INSERT;
}
if (isFocused_) {
if (cursorAnimator_ != nullptr) {
static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
}
}
}
void UIEditText::CalculatedCursorPos(bool drawPlaceholder)
{
if (!inputText_ || !placeholderText_) {
return;
}
Style style = GetStyleConst();
Rect contentRect = GetContentRect();
UITextLanguageDirect direct = drawPlaceholder ? placeholderText_->GetDirect() : inputText_->GetDirect();
if (direct == UITextLanguageDirect::TEXT_DIRECT_LTR) {
cursorPosX_ = contentRect.GetX() + inputText_->GetPosXByLetterIndex(contentRect,
style, 0, cursorIndex_) + offsetX_;
} else if (direct == UITextLanguageDirect::TEXT_DIRECT_RTL) {
cursorPosX_ = GetRect().GetRight() - style.borderWidth_ - style.paddingRight_
- inputText_->GetPosXByLetterIndex(contentRect, style, 0, cursorIndex_)
- offsetX_;
}
}
uint16_t UIEditText::GetCursorIndex()
{
return cursorIndex_;
}
void UIEditText::SetCursorIndex(uint16_t cursorIndex)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::wstring wideText = convert.from_bytes(textStr_);
if (cursorIndex > wideText.length()) {
cursorIndex_ = wideText.length();
} else {
cursorIndex_ = cursorIndex;
}
offsetState_ = UPDATE_OFFSET_SET_CURSOR;
RefreshText();
if (cursorAnimator_ != nullptr) {
static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
}
}
void UIEditText::UpdateOffsetBySetCursorIndex()
{
if (!inputText_) {
return;
}
uint16_t textWidth = GetTextWidthByCursorIndex(cursorIndex_);
Rect contentRect = GetContentRect();
int16_t newPosX = 0;
if (inputText_->GetDirect() == UITextLanguageDirect::TEXT_DIRECT_LTR) {
newPosX = contentRect.GetX() + textWidth + offsetX_;
if (newPosX <= contentRect.GetX()) {
offsetX_ = -(textWidth - DEFAULT_TEXT_OFFSET);
} else if (newPosX >= GetRect().GetRight()) {
offsetX_ = -(textWidth - GetRect().GetWidth() + BOTH_SIDE_TEXT_OFFSET);
}
}
}
void UIEditText::DeleteBackward(uint32_t length)
{
if ((length == 0) || (textStr_.length() == 0) || (cursorIndex_ == 0)) {
return;
}
if (!inputText_) {
return;
}
std::string newText;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::wstring wideText = convert.from_bytes(textStr_);
if (length > wideText.size()) {
cursorIndex_ = 0;
} else {
uint32_t deleteLength = cursorIndex_ >= length ? length : length - cursorIndex_;
if (wideText.size() >= cursorIndex_) {
wideText.erase(cursorIndex_ - deleteLength, deleteLength);
}
newText = convert.to_bytes(wideText);
cursorIndex_ -= deleteLength;
deleteTextWidth_ = inputText_->GetNextCharacterFullDispalyOffset(GetContentRect(),
GetStyleConst(), cursorIndex_, deleteLength);
offsetState_ = UPDATE_OFFSET_DELETE;
}
SetText(newText);
if (cursorAnimator_ != nullptr) {
static_cast<CursorAnimator*>(cursorAnimator_)->StartAnimator();
}
}
void UIEditText::UpdateInnerText()
{
InitText();
if (inputType_ == InputType::TEXT_TYPE) {
inputText_->SetText(GetInnerText().c_str());
} else {
inputText_->SetText(GetInnerPassword().c_str());
}
RefreshText();
}
void UIEditText::SetMaxLength(uint16_t maxLength)
{
InitText();
if (maxLength > MAX_TEXT_LENGTH) {
maxLength = MAX_TEXT_LENGTH;
}
maxLength_ = maxLength;
if (textStr_.length() > maxLength) {
SetText(textStr_.substr(0, maxLength));
}
}
uint16_t UIEditText::GetMaxLength()
{
return maxLength_;
}
void UIEditText::SetInputType(InputType inputType)
{
if (inputType_ == inputType) {
return;
}
inputType_ = inputType;
UpdateInnerText();
offsetState_ = UPDATE_OFFSET_INPUT_TYPE;
}
void UIEditText::UpdateOffsetByInputType()
{
if (!inputText_) {
return;
}
Rect contentRect = GetContentRect();
if (GetTextWidth() <= contentRect.GetWidth() - DEFAULT_TEXT_OFFSET) {
offsetX_ = DEFAULT_TEXT_OFFSET;
return;
}
if (cursorIndex_ == GetTextLength()) {
offsetX_ = -(GetTextWidth() - GetRect().GetWidth() + BOTH_SIDE_TEXT_OFFSET);
return;
}
if (offsetX_ + GetTextWidth() < contentRect.GetWidth() - DEFAULT_TEXT_OFFSET) {
offsetX_ = contentRect.GetWidth() - DEFAULT_TEXT_OFFSET - GetTextWidth();
return;
}
UpdateOffsetBySetCursorIndex();
}
void UIEditText::CheckValueChange(std::string text)
{
if (onChangeListener_ == nullptr) {
return;
}
if (textStr_.compare(text) != 0) {
onChangeListener_->OnChange(*this, text.c_str());
}
}
uint16_t UIEditText::GetTextLength()
{
if ((inputText_ == nullptr) || (inputText_->GetText() == nullptr)) {
return 0;
}
return TypedText::GetUTF8CharacterSize(inputText_->GetText(),
std::string(inputText_->GetText()).length());
}
uint16_t UIEditText::GetTextWidthByIndex(const uint16_t cursorIndex, uint16_t length)
{
return GetTextWidthByCursorIndex(cursorIndex);
}
uint16_t UIEditText::GetTextCountByTextWidth(uint32_t textWidth)
{
if (inputText_ == nullptr) {
return 0;
}
return inputText_->GetLetterIndexByLinePosition(GetStyleConst(),
GetContentRect().GetWidth(), textWidth, offsetX_);
}
}