vkdoom_m/libraries/ZWidget/src/widgets/lineedit/lineedit.cpp

1193 lines
26 KiB
C++

#include "widgets/lineedit/lineedit.h"
#include "core/utf8reader.h"
#include "core/colorf.h"
LineEdit::LineEdit(Widget* parent) : Widget(parent)
{
SetNoncontentSizes(5.0, 3.0, 5.0, 3.0);
timer = new Timer(this);
timer->FuncExpired = [=]() { OnTimerExpired(); };
scroll_timer = new Timer(this);
scroll_timer->FuncExpired = [=]() { OnScrollTimerExpired(); };
SetCursor(StandardCursor::ibeam);
}
LineEdit::~LineEdit()
{
}
bool LineEdit::IsReadOnly() const
{
return readonly;
}
LineEdit::Alignment LineEdit::GetAlignment() const
{
return align_left;
}
bool LineEdit::IsLowercase() const
{
return lowercase;
}
bool LineEdit::IsUppercase() const
{
return uppercase;
}
bool LineEdit::IsPasswordMode() const
{
return password_mode;
}
int LineEdit::GetMaxLength() const
{
return max_length;
}
std::string LineEdit::GetText() const
{
return text;
}
int LineEdit::GetTextInt() const
{
return std::atoi(text.c_str());
}
float LineEdit::GetTextFloat() const
{
return (float)std::atof(text.c_str());
}
std::string LineEdit::GetSelection() const
{
int start = std::min(selection_start, selection_start + selection_length);
return text.substr(start, std::abs(selection_length));
}
int LineEdit::GetSelectionStart() const
{
return selection_start;
}
int LineEdit::GetSelectionLength() const
{
return selection_length;
}
int LineEdit::GetCursorPos() const
{
return cursor_pos;
}
Size LineEdit::GetTextSize()
{
Canvas* canvas = GetCanvas();
return GetVisualTextSize(canvas);
}
Size LineEdit::GetTextSize(const std::string& str)
{
Canvas* canvas = GetCanvas();
return canvas->measureText(str).size();
}
double LineEdit::GetPreferredContentWidth()
{
return GetTextSize().width;
}
double LineEdit::GetPreferredContentHeight(double width)
{
return GetTextSize().height;
}
void LineEdit::SelectAll()
{
SetTextSelection(0, (int)text.size());
Update();
}
void LineEdit::SetReadOnly(bool enable)
{
if (readonly != enable)
{
readonly = enable;
Update();
}
}
void LineEdit::SetAlignment(Alignment newalignment)
{
if (alignment != newalignment)
{
alignment = newalignment;
Update();
}
}
void LineEdit::SetLowercase(bool enable)
{
if (lowercase != enable)
{
lowercase = enable;
text = ToLower(text);
Update();
}
}
void LineEdit::SetUppercase(bool enable)
{
if (uppercase != enable)
{
uppercase = enable;
text = ToUpper(text);
Update();
}
}
void LineEdit::SetPasswordMode(bool enable)
{
if (password_mode != enable)
{
password_mode = enable;
Update();
}
}
void LineEdit::SetMaxLength(int length)
{
if (max_length != length)
{
max_length = length;
if ((int)text.length() > length)
{
if (FuncBeforeEditChanged)
FuncBeforeEditChanged();
text = text.substr(0, length);
if (FuncAfterEditChanged)
FuncAfterEditChanged();
}
Update();
}
}
void LineEdit::SetText(const std::string& newtext)
{
if (lowercase)
text = ToLower(newtext);
else if (uppercase)
text = ToUpper(newtext);
else
text = newtext;
clip_start_offset = 0;
UpdateTextClipping();
SetCursorPos((int)text.size());
SetTextSelection(0, 0);
Update();
}
void LineEdit::SetTextInt(int number)
{
text = std::to_string(number);
clip_start_offset = 0;
UpdateTextClipping();
SetCursorPos((int)text.size());
SetTextSelection(0, 0);
Update();
}
void LineEdit::SetTextFloat(float number, int num_decimal_places)
{
text = ToFixed(number, num_decimal_places);
clip_start_offset = 0;
UpdateTextClipping();
SetCursorPos((int)text.size());
SetTextSelection(0, 0);
Update();
}
void LineEdit::SetSelection(int pos, int length)
{
//don't call FuncSelectionChanged() here, because this
//member is for public usage
selection_start = pos;
selection_length = length;
Update();
}
void LineEdit::ClearSelection()
{
//don't call FuncSelectionChanged() here, because this
//member is for public usage
SetSelection(0, 0);
Update();
}
void LineEdit::DeleteSelectedText()
{
if (GetSelectionLength() == 0)
return;
int sel_start = selection_start;
int sel_end = selection_start + selection_length;
if (sel_start > sel_end)
std::swap(sel_start, sel_end);
text = text.substr(0, sel_start) + text.substr(sel_end, text.size());
cursor_pos = sel_start;
SetTextSelection(0, 0);
int old_pos = GetCursorPos();
SetCursorPos(0);
SetCursorPos(old_pos);
}
void LineEdit::SetCursorPos(int pos)
{
cursor_pos = pos;
UpdateTextClipping();
Update();
}
void LineEdit::SetInputMask(const std::string& mask)
{
input_mask = mask;
}
void LineEdit::SetNumericMode(bool enable, bool decimals)
{
numeric_mode = enable;
numeric_mode_decimals = decimals;
}
void LineEdit::SetDecimalCharacter(const std::string& new_decimal_char)
{
decimal_char = new_decimal_char;
}
void LineEdit::SetSelectAllOnFocusGain(bool enable)
{
select_all_on_focus_gain = enable;
}
void LineEdit::OnMouseMove(const Point& pos)
{
if (mouse_selecting && !ignore_mouse_events)
{
if (pos.x < 0.0 || pos.x >= GetWidth())
{
if (pos.x < 0.0)
mouse_moves_left = true;
else
mouse_moves_left = false;
if (!readonly)
scroll_timer->Start(50, true);
}
else
{
scroll_timer->Stop();
cursor_pos = GetCharacterIndex(pos.x);
SetSelectionLength(cursor_pos - selection_start);
Update();
}
}
}
bool LineEdit::OnMouseDown(const Point& pos, int key)
{
if (key == IK_LeftMouse)
{
if (HasFocus())
{
CaptureMouse();
mouse_selecting = true;
cursor_pos = GetCharacterIndex(pos.x);
SetTextSelection(cursor_pos, 0);
}
else
{
SetFocus();
}
Update();
}
return true;
}
bool LineEdit::OnMouseDoubleclick(const Point& pos, int key)
{
return true;
}
bool LineEdit::OnMouseUp(const Point& pos, int key)
{
if (mouse_selecting && key == IK_LeftMouse)
{
if (ignore_mouse_events) // This prevents text selection from changing from what was set when focus was gained.
{
ReleaseMouseCapture();
ignore_mouse_events = false;
mouse_selecting = false;
}
else
{
scroll_timer->Stop();
ReleaseMouseCapture();
mouse_selecting = false;
int sel_end = GetCharacterIndex(pos.x);
SetSelectionLength(sel_end - selection_start);
cursor_pos = sel_end;
SetFocus();
Update();
}
}
return true;
}
void LineEdit::OnKeyChar(std::string chars)
{
if (FuncFilterKeyChar)
{
chars = FuncFilterKeyChar(chars);
if (chars.empty())
return;
}
if (!chars.empty() && !(chars[0] >= 0 && chars[0] < 32))
{
if (FuncBeforeEditChanged)
FuncBeforeEditChanged();
DeleteSelectedText();
if (input_mask.empty())
{
if (numeric_mode)
{
// '-' can only be added once, and only as the first character.
if (chars == "-" && cursor_pos == 0 && text.find("-") == std::string::npos)
{
if (InsertText(cursor_pos, chars))
cursor_pos += (int)chars.size();
}
else if (numeric_mode_decimals && chars == decimal_char && cursor_pos > 0) // add decimal char
{
if (text.find(decimal_char) == std::string::npos) // allow only one decimal char.
{
if (InsertText(cursor_pos, chars))
cursor_pos += (int)chars.size();
}
}
else if (numeric_mode_characters.find(chars) != std::string::npos) // 0-9
{
if (InsertText(cursor_pos, chars))
cursor_pos += (int)chars.size();
}
}
else
{
// not in any special mode, just insert the string.
if (InsertText(cursor_pos, chars))
cursor_pos += (int)chars.size();
}
}
else
{
if (InputMaskAcceptsInput(cursor_pos, chars))
{
if (InsertText(cursor_pos, chars))
cursor_pos += (int)chars.size();
}
}
UpdateTextClipping();
if (FuncAfterEditChanged)
FuncAfterEditChanged();
}
}
void LineEdit::OnKeyDown(EInputKey key)
{
if (FuncIgnoreKeyDown && FuncIgnoreKeyDown(key))
return;
if (key == IK_Enter)
{
if (FuncEnterPressed)
FuncEnterPressed();
return;
}
if (!readonly) // Do not flash cursor when readonly
{
cursor_blink_visible = true;
timer->Start(500); // don't blink cursor when moving or typing.
}
if (key == IK_Enter || key == IK_Escape || key == IK_Tab)
{
// Do not consume these.
return;
}
else if (key == IK_A && GetKeyState(IK_Ctrl))
{
// select all
SetTextSelection(0, (int)text.size());
cursor_pos = selection_length;
UpdateTextClipping();
Update();
}
else if (key == IK_C && GetKeyState(IK_Ctrl))
{
if (!password_mode) // Do not allow copying the password to clipboard
{
std::string str = GetSelection();
SetClipboardText(str);
}
}
else if (readonly)
{
// Do not consume messages on read only component (only allow CTRL-A and CTRL-C)
return;
}
else if (key == IK_Left)
{
Move(-1, GetKeyState(IK_Ctrl), GetKeyState(IK_Shift));
}
else if (key == IK_Right)
{
Move(1, GetKeyState(IK_Ctrl), GetKeyState(IK_Shift));
}
else if (key == IK_Backspace)
{
Backspace();
UpdateTextClipping();
}
else if (key == IK_Delete)
{
Del();
UpdateTextClipping();
}
else if (key == IK_Home)
{
SetSelectionStart(cursor_pos);
cursor_pos = 0;
if (GetKeyState(IK_Shift))
SetSelectionLength(-selection_start);
else
SetTextSelection(0, 0);
UpdateTextClipping();
Update();
}
else if (key == IK_End)
{
SetSelectionStart(cursor_pos);
cursor_pos = (int)text.size();
if (GetKeyState(IK_Shift))
SetSelectionLength((int)text.size() - selection_start);
else
SetTextSelection(0, 0);
UpdateTextClipping();
Update();
}
else if (key == IK_X && GetKeyState(IK_Ctrl))
{
std::string str = GetSelection();
DeleteSelectedText();
SetClipboardText(str);
UpdateTextClipping();
}
else if (key == IK_V && GetKeyState(IK_Ctrl))
{
std::string str = GetClipboardText();
std::string::const_iterator end_str = std::remove(str.begin(), str.end(), '\n');
str.resize(end_str - str.begin());
end_str = std::remove(str.begin(), str.end(), '\r');
str.resize(end_str - str.begin());
DeleteSelectedText();
if (input_mask.empty())
{
if (numeric_mode)
{
std::string present_text = GetText();
bool present_minus = present_text.find('-') != std::string::npos;
bool str_minus = str.find('-') != std::string::npos;
if (!present_minus || !str_minus)
{
if ((!present_minus && !str_minus) || //if no minus found
(str_minus && cursor_pos == 0 && str[0] == '-') || //if there's minus in text to paste
(present_minus && cursor_pos > 0)) //if there's minus in the beginning of control's text
{
if (numeric_mode_decimals)
{
std::string::size_type decimal_point_pos;
if ((decimal_point_pos = str.find_first_not_of(numeric_mode_characters, str[0] == '-' ? 1 : 0)) == std::string::npos) //no decimal char inside string to paste
{ //we don't look at the position of decimal char inside of text in the textbox, if it's present
if (InsertText(cursor_pos, str))
SetCursorPos(cursor_pos + (int)str.length());
}
else
{
if (present_text.find(decimal_char) == std::string::npos &&
str[decimal_point_pos] == decimal_char[0] &&
str.find_first_not_of(numeric_mode_characters, decimal_point_pos + 1) == std::string::npos) //allow only one decimal char in the string to paste
{
if (InsertText(cursor_pos, str))
SetCursorPos(cursor_pos + (int)str.length());
}
}
}
else
{
if (str.find_first_not_of(numeric_mode_characters, str[0] == '-' ? 1 : 0) == std::string::npos)
{
if (InsertText(cursor_pos, str))
SetCursorPos(cursor_pos + (int)str.length());
}
}
}
}
}
else
{
if (InsertText(cursor_pos, str))
SetCursorPos(cursor_pos + (int)str.length());
}
}
else
{
if (InputMaskAcceptsInput(cursor_pos, str))
{
if (InsertText(cursor_pos, str))
SetCursorPos(cursor_pos + (int)str.length());
}
}
UpdateTextClipping();
}
else if (GetKeyState(IK_Ctrl) && key == IK_Z)
{
if (!readonly)
{
std::string tmp = undo_info.undo_text;
undo_info.undo_text = GetText();
SetText(tmp);
}
}
else if (key == IK_Shift)
{
if (selection_start == -1)
SetTextSelection(cursor_pos, 0);
}
if (FuncAfterEditChanged)
FuncAfterEditChanged();
}
void LineEdit::OnKeyUp(EInputKey key)
{
}
void LineEdit::OnSetFocus()
{
if (!readonly)
timer->Start(500);
if (select_all_on_focus_gain)
SelectAll();
ignore_mouse_events = true;
cursor_pos = (int)text.length();
Update();
if (FuncFocusGained)
FuncFocusGained();
}
void LineEdit::OnLostFocus()
{
timer->Stop();
SetTextSelection(0, 0);
Update();
if (FuncFocusLost)
FuncFocusLost();
}
void LineEdit::Move(int steps, bool ctrl, bool shift)
{
if (shift && selection_length == 0)
SetSelectionStart(cursor_pos);
// Jump over words if control is pressed.
if (ctrl)
{
if (steps < 0)
steps = FindPreviousBreakCharacter(cursor_pos - 1) - cursor_pos;
else
steps = FindNextBreakCharacter(cursor_pos + 1) - cursor_pos;
cursor_pos += steps;
if (cursor_pos < 0)
cursor_pos = 0;
if (cursor_pos > (int)text.size())
cursor_pos = (int)text.size();
}
else
{
UTF8Reader utf8_reader(text.data(), text.length());
utf8_reader.set_position(cursor_pos);
if (steps > 0)
{
for (int i = 0; i < steps; i++)
utf8_reader.next();
}
else if (steps < 0)
{
for (int i = 0; i < -steps; i++)
utf8_reader.prev();
}
cursor_pos = (int)utf8_reader.position();
}
// Clear the selection if a cursor key is pressed but shift isn't down.
if (shift)
SetSelectionLength(cursor_pos - selection_start);
else
SetTextSelection(-1, 0);
UpdateTextClipping();
Update();
undo_info.first_text_insert = true;
}
bool LineEdit::InsertText(int pos, const std::string& str)
{
undo_info.first_erase = false;
if (undo_info.first_text_insert)
{
undo_info.undo_text = GetText();
undo_info.first_text_insert = false;
}
// checking if insert exceeds max length
if (UTF8Reader::utf8_length(text) + UTF8Reader::utf8_length(str) > max_length)
{
return false;
}
if (lowercase)
text.insert(pos, ToLower(str));
else if (uppercase)
text.insert(pos, ToUpper(str));
else
text.insert(pos, str);
UpdateTextClipping();
Update();
return true;
}
void LineEdit::Backspace()
{
if (undo_info.first_erase)
{
undo_info.first_erase = false;
undo_info.undo_text = GetText();
}
if (GetSelectionLength() != 0)
{
DeleteSelectedText();
Update();
}
else
{
if (cursor_pos > 0)
{
UTF8Reader utf8_reader(text.data(), text.length());
utf8_reader.set_position(cursor_pos);
utf8_reader.prev();
size_t length = utf8_reader.char_length();
text.erase(cursor_pos - length, length);
cursor_pos -= (int)length;
Update();
}
}
int old_pos = GetCursorPos();
SetCursorPos(0);
SetCursorPos(old_pos);
}
void LineEdit::Del()
{
if (undo_info.first_erase)
{
undo_info.first_erase = false;
undo_info.undo_text = GetText();
}
if (GetSelectionLength() != 0)
{
DeleteSelectedText();
Update();
}
else
{
if (cursor_pos < (int)text.size())
{
UTF8Reader utf8_reader(text.data(), text.length());
utf8_reader.set_position(cursor_pos);
size_t length = utf8_reader.char_length();
text.erase(cursor_pos, length);
Update();
}
}
}
int LineEdit::GetCharacterIndex(double mouse_x)
{
if (text.size() <= 1)
{
return (int)text.size();
}
Canvas* canvas = GetCanvas();
UTF8Reader utf8_reader(text.data(), text.length());
int seek_start = clip_start_offset;
int seek_end = (int)text.size();
int seek_center = (seek_start + seek_end) / 2;
//fast search
while (true)
{
utf8_reader.set_position(seek_center);
utf8_reader.move_to_leadbyte();
seek_center = (int)utf8_reader.position();
Size text_size = GetVisualTextSize(canvas, clip_start_offset, seek_center - clip_start_offset);
if (text_size.width > mouse_x)
seek_end = seek_center;
else
seek_start = seek_center;
if (seek_end - seek_start < 7)
break; //go to accurate search
seek_center = (seek_start + seek_end) / 2;
}
utf8_reader.set_position(seek_start);
utf8_reader.move_to_leadbyte();
//accurate search
while (true)
{
seek_center = (int)utf8_reader.position();
Size text_size = GetVisualTextSize(canvas, clip_start_offset, seek_center - clip_start_offset);
if (text_size.width > mouse_x || utf8_reader.is_end())
break;
utf8_reader.next();
}
return seek_center;
}
void LineEdit::UpdateTextClipping()
{
Canvas* canvas = GetCanvas();
if (!canvas)
return;
Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset);
if (cursor_pos < clip_start_offset)
clip_start_offset = cursor_pos;
Rect cursor_rect = GetCursorRect();
UTF8Reader utf8_reader(text.data(), text.length());
double width = GetWidth();
while (cursor_rect.x + cursor_rect.width > width)
{
utf8_reader.set_position(clip_start_offset);
utf8_reader.next();
clip_start_offset = (int)utf8_reader.position();
if (clip_start_offset == text.size())
break;
cursor_rect = GetCursorRect();
}
// Get number of chars of current text fitting in the lineedit.
int search_upper = (int)text.size();
int search_lower = clip_start_offset;
while (true)
{
int midpoint = (search_lower + search_upper) / 2;
utf8_reader.set_position(midpoint);
utf8_reader.move_to_leadbyte();
if (midpoint != utf8_reader.position())
utf8_reader.next();
midpoint = (int)utf8_reader.position();
if (midpoint == search_lower || midpoint == search_upper)
break;
Size midpoint_size = GetVisualTextSize(canvas, clip_start_offset, midpoint - clip_start_offset);
if (width < midpoint_size.width)
search_upper = midpoint;
else
search_lower = midpoint;
}
clip_end_offset = search_upper;
if (cursor_rect.x < 0.0)
{
clip_start_offset = cursor_pos;
}
}
Rect LineEdit::GetCursorRect()
{
Canvas* canvas = GetCanvas();
int substr_end = cursor_pos - clip_start_offset;
if (substr_end < 0)
substr_end = 0;
std::string clipped_text = text.substr(clip_start_offset, substr_end);
if (password_mode)
{
// If we are in password mode, we gonna return the right characters
clipped_text = CreatePassword(UTF8Reader::utf8_length(clipped_text));
}
Size text_size_before_cursor = canvas->measureText(clipped_text).size();
Rect cursor_rect;
cursor_rect.x = text_size_before_cursor.width;
cursor_rect.width = 1.0f;
cursor_rect.y = vertical_text_align.top;
cursor_rect.height = vertical_text_align.bottom - vertical_text_align.top;
return cursor_rect;
}
Rect LineEdit::GetSelectionRect()
{
Canvas* canvas = GetCanvas();
// text before selection:
std::string txt_before = GetVisibleTextBeforeSelection();
Size text_size_before_selection = canvas->measureText(txt_before).size();
// selection text:
std::string txt_selected = GetVisibleSelectedText();
Size text_size_selection = canvas->measureText(txt_selected).size();
Rect selection_rect;
selection_rect.x = text_size_before_selection.width;
selection_rect.width = text_size_selection.width;
selection_rect.y = vertical_text_align.top;
selection_rect.height = vertical_text_align.bottom - vertical_text_align.top;
return selection_rect;
}
int LineEdit::FindNextBreakCharacter(int search_start)
{
if (search_start >= int(text.size()) - 1)
return (int)text.size();
size_t pos = text.find_first_of(break_characters, search_start);
if (pos == std::string::npos)
return (int)text.size();
return (int)pos;
}
int LineEdit::FindPreviousBreakCharacter(int search_start)
{
if (search_start <= 0)
return 0;
size_t pos = text.find_last_of(break_characters, search_start);
if (pos == std::string::npos)
return 0;
return (int)pos;
}
void LineEdit::OnTimerExpired()
{
if (!IsVisible())
{
timer->Stop();
return;
}
if (cursor_blink_visible)
timer->Start(500);
else
timer->Start(500);
cursor_blink_visible = !cursor_blink_visible;
Update();
}
void LineEdit::OnGeometryChanged()
{
Canvas* canvas = GetCanvas();
vertical_text_align = canvas->verticalTextAlign();
clip_start_offset = 0;
cursor_pos = 0;
UpdateTextClipping();
}
std::string LineEdit::GetVisibleTextBeforeSelection()
{
std::string ret;
int sel_start = std::min(selection_start, selection_start + selection_length);
int start = std::min(sel_start, clip_start_offset);
if (start < clip_start_offset)
return ret;
int end = std::min(sel_start, clip_end_offset);
ret = text.substr(start, end - start);
// If we are in password mode, we gonna return the right characters
if (password_mode)
ret = CreatePassword(UTF8Reader::utf8_length(ret));
return ret;
}
std::string LineEdit::GetVisibleSelectedText()
{
std::string ret;
if (selection_length == 0)
return ret;
int sel_start = std::min(selection_start, selection_start + selection_length);
int sel_end = std::max(selection_start, selection_start + selection_length);
int end = std::min(clip_end_offset, sel_end);
int start = std::max(clip_start_offset, sel_start);
if (start > end)
return ret;
if (start == end)
return ret;
ret = text.substr(start, end - start);
// If we are in password mode, we gonna return the right characters
if (password_mode)
ret = CreatePassword(UTF8Reader::utf8_length(ret));
return ret;
}
void LineEdit::SetSelectionStart(int start)
{
if (FuncSelectionChanged && selection_length && selection_start != start)
FuncSelectionChanged();
selection_start = start;
}
void LineEdit::SetSelectionLength(int length)
{
if (FuncSelectionChanged && selection_length != length)
FuncSelectionChanged();
selection_length = length;
}
void LineEdit::SetTextSelection(int start, int length)
{
if (FuncSelectionChanged && (selection_length != length || (selection_length && selection_start != start)))
FuncSelectionChanged();
selection_start = start;
selection_length = length;
}
std::string LineEdit::GetVisibleTextAfterSelection()
{
// returns the whole visible string if there is no selection.
std::string ret;
int sel_end = std::max(selection_start, selection_start + selection_length);
int start = std::max(clip_start_offset, sel_end);
int end = clip_end_offset;
if (start > end)
return ret;
if (clip_end_offset == sel_end)
return ret;
if (sel_end <= 0)
return ret;
else
{
ret = text.substr(start, end - start);
// If we are in password mode, we gonna return the right characters
if (password_mode)
ret = CreatePassword(UTF8Reader::utf8_length(ret));
return ret;
}
}
void LineEdit::OnPaintFrame(Canvas* canvas)
{
double w = GetFrameGeometry().width;
double h = GetFrameGeometry().height;
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
canvas->fillRect(Rect::xywh(w - 1.0, 0.0, 1.0, h - 0.0), bordercolor);
}
void LineEdit::OnPaint(Canvas* canvas)
{
std::string txt_before = GetVisibleTextBeforeSelection();
std::string txt_selected = GetVisibleSelectedText();
std::string txt_after = GetVisibleTextAfterSelection();
if (txt_before.empty() && txt_selected.empty() && txt_after.empty())
{
txt_after = text.substr(clip_start_offset, clip_end_offset - clip_start_offset);
// If we are in password mode, we gonna return the right characters
if (password_mode)
txt_after = CreatePassword(UTF8Reader::utf8_length(txt_after));
}
Size size_before = canvas->measureText(txt_before).size();
Size size_selected = canvas->measureText(txt_selected).size();
if (!txt_selected.empty())
{
// Draw selection box.
Rect selection_rect = GetSelectionRect();
canvas->fillRect(selection_rect, HasFocus() ? Colorf::fromRgba8(100, 100, 100) : Colorf::fromRgba8(68, 68, 68));
}
// Draw text before selection
if (!txt_before.empty())
{
canvas->drawText(Point(0.0, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_before);
}
if (!txt_selected.empty())
{
canvas->drawText(Point(size_before.width, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_selected);
}
if (!txt_after.empty())
{
canvas->drawText(Point(size_before.width + size_selected.width, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_after);
}
// draw cursor
if (HasFocus())
{
if (cursor_blink_visible)
{
Rect cursor_rect = GetCursorRect();
canvas->fillRect(cursor_rect, Colorf::fromRgba8(255, 255, 255));
}
}
}
void LineEdit::OnScrollTimerExpired()
{
if (mouse_moves_left)
Move(-1, false, false);
else
Move(1, false, false);
}
void LineEdit::OnEnableChanged()
{
bool enabled = IsEnabled();
if (!enabled)
{
cursor_blink_visible = false;
timer->Stop();
}
Update();
}
bool LineEdit::InputMaskAcceptsInput(int cursor_pos, const std::string& str)
{
return str.find_first_not_of(input_mask) == std::string::npos;
}
std::string LineEdit::CreatePassword(std::string::size_type num_letters) const
{
return std::string(num_letters, '*');
}
Size LineEdit::GetVisualTextSize(Canvas* canvas, int pos, int npos) const
{
return canvas->measureText(password_mode ? CreatePassword(UTF8Reader::utf8_length(text.substr(pos, npos))) : text.substr(pos, npos)).size();
}
Size LineEdit::GetVisualTextSize(Canvas* canvas) const
{
return canvas->measureText(password_mode ? CreatePassword(UTF8Reader::utf8_length(text)) : text).size();
}
std::string LineEdit::ToFixed(float number, int num_decimal_places)
{
for (int i = 0; i < num_decimal_places; i++)
number *= 10.0f;
std::string val = std::to_string((int)std::round(number));
if ((int)val.size() < num_decimal_places)
val.resize(num_decimal_places + 1, 0);
return val.substr(0, val.size() - num_decimal_places) + "." + val.substr(val.size() - num_decimal_places);
}
std::string LineEdit::ToLower(const std::string& text)
{
return text;
}
std::string LineEdit::ToUpper(const std::string& text)
{
return text;
}
const std::string LineEdit::break_characters = " ::;,.-";
const std::string LineEdit::numeric_mode_characters = "0123456789";