Files
wnsrc/lua/sui/vgui/sui_text_entry.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

389 lines
8.5 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local sui = sui
local surface = surface
local utf8 = sui.utf8
local draw = draw
local math = math
local IsValid = IsValid
local tostring = tostring
local tonumber = tonumber
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
local GetColor = SUI.GetColor
local TEXT_ENTRY_FONT = SUI.CreateFont("TextEntry", "Roboto Regular", 16)
local Panel = {}
sui.scaling_functions(Panel)
AccessorFunc(Panel, "m_FontName", "Font", FORCE_STRING)
AccessorFunc(Panel, "m_Editable", "Editable", FORCE_BOOL)
AccessorFunc(Panel, "m_Placeholder", "Placeholder", FORCE_STRING)
AccessorFunc(Panel, "m_MaxChars", "MaxChars", FORCE_NUMBER)
AccessorFunc(Panel, "m_Numeric", "Numeric", FORCE_BOOL)
AccessorFunc(Panel, "m_NoBar", "NoBar", FORCE_BOOL)
AccessorFunc(Panel, "m_BarColor", "BarColor")
AccessorFunc(Panel, "m_Background", "Background")
AccessorFunc(Panel, "m_Radius", "Radius")
AccessorFunc(Panel, "m_NoEnter", "NoEnter")
Panel:SetRadius(3)
function Panel:Init()
self:ScaleInit()
self:SetupTransition("TextEntryReady", 0.9, function()
return self:IsEditing() or self:GetBarColor() ~= nil
end)
self:SetUpdateOnType(true)
self:SetCursor("beam")
self:SetFont(TEXT_ENTRY_FONT)
self:SetPlaceholder("Placeholder text")
self:SetSize(200, 22)
self.allowed_numeric_characters = "1234567890.-"
self.history = {}
self.history_pos = 1
self.can_use_history = true
self:OnScaleChange()
end
function Panel:SetCaretPos(pos)
DTextEntry.SetCaretPos(self, math.Clamp(pos, 0, utf8.len(self:GetText())))
end
function Panel:SetValue(value)
self:SetText(value)
self:OnValueChange(value)
end
function Panel:AllowInput(ch)
if self:CheckNumeric(ch) then return true end
if sui.wspace_chs[ch] or sui.cntrl_chs[ch] then
return true
end
local max_chars = self:GetMaxChars()
if max_chars and #self:GetText() >= max_chars then
surface.PlaySound("resource/warning.wav")
return true
end
end
function Panel:AddValue(v, i, j)
local original_text = self:GetText()
local start
if i then
start = original_text:sub(1, i)
else
start = utf8.sub(original_text, 1, self:GetCaretPos())
end
local text = start .. v
local caret_pos = utf8.len(text)
local _end
if j then
_end = original_text:sub(j)
else
_end = utf8.sub(original_text, utf8.len(start) + 1)
end
text = text .. _end
local max_chars = self:GetMaxChars()
if max_chars then
text = text:sub(1, max_chars)
end
self:SetValue(text)
self:SetCaretPos(caret_pos)
end
function Panel:OnKeyCodeTyped(code)
if self.no_down then
self.no_down = nil
return
end
if code == KEY_UP or code == KEY_DOWN then
if not self:UpdateFromHistory(code) then
return true
end
local lines, caret_line = self:GetNumLines()
if lines == 1 then
return true
end
--
-- this fixes a weird issue
-- make the text entry has at least 2 lines, go up then go down, you won't be able to go up again
--
if code == KEY_DOWN and lines == caret_line + 1 then
self.no_down = true
gui.InternalKeyCodeTyped(KEY_DOWN)
end
end
self:OnKeyCode(code)
if code == KEY_ENTER then
if IsValid(self.Menu) then
self.Menu:Remove()
end
if not self:GetNoEnter() then
self:FocusNext()
self:OnEnter()
end
end
end
function Panel:DisallowFloats(disallow)
if not isbool(disallow) then
disallow = true
end
if disallow then
self.allowed_numeric_characters = self.allowed_numeric_characters:gsub("%.", "", 1)
elseif not self.allowed_numeric_characters:find(".", 1, true) then
self.allowed_numeric_characters = self.allowed_numeric_characters .. "."
end
end
function Panel:DisallowNegative(disallow)
if not isbool(disallow) then
disallow = true
end
if disallow then
self.allowed_numeric_characters = self.allowed_numeric_characters:gsub("%-", "", 1)
elseif not self.allowed_numeric_characters:find("-", 1, true) then
self.allowed_numeric_characters = self.allowed_numeric_characters .. "-"
end
end
function Panel:CheckNumeric(value)
if not self:GetNumeric() then return false end
if not self.allowed_numeric_characters:find(value, 1, true) then
return true
end
local new_value = ""
local current_value = tostring(self:GetText())
local caret_pos = self:GetCaretPos()
for i = 0, #current_value do
new_value = new_value .. current_value:sub(i, i)
if i == caret_pos then
new_value = new_value .. value
end
end
if #current_value ~= 0 and not tonumber(new_value) then
return true
end
return false
end
function Panel:AddHistory(txt)
if not txt or txt == "" then return end
local history = self.history
if history[#history] ~= txt then
table.insert(history, txt)
end
end
function Panel:UpdateFromHistory(code)
if not self.can_use_history then return end
local lines, caret_line = self:GetNumLines()
if code == KEY_UP then
if caret_line > 1 then return true end -- enable the caret to move up and down
if self.history_pos <= 1 then return end
self.history_pos = self.history_pos - 1
elseif code == KEY_DOWN then
if caret_line ~= lines then return true end
if self.history_pos >= #self.history then
self:SetValue("")
self:SetCaretPos(0)
self.history_pos = #self.history + 1
return
end
self.history_pos = self.history_pos + 1
end
local text = self.history[self.history_pos]
if not text then return end
self:SetValue(text)
self:SetCaretPos(utf8.len(text))
end
function Panel:OnTextChanged()
self.history_pos = #self.history + 1
local text = self:GetText()
self.can_use_history = text == "" and true or false
if self:GetUpdateOnType() then
self:UpdateConvarValue()
self:OnValueChange(text)
end
self:OnChange()
end
function Panel:OnScaleChange()
self:InvalidateLayout()
self:InvalidateLayout(true)
end
function Panel:Paint(w, h)
self:RoundedBox("Background", self:GetRadius(), 0, 0, w, h, GetColor("text_entry_bg") or self:GetBackground())
local text_entry_3 = GetColor("text_entry_3")
if self:GetText() == "" then
local old_text = self:GetText()
self:SetText(self:GetPlaceholder())
self:DrawTextEntryText(GetColor("text_entry_2"), text_entry_3, text_entry_3)
self:SetText(old_text)
else
self:DrawTextEntryText(GetColor("text_entry"), text_entry_3, text_entry_3)
end
if not self:GetNoBar() then
local bar_color = self:GetBarColor()
self:RoundedBox("Bar1", 0, 0, h - 1, w, 1, GetColor("text_entry_bar_color"))
local bar = math.Round(w * self.TextEntryReady)
if bar > 0 then
self:RoundedBox("Bar2", 0, (w / 2) - (bar / 2), h - 1, bar, 1, bar_color or text_entry_3)
end
end
end
-- https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/vgui2/vgui_controls/TextEntry.cpp#L969
function Panel:GetNumLines(wide)
local num_lines = 1
wide = wide or self:GetWide() - 2
local vbar = self:GetChildren()[1]
if vbar then
wide = wide - vbar:GetWide()
end
local char_width
local x = 3
local word_start_index = 1
local word_start_len
local word_length = 0
local has_word = false
local just_started_new_line = true
local word_started_on_new_line = true
local start_char = 1
surface.SetFont(self:GetFont())
local i = start_char
local text, n = utf8.force(self:GetText())
local caret_line = 0
local caret_pos = self:GetCaretPos()
local caret_i = 1
while i <= n do
local ch_len = utf8.char_bytes(text:byte(i))
local ch = text:sub(i, i + ch_len - 1)
if ch ~= " " then
if not has_word then
word_start_index = i
word_start_len = ch_len
has_word = true
word_started_on_new_line = just_started_new_line
word_length = 0
end
else
has_word = false
end
char_width = surface.GetTextSize(ch)
just_started_new_line = false
if (x + char_width) >= wide then
x = 3
just_started_new_line = true
has_word = false
if word_started_on_new_line then
num_lines = num_lines + 1
else
num_lines = num_lines + 1
i = (word_start_index + word_start_len) - ch_len
end
word_length = 0
end
x = x + char_width
word_length = word_length + char_width
if caret_i == caret_pos then
caret_line = num_lines
end
i = i + ch_len
caret_i = caret_i + 1
end
return num_lines, caret_line
end
function Panel:SetCheck(func, col)
function self:OnValueChange(text)
if func(text) == false then
self.valid = false
self:SetBarColor(GetColor("close_hover"))
self:SetNoEnter(true)
else
self.valid = true
self:SetBarColor(col)
self:SetNoEnter(false)
end
end
self:SetValue(self:GetText())
end
sui.register("TextEntry", Panel, "DTextEntry")