dobrograd-13-06-2022/garrysmod/addons/util-dlib/lua/dlib/vgui/dtextentry.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

578 lines
12 KiB
Lua

-- Copyright (C) 2017-2020 DBotThePony
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.
local vgui = vgui
local hook = hook
local IsValid = IsValid
local DLib = DLib
local RealTimeL = RealTimeL
local color_white = color_white
local Color = Color
local color_dlib = color_dlib
local tonumber = tonumber
local surface = surface
local draw = draw
--[[
@doc
@panel DLib_TextEntry
@parent DTextEntry
@desc
DTextEntry with `:OnEnter(value)` callback (which means it will be called after user press Enter)
also contains autocomplete qol fixes
@enddesc
]]
local PANEL = {}
DLib.VGUI.TextEntry = PANEL
surface.CreateFont('DLib_TextEntry', {
font = 'Roboto',
size = 16,
weight = 500,
extended = true
})
function PANEL:Init()
self:SetText('')
self:SetKeyboardInputEnabled(true)
self:SetMouseInputEnabled(true)
self:SetFont('DLib_TextEntry')
end
function PANEL:OnEnter(value)
end
function PANEL:OnGetFocus(...)
if DTextEntry.OnGetFocus then
DTextEntry.OnGetFocus(self, ...)
end
local autocomplete = self:GetAutoComplete(self:GetText())
if autocomplete then
self:OpenAutoComplete(autocomplete)
end
end
function PANEL:OnMousePressed(mcode)
if DTextEntry.OnMousePressed then
DTextEntry.OnMousePressed(self, mcode)
end
if mcode == MOUSE_LEFT then
local autocomplete = self:GetAutoComplete(self:GetText())
if autocomplete then
self:OpenAutoComplete(autocomplete)
end
end
end
function PANEL:Think()
if not IsValid(self.Menu) then
self:ConVarStringThink()
end
end
function PANEL:OpenAutoComplete(tab)
if not tab or #tab == 0 then return end
if IsValid(self.Menu) then
self.Menu:Remove()
end
self.Menu = DermaMenu()
for k, line in ipairs(tab) do
self.Menu:AddOption(line, function()
self:SetText(line)
self:SetCaretPos(#line)
self:UpdateConvarValue()
self:RequestFocus()
self:OnValueChange(line)
end)
end
local x, y = self:LocalToScreen(0, self:GetTall())
self.Menu:SetMinimumWidth(self:GetWide())
self.Menu:Open(x, y, true, self)
self.Menu:SetPos(x, y)
self.Menu:SetMaxHeight(ScrHL() - y - 10)
end
function PANEL:OnKeyCodeTyped(key)
if key == KEY_FIRST or key == KEY_NONE or key == KEY_TAB then
return true
elseif key == KEY_ENTER then
self:OnEnter((self:GetValue() or ''):Trim())
self:KillFocus()
if IsValid(self.Menu) then
self.Menu:Remove()
end
return true
end
if DTextEntry.OnKeyCodeTyped then
return DTextEntry.OnKeyCodeTyped(self, key)
end
return false
end
function PANEL:GetValueBeforeCaret()
local value = self:GetValue() or ''
return value:sub(1, self:GetCaretPos())
end
function PANEL:GetValueAfterCaret()
local value = self:GetValue() or ''
return value:sub(self:GetCaretPos() + 1)
end
vgui.Register('DLib_TextEntry', PANEL, 'DTextEntry')
local TEXTENTRY = PANEL
--[[
@doc
@panel DLib_TextEntry_Configurable
@parent DTextEntry
@desc
base panel for creating configuration based text entries
@enddesc
]]
PANEL = {}
DLib.VGUI.TextEntry_Configurable = PANEL
--[[
@doc
@fname DLib_TextEntry_Configurable:GetLengthLimit
@returns
number
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetLengthLimit
@args number value
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetTooltipTime
@returns
number
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetTooltipTime
@args number value
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetTooltipShown
@returns
boolean
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetTooltipShown
@internal
@args boolean value
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetIsWhitelistMode
@returns
boolean
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetIsWhitelistMode
@args boolean value
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetDisallowedHashSet
@returns
DLib.HashSet: or nil
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetDisallowedHashSet
@internal
@args table hashset
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetAllowedHashSet
@returns
DLib.HashSet: or nil
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetAllowedHashSet
@internal
@args table hashset
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:GetDefaultReason
@returns
string
]]
--[[
@doc
@fname DLib_TextEntry_Configurable:SetDefaultReason
@internal
@args string value
]]
AccessorFunc(PANEL, 'lengthLimit', 'LengthLimit')
AccessorFunc(PANEL, 'tooltipTime', 'TooltipTime')
AccessorFunc(PANEL, 'tooltip', 'TooltipShown')
AccessorFunc(PANEL, 'whitelistMode', 'IsWhitelistMode')
AccessorFunc(PANEL, 'disallowed', 'DisallowedHashSet')
AccessorFunc(PANEL, 'allowed', 'AllowedHashSet')
AccessorFunc(PANEL, 'defaultReason', 'DefaultReason')
function PANEL:Init()
self.allowed = DLib.HashSet()
self.disallowed = DLib.HashSet()
self.whitelistMode = false
self.tooltipTime = 0
self.tooltip = false
self.lengthLimit = 0
self.tooltipReason = 'gui.entry.invalidsymbol'
self.defaultReason = 'gui.entry.invalidsymbol'
hook.Add('PostRenderVGUI', self, self.PostRenderVGUI)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:PredictValueChange
@args string keycode
@internal
@returns
string
]]
function PANEL:PredictValueChange(key)
local value1 = self:GetValueBeforeCaret()
local value2 = self:GetValueAfterCaret()
local char = DLib.KeyMap.KEY[key]
if char then
return value1 .. char .. value2
elseif key == KEY_BACKSPACE then
return value1:sub(1, #value1 - 1) .. value2
elseif key == KEY_DELETE then
return value1 .. value2:sub(1, #value2 - 1)
end
return self:GetValue()
end
local function isTechincal(key)
return key == KEY_BACKSPACE or
key == KEY_DELETE or
key == KEY_UP or
key == KEY_DOWN or
key == KEY_LEFT or
key == KEY_RIGHT
end
local function isControl(key)
return key == KEY_UP or
key == KEY_DOWN or
key == KEY_LEFT or
key == KEY_RIGHT
end
function PANEL:OnKeyCodeTyped(key)
local reply = TEXTENTRY.OnKeyCodeTyped(self, key)
if reply == true then return reply end
if not isTechincal then
if self.whitelistMode then
if not self.allowed:has(key) then
self:Ding()
--return true
end
else
if self.disallowed:has(key) then
self:Ding()
--return true
end
end
end
if self.lengthLimit > 0 and #(self:GetValue() or '') + 1 > self.lengthLimit then
self:Ding('gui.entry.limit')
--return true
end
return false
end
--[[
@doc
@fname DLib_TextEntry_Configurable:AddToBlacklist
@args string keycode
]]
function PANEL:AddToBlacklist(value)
return self.disallowed:add(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:AddToWhitelist
@args string keycode
]]
function PANEL:AddToWhitelist(value)
return self.allowed:add(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:RemoveFromBlacklist
@args string keycode
]]
function PANEL:RemoveFromBlacklist(value)
return self.disallowed:remove(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:RemoveFromWhitelist
@args string keycode
]]
function PANEL:RemoveFromWhitelist(value)
return self.allowed:remove(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:InBlacklist
@args string keycode
]]
function PANEL:InBlacklist(value)
return self.disallowed:has(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:InWhitelist
@args string keycode
]]
function PANEL:InWhitelist(value)
return self.allowed:add(value)
end
--[[
@doc
@fname DLib_TextEntry_Configurable:Ding
@args string reason
]]
function PANEL:Ding(reason)
reason = reason or self.defaultReason
self.tooltipReason = DLib.i18n.localize(reason)
if self.tooltipTime - 1.95 > RealTimeL() then
self.tooltipTime = RealTimeL() + 1
self.tooltip = true
return
end
self.tooltipTime = RealTimeL() + 2
--surface.PlaySound('resource/warning.wav')
self.tooltip = true
end
surface.CreateFont('DLib_TextEntry_Warning', {
font = 'Open Sans',
size = 20,
weight = 500
})
--[[
@doc
@fname DLib_TextEntry_Configurable:PostRenderVGUI
@internal
]]
function PANEL:PostRenderVGUI()
if not IsValid(self) then return end
if not self.tooltip then return end
local time = RealTimeL()
if self.tooltipTime < time then
self.tooltip = false
return
end
local x, y = self:LocalToScreen(0, 0)
local w, h = self:GetSize()
y = y + h + 2
local fade = math.min(1, (self.tooltipTime - time) * 1.25 + 0.4)
local value = self:GetValueBeforeCaret()
surface.SetFont(self:GetFont())
local w = surface.GetTextSize(value)
surface.SetDrawColor(0, 0, 0, fade * 255)
draw.NoTexture()
DLib.HUDCommons.DrawTriangle(x + 3 + w, y, 15, 20)
DLib.HUDCommons.WordBox(self.tooltipReason, 'DLib_TextEntry_Warning', x + w, y + 20, Color(255, 255, 255, fade * 255))
end
vgui.Register('DLib_TextEntry_Configurable', PANEL, 'DLib_TextEntry')
--[[
@doc
@panel DLib_TextEntry_Number
@parent DLib_TextEntry_Configurable
@desc
!p:DLib_TextEntry_Configurable which accept only numbers
@enddesc
]]
local TEXTENTRY_CUSTOM = PANEL
PANEL = {}
DLib.VGUI.TextEntry_Number = PANEL
--[[
@doc
@fname DLib_TextEntry_Number:GetDefaultNumber
@returns
number
]]
--[[
@doc
@fname DLib_TextEntry_Number:SetDefaultNumber
@args number value
]]
--[[
@doc
@fname DLib_TextEntry_Number:GetIsFloatAllowed
@returns
boolean
]]
--[[
@doc
@fname DLib_TextEntry_Number:SetIsFloatAllowed
@args boolean value
]]
--[[
@doc
@fname DLib_TextEntry_Number:GetIsNegativeValueAllowed
@returns
boolean
]]
--[[
@doc
@fname DLib_TextEntry_Number:SetIsNegativeValueAllowed
@args boolean value
]]
AccessorFunc(PANEL, 'defaultNumber', 'DefaultNumber')
AccessorFunc(PANEL, 'allowFloats', 'IsFloatAllowed')
AccessorFunc(PANEL, 'allowNegative', 'IsNegativeValueAllowed')
function PANEL:Init()
self:SetIsWhitelistMode(true)
self:SetDefaultReason('Only numbers are allowed.')
self.defaultNumber = 0
self.allowFloats = true
self.allowNegative = true
for i, number in ipairs(DLib.KeyMap.NUMBERS_LIST) do
self:AddToWhitelist(number)
end
end
--[[
@doc
@fname DLib_TextEntry_Number:GetNumber
@returns
number
]]
function PANEL:GetNumber()
return tonumber(self:GetValue() or '') or self.defaultNumber
end
function PANEL:OnKeyCodeTyped(key)
local reply = TEXTENTRY_CUSTOM.OnKeyCodeTyped(self, key)
if reply then return true end
if not self.allowNegative and (key == KEY_MINUS or key == KEY_PAD_MINUS) then
self:Ding('Negative values are not allowed here')
--return true
end
if not self.allowFloats and (key == KEY_PAD_DECIMAL) then
self:Ding('Floating point values are not allowed here')
--return true
end
if not isControl(key) then
local newValue = self:PredictValueChange(key)
if not tonumber(newValue) then
self:Ding('Doing this here will mangle the current value')
--return true
end
end
return false
end
vgui.Register('DLib_TextEntry_Number', PANEL, 'DLib_TextEntry_Configurable')