mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
Upload
This commit is contained in:
940
lua/sui/cl_base.lua
Normal file
940
lua/sui/cl_base.lua
Normal file
@@ -0,0 +1,940 @@
|
||||
--[[
|
||||
| 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 hook = hook
|
||||
local bit = bit
|
||||
local math = math
|
||||
|
||||
local Color = Color
|
||||
local ipairs = ipairs
|
||||
local RealFrameTime = RealFrameTime
|
||||
|
||||
local color_white = color_white
|
||||
local color_black = color_black
|
||||
|
||||
local sui = sui
|
||||
|
||||
local isfunction = sui.isfunction
|
||||
local isstring = sui.isstring
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
function sui.scale(v)
|
||||
return ScrH() * (v / 900)
|
||||
end
|
||||
|
||||
function sui.hex_rgb(hex)
|
||||
hex = tonumber(hex:gsub("^([%w])([%w])([%w])$", "%1%1%2%2%3%3", 1), 16)
|
||||
|
||||
return Color(
|
||||
bit.band(bit.rshift(hex, 16), 0xFF),
|
||||
bit.band(bit.rshift(hex, 8), 0xFF),
|
||||
bit.band(hex, 0xFF)
|
||||
)
|
||||
end
|
||||
|
||||
function sui.rgb_hex(c)
|
||||
return bit.tohex((c.r * 0x10000) + (c.g * 0x100) + c.b, 6)
|
||||
end
|
||||
local rgb_hex = sui.rgb_hex
|
||||
|
||||
function sui.lerp_color(from, to)
|
||||
local frac = RealFrameTime() * 10
|
||||
from.r = Lerp(frac, from.r, to.r)
|
||||
from.g = Lerp(frac, from.g, to.g)
|
||||
from.b = Lerp(frac, from.b, to.b)
|
||||
from.a = Lerp(frac, from.a, to.a)
|
||||
end
|
||||
|
||||
do
|
||||
local colors = {
|
||||
["41b9ff"] = Color(44, 62, 80),
|
||||
["00c853"] = Color(44, 62, 80),
|
||||
["181818"] = Color(242, 241, 239),
|
||||
["212121"] = Color(242, 241, 239),
|
||||
}
|
||||
|
||||
function sui.contrast_color(color)
|
||||
local c = colors[rgb_hex(color)]
|
||||
if c then return c end
|
||||
|
||||
local luminance = (0.299 * color.r + 0.587 * color.g + 0.114 * color.b) / 255
|
||||
return luminance > 0.5 and color_black or color_white
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local SetDrawColor = surface.SetDrawColor
|
||||
local SetMaterial = surface.SetMaterial
|
||||
local DrawTexturedRectRotated = surface.DrawTexturedRectRotated
|
||||
function sui.draw_material(mat, x, y, size, col, rot)
|
||||
SetDrawColor(col)
|
||||
|
||||
if x == -1 then
|
||||
x = size / 2
|
||||
end
|
||||
|
||||
if y == -1 then
|
||||
y = size / 2
|
||||
end
|
||||
|
||||
if mat then
|
||||
SetMaterial(mat)
|
||||
end
|
||||
|
||||
DrawTexturedRectRotated(x, y, size, size, rot or 0)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local hsv_t = {
|
||||
[0] = function(v, p, q, t)
|
||||
return v, t, p
|
||||
end,
|
||||
[1] = function(v, p, q, t)
|
||||
return q, v, p
|
||||
end,
|
||||
[2] = function(v, p, q, t)
|
||||
return p, v, t
|
||||
end,
|
||||
[3] = function(v, p, q, t)
|
||||
return p, q, v
|
||||
end,
|
||||
[4] = function(v, p, q, t)
|
||||
return t, p, v
|
||||
end,
|
||||
[5] = function(v, p, q, t)
|
||||
return v, p, q
|
||||
end
|
||||
}
|
||||
|
||||
function sui.hsv_to_rgb(h, s, v)
|
||||
local i = floor(h * 6)
|
||||
local f = h * 6 - i
|
||||
|
||||
return hsv_t[i % 6](
|
||||
v * 255, -- v
|
||||
(v * (1 - s)) * 255, -- p
|
||||
(v * (1 - f * s)) * 255, -- q
|
||||
(v * (1 - (1 - f) * s)) * 255 -- t
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local Panel = FindMetaTable("Panel")
|
||||
local SetSize = Panel.SetSize
|
||||
local GetWide = Panel.GetWide
|
||||
local GetTall = Panel.GetTall
|
||||
function sui.scaling_functions(panel)
|
||||
local scale_changed
|
||||
local SUI = CURRENT_SUI
|
||||
|
||||
local dock_top = function(s, h)
|
||||
if not h then return end
|
||||
|
||||
if not scale_changed then
|
||||
s.real_h = h
|
||||
end
|
||||
|
||||
if not s.no_scale then
|
||||
h = SUI.Scale(h)
|
||||
end
|
||||
|
||||
if GetTall(s) == h then return end
|
||||
|
||||
SetSize(s, GetWide(s), h)
|
||||
end
|
||||
|
||||
local dock_right = function(s, w)
|
||||
if not w then return end
|
||||
|
||||
if not scale_changed then
|
||||
s.real_w = w
|
||||
end
|
||||
|
||||
if not s.no_scale then
|
||||
w = SUI.Scale(w)
|
||||
end
|
||||
|
||||
if GetWide(s) == w then return end
|
||||
|
||||
SetSize(s, w, GetTall(s))
|
||||
end
|
||||
|
||||
local size_changed = function(s, w, h)
|
||||
if s.using_scale then return end
|
||||
|
||||
s.using_scale = true
|
||||
|
||||
local dock = s:GetDock()
|
||||
|
||||
if dock ~= FILL then
|
||||
if dock == NODOCK then
|
||||
dock_top(s, h)
|
||||
dock_right(s, w)
|
||||
elseif dock == TOP or dock == BOTTOM then
|
||||
dock_top(s, h)
|
||||
else
|
||||
dock_right(s, w)
|
||||
end
|
||||
end
|
||||
|
||||
s.using_scale = nil
|
||||
end
|
||||
|
||||
local wide_changed = function(s, w)
|
||||
size_changed(s, w)
|
||||
end
|
||||
|
||||
local tall_changed = function(s, h)
|
||||
size_changed(s, nil, h)
|
||||
end
|
||||
|
||||
function panel:ScaleChanged()
|
||||
scale_changed = true
|
||||
size_changed(self, self.real_w, self.real_h)
|
||||
scale_changed = nil
|
||||
if self.OnScaleChange then
|
||||
self:OnScaleChange()
|
||||
end
|
||||
end
|
||||
|
||||
local on_remove = function(s)
|
||||
SUI.RemoveScaleHook(s)
|
||||
end
|
||||
|
||||
function panel:ScaleInit()
|
||||
self.SetSize = size_changed
|
||||
self.SetWide = wide_changed
|
||||
self.SetTall = tall_changed
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
self:On("OnRemove", on_remove)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local utf8 = {}
|
||||
|
||||
local str_rel_to_abs = function(str, v, str_n)
|
||||
return v > 0 and v or math.max(str_n + v + 1, 1)
|
||||
end
|
||||
|
||||
local utf8_decode = function(str, start_pos, str_n)
|
||||
start_pos = str_rel_to_abs(str, start_pos or 1, str_n)
|
||||
|
||||
local b1 = str:byte(start_pos, start_pos)
|
||||
if not b1 then return nil end
|
||||
if b1 < 0x80 then return start_pos, start_pos, b1 end
|
||||
if b1 > 0xF4 or b1 < 0xC2 then return nil end
|
||||
|
||||
local cont_byte_count = b1 >= 0xF0 and 3 or b1 >= 0xE0 and 2 or b1 >= 0xC0 and 1
|
||||
local end_pos = start_pos + cont_byte_count
|
||||
local code_point = 0
|
||||
|
||||
if str_n < end_pos then return nil end
|
||||
|
||||
local bytes = {str:byte(start_pos + 1, end_pos)}
|
||||
for i = 1, #bytes do
|
||||
local b_x = bytes[i]
|
||||
if bit.band(b_x, 0xC0) ~= 0x80 then return nil end
|
||||
code_point = bit.bor(bit.lshift(code_point, 6), bit.band(b_x, 0x3F))
|
||||
b1 = bit.lshift(b1, 1)
|
||||
end
|
||||
|
||||
code_point = bit.bor(code_point, bit.lshift(bit.band(b1, 0x7F), cont_byte_count * 5))
|
||||
|
||||
return start_pos, end_pos, code_point
|
||||
end
|
||||
|
||||
local replacement = string.char(239, 191, 189)
|
||||
|
||||
function utf8.force(str)
|
||||
local end_pos = #str
|
||||
if end_pos == 0 then return str, end_pos end
|
||||
|
||||
local ret = ""
|
||||
local cur_pos = 1
|
||||
|
||||
repeat
|
||||
local seq_start_pos, seq_end_pos = utf8_decode(str, cur_pos, end_pos)
|
||||
|
||||
if not seq_start_pos then
|
||||
ret = ret .. replacement
|
||||
cur_pos = cur_pos + 1
|
||||
else
|
||||
ret = ret .. str:sub(seq_start_pos, seq_end_pos)
|
||||
cur_pos = seq_end_pos + 1
|
||||
end
|
||||
until cur_pos > end_pos
|
||||
|
||||
return ret, #ret
|
||||
end
|
||||
|
||||
-- https://gist.github.com/gdeglin/4128882
|
||||
|
||||
local utf8_char_bytes = function(c)
|
||||
if c > 0 and c <= 127 then
|
||||
return 1
|
||||
elseif c >= 194 and c <= 223 then
|
||||
return 2
|
||||
elseif c >= 224 and c <= 239 then
|
||||
return 3
|
||||
elseif c >= 240 and c <= 244 then
|
||||
return 4
|
||||
end
|
||||
end
|
||||
utf8.char_bytes = utf8_char_bytes
|
||||
|
||||
function utf8.len(str)
|
||||
local length = #str
|
||||
|
||||
local len = 0
|
||||
|
||||
local pos = 1
|
||||
while pos <= length do
|
||||
len = len + 1
|
||||
pos = pos + utf8_char_bytes(str:byte(pos))
|
||||
end
|
||||
|
||||
return len
|
||||
end
|
||||
|
||||
function utf8.sub(str, i, j)
|
||||
j = j or -1
|
||||
|
||||
if i == nil then return "" end
|
||||
|
||||
local l = (i >= 0 and j >= 0) or utf8.len(str)
|
||||
local start_char = (i >= 0) and i or l + i + 1
|
||||
local end_char = (j >= 0) and j or l + j + 1
|
||||
|
||||
if start_char > end_char then return "" end
|
||||
|
||||
local pos = 1
|
||||
local length = #str
|
||||
local len = 0
|
||||
|
||||
local start_byte, end_byte = 1, length
|
||||
|
||||
while pos <= length do
|
||||
len = len + 1
|
||||
|
||||
if len == start_char then
|
||||
start_byte = pos
|
||||
end
|
||||
|
||||
pos = pos + utf8_char_bytes(str:byte(pos))
|
||||
|
||||
if len == end_char then
|
||||
end_byte = pos - 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return str:sub(start_byte, end_byte)
|
||||
end
|
||||
|
||||
sui.utf8 = utf8
|
||||
end
|
||||
|
||||
--
|
||||
-- thanks falco!
|
||||
-- https://github.com/FPtje/DarkRP/blob/4fd2c3c315427e79bb7624702cfaefe9ad26ac7e/gamemode/modules/base/cl_util.lua#L42
|
||||
--
|
||||
do
|
||||
local utf8 = utf8
|
||||
local surface = surface
|
||||
|
||||
local max_width, original_width, can_fix
|
||||
|
||||
local fix_width = function()
|
||||
if can_fix then
|
||||
can_fix = false
|
||||
max_width = original_width
|
||||
end
|
||||
end
|
||||
|
||||
local char_wrap = function(text, remaining_width)
|
||||
local total_width = 0
|
||||
|
||||
local new_text = ""
|
||||
for char in text:gmatch(utf8.charpattern) do
|
||||
total_width = total_width + surface.GetTextSize(char)
|
||||
if total_width >= remaining_width then
|
||||
total_width = surface.GetTextSize(char)
|
||||
fix_width()
|
||||
remaining_width = max_width
|
||||
|
||||
new_text = new_text .. ("\n" .. char)
|
||||
else
|
||||
new_text = new_text .. char
|
||||
end
|
||||
end
|
||||
|
||||
return new_text, total_width
|
||||
end
|
||||
|
||||
function sui.wrap_text(text, font, width, first_width)
|
||||
text = sui.utf8.force(text)
|
||||
|
||||
local total_width = 0
|
||||
can_fix = first_width and true or false
|
||||
max_width, original_width = first_width or width, width
|
||||
|
||||
surface.SetFont(font)
|
||||
|
||||
local space_width = surface.GetTextSize(" ")
|
||||
|
||||
text = text:gsub("(%s?[%S]*)", function(word)
|
||||
local char = word:sub(1, 1)
|
||||
if char == "\n" then
|
||||
total_width = 0
|
||||
fix_width()
|
||||
end
|
||||
|
||||
local wordlen = surface.GetTextSize(word)
|
||||
total_width = total_width + wordlen
|
||||
|
||||
if wordlen >= max_width then
|
||||
local split_word
|
||||
split_word, total_width = char_wrap(word, max_width - (total_width - wordlen))
|
||||
return split_word
|
||||
elseif total_width < max_width then
|
||||
return word
|
||||
end
|
||||
|
||||
fix_width()
|
||||
|
||||
total_width = wordlen
|
||||
|
||||
if char == " " then
|
||||
total_width = total_width - space_width
|
||||
return "\n" .. word:sub(2)
|
||||
end
|
||||
|
||||
return "\n" .. word
|
||||
end)
|
||||
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
function sui.register(classname, panel_table, parent_class)
|
||||
sui.TDLib.Install(panel_table)
|
||||
|
||||
if not panel_table.Add then
|
||||
function panel_table:Add(pnl)
|
||||
return vgui.Create(pnl, self)
|
||||
end
|
||||
end
|
||||
|
||||
if not panel_table.NoOverrideClear and not panel_table.Clear then
|
||||
function panel_table:Clear()
|
||||
local children = self:GetChildren()
|
||||
for i = 1, #children do
|
||||
children[i]:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local SUI = CURRENT_SUI
|
||||
|
||||
for k, v in pairs(SUI.panels_funcs) do
|
||||
panel_table[k] = v
|
||||
end
|
||||
|
||||
panel_table.SUI_GetColor = function(name)
|
||||
return SUI.GetColor(name)
|
||||
end
|
||||
|
||||
SUI.panels[classname] = panel_table
|
||||
|
||||
return vgui.Register(SUI.name .. "." .. classname, panel_table, parent_class)
|
||||
end
|
||||
|
||||
local Material; do
|
||||
local C_Material, material_str = select(2, debug.getupvalue(_G.Material, 1)), "0001010" -- [["mips smooth"]]
|
||||
Material = function(name)
|
||||
return C_Material(name, material_str)
|
||||
end
|
||||
end
|
||||
sui.Material = Material
|
||||
|
||||
local function prepare_theme(theme)
|
||||
for k, v in pairs(theme) do
|
||||
if IsColor(v) then continue end
|
||||
|
||||
if istable(v) then
|
||||
prepare_theme(v)
|
||||
elseif isstring(v) and v:sub(1, 1) == "#" then
|
||||
theme[k] = sui.hex_rgb(v:sub(2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sui.new(addon_name, default_scaling, panels_funcs)
|
||||
local SUI = {
|
||||
name = addon_name,
|
||||
panels = {},
|
||||
panels_funcs = panels_funcs or {}
|
||||
}
|
||||
|
||||
CURRENT_SUI = SUI
|
||||
|
||||
do
|
||||
local themes = table.Copy(sui.themes)
|
||||
local current_theme_table
|
||||
|
||||
function SUI.GetColor(color_name)
|
||||
return current_theme_table[color_name]
|
||||
end
|
||||
|
||||
function SUI.SetTheme(theme_name)
|
||||
SUI.current_theme = theme_name
|
||||
current_theme_table = themes[theme_name]
|
||||
hook.Call(addon_name .. ".ThemeChanged")
|
||||
end
|
||||
|
||||
function SUI.GetThemes()
|
||||
return themes
|
||||
end
|
||||
|
||||
function SUI.AddToTheme(theme_name, tbl)
|
||||
local theme = themes[theme_name]
|
||||
for k, v in pairs(tbl) do
|
||||
theme[k] = v
|
||||
end
|
||||
prepare_theme(theme)
|
||||
end
|
||||
|
||||
function SUI.RemoveTheme(theme_name)
|
||||
themes[theme_name] = nil
|
||||
if theme_name == SUI.current_theme then
|
||||
SUI.SetTheme(next(themes))
|
||||
end
|
||||
end
|
||||
|
||||
function SUI.AddTheme(theme_name, tbl)
|
||||
prepare_theme(tbl)
|
||||
themes[theme_name] = tbl
|
||||
end
|
||||
|
||||
SUI.themes = themes
|
||||
end
|
||||
|
||||
local Scale
|
||||
do
|
||||
local scale = 1
|
||||
|
||||
if default_scaling then
|
||||
SUI.Scale = sui.scale
|
||||
else
|
||||
function SUI.Scale(v)
|
||||
return floor((v * scale) + 0.5)
|
||||
end
|
||||
end
|
||||
Scale = SUI.Scale
|
||||
|
||||
function SUI.ScaleEven(v)
|
||||
v = Scale(v)
|
||||
if v % 2 ~= 0 then
|
||||
v = v + 1
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
function SUI.SetScale(_scale)
|
||||
if _scale == scale then return end
|
||||
|
||||
scale = _scale
|
||||
SUI.scale = _scale
|
||||
|
||||
for k, v in pairs(SUI.fonts) do
|
||||
SUI.CreateFont(k:sub(#addon_name + 1), v.font, v.size, v.weight)
|
||||
end
|
||||
|
||||
SUI.CallScaleChanged()
|
||||
end
|
||||
|
||||
local n = 0
|
||||
local keys = {}
|
||||
local hooks = {}
|
||||
_G[addon_name .. "_HOOKS"] = keys
|
||||
_G[addon_name .. "_KEYS"] = hooks
|
||||
_G[addon_name .. "_N"] = function()
|
||||
return n
|
||||
end
|
||||
function SUI.OnScaleChanged(name, func)
|
||||
if not isfunction(func) then
|
||||
error("Invalid function?")
|
||||
end
|
||||
|
||||
if not name then
|
||||
error("Invalid name?")
|
||||
end
|
||||
|
||||
if not isstring(name) then
|
||||
local _func = func
|
||||
func = function()
|
||||
local isvalid = name.IsValid
|
||||
if isvalid and isvalid(name) then
|
||||
_func(name)
|
||||
else
|
||||
SUI.RemoveScaleHook(name, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local pos = keys[name]
|
||||
if pos then
|
||||
hooks[pos + 1] = func
|
||||
else
|
||||
hooks[n + 1] = name
|
||||
hooks[n + 2] = func
|
||||
keys[name] = n + 1
|
||||
n = n + 2
|
||||
end
|
||||
end
|
||||
|
||||
function SUI.RemoveScaleHook(name, in_hook)
|
||||
local pos = keys[name]
|
||||
if not pos then return end
|
||||
|
||||
if in_hook then
|
||||
hooks[pos] = nil
|
||||
hooks[pos + 1] = nil
|
||||
else
|
||||
local new_name = hooks[n - 1]
|
||||
if new_name then
|
||||
hooks[pos], hooks[pos + 1] = new_name, hooks[n]
|
||||
hooks[n - 1], hooks[n] = nil, nil
|
||||
keys[new_name] = pos
|
||||
end
|
||||
n = n - 2
|
||||
end
|
||||
keys[name] = nil
|
||||
end
|
||||
|
||||
function SUI.CallScaleChanged()
|
||||
if n == 0 then return end
|
||||
|
||||
local i, c_n = 2, n
|
||||
::loop::
|
||||
local func = hooks[i]
|
||||
if func then
|
||||
func()
|
||||
i = i + 2
|
||||
else
|
||||
local _n, _i = c_n, i
|
||||
if n ~= c_n then
|
||||
_n = n
|
||||
i = i + 2
|
||||
else
|
||||
c_n = c_n - 2
|
||||
end
|
||||
|
||||
local new_name = hooks[_n - 1]
|
||||
if new_name then
|
||||
hooks[_i - 1], hooks[_i] = new_name, hooks[_n]
|
||||
hooks[_n - 1], hooks[_n] = nil, nil
|
||||
keys[new_name] = _i - 1
|
||||
end
|
||||
|
||||
n = n - 2
|
||||
end
|
||||
|
||||
if i <= c_n then
|
||||
goto loop
|
||||
end
|
||||
end
|
||||
|
||||
function SUI.GetScale()
|
||||
return scale
|
||||
end
|
||||
|
||||
SUI.scale = 1
|
||||
end
|
||||
|
||||
do
|
||||
local fonts = {}
|
||||
|
||||
function SUI.CreateFont(font_name, font, size, weight)
|
||||
font_name = addon_name .. font_name
|
||||
|
||||
fonts[font_name] = fonts[font_name] or {
|
||||
font = font,
|
||||
size = size,
|
||||
weight = weight
|
||||
}
|
||||
|
||||
surface.CreateFont(font_name, {
|
||||
font = font,
|
||||
size = Scale(size),
|
||||
weight = weight,
|
||||
extended = true
|
||||
})
|
||||
|
||||
return font_name
|
||||
end
|
||||
|
||||
function SUI.GetFont(font_name)
|
||||
return addon_name .. font_name
|
||||
end
|
||||
|
||||
function SUI.GetFontHeight(font_name)
|
||||
local font = fonts[addon_name .. font_name] or fonts[font_name]
|
||||
if not font then return 0 end
|
||||
|
||||
return floor(Scale(font.size or 0))
|
||||
end
|
||||
|
||||
SUI.fonts = fonts
|
||||
end
|
||||
|
||||
do
|
||||
local materials = {}
|
||||
|
||||
local delay = 0.008
|
||||
local next_run = UnPredictedCurTime()
|
||||
|
||||
function SUI.Material(mat, allow_delay)
|
||||
local _mat = materials[mat]
|
||||
if _mat then return _mat end
|
||||
|
||||
if allow_delay then
|
||||
if UnPredictedCurTime() < next_run then return end
|
||||
next_run = UnPredictedCurTime() + delay
|
||||
end
|
||||
|
||||
materials[mat] = Material(mat)
|
||||
|
||||
return materials[mat]
|
||||
end
|
||||
|
||||
SUI.materials = materials
|
||||
end
|
||||
|
||||
SUI.SetTheme("Dark")
|
||||
|
||||
for _, f in ipairs(file.Find("sui/vgui/sui_*.lua", "LUA")) do
|
||||
include("sui/vgui/" .. f)
|
||||
end
|
||||
|
||||
for _, f in ipairs(file.Find(string.format("sui/vgui/%s_*.lua", addon_name:lower()), "LUA")) do
|
||||
include("sui/vgui/" .. f)
|
||||
end
|
||||
|
||||
return SUI
|
||||
end
|
||||
|
||||
sui.themes = sui.themes or {}
|
||||
function sui.add_theme(name, tbl)
|
||||
prepare_theme(tbl)
|
||||
sui.themes[name] = tbl
|
||||
end
|
||||
|
||||
function sui.valid_options()
|
||||
local objs = {}
|
||||
objs.IsValid = function()
|
||||
local valid = true
|
||||
for i = 1, #objs do
|
||||
local obj = objs[i]
|
||||
if obj:IsValid() and obj.valid == false then
|
||||
valid = false
|
||||
break
|
||||
end
|
||||
end
|
||||
return valid
|
||||
end
|
||||
objs.Add = function(obj)
|
||||
table.insert(objs, obj)
|
||||
end
|
||||
return objs
|
||||
end
|
||||
|
||||
do
|
||||
local SURFACE = Color(31, 31, 31)
|
||||
local PRIMARY = Color(65, 185, 255)
|
||||
|
||||
local ON_SURFACE = Color(255, 255, 255)
|
||||
local ON_SURFACE_HIGH_EMPHASIS = ColorAlpha(ON_SURFACE, 221)
|
||||
local ON_SURFACE_MEDIUM_EMPHASIS = ColorAlpha(ON_SURFACE, 122)
|
||||
local ON_SURFACE_DISABLED = ColorAlpha(ON_SURFACE, 97)
|
||||
|
||||
local ON_PRIMARY = Color(60, 60, 60)
|
||||
|
||||
sui.add_theme("Dark", {
|
||||
frame = Color(18, 18, 18),
|
||||
frame_blur = false,
|
||||
|
||||
title = ON_SURFACE,
|
||||
header = SURFACE,
|
||||
|
||||
close = ON_SURFACE_MEDIUM_EMPHASIS,
|
||||
close_hover = Color(255, 60, 60),
|
||||
close_press = Color(255, 255, 255, 30),
|
||||
|
||||
button = PRIMARY,
|
||||
button_text = "#050709",
|
||||
button_hover = ColorAlpha(ON_PRIMARY, 100),
|
||||
button_click = ColorAlpha(ON_PRIMARY, 240),
|
||||
button_disabled = Color(100, 100, 100),
|
||||
button_disabled_text = "#bdbdbd",
|
||||
|
||||
button2_hover = ColorAlpha(PRIMARY, 5),
|
||||
button2_selected = ColorAlpha(PRIMARY, 15),
|
||||
|
||||
scroll = ColorAlpha(PRIMARY, 97),
|
||||
scroll_grip = PRIMARY,
|
||||
|
||||
scroll_panel = Color(29, 29, 29),
|
||||
scroll_panel_outline = false,
|
||||
|
||||
text_entry_bg = Color(34, 34, 34),
|
||||
text_entry_bar_color = Color(0, 0, 0, 0),
|
||||
text_entry = ON_SURFACE_HIGH_EMPHASIS,
|
||||
text_entry_2 = ON_SURFACE_MEDIUM_EMPHASIS,
|
||||
text_entry_3 = PRIMARY,
|
||||
|
||||
property_sheet_bg = Color(39, 39, 39),
|
||||
property_sheet_tab = Color(150, 150, 150),
|
||||
property_sheet_tab_click = Color(255, 255, 255, 30),
|
||||
property_sheet_tab_active = PRIMARY,
|
||||
|
||||
toggle_button = ON_SURFACE_DISABLED,
|
||||
toggle_button_switch = ON_SURFACE_HIGH_EMPHASIS,
|
||||
|
||||
toggle_button_active = ColorAlpha(PRIMARY, 65),
|
||||
toggle_button_switch_active = PRIMARY,
|
||||
|
||||
slider_knob = PRIMARY,
|
||||
slider_track = ColorAlpha(PRIMARY, 65),
|
||||
slider_hover = ColorAlpha(PRIMARY, 5),
|
||||
slider_pressed = ColorAlpha(PRIMARY, 30),
|
||||
|
||||
on_sheet = Color(43, 43, 43, 200),
|
||||
on_sheet_hover = Color(200, 200, 200, 20),
|
||||
|
||||
--=--
|
||||
query_box_bg = "#181818",
|
||||
query_box_cancel = Color(244, 67, 54, 30),
|
||||
query_box_cancel_text = "#f44336",
|
||||
--=--
|
||||
|
||||
--=--
|
||||
menu = "#212121",
|
||||
|
||||
menu_option = "#212121",
|
||||
menu_option_text = "#bdbdbd",
|
||||
menu_option_hover = "#3b3b3b",
|
||||
menu_option_hover_text = "#fefefe",
|
||||
|
||||
menu_spacer = "#303030",
|
||||
--=--
|
||||
|
||||
line = "#303030",
|
||||
|
||||
--=--
|
||||
column_sheet = "#263238",
|
||||
column_sheet_bar = "#202020",
|
||||
|
||||
column_sheet_tab = "#202020",
|
||||
column_sheet_tab_hover = "#2e2e2e",
|
||||
column_sheet_tab_active = "#383838",
|
||||
|
||||
column_sheet_tab_icon = "#909090",
|
||||
column_sheet_tab_icon_hover = "#f0f0f0",
|
||||
column_sheet_tab_icon_active = "#34a1e0",
|
||||
--=--
|
||||
|
||||
--=--
|
||||
collapse_category_header = "#272727",
|
||||
collapse_category_header_hover = "#2a2a2a",
|
||||
collapse_category_header_active = "#2e2e2e",
|
||||
|
||||
collapse_category_header_text = "#aaaaaa",
|
||||
collapse_category_header_text_hover = "#dcdcdc",
|
||||
collapse_category_header_text_active = "#34A1E0",
|
||||
|
||||
collapse_category_item = "#343434",
|
||||
collapse_category_item_hover = "#464646",
|
||||
collapse_category_item_active = "#535353",
|
||||
|
||||
collapse_category_item_text = "#aaaaaa",
|
||||
collapse_category_item_text_hover = "#dcdcdc",
|
||||
collapse_category_item_text_active = "#ffffff",
|
||||
--=--
|
||||
})
|
||||
end
|
||||
|
||||
do
|
||||
local PRIMARY = Color(65, 185, 255)
|
||||
|
||||
local ON_PRIMARY = Color(220, 220, 220)
|
||||
|
||||
sui.add_theme("Blur", {
|
||||
frame = Color(30, 30, 30, 220),
|
||||
frame_blur = true,
|
||||
|
||||
title = Color(255, 255, 255),
|
||||
header = Color(60, 60, 60, 200),
|
||||
|
||||
close = Color(200, 200, 200),
|
||||
close_hover = Color(255, 60, 60),
|
||||
close_press = Color(255, 255, 255, 30),
|
||||
|
||||
button = ColorAlpha(PRIMARY, 130),
|
||||
button_text = ON_PRIMARY,
|
||||
button_hover = Color(0, 0, 0, 30),
|
||||
button_click = PRIMARY,
|
||||
button_disabled = Color(100, 100, 100),
|
||||
button_disabled_text = "#bdbdbd",
|
||||
|
||||
button2_hover = ColorAlpha(PRIMARY, 5),
|
||||
button2_selected = ColorAlpha(PRIMARY, 15),
|
||||
|
||||
scroll = Color(0, 0, 0, 100),
|
||||
scroll_grip = PRIMARY,
|
||||
|
||||
scroll_panel = Color(255, 255, 255, 10),
|
||||
scroll_panel_outline = false,
|
||||
|
||||
text_entry_bg = Color(0, 0, 0, 0),
|
||||
text_entry_bar_color = Color(200, 200, 200, 153),
|
||||
text_entry = Color(240, 240, 240, 221),
|
||||
text_entry_2 = Color(200, 200, 200, 153),
|
||||
text_entry_3 = PRIMARY,
|
||||
|
||||
property_sheet_bg = Color(60, 60, 60, 200),
|
||||
property_sheet_tab = Color(150, 150, 150),
|
||||
property_sheet_tab_click = Color(255, 255, 255, 40),
|
||||
property_sheet_tab_active = PRIMARY,
|
||||
|
||||
toggle_button = Color(244, 67, 54),
|
||||
toggle_button_switch = Color(230, 230, 230),
|
||||
|
||||
toggle_button_active = PRIMARY,
|
||||
toggle_button_switch_active = Color(230, 230, 230),
|
||||
|
||||
slider_knob = PRIMARY,
|
||||
slider_track = ColorAlpha(PRIMARY, 100),
|
||||
slider_hover = ColorAlpha(PRIMARY, 40),
|
||||
slider_pressed = ColorAlpha(PRIMARY, 70),
|
||||
|
||||
on_sheet = Color(60, 60, 60, 180),
|
||||
on_sheet_hover = Color(30, 30, 30, 70),
|
||||
|
||||
--=--
|
||||
query_box_bg = Color(0, 0, 0, 100),
|
||||
query_box_cancel = Color(244, 67, 54, 30),
|
||||
query_box_cancel_text = "#f44336",
|
||||
--=--
|
||||
})
|
||||
end
|
||||
104
lua/sui/libs/bshadows.lua
Normal file
104
lua/sui/libs/bshadows.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
--[[
|
||||
| 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 ScrW = ScrW
|
||||
local ScrH = ScrH
|
||||
|
||||
local sin = math.sin
|
||||
local cos = math.cos
|
||||
local rad = math.rad
|
||||
local ceil = math.ceil
|
||||
|
||||
local Start2D = cam.Start2D
|
||||
local End2D = cam.End2D
|
||||
|
||||
local PushRenderTarget = render.PushRenderTarget
|
||||
local OverrideAlphaWriteEnable = render.OverrideAlphaWriteEnable
|
||||
local Clear = render.Clear
|
||||
local CopyRenderTargetToTexture = render.CopyRenderTargetToTexture
|
||||
local BlurRenderTarget = render.BlurRenderTarget
|
||||
local PopRenderTarget = render.PopRenderTarget
|
||||
local SetMaterial = render.SetMaterial
|
||||
local DrawScreenQuadEx = render.DrawScreenQuadEx
|
||||
local DrawScreenQuad = render.DrawScreenQuad
|
||||
|
||||
local RenderTarget, RenderTarget2
|
||||
local load_render_targets = function()
|
||||
local w, h = ScrW(), ScrH()
|
||||
RenderTarget = GetRenderTarget("sui_bshadows_original" .. w .. h, w, h)
|
||||
RenderTarget2 = GetRenderTarget("sui_bshadows_shadow" .. w .. h, w, h)
|
||||
end
|
||||
load_render_targets()
|
||||
hook.Add("OnScreenSizeChanged", "SUI.BShadows", load_render_targets)
|
||||
|
||||
local ShadowMaterial = CreateMaterial("sui_bshadows", "UnlitGeneric", {
|
||||
["$translucent"] = 1,
|
||||
["$vertexalpha"] = 1,
|
||||
["alpha"] = 1
|
||||
})
|
||||
|
||||
local ShadowMaterialGrayscale = CreateMaterial("sui_bshadows_grayscale", "UnlitGeneric", {
|
||||
["$translucent"] = 1,
|
||||
["$vertexalpha"] = 1,
|
||||
["$alpha"] = 1,
|
||||
["$color"] = "0 0 0",
|
||||
["$color2"] = "0 0 0"
|
||||
})
|
||||
|
||||
local SetTexture = ShadowMaterial.SetTexture
|
||||
|
||||
local BSHADOWS = {}
|
||||
|
||||
BSHADOWS.BeginShadow = function()
|
||||
PushRenderTarget(RenderTarget)
|
||||
|
||||
OverrideAlphaWriteEnable(true, true)
|
||||
Clear(0, 0, 0, 0)
|
||||
OverrideAlphaWriteEnable(false, false)
|
||||
|
||||
Start2D()
|
||||
end
|
||||
|
||||
BSHADOWS.EndShadow = function(intensity, spread, blur, opacity, direction, distance, _shadowOnly)
|
||||
opacity = opacity or 255
|
||||
direction = direction or 0
|
||||
distance = distance or 0
|
||||
|
||||
CopyRenderTargetToTexture(RenderTarget2)
|
||||
|
||||
if blur > 0 then
|
||||
OverrideAlphaWriteEnable(true, true)
|
||||
BlurRenderTarget(RenderTarget2, spread, spread, blur)
|
||||
OverrideAlphaWriteEnable(false, false)
|
||||
end
|
||||
|
||||
PopRenderTarget()
|
||||
|
||||
SetTexture(ShadowMaterial, "$basetexture", RenderTarget)
|
||||
SetTexture(ShadowMaterialGrayscale, "$basetexture", RenderTarget2)
|
||||
|
||||
local xOffset = sin(rad(direction)) * distance
|
||||
local yOffset = cos(rad(direction)) * distance
|
||||
|
||||
SetMaterial(ShadowMaterialGrayscale)
|
||||
for i = 1, ceil(intensity) do
|
||||
DrawScreenQuadEx(xOffset, yOffset, ScrW(), ScrH())
|
||||
end
|
||||
|
||||
if not _shadowOnly then
|
||||
SetTexture(ShadowMaterial, "$basetexture", RenderTarget)
|
||||
SetMaterial(ShadowMaterial)
|
||||
DrawScreenQuad()
|
||||
end
|
||||
|
||||
End2D()
|
||||
end
|
||||
|
||||
sui.BSHADOWS = BSHADOWS
|
||||
442
lua/sui/libs/gif_loader.lua
Normal file
442
lua/sui/libs/gif_loader.lua
Normal file
@@ -0,0 +1,442 @@
|
||||
--[[
|
||||
| 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 byte = string.byte
|
||||
local sub = string.sub
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
local bor = bit.bor
|
||||
local band = bit.band
|
||||
|
||||
local GIFDecoder = {}
|
||||
local GIFDecoderMethods = {}
|
||||
local GIFDecoder_meta = {__index = GIFDecoderMethods}
|
||||
|
||||
function GIFDecoder.new(buf)
|
||||
local buf_n = #buf
|
||||
local this = setmetatable({
|
||||
p = 1,
|
||||
buf = buf
|
||||
}, GIFDecoder_meta)
|
||||
|
||||
local version = this:read(6)
|
||||
assert(version == "GIF89a" or version == "GIF87a", "wrong file format")
|
||||
|
||||
this.width = this:word()
|
||||
this.height = this:word()
|
||||
|
||||
local pf0 = this:byte()
|
||||
local global_palette_flag = rshift(pf0, 7)
|
||||
local num_global_colors_pow2 = band(pf0, 0x7)
|
||||
local num_global_colors = lshift(1, num_global_colors_pow2 + 1)
|
||||
this:skip(2)
|
||||
|
||||
local global_palette_offset = nil
|
||||
local global_palette_size = nil
|
||||
|
||||
if global_palette_flag > 0 then
|
||||
global_palette_offset = this.p
|
||||
this.global_palette_offset = global_palette_offset
|
||||
global_palette_size = num_global_colors
|
||||
this:skip(num_global_colors * 3)
|
||||
end
|
||||
|
||||
local no_eof = true
|
||||
|
||||
local frames = {}
|
||||
|
||||
local delay = 0
|
||||
local transparent_index = nil
|
||||
local disposal = 1
|
||||
|
||||
while no_eof and this.p <= buf_n do
|
||||
local b = this:byte()
|
||||
if b == 0x3b then
|
||||
no_eof = false
|
||||
elseif b == 0x2c then
|
||||
local x, y, w, h = this:word(), this:word(), this:word(), this:word()
|
||||
local pf2 = this:byte()
|
||||
local local_palette_flag = rshift(pf2, 7)
|
||||
local interlace_flag = band(rshift(pf2, 6), 1)
|
||||
local num_local_colors_pow2 = band(pf2, 0x7)
|
||||
local num_local_colors = lshift(1, num_local_colors_pow2 + 1)
|
||||
local palette_offset = global_palette_offset
|
||||
local palette_size = global_palette_size
|
||||
local has_local_palette = false
|
||||
if local_palette_flag ~= 0 then
|
||||
has_local_palette = true
|
||||
palette_offset = this.p
|
||||
palette_size = num_local_colors
|
||||
this:skip(num_local_colors * 3)
|
||||
end
|
||||
|
||||
local data_offset = this.p
|
||||
|
||||
this:skip(1)
|
||||
this:skip_eob()
|
||||
|
||||
table.insert(frames, {
|
||||
x = x,
|
||||
y = y,
|
||||
width = w,
|
||||
height = h,
|
||||
has_local_palette = has_local_palette,
|
||||
palette_offset = palette_offset,
|
||||
palette_size = palette_size,
|
||||
data_offset = data_offset,
|
||||
data_length = this.p - data_offset,
|
||||
transparent_index = transparent_index,
|
||||
interlaced = interlace_flag > 0,
|
||||
delay = delay,
|
||||
disposal = disposal
|
||||
})
|
||||
elseif b == 0x21 then
|
||||
local b2 = this:byte()
|
||||
if b2 == 0xf9 then
|
||||
local len, flags = this:bytes(2)
|
||||
delay = this:word()
|
||||
local transparent, terminator = this:bytes(2)
|
||||
|
||||
assert(len == 4 and terminator == 0, "Invalid graphics extension block.")
|
||||
|
||||
if flags % 2 == 1 then
|
||||
transparent_index = transparent
|
||||
else
|
||||
transparent_index = nil
|
||||
end
|
||||
|
||||
disposal = math.floor(flags / 4) % 8
|
||||
elseif b2 == 0xff then
|
||||
this:read(this:byte())
|
||||
this:skip_eob()
|
||||
else
|
||||
this:skip_eob()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
this.frames = frames
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:skip(offset)
|
||||
self.p = self.p + offset
|
||||
end
|
||||
|
||||
-- skip to end of block
|
||||
function GIFDecoderMethods:skip_eob()
|
||||
repeat
|
||||
local size = self:byte()
|
||||
self:skip(size)
|
||||
until size == 0
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:byte()
|
||||
local b = byte(self.buf, self.p)
|
||||
self:skip(1)
|
||||
return b
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:bytes(len)
|
||||
local _p = self.p
|
||||
self:skip(len)
|
||||
return byte(self.buf, _p, len + _p - 1)
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:read(len)
|
||||
local _p = self.p
|
||||
self:skip(len)
|
||||
return sub(self.buf, _p, len + _p - 1)
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:word()
|
||||
return bor(self:byte(), lshift(self:byte(), 8))
|
||||
end
|
||||
|
||||
local GifReaderLZWOutputIndexStream = function(this, output, output_length)
|
||||
local min_code_size = this:byte()
|
||||
local clear_code = lshift(1, min_code_size)
|
||||
local eoi_code = clear_code + 1
|
||||
local next_code = eoi_code + 1
|
||||
local cur_code_size = min_code_size + 1
|
||||
|
||||
local code_mask = lshift(1, cur_code_size) - 1
|
||||
local cur_shift = 0
|
||||
local cur = 0
|
||||
local op = 0
|
||||
|
||||
local subblock_size = this:byte()
|
||||
|
||||
local code_table = {}
|
||||
|
||||
local prev_code = nil
|
||||
|
||||
while true do
|
||||
while cur_shift < 16 do
|
||||
if subblock_size == 0 then break end
|
||||
|
||||
cur = bor(cur, lshift(this:byte(), cur_shift))
|
||||
cur_shift = cur_shift + 8
|
||||
|
||||
if subblock_size == 1 then
|
||||
subblock_size = this:byte()
|
||||
else
|
||||
subblock_size = subblock_size - 1
|
||||
end
|
||||
end
|
||||
|
||||
if cur_shift < cur_code_size then break end
|
||||
|
||||
local code = band(cur, code_mask)
|
||||
cur = rshift(cur, cur_code_size)
|
||||
cur_shift = cur_shift - cur_code_size
|
||||
|
||||
if code == clear_code then
|
||||
next_code = eoi_code + 1
|
||||
cur_code_size = min_code_size + 1
|
||||
code_mask = lshift(1, cur_code_size) - 1
|
||||
|
||||
prev_code = null
|
||||
continue
|
||||
elseif code == eoi_code then
|
||||
break
|
||||
end
|
||||
|
||||
local chase_code = code < next_code and code or prev_code
|
||||
local chase_length = 0
|
||||
local chase = chase_code
|
||||
while chase > clear_code do
|
||||
chase = rshift(code_table[chase], 8)
|
||||
chase_length = chase_length + 1
|
||||
end
|
||||
|
||||
local k = chase
|
||||
local op_end = op + chase_length + (chase_code ~= code and 1 or 0)
|
||||
if op_end > output_length then
|
||||
Error("Warning, gif stream longer than expected.")
|
||||
return
|
||||
end
|
||||
|
||||
output[op] = k; op = op + 1
|
||||
op = op + chase_length
|
||||
|
||||
local b = op
|
||||
|
||||
if chase_code ~= code then
|
||||
output[op] = k; op = op + 1
|
||||
end
|
||||
chase = chase_code
|
||||
|
||||
while chase_length > 0 do
|
||||
chase_length = chase_length - 1
|
||||
chase = code_table[chase]
|
||||
b = b - 1
|
||||
output[b] = band(chase, 0xff)
|
||||
|
||||
chase = rshift(chase, 8)
|
||||
end
|
||||
|
||||
if prev_code ~= nil and next_code < 4096 then
|
||||
code_table[next_code] = bor(lshift(prev_code, 8), k)
|
||||
next_code = next_code + 1
|
||||
|
||||
if next_code >= code_mask + 1 and cur_code_size < 12 then
|
||||
cur_code_size = cur_code_size + 1
|
||||
code_mask = bor(lshift(code_mask, 1), 1)
|
||||
end
|
||||
end
|
||||
|
||||
prev_code = code
|
||||
end
|
||||
|
||||
if op ~= output_length then
|
||||
Error("Warning, gif stream shorter than expected.")
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:decode_and_blit_frame_RGBA(frame_num, pixels)
|
||||
local frame = self.frames[frame_num]
|
||||
local num_pixels = frame.width * frame.height
|
||||
local index_stream = {}
|
||||
|
||||
self.p = frame.data_offset
|
||||
GifReaderLZWOutputIndexStream(self, index_stream, num_pixels)
|
||||
local palette_offset = frame.palette_offset
|
||||
|
||||
local trans = frame.transparent_index
|
||||
if trans == nil then
|
||||
trans = 256
|
||||
end
|
||||
|
||||
local width = self.width
|
||||
local framewidth = frame.width
|
||||
local framestride = width - framewidth
|
||||
local xleft = framewidth
|
||||
|
||||
local opbeg = (frame.y * width + frame.x) * 4
|
||||
|
||||
local opend = ((frame.y + frame.height) * width + frame.x) * 4
|
||||
local op = opbeg
|
||||
local scanstride = framestride * 4
|
||||
|
||||
if frame.interlaced == true then
|
||||
scanstride = scanstride + (width * 4 * 7)
|
||||
end
|
||||
|
||||
local interlaceskip = 8
|
||||
|
||||
local i = 0
|
||||
local buf = self.buf
|
||||
while i < num_pixels do
|
||||
local index = index_stream[i]
|
||||
|
||||
if xleft == 0 then
|
||||
op = op + scanstride
|
||||
xleft = framewidth
|
||||
|
||||
if op >= opend then
|
||||
scanstride =
|
||||
framestride * 4 + width * 4 * (interlaceskip - 1)
|
||||
|
||||
op =
|
||||
opbeg +
|
||||
(framewidth + framestride) * lshift(interlaceskip, 1)
|
||||
interlaceskip = rshift(interlaceskip, 1)
|
||||
end
|
||||
end
|
||||
|
||||
if index ~= trans then
|
||||
index = palette_offset + index * 3
|
||||
pixels[op + 0] = byte(buf, index)
|
||||
pixels[op + 1] = byte(buf, index + 1)
|
||||
pixels[op + 2] = byte(buf, index + 2)
|
||||
pixels[op + 3] = 255
|
||||
end
|
||||
|
||||
op = op + 4
|
||||
|
||||
xleft = xleft - 1
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:clear_frame(frame_num, pixels)
|
||||
local frame = self.frames[frame_num]
|
||||
|
||||
self.p = frame.data_offset
|
||||
|
||||
local width = self.width
|
||||
local framewidth = frame.width
|
||||
local framestride = width - framewidth
|
||||
local xleft = framewidth
|
||||
|
||||
local opbeg = (frame.y * width + frame.x) * 4
|
||||
|
||||
local opend = ((frame.y + frame.height) * width + frame.x) * 4
|
||||
local op = opbeg
|
||||
local scanstride = framestride * 4
|
||||
|
||||
if frame.interlaced == true then
|
||||
scanstride = scanstride + (width * 4 * 7)
|
||||
end
|
||||
|
||||
local interlaceskip = 8
|
||||
|
||||
local i = 0
|
||||
local num_pixels = frame.width * frame.height
|
||||
while i < num_pixels do
|
||||
if xleft == 0 then
|
||||
op = op + scanstride
|
||||
xleft = framewidth
|
||||
|
||||
if op >= opend then
|
||||
scanstride =
|
||||
framestride * 4 + width * 4 * (interlaceskip - 1)
|
||||
|
||||
op =
|
||||
opbeg +
|
||||
(framewidth + framestride) * lshift(interlaceskip, 1)
|
||||
interlaceskip = rshift(interlaceskip, 1)
|
||||
end
|
||||
end
|
||||
|
||||
pixels[op + 0] = 0
|
||||
pixels[op + 1] = 0
|
||||
pixels[op + 2] = 0
|
||||
pixels[op + 3] = 0
|
||||
op = op + 4
|
||||
|
||||
xleft = xleft - 1
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function GIFDecoderMethods:get_frames()
|
||||
local num_pixels = self.width * self.height * 4 + 4
|
||||
local frames = {}
|
||||
local numFrames = #self.frames
|
||||
local last_frame
|
||||
local restore_from
|
||||
for i = 1, numFrames do
|
||||
local frame = self.frames[i]
|
||||
|
||||
local data = {}
|
||||
|
||||
if last_frame then
|
||||
local _data = last_frame.data
|
||||
for k = 0, num_pixels do
|
||||
data[k] = _data[k]
|
||||
end
|
||||
end
|
||||
|
||||
if i > 1 then
|
||||
local last_disposal = last_frame.disposal
|
||||
if last_disposal == 3 then
|
||||
if restore_from then
|
||||
for k = 0, num_pixels do
|
||||
data[k] = restore_from[k]
|
||||
end
|
||||
else
|
||||
self:clear_frame(i - 1, data)
|
||||
end
|
||||
end
|
||||
|
||||
if last_disposal == 2 then
|
||||
self:clear_frame(i - 1, data)
|
||||
end
|
||||
end
|
||||
|
||||
self:decode_and_blit_frame_RGBA(i, data)
|
||||
|
||||
local delay = frame.delay
|
||||
if delay < 2 then
|
||||
delay = 10
|
||||
end
|
||||
|
||||
local disposal = frame.disposal
|
||||
last_frame = {
|
||||
data = data,
|
||||
delay = delay,
|
||||
disposal = disposal
|
||||
}
|
||||
frames[i] = last_frame
|
||||
|
||||
if disposal ~= 3 then
|
||||
restore_from = data
|
||||
end
|
||||
end
|
||||
|
||||
return frames
|
||||
end
|
||||
|
||||
return GIFDecoder.new
|
||||
215
lua/sui/libs/png_encoder.lua
Normal file
215
lua/sui/libs/png_encoder.lua
Normal file
@@ -0,0 +1,215 @@
|
||||
--[[
|
||||
| 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 string = string
|
||||
local table = table
|
||||
local bit = bit
|
||||
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
|
||||
local insert = table.insert
|
||||
local concat = table.concat
|
||||
|
||||
local bor = bit.bor
|
||||
local bxor = bit.bxor
|
||||
local band = bit.band
|
||||
local bnot = bit.bnot
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
|
||||
local ceil = math.ceil
|
||||
|
||||
local SIGNATURE = char(137, 80, 78, 71, 13, 10, 26, 10)
|
||||
|
||||
local crc_table = {}; do
|
||||
local n = 0
|
||||
while n < 256 do
|
||||
local c = n
|
||||
local k = 0
|
||||
while k < 8 do
|
||||
if band(c, 1) ~= 0 then
|
||||
c = bxor(0xedb88320, rshift(c, 1))
|
||||
else
|
||||
c = rshift(c, 1)
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
crc_table[n + 1] = c
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
|
||||
local crc = function(buf)
|
||||
local c = 0xffffffff
|
||||
for i = 1, #buf do
|
||||
c = bxor(crc_table[band(bxor(c, byte(buf, i)), 0xff) + 1], rshift(c, 8))
|
||||
end
|
||||
return bxor(c, 0xffffffff)
|
||||
end
|
||||
|
||||
local dword_as_string = function(dword)
|
||||
return char(
|
||||
rshift(band(dword, 0xff000000), 24),
|
||||
rshift(band(dword, 0x00ff0000), 16),
|
||||
rshift(band(dword, 0x0000ff00), 8),
|
||||
band(dword, 0x000000ff)
|
||||
)
|
||||
end
|
||||
|
||||
local create_chunk = function(type, data, length)
|
||||
local CRC = crc(type .. data)
|
||||
return concat({
|
||||
dword_as_string(length or #data),
|
||||
type,
|
||||
data,
|
||||
dword_as_string(CRC)
|
||||
}, "", 1, 4)
|
||||
end
|
||||
|
||||
local create_IHDR; do
|
||||
local ARGS = (
|
||||
-- bit depth
|
||||
char(8) ..
|
||||
-- color type: 6=truecolor with alpha
|
||||
char(6) ..
|
||||
-- compression method: 0=deflate, only allowed value
|
||||
char(0) ..
|
||||
-- filtering: 0=adaptive, only allowed value
|
||||
char(0) ..
|
||||
-- interlacing: 0=none
|
||||
char(0)
|
||||
)
|
||||
|
||||
create_IHDR = function(w, h)
|
||||
return create_chunk("IHDR", concat({
|
||||
dword_as_string(w),
|
||||
dword_as_string(h),
|
||||
ARGS
|
||||
}, "", 1, 3), 13)
|
||||
end
|
||||
end
|
||||
|
||||
local deflate_pack; do
|
||||
local BASE = 6552
|
||||
local NMAX = 5552
|
||||
local adler32 = function(str)
|
||||
local s1 = 1
|
||||
local s2 = 0
|
||||
local n = NMAX
|
||||
|
||||
for i = 1, #str do
|
||||
s1 = s1 + byte(str, i)
|
||||
s2 = s2 + s1
|
||||
|
||||
n = n - 1
|
||||
if n == 0 then
|
||||
s1 = s1 % BASE
|
||||
s2 = s2 % BASE
|
||||
n = NMAX
|
||||
end
|
||||
end
|
||||
|
||||
s1 = s1 % BASE
|
||||
s2 = s2 % BASE
|
||||
|
||||
return bor(lshift(s2, 16), s1)
|
||||
end
|
||||
|
||||
local splitChunks = function(chunk, chunkSize)
|
||||
local len = ceil(#chunk / chunkSize)
|
||||
local ret = {}
|
||||
for i = 1, len do
|
||||
ret[i - 1] = chunk:sub(((i - 1) * chunkSize) + 1, chunkSize)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
deflate_pack = function(str)
|
||||
local ret = {"\x78\x9c"}
|
||||
|
||||
local chunks = splitChunks(str, 0xFFFF)
|
||||
local len = #chunks
|
||||
|
||||
local i = 0
|
||||
while i < (len + 1) do
|
||||
local chunk = chunks[i]
|
||||
local chunk_n = #chunk
|
||||
|
||||
insert(ret, i < len and "\x00" or "\x01")
|
||||
insert(ret, char(band(chunk_n, 0xff), band(rshift(chunk_n, 8), 0xff)))
|
||||
insert(ret, char(band(bnot(chunk_n), 0xff), band(rshift(bnot(chunk_n), 8), 0xff)))
|
||||
insert(ret, chunk)
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
local t = adler32(str)
|
||||
t = char(
|
||||
band(rshift(t, 24), 0xff),
|
||||
band(rshift(t, 16), 0xff),
|
||||
band(rshift(t, 8), 0xff),
|
||||
band(t, 0xff)
|
||||
)
|
||||
|
||||
insert(ret, t)
|
||||
|
||||
return concat(ret)
|
||||
end
|
||||
end
|
||||
|
||||
local create_IDAT; do
|
||||
local slice = function(a, s, e)
|
||||
local ret, j = {}, 0
|
||||
for i = s, e - 1 do
|
||||
ret[j] = char(band(a[i] or 0, 0xFF))
|
||||
j = j + 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local array_split_chunks = function(w, h, array, chunkSize)
|
||||
local ret = {}
|
||||
local i = 0
|
||||
local len = ceil((w * h * 4 + 4) / chunkSize)
|
||||
while i < len do
|
||||
ret[i] = slice(array, i * chunkSize, (i + 1) * chunkSize)
|
||||
i = i + 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
create_IDAT = function(w, h, chunk)
|
||||
local scanlines = array_split_chunks(w, h, chunk, w * 4)
|
||||
|
||||
local image_bytes = {}
|
||||
for i = 0, #scanlines do
|
||||
local scanline = scanlines[i]
|
||||
insert(image_bytes, char(band(0, 0xFF)))
|
||||
insert(image_bytes, concat(scanline, "", 0, #scanline))
|
||||
end
|
||||
image_bytes = deflate_pack(concat(image_bytes))
|
||||
|
||||
return create_chunk("IDAT", image_bytes)
|
||||
end
|
||||
end
|
||||
|
||||
local IEND = create_chunk("IEND", "", 0)
|
||||
local to_return = {SIGNATURE, nil, nil, IEND}
|
||||
local generate_png = function(w, h, chunk)
|
||||
local IHDR = create_IHDR(w, h)
|
||||
local IDAT = create_IDAT(w, h, chunk)
|
||||
|
||||
to_return[2] = IHDR
|
||||
to_return[3] = IDAT
|
||||
|
||||
return concat(to_return, "", 1, 4)
|
||||
end
|
||||
|
||||
return generate_png
|
||||
924
lua/sui/libs/tdlib/cl_tdlib.lua
Normal file
924
lua/sui/libs/tdlib/cl_tdlib.lua
Normal file
@@ -0,0 +1,924 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
Three's Derma Lib
|
||||
Made by Threebow
|
||||
|
||||
You are free to use this anywhere you like, or sell any addons
|
||||
made using this, as long as I am properly accredited.
|
||||
]]
|
||||
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
local Color = Color
|
||||
local render = render
|
||||
local SysTime = SysTime
|
||||
local Lerp, RealFrameTime = Lerp, RealFrameTime
|
||||
local RoundedBox, RoundedBoxEx, NoTexture = draw.RoundedBox, draw.RoundedBoxEx, draw.NoTexture
|
||||
local SetDrawColor, DrawRect = surface.SetDrawColor, surface.DrawRect
|
||||
local DrawPoly = surface.DrawPoly
|
||||
local sui = sui
|
||||
|
||||
local Panel = FindMetaTable("Panel")
|
||||
|
||||
--[[
|
||||
Constants
|
||||
]]
|
||||
local BLUR = CreateMaterial("SUI.TDLib.Blur", "gmodscreenspace", {
|
||||
["$basetexture"] = "_rt_fullframefb",
|
||||
["$blur"] = (1 / 3) * 7,
|
||||
})
|
||||
|
||||
local COL_WHITE_1 = Color(255, 255, 255)
|
||||
local COL_WHITE_2 = Color(255, 255, 255, 30)
|
||||
|
||||
--[[
|
||||
credits to http://slabode.exofire.net/circle_draw.shtml
|
||||
]]
|
||||
local calculate_circle do
|
||||
local cos = math.cos
|
||||
local sin = math.sin
|
||||
local round = math.Round
|
||||
local sqrt = math.sqrt
|
||||
local pi = math.pi
|
||||
calculate_circle = function(circle, x_centre, y_centre, r)
|
||||
if circle.x == x_centre and circle.y == y_centre and circle.r == r then return end
|
||||
|
||||
local step = (2 * pi) / round(6 * sqrt(r))
|
||||
|
||||
local i = 0
|
||||
for theta = 2 * pi, 0, -step do
|
||||
local x = x_centre + r * cos(theta)
|
||||
local y = y_centre - r * sin(theta)
|
||||
i = i + 1
|
||||
circle[i] = {
|
||||
x = x,
|
||||
y = y
|
||||
}
|
||||
end
|
||||
|
||||
for i = i + 1, #circle do
|
||||
circle[i] = nil
|
||||
end
|
||||
|
||||
circle.x = x_centre
|
||||
circle.y = y_centre
|
||||
circle.r = r
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
void DrawArc(float cx, float cy, float r, float start_angle, float arc_angle, int num_segments)
|
||||
{
|
||||
float theta = arc_angle / float(num_segments - 1);//theta is now calculated from the arc angle instead, the - 1 bit comes from the fact that the arc is open
|
||||
|
||||
float tangetial_factor = tanf(theta);
|
||||
|
||||
float radial_factor = cosf(theta);
|
||||
|
||||
|
||||
float x = r * cosf(start_angle);//we now start at the start angle
|
||||
float y = r * sinf(start_angle);
|
||||
|
||||
glBegin(GL_LINE_STRIP);//since the arc is not a closed curve, this is a strip now
|
||||
for(int ii = 0; ii < num_segments; ii++)
|
||||
{
|
||||
glVertex2f(x + cx, y + cy);
|
||||
|
||||
float tx = -y;
|
||||
float ty = x;
|
||||
|
||||
x += tx * tangetial_factor;
|
||||
y += ty * tangetial_factor;
|
||||
|
||||
x *= radial_factor;
|
||||
y *= radial_factor;
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
]]
|
||||
|
||||
local copy_color = function(color)
|
||||
return Color(color.r, color.g, color.b, color.a)
|
||||
end
|
||||
|
||||
local color_alpha = function(color, a)
|
||||
color.a = a
|
||||
return color
|
||||
end
|
||||
|
||||
--[[
|
||||
Collection of various utilities
|
||||
]]
|
||||
|
||||
local TDLibUtil = {}
|
||||
|
||||
function TDLibUtil.DrawCircle(circle, x, y, r, color)
|
||||
calculate_circle(circle, x, y, r)
|
||||
|
||||
SetDrawColor(color)
|
||||
NoTexture()
|
||||
DrawPoly(circle)
|
||||
end
|
||||
local DrawCircle = TDLibUtil.DrawCircle
|
||||
|
||||
do
|
||||
local SetMaterial = surface.SetMaterial
|
||||
local UpdateScreenEffectTexture, DrawTexturedRect, SetScissorRect = render.UpdateScreenEffectTexture, surface.DrawTexturedRect, render.SetScissorRect
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
hook.Add("OnScreenSizeChanged", "SUI.TDLib", function()
|
||||
scrW, scrH = ScrW(), ScrH()
|
||||
end)
|
||||
|
||||
function TDLibUtil.BlurPanel(s)
|
||||
local x, y = s:LocalToScreen(0, 0)
|
||||
|
||||
SetDrawColor(255, 255, 255)
|
||||
SetMaterial(BLUR)
|
||||
|
||||
for i = 1, 2 do
|
||||
UpdateScreenEffectTexture()
|
||||
DrawTexturedRect(x * -1, y * -1, scrW, scrH)
|
||||
end
|
||||
end
|
||||
|
||||
function TDLibUtil.DrawBlur(x, y, w, h)
|
||||
SetDrawColor(255, 255, 255)
|
||||
SetMaterial(BLUR)
|
||||
|
||||
SetScissorRect(x, y, x + w, y + h, true)
|
||||
for i = 1, 2 do
|
||||
UpdateScreenEffectTexture()
|
||||
DrawTexturedRect(-1, -1, scrW, scrH)
|
||||
end
|
||||
SetScissorRect(0, 0, 0, 0, false)
|
||||
end
|
||||
end
|
||||
|
||||
local LibClasses = {}
|
||||
|
||||
do
|
||||
local on_funcs = {}
|
||||
|
||||
function LibClasses:On(name, func)
|
||||
local old_func = self[name]
|
||||
|
||||
if not old_func then
|
||||
self[name] = func
|
||||
return self
|
||||
end
|
||||
|
||||
local name_2 = name .. "_funcs"
|
||||
|
||||
-- we gotta avoid creating 13535035 closures
|
||||
if not on_funcs[name] then
|
||||
on_funcs[name] = function(s, a1, a2, a3, a4)
|
||||
local funcs = s[name_2]
|
||||
local i, n = 0, #funcs
|
||||
::loop::
|
||||
i = i + 1
|
||||
if i <= n then
|
||||
funcs[i](s, a1, a2, a3, a4)
|
||||
goto loop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not self[name_2] then
|
||||
self[name] = on_funcs[name]
|
||||
self[name_2] = {
|
||||
old_func,
|
||||
func
|
||||
}
|
||||
else
|
||||
table.insert(self[name_2], func)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local UnPredictedCurTime = UnPredictedCurTime
|
||||
|
||||
local transition_func = function(s)
|
||||
local transitions = s.transitions
|
||||
local i, n = 0, #transitions
|
||||
::loop::
|
||||
i = i + 1
|
||||
|
||||
if i <= n then
|
||||
local v = transitions[i]
|
||||
local name = v.name
|
||||
local v2 = s[name]
|
||||
if v.func(s) then
|
||||
if v.start_0 then
|
||||
v.start_1, v.start_0 = UnPredictedCurTime(), nil
|
||||
end
|
||||
|
||||
if v2 ~= 1 then
|
||||
s[name] = Lerp((UnPredictedCurTime() - v.start_1) / v.time, v2, 1)
|
||||
end
|
||||
else
|
||||
if v.start_1 then
|
||||
v.start_0, v.start_1 = UnPredictedCurTime(), nil
|
||||
end
|
||||
|
||||
if v2 ~= 0 then
|
||||
s[name] = Lerp((UnPredictedCurTime() - v.start_0) / v.time, v2, 0)
|
||||
end
|
||||
end
|
||||
|
||||
goto loop
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:SetupTransition(name, time, func)
|
||||
self[name] = 0
|
||||
|
||||
local transition = {
|
||||
name = name,
|
||||
time = time,
|
||||
func = func,
|
||||
start_0 = 0,
|
||||
start_1 = 0,
|
||||
}
|
||||
|
||||
if self.transitions then
|
||||
for k, v in ipairs(self.transitions) do
|
||||
if v.name == name then
|
||||
self.transitions[k] = transition
|
||||
return self
|
||||
end
|
||||
end
|
||||
table.insert(self.transitions, transition)
|
||||
else
|
||||
self.transitions = {transition}
|
||||
self:On("Think", transition_func)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:ClearPaint()
|
||||
self.Paint = nil
|
||||
self.Paint_funcs = nil
|
||||
local SetPaintBackgroundEnabled = self.SetPaintBackgroundEnabled
|
||||
if SetPaintBackgroundEnabled then
|
||||
SetPaintBackgroundEnabled(self, false)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function LibClasses:RoundedBox(id, r, x, y, w, h, c)
|
||||
self.colors = self.colors or {}
|
||||
local colors = self.colors
|
||||
|
||||
local id_c = colors[id]
|
||||
if not id_c then
|
||||
id_c = Color(c:Unpack())
|
||||
colors[id] = id_c
|
||||
end
|
||||
|
||||
sui.lerp_color(id_c, c)
|
||||
RoundedBox(r, x, y, w, h, id_c)
|
||||
end
|
||||
|
||||
do
|
||||
local SetFGColor = Panel.SetFGColor
|
||||
|
||||
local set_color = function(s, col)
|
||||
s.m_colText = col
|
||||
SetFGColor(s, col.r, col.g, col.b, col.a)
|
||||
end
|
||||
|
||||
local paint = function(s)
|
||||
local col = s.sui_textcolor
|
||||
sui.lerp_color(col, s.new_col)
|
||||
set_color(s, col)
|
||||
end
|
||||
|
||||
function LibClasses:TextColor(c, use_paint)
|
||||
local col = self.sui_textcolor
|
||||
if not col then
|
||||
col = Color(c:Unpack())
|
||||
self.sui_textcolor = col
|
||||
|
||||
if use_paint then
|
||||
self:On("Paint", paint)
|
||||
end
|
||||
end
|
||||
|
||||
if use_paint then
|
||||
self.new_col = c
|
||||
else
|
||||
sui.lerp_color(col, c)
|
||||
self:SetTextColor(col)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local fade_hover_Paint = function(s, w, h)
|
||||
if s.FadeHovers ~= 0 then
|
||||
color_alpha(s.fadehover_color, s.fadehover_old_alpha * s.FadeHovers)
|
||||
if s.fadehover_radius > 0 then
|
||||
RoundedBox(s.fadehover_radius, 0, 0, w, h, s.fadehover_color)
|
||||
else
|
||||
SetDrawColor(s.fadehover_color)
|
||||
DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:FadeHover(color, time, radius, func)
|
||||
color = copy_color(color or COL_WHITE_2)
|
||||
self.fadehover_color = color
|
||||
self.fadehover_radius = radius or 0
|
||||
self.fadehover_old_alpha = color.a
|
||||
self:SetupTransition("FadeHovers", time or 0.8, func or TDLibUtil.HoverFunc)
|
||||
self:On("Paint", fade_hover_Paint)
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:BarHover(color, height, time)
|
||||
color = color or COL_WHITE_1
|
||||
height = height or 2
|
||||
time = time or 1.6
|
||||
self:SetupTransition("BarHovers", time, TDLibUtil.HoverFunc)
|
||||
self:On("Paint", function(s, w, h)
|
||||
if s.BarHovers ~= 0 then
|
||||
local bar = Round(w * s.BarHovers)
|
||||
SetDrawColor(color)
|
||||
DrawRect((w / 2) - (bar / 2), h - height, bar, height)
|
||||
end
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
do
|
||||
local paint = function(s, w, h)
|
||||
draw.RoundedBox(0, 0, 0, w, h, s.SUI_GetColor("line"))
|
||||
end
|
||||
|
||||
function LibClasses:Line(dock, m1, m2, m3, m4)
|
||||
self.making_line = true
|
||||
|
||||
local line = self:Add("SAM.Panel")
|
||||
line:Dock(dock or TOP)
|
||||
|
||||
if self.line_margin then
|
||||
line:DockMargin(unpack(self.line_margin))
|
||||
else
|
||||
line:DockMargin(m1 or 0, m2 or 0, m3 or 0, m4 or 10)
|
||||
end
|
||||
|
||||
line.no_scale = true
|
||||
line:SetTall(1)
|
||||
line.Paint = paint
|
||||
|
||||
self.making_line = false
|
||||
return line
|
||||
end
|
||||
|
||||
function LibClasses:LineMargin(m1, m2, m3, m4)
|
||||
self.line_margin = {m1 or 0, m2 or 0, m3 or 0, m4 or 0}
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local background_Paint_1 = function(s)
|
||||
s:SetBGColor(s.background_color)
|
||||
end
|
||||
|
||||
local background_Paint_2 = function(s, w, h)
|
||||
RoundedBoxEx(s.background_radius, 0, 0, w, h, s.background_color, true, true, true, true)
|
||||
end
|
||||
|
||||
local background_Paint_3 = function(s, w, h)
|
||||
RoundedBoxEx(s.background_radius, 0, 0, w, h, s.background_color, s.background_r_tl, s.background_r_tr, s.background_r_bl, s.background_r_br)
|
||||
end
|
||||
|
||||
function LibClasses:Background(color, radius, r_tl, r_tr, r_bl, r_br)
|
||||
self.background_color = color
|
||||
if isnumber(radius) and radius ~= 0 then
|
||||
self.background_radius = radius
|
||||
if isbool(r_tl) or isbool(r_tr) or isbool(r_bl) or isbool(r_br) then
|
||||
self.background_r_tl = r_tl
|
||||
self.background_r_tr = r_tr
|
||||
self.background_r_bl = r_bl
|
||||
self.background_r_br = r_br
|
||||
self:On("Paint", background_Paint_3)
|
||||
else
|
||||
self:On("Paint", background_Paint_2)
|
||||
end
|
||||
else
|
||||
self:SetPaintBackgroundEnabled(true)
|
||||
self:On("ApplySchemeSettings", background_Paint_1)
|
||||
self:On("PerformLayout", background_Paint_1)
|
||||
end
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:CircleClick(color, speed, target_radius)
|
||||
self.circle_click_color = color or COL_WHITE_2
|
||||
|
||||
speed = speed or 5
|
||||
target_radius = isnumber(target_radius) and target_radius or false
|
||||
|
||||
local radius, alpha, click_x, click_y = 0, -1, 0, 0
|
||||
local circle = {}
|
||||
self:On("Paint", function(s, w)
|
||||
if alpha >= 0 then
|
||||
DrawCircle(circle, click_x, click_y, radius, ColorAlpha(self.circle_click_color, alpha))
|
||||
local frame_time = RealFrameTime()
|
||||
radius, alpha = Lerp(frame_time * speed, radius, target_radius or w), Lerp(frame_time * speed, alpha, -1)
|
||||
end
|
||||
end)
|
||||
self:On("DoClick", function()
|
||||
click_x, click_y = self:CursorPos()
|
||||
radius, alpha = 0, self.circle_click_color.a
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
do
|
||||
local min = math.min
|
||||
function LibClasses:CircleClick2(color, speed, target_radius, start_radius)
|
||||
color = color or COL_WHITE_2
|
||||
local _color = Color(color:Unpack())
|
||||
|
||||
speed = speed or 5
|
||||
target_radius = isnumber(target_radius) and target_radius or false
|
||||
|
||||
local radius, alpha = 0, -1
|
||||
local circle = {}
|
||||
self:On("Paint", function(s, w, h)
|
||||
if alpha >= 0 then
|
||||
_color.a = alpha
|
||||
DrawCircle(circle, w / 2, h / 2, radius, _color)
|
||||
|
||||
local frame_time = RealFrameTime()
|
||||
radius, alpha = Lerp(frame_time * speed, radius, target_radius or min(w, h) / 2), Lerp(frame_time * speed, alpha, -1)
|
||||
end
|
||||
end)
|
||||
|
||||
self:On("DoClick", function()
|
||||
radius, alpha = start_radius or 0, color.a
|
||||
end)
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
-- https://github.com/Facepunch/garrysmod/pull/1520#issuecomment-410458090
|
||||
function LibClasses:Outline(color, width)
|
||||
color = color or COL_WHITE_1
|
||||
width = width or 1
|
||||
self:On("Paint", function(s, w, h)
|
||||
SetDrawColor(color)
|
||||
DrawRect(0, 0, w, width)
|
||||
DrawRect(0, h - width, w, width)
|
||||
DrawRect(0, width, width, h - (width * 2))
|
||||
DrawRect(w - width, width, width, h - (width * 2))
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
function LibClasses:LinedCorners(color, len)
|
||||
color = color or COL_WHITE_1
|
||||
len = len or 15
|
||||
self:On("Paint", function(s, w, h)
|
||||
SetDrawColor(color)
|
||||
DrawRect(0, 0, len, 1)
|
||||
DrawRect(0, 1, 1, len - 1)
|
||||
DrawRect(w - len, h - 1, len, 1)
|
||||
DrawRect(w - 1, h - len, 1, len - 1)
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
function LibClasses:SideBlock(color, size, side)
|
||||
color = color or COL_WHITE_1
|
||||
size = size or 3
|
||||
side = side or LEFT
|
||||
self:On("Paint", function(s, w, h)
|
||||
SetDrawColor(color)
|
||||
if side == LEFT then
|
||||
DrawRect(0, 0, size, h)
|
||||
elseif side == TOP then
|
||||
DrawRect(0, 0, w, size)
|
||||
elseif size == RIGHT then
|
||||
DrawRect(w - size, 0, size, h)
|
||||
elseif side == BOTTOM then
|
||||
DrawRect(0, h - size, w, size)
|
||||
end
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
function LibClasses:Blur()
|
||||
self:On("Paint", TDLibUtil.BlurPanel)
|
||||
return self
|
||||
end
|
||||
|
||||
do
|
||||
local STENCILOPERATION_REPLACE = STENCILOPERATION_REPLACE
|
||||
local STENCILOPERATION_ZERO = STENCILOPERATION_ZERO
|
||||
local STENCILCOMPARISONFUNCTION_NEVER = STENCILCOMPARISONFUNCTION_NEVER
|
||||
local STENCILCOMPARISONFUNCTION_EQUAL = STENCILCOMPARISONFUNCTION_EQUAL
|
||||
|
||||
local ClearStencil = render.ClearStencil
|
||||
local SetStencilEnable = render.SetStencilEnable
|
||||
local SetStencilWriteMask = render.SetStencilWriteMask
|
||||
local SetStencilTestMask = render.SetStencilTestMask
|
||||
local SetStencilFailOperation = render.SetStencilFailOperation
|
||||
local SetStencilPassOperation = render.SetStencilPassOperation
|
||||
local SetStencilZFailOperation = render.SetStencilZFailOperation
|
||||
local SetStencilCompareFunction = render.SetStencilCompareFunction
|
||||
local SetStencilReferenceValue = render.SetStencilReferenceValue
|
||||
|
||||
local color_white = color_white
|
||||
|
||||
local avatar_setplayer = function(s, ply, size)
|
||||
s.avatar:SetPlayer(ply, size)
|
||||
end
|
||||
|
||||
local avatar_setsteamid = function(s, steamid, size)
|
||||
s.avatar:SetSteamID(steamid, size)
|
||||
end
|
||||
|
||||
function LibClasses:CircleAvatar()
|
||||
local avatar = self:Add("AvatarImage")
|
||||
avatar:Dock(FILL)
|
||||
avatar:SetPaintedManually(true)
|
||||
self.avatar = avatar
|
||||
self.SetSteamID = avatar_setsteamid
|
||||
self.SetPlayer = avatar_setplayer
|
||||
|
||||
local circle = {}
|
||||
local PaintManual = avatar.PaintManual
|
||||
self.Paint = function(s, w, h)
|
||||
ClearStencil()
|
||||
SetStencilEnable(true)
|
||||
|
||||
SetStencilWriteMask(1)
|
||||
SetStencilTestMask(1)
|
||||
|
||||
SetStencilFailOperation(STENCILOPERATION_REPLACE)
|
||||
SetStencilPassOperation(STENCILOPERATION_ZERO)
|
||||
SetStencilZFailOperation(STENCILOPERATION_ZERO)
|
||||
SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER)
|
||||
SetStencilReferenceValue(1)
|
||||
|
||||
local a = w / 2
|
||||
DrawCircle(circle, a, a, a, color_white)
|
||||
|
||||
SetStencilFailOperation(STENCILOPERATION_ZERO)
|
||||
SetStencilPassOperation(STENCILOPERATION_REPLACE)
|
||||
SetStencilZFailOperation(STENCILOPERATION_ZERO)
|
||||
SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL)
|
||||
SetStencilReferenceValue(1)
|
||||
|
||||
PaintManual(avatar)
|
||||
|
||||
SetStencilEnable(false)
|
||||
end
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
function LibClasses:AnimationThinkInternal()
|
||||
local systime = SysTime()
|
||||
|
||||
if self.Term and self.Term <= systime then
|
||||
self:Remove()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local m_AnimList = self.m_AnimList
|
||||
if not m_AnimList then return end
|
||||
|
||||
for i = #m_AnimList, 1, -1 do
|
||||
local anim = m_AnimList[i]
|
||||
if systime >= anim.StartTime then
|
||||
local frac = math.TimeFraction(anim.StartTime, anim.EndTime, systime)
|
||||
frac = math.Clamp(frac, 0, 1)
|
||||
|
||||
local Think = anim.Think
|
||||
if Think then
|
||||
Think(anim, self, frac ^ (1.0 - (frac - 0.5)))
|
||||
end
|
||||
|
||||
if frac == 1 then
|
||||
local OnEnd = anim.OnEnd
|
||||
if OnEnd then
|
||||
OnEnd(anim, self)
|
||||
end
|
||||
|
||||
m_AnimList[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local sort = function(a, b)
|
||||
return a.EndTime > b.EndTime
|
||||
end
|
||||
|
||||
function LibClasses:NewAnimation(length, delay, ease, callback)
|
||||
delay = delay or 0
|
||||
ease = ease or -1
|
||||
|
||||
if self.m_AnimQueue then
|
||||
delay = delay + self:AnimTail()
|
||||
self.m_AnimQueue = false
|
||||
else
|
||||
delay = delay + SysTime()
|
||||
end
|
||||
|
||||
local anim = {
|
||||
StartTime = delay,
|
||||
EndTime = delay + length,
|
||||
Ease = ease,
|
||||
OnEnd = callback
|
||||
}
|
||||
|
||||
self:SetAnimationEnabled(true)
|
||||
|
||||
if self.m_AnimList == nil then
|
||||
self.m_AnimList = {}
|
||||
end
|
||||
|
||||
table.insert(self.m_AnimList, anim)
|
||||
table.sort(self.m_AnimList, sort)
|
||||
|
||||
self.AnimationThink = self.AnimationThinkInternal
|
||||
|
||||
return anim
|
||||
end
|
||||
|
||||
local MoveThink = function(anim, panel, frac)
|
||||
if not anim.startx then
|
||||
anim.startx = panel.x
|
||||
anim.starty = panel.y
|
||||
end
|
||||
|
||||
local x = Lerp(frac, anim.startx, anim.x)
|
||||
local y = Lerp(frac, anim.starty, anim.y)
|
||||
panel:SetPos(x, y)
|
||||
end
|
||||
|
||||
function LibClasses:MoveTo(x, y, length, delay, ease, callback)
|
||||
if self.x == x and self.y == y then return end
|
||||
|
||||
local anim = self:NewAnimation(length, delay, ease, callback)
|
||||
anim.x = x
|
||||
anim.y = y
|
||||
anim.Think = MoveThink
|
||||
end
|
||||
|
||||
local SetSize = Panel.SetSize
|
||||
local SizeThink = function(anim, panel, frac)
|
||||
if not anim.startw then
|
||||
anim.startw, anim.starth = panel:GetSize()
|
||||
end
|
||||
|
||||
local w, h
|
||||
if anim.sizew and anim.sizeh then
|
||||
w = Lerp(frac, anim.startw, anim.w)
|
||||
h = Lerp(frac, anim.starth, anim.h)
|
||||
SetSize(panel, w, h)
|
||||
elseif anim.sizew then
|
||||
w = Lerp(frac, anim.startw, anim.w)
|
||||
SetSize(panel, w, panel.starth)
|
||||
else
|
||||
h = Lerp(frac, anim.starth, anim.h)
|
||||
SetSize(panel, panel.startw, h)
|
||||
end
|
||||
|
||||
if panel:GetDock() > 0 then
|
||||
panel:InvalidateParent()
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:SizeTo(w, h, length, delay, ease, callback)
|
||||
local anim = self:NewAnimation(length, delay, ease, callback)
|
||||
|
||||
if w ~= -1 then
|
||||
anim.sizew = true
|
||||
end
|
||||
|
||||
if h ~= -1 then
|
||||
anim.sizeh = true
|
||||
end
|
||||
|
||||
anim.w, anim.h = w, h
|
||||
anim.Think = SizeThink
|
||||
|
||||
return anim
|
||||
end
|
||||
|
||||
local SetVisible = Panel.SetVisible
|
||||
local IsVisible = Panel.IsVisible
|
||||
|
||||
local is_visible = function(s)
|
||||
local state = s.visible_state
|
||||
if state ~= nil then
|
||||
return state
|
||||
else
|
||||
return IsVisible(s)
|
||||
end
|
||||
end
|
||||
|
||||
function LibClasses:AnimatedSetVisible(visible, cb)
|
||||
if visible == is_visible(self) then
|
||||
if cb then
|
||||
cb()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if visible then
|
||||
SetVisible(self, true)
|
||||
end
|
||||
|
||||
self.visible_state = visible
|
||||
self:Stop()
|
||||
|
||||
self:AlphaTo(visible and 255 or 0, 0.2, 0, function()
|
||||
SetVisible(self, visible)
|
||||
self:InvalidateParent(true)
|
||||
if cb then
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
|
||||
self:InvalidateParent(true)
|
||||
end
|
||||
|
||||
function LibClasses:AnimatedToggleVisible()
|
||||
self:AnimatedSetVisible(not is_visible(self))
|
||||
end
|
||||
|
||||
function LibClasses:AnimatedIsVisible()
|
||||
return is_visible(self)
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:SUI_TDLib()
|
||||
for k, v in pairs(LibClasses) do
|
||||
self[k] = v
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
TDLibUtil.Install = Panel.SUI_TDLib
|
||||
|
||||
local count = 0
|
||||
TDLibUtil.Start = function()
|
||||
count = count + 1
|
||||
for k, v in pairs(LibClasses) do
|
||||
if not Panel["SUI_OLD" .. k] then
|
||||
local old = Panel[k]
|
||||
if old == nil then
|
||||
old = v
|
||||
end
|
||||
Panel[k], Panel["SUI_OLD" .. k] = v, old
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TDLibUtil.End = function()
|
||||
count = count - 1
|
||||
if count > 0 then return end
|
||||
for k, v in pairs(LibClasses) do
|
||||
local old = Panel["SUI_OLD" .. k]
|
||||
if old == v then
|
||||
Panel[k] = nil
|
||||
else
|
||||
Panel[k] = old
|
||||
end
|
||||
Panel["SUI_OLD" .. k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
TDLibUtil.HoverFunc = function(p)
|
||||
return p:IsHovered() and not p:GetDisabled()
|
||||
end
|
||||
|
||||
TDLibUtil.DrawOutlinedBox = function(radius, x, y, w, h, bg, outline, thickness)
|
||||
thickness = thickness or 2
|
||||
draw.RoundedBox(radius, x, y, w, h, outline)
|
||||
draw.RoundedBox(radius, x + thickness, y + thickness, w - (thickness * 2), h - (thickness * 2), bg)
|
||||
end
|
||||
|
||||
do
|
||||
local cos, sin, sqrt = math.cos, math.sin, math.sqrt
|
||||
local clamp, floor = math.Clamp, math.floor
|
||||
local min, max = math.min, math.max
|
||||
|
||||
local calc_ellipse_points = function(rx, ry)
|
||||
local points = sqrt(((rx * ry) / 2) * 6)
|
||||
return max(points, 8)
|
||||
end
|
||||
|
||||
local M_PI = 3.14159265358979323846
|
||||
calc_rect = function(c, r, x, y, w, h)
|
||||
if
|
||||
(c.r == r) and
|
||||
(c.x == x and c.y == y) and
|
||||
(c.w == w and c.h == h)
|
||||
then return end
|
||||
|
||||
r = clamp(r, 0, min(w, h) / 2)
|
||||
|
||||
local rx, ry = r, r
|
||||
if w >= 0.02 then
|
||||
rx = min(rx, w / 2.0 - 0.01)
|
||||
end
|
||||
if h >= 0.02 then
|
||||
ry = min(ry, h / 2.0 - 0.01)
|
||||
end
|
||||
|
||||
local points = max(calc_ellipse_points(rx, ry) / 4, 1)
|
||||
points = floor(points)
|
||||
|
||||
local half_pi = M_PI / 2
|
||||
local angle_shift = half_pi / (points + 1)
|
||||
|
||||
local phi = 0
|
||||
for i = 1, points + 2 do
|
||||
c[i] = {
|
||||
x = x + rx * (1 - cos(phi)),
|
||||
y = y + ry * (1 - sin(phi))
|
||||
}
|
||||
phi = phi + angle_shift
|
||||
end
|
||||
|
||||
phi = half_pi
|
||||
for i = points + 3, 2 * (points + 2) do
|
||||
c[i] = {
|
||||
x = x + w - rx * (1 + cos(phi)),
|
||||
y = y + ry * (1 - sin(phi))
|
||||
}
|
||||
phi = phi + angle_shift
|
||||
end
|
||||
|
||||
phi = 2 * half_pi
|
||||
for i = (2 * (points + 2)) + 1, 3 * (points + 2) do
|
||||
c[i] = {
|
||||
x = x + w - rx * (1 + cos(phi)),
|
||||
y = y + h - ry * (1 + sin(phi))
|
||||
}
|
||||
phi = phi + angle_shift
|
||||
end
|
||||
|
||||
phi = 3 * half_pi
|
||||
for i = (3 * (points + 2)) + 1, 4 * (points + 2) do
|
||||
c[i] = {
|
||||
x = x + rx * (1 - cos(phi)),
|
||||
y = y + h - ry * (1 + sin(phi))
|
||||
}
|
||||
phi = phi + angle_shift
|
||||
end
|
||||
|
||||
local last = (points + 2) * 4 + 1
|
||||
c[last] = c[1]
|
||||
|
||||
for i = last + 1, #c do
|
||||
c[i] = nil
|
||||
end
|
||||
|
||||
c.r = r
|
||||
c.x, c.y = x, y
|
||||
c.w, c.h = w, h
|
||||
end
|
||||
|
||||
TDLibUtil.RoundedBox = function(c, r, x, y, w, h, color)
|
||||
calc_rect(c, r, x, y, w, h)
|
||||
|
||||
SetDrawColor(color)
|
||||
NoTexture()
|
||||
DrawPoly(c)
|
||||
end
|
||||
end
|
||||
|
||||
TDLibUtil.LibClasses = LibClasses
|
||||
|
||||
sui.TDLib = TDLibUtil
|
||||
92
lua/sui/libs/types.lua
Normal file
92
lua/sui/libs/types.lua
Normal file
@@ -0,0 +1,92 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- https://gist.github.com/CapsAdmin/0d9c1e77d0fc22d910e182bfeb9812e5
|
||||
local getmetatable = getmetatable
|
||||
|
||||
do
|
||||
local types = {
|
||||
["string"] = "",
|
||||
["boolean"] = true,
|
||||
["number"] = 0,
|
||||
["function"] = function() end,
|
||||
["thread"] = coroutine.create(getmetatable),
|
||||
["Color"] = Color(0, 0, 0),
|
||||
}
|
||||
|
||||
for k, v in pairs(types) do
|
||||
if not getmetatable(v) then
|
||||
debug.setmetatable(v, {MetaName = k})
|
||||
else
|
||||
getmetatable(v).MetaName = k
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sui.type(value)
|
||||
if value == nil then
|
||||
return "nil"
|
||||
end
|
||||
local meta = getmetatable(value)
|
||||
if meta then
|
||||
meta = meta.MetaName
|
||||
if meta then
|
||||
return meta
|
||||
end
|
||||
end
|
||||
return "table"
|
||||
end
|
||||
|
||||
do
|
||||
local function add(name)
|
||||
local new_name = name
|
||||
if name == "bool" then
|
||||
new_name = "boolean"
|
||||
end
|
||||
sui["is" .. name:lower()] = function(value)
|
||||
local meta = getmetatable(value)
|
||||
if meta and meta.MetaName == new_name then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add("string")
|
||||
add("number")
|
||||
add("bool")
|
||||
add("function")
|
||||
|
||||
add("Angle")
|
||||
add("Vector")
|
||||
add("Panel")
|
||||
add("Matrix")
|
||||
end
|
||||
|
||||
function sui.isentity(value)
|
||||
local meta = getmetatable(value)
|
||||
if meta then
|
||||
if meta.MetaName == "Entity" then
|
||||
return true
|
||||
end
|
||||
meta = meta.MetaBaseClass
|
||||
if meta then
|
||||
return meta.MetaName == "Entity"
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
sui.IsEntity = sui.isentity
|
||||
|
||||
local type = sui.type
|
||||
function sui.istable(value)
|
||||
return type(value) == "table"
|
||||
end
|
||||
192
lua/sui/vgui/sam_player_line.lua
Normal file
192
lua/sui/vgui/sam_player_line.lua
Normal file
@@ -0,0 +1,192 @@
|
||||
--[[
|
||||
| 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, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local TDLib = sui.TDLib
|
||||
local draw_material = sui.draw_material
|
||||
local lerp_color = sui.lerp_color
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local RoundedBox = TDLib.RoundedBox
|
||||
local CircleAvatar = TDLib.LibClasses.CircleAvatar
|
||||
local CircleClick2 = TDLib.LibClasses.CircleClick2
|
||||
|
||||
local PLAYER_LINE_NAME = SUI.CreateFont("PlayerLineName", "Roboto Bold", 17)
|
||||
local PLAYER_LINE_RANK = SUI.CreateFont("PlayerLineRank", "Roboto Bold", 13)
|
||||
local PLAYER_LINE_STEAMID = SUI.CreateFont("PlayerLineSteamID", "Roboto Medium", 12)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
local size = SUI.Scale(34)
|
||||
|
||||
self:Dock(TOP)
|
||||
self:SetTall(size)
|
||||
|
||||
self.size = size
|
||||
end
|
||||
|
||||
local rank_Paint = function(s, w, h)
|
||||
RoundedBox(s.rect, SUI.Scale(10), 0, 0, w, h, s.col)
|
||||
end
|
||||
|
||||
function PANEL:SetInfo(info)
|
||||
local size = self.size
|
||||
|
||||
local container
|
||||
do
|
||||
local w = SUI.Scale(280) + size
|
||||
|
||||
local _container = self:Add("Panel")
|
||||
_container:Dock(LEFT)
|
||||
_container:SetMouseInputEnabled(false)
|
||||
_container:SetWide(w)
|
||||
|
||||
container = _container:Add("Panel")
|
||||
container:SetSize(w, size)
|
||||
|
||||
function _container:PerformLayout()
|
||||
container:Center()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local avatar = container:Add("Panel")
|
||||
avatar:Dock(LEFT)
|
||||
avatar:DockMargin(0, 0, 5, 0)
|
||||
avatar:SetWide(size)
|
||||
avatar:SetMouseInputEnabled(false)
|
||||
CircleAvatar(avatar)
|
||||
|
||||
avatar:SetSteamID(util.SteamIDTo64(info.steamid), size)
|
||||
end
|
||||
|
||||
do
|
||||
local top_container = container:Add("Panel")
|
||||
top_container:Dock(TOP)
|
||||
top_container:DockMargin(0, 0, 0, 2)
|
||||
|
||||
local name = top_container:Add("SAM.Label")
|
||||
name:Dock(LEFT)
|
||||
name:SetFont(PLAYER_LINE_NAME)
|
||||
self.name = name
|
||||
|
||||
local pname = info.name
|
||||
if not pname or pname == "" then
|
||||
name:SetTextColor(GetColor("player_list_names_2"))
|
||||
self:SetName("N/A")
|
||||
else
|
||||
name:SetTextColor(GetColor("player_list_names"))
|
||||
self:SetName(pname)
|
||||
end
|
||||
|
||||
if info.rank then
|
||||
local rank_bg = top_container:Add("Panel")
|
||||
rank_bg:Dock(LEFT)
|
||||
rank_bg:DockMargin(5, 0, 0, 0)
|
||||
|
||||
rank_bg.rect = {}
|
||||
rank_bg.col = info.rank_bg or GetColor("player_list_rank")
|
||||
rank_bg.Paint = rank_Paint
|
||||
|
||||
local rank = rank_bg:Add("SAM.Label")
|
||||
rank:Dock(FILL)
|
||||
rank:DockMargin(SUI.Scale(8), 0, 0, 0)
|
||||
rank:SetTextColor(GetColor("player_list_rank_text"))
|
||||
rank:SetFont(PLAYER_LINE_RANK)
|
||||
rank.bg = rank_bg
|
||||
|
||||
self.rank = rank
|
||||
self:SetRank(info.rank)
|
||||
|
||||
rank_bg:SetSize(rank:GetTextSize() + SUI.Scale(8) * 2)
|
||||
end
|
||||
|
||||
top_container:SizeToChildren(true, true)
|
||||
end
|
||||
|
||||
local steamid = container:Add("SAM.Label")
|
||||
steamid:Dock(TOP)
|
||||
steamid:SetTextColor(GetColor("player_list_steamid"))
|
||||
steamid:SetFont(PLAYER_LINE_STEAMID)
|
||||
steamid:SetText(info.steamid)
|
||||
steamid:SizeToContents()
|
||||
steamid:SetAutoStretchVertical(true)
|
||||
|
||||
self.container = container
|
||||
end
|
||||
|
||||
function PANEL:SetName(new_name)
|
||||
local name = self.name
|
||||
name:SetText(new_name)
|
||||
name:SizeToContents()
|
||||
if name:GetWide() > 160 then
|
||||
name:SetWide(158)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetRank(new_rank)
|
||||
local rank = self.rank
|
||||
rank:SetText(new_rank)
|
||||
rank:SizeToContents()
|
||||
rank.bg:SetSize(rank:GetTextSize() + SUI.Scale(8) * 2)
|
||||
end
|
||||
|
||||
function PANEL:Actions()
|
||||
local container
|
||||
do
|
||||
local size = self.size
|
||||
|
||||
local _container = self:Add("Panel")
|
||||
_container:Dock(RIGHT)
|
||||
_container:SetWide(size)
|
||||
|
||||
container = _container:Add("Panel")
|
||||
container:SetSize(size, size)
|
||||
|
||||
function _container:PerformLayout()
|
||||
container:Center()
|
||||
end
|
||||
end
|
||||
|
||||
local actions_button = container:Add("SAM.Button")
|
||||
actions_button:SetText("")
|
||||
actions_button:ClearPaint()
|
||||
|
||||
function container:PerformLayout(w, h)
|
||||
actions_button:SetSize(h, h)
|
||||
actions_button:Center()
|
||||
end
|
||||
|
||||
local image = actions_button:Add("SAM.Image")
|
||||
image:Dock(FILL)
|
||||
image:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/dots_verticle.png")
|
||||
|
||||
local current_icon_color = Color(GetColor("actions_button_icon"):Unpack())
|
||||
function image:Draw(w, h)
|
||||
if not h then return end
|
||||
|
||||
if actions_button.Hovered then
|
||||
lerp_color(current_icon_color, GetColor("actions_button_icon_hover"))
|
||||
else
|
||||
lerp_color(current_icon_color, GetColor("actions_button_icon"))
|
||||
end
|
||||
|
||||
draw_material(nil, w / 2, h / 2, SUI.ScaleEven(20), current_icon_color)
|
||||
end
|
||||
|
||||
CircleClick2(actions_button, Color(62, 62, 62), 10)
|
||||
actions_button:Center()
|
||||
|
||||
return actions_button
|
||||
end
|
||||
|
||||
sui.register("PlayerLine", PANEL, "Panel")
|
||||
76
lua/sui/vgui/sui_combobox.lua
Normal file
76
lua/sui/vgui/sui_combobox.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
--[[
|
||||
| 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, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local TEXT_FONT = SUI.CreateFont("ComboBox", "Roboto Regular", 16)
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local draw_material = sui.draw_material
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
PANEL.NoOverrideClear = true
|
||||
|
||||
sui.scaling_functions(PANEL)
|
||||
|
||||
function PANEL:Init()
|
||||
self:ScaleInit()
|
||||
self.DropButton:Remove()
|
||||
self:SetFont(TEXT_FONT)
|
||||
self:SetSize(34, 22)
|
||||
self:SetIsMenu(true)
|
||||
|
||||
local image = self:Add(NAME .. ".Image")
|
||||
image:Dock(FILL)
|
||||
image:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sui/arrow.png")
|
||||
image.Draw = self.Paint
|
||||
end
|
||||
|
||||
function PANEL:OpenMenu(pControlOpener)
|
||||
if pControlOpener and pControlOpener == self.TextEntry then return end
|
||||
if #self.Choices == 0 then return end
|
||||
|
||||
if IsValid(self.Menu) then
|
||||
self.Menu:Remove()
|
||||
self.Menu = nil
|
||||
end
|
||||
|
||||
self.Menu = vgui.Create(NAME .. ".Menu", self)
|
||||
self.Menu:SetInternal(self)
|
||||
|
||||
for k, v in ipairs(self.Choices) do
|
||||
self.Menu:AddOption(v, function()
|
||||
self:ChooseOption(v, k)
|
||||
end)
|
||||
end
|
||||
|
||||
local x, y = self:LocalToScreen(0, self:GetTall())
|
||||
self.Menu:SetMinimumWidth(self:GetWide())
|
||||
self.Menu:Open(x, y, false, self)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h, from_image)
|
||||
local text_color = GetColor("menu_option_hover_text")
|
||||
|
||||
if from_image then
|
||||
local size = SUI.ScaleEven(10)
|
||||
draw_material(nil, w - (size / 2) - 6, h / 2, size, text_color)
|
||||
else
|
||||
local col = GetColor("menu")
|
||||
self:RoundedBox("Background", 4, 0, 0, w, h, col)
|
||||
self:SetTextColor(text_color)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
end
|
||||
|
||||
sui.register("ComboBox", PANEL, "DComboBox")
|
||||
326
lua/sui/vgui/sui_frame.lua
Normal file
326
lua/sui/vgui/sui_frame.lua
Normal file
@@ -0,0 +1,326 @@
|
||||
--[[
|
||||
| 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 math = math
|
||||
local gui = gui
|
||||
local draw = draw
|
||||
local surface = surface
|
||||
|
||||
local ScrW = ScrW
|
||||
local ScrH = ScrH
|
||||
local IsValid = IsValid
|
||||
local ipairs = ipairs
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
local TDLib = sui.TDLib
|
||||
|
||||
local FRAME_FONT = SUI.CreateFont("Frame", "Roboto", 18)
|
||||
|
||||
local Panel = FindMetaTable("Panel")
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "m_bHeaderHeight", "HeaderHeight", FORCE_NUMBER)
|
||||
AccessorFunc(PANEL, "m_bTitleFont", "TitleFont", FORCE_STRING)
|
||||
AccessorFunc(PANEL, "m_bSizable", "Sizable", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_iMinWidth", "MinWidth", FORCE_NUMBER)
|
||||
AccessorFunc(PANEL, "m_iMinHeight", "MinHeight", FORCE_NUMBER)
|
||||
|
||||
local header_Think = function(s)
|
||||
local parent = s.parent
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
|
||||
if s.dragging then
|
||||
local x, y = input.GetCursorPos()
|
||||
x, y = math.Clamp(x, 1, sw - 1), math.Clamp(y, 1, sh - 1)
|
||||
x, y = x - s.dragging[1], y - s.dragging[2]
|
||||
|
||||
parent:SetPos(x, y)
|
||||
parent:InvalidateLayout(true)
|
||||
parent:OnPosChanged()
|
||||
else
|
||||
local x, y, w, h = parent:GetBounds()
|
||||
parent:SetPos(math.Clamp(x, 5, sw - w - 5), math.Clamp(y, 5, sh - h - 5))
|
||||
end
|
||||
end
|
||||
|
||||
local header_OnMousePressed = function(s)
|
||||
local parent = s.parent
|
||||
s.dragging = {gui.MouseX() - parent.x, gui.MouseY() - parent.y}
|
||||
s:MouseCapture(true)
|
||||
end
|
||||
|
||||
local header_OnMouseReleased = function(s)
|
||||
s.dragging = nil
|
||||
s:MouseCapture(false)
|
||||
end
|
||||
|
||||
local title_SetBGColor = function(s, c)
|
||||
s:SetVisible(c and true or false)
|
||||
end
|
||||
|
||||
local title_update_color = function(s)
|
||||
s:SetTextColor(SUI.GetColor("title"))
|
||||
end
|
||||
|
||||
local close_DoClick = function(s)
|
||||
s.frame:Remove()
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
local header_buttons = {}
|
||||
self.header_buttons = header_buttons
|
||||
|
||||
self:Center()
|
||||
self:SetHeaderHeight(28)
|
||||
|
||||
local header = self:Add("PANEL")
|
||||
header:Dock(TOP)
|
||||
header.Paint = self.HeaderPaint
|
||||
header:SetCursor("sizeall")
|
||||
|
||||
header.parent = self
|
||||
header.Think = header_Think
|
||||
header.OnMousePressed = header_OnMousePressed
|
||||
header.OnMouseReleased = header_OnMouseReleased
|
||||
self.header = header
|
||||
|
||||
local title = header:Add(NAME .. ".Label")
|
||||
title:Dock(LEFT)
|
||||
title:DockMargin(6, 2, 0, 2)
|
||||
title:SetText("")
|
||||
title:SetTextColor(SUI.GetColor("title"))
|
||||
title:SizeToContents()
|
||||
title.SetBGColor = title_SetBGColor
|
||||
hook.Add(NAME .. ".ThemeChanged", title, title_update_color)
|
||||
self.title = title
|
||||
|
||||
self.close = self:AddHeaderButton("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sui/close.png", "close", close_DoClick)
|
||||
self.close.frame = self
|
||||
|
||||
self:SetSize(SUI.Scale(520), SUI.Scale(364))
|
||||
self:SetTitleFont(FRAME_FONT)
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
|
||||
function self:PerformLayout(w, h)
|
||||
if IsValid(title) then
|
||||
title:SizeToContents()
|
||||
end
|
||||
|
||||
if IsValid(header) then
|
||||
header:SetTall(SUI.Scale(self:GetHeaderHeight()))
|
||||
end
|
||||
|
||||
for k, v in ipairs(header_buttons) do
|
||||
if IsValid(v) then
|
||||
v:SetWide(v:GetTall())
|
||||
local margin = SUI.Scale(4)
|
||||
v.image:DockMargin(margin, margin, margin, margin)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetSize(w, h)
|
||||
self.real_w, self.real_h = w, h
|
||||
self:ScaleChanged()
|
||||
end
|
||||
|
||||
function PANEL:HeaderPaint(w, h)
|
||||
draw.RoundedBoxEx(3, 0, 0, w, h, SUI.GetColor("header"), true, true)
|
||||
end
|
||||
|
||||
local SetSize = Panel.SetSize
|
||||
PANEL.RealSetSize = SetSize
|
||||
function PANEL:ScaleChanged()
|
||||
if self.sizing then return end
|
||||
|
||||
local new_w, new_h = SUI.Scale(self.real_w), SUI.Scale(self.real_h)
|
||||
self.x, self.y = self.x + (self:GetWide() / 2 - new_w / 2), self.y + (self:GetTall() / 2 - new_h / 2)
|
||||
SetSize(self, new_w, new_h)
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if SUI.GetColor("frame_blur") then
|
||||
TDLib.BlurPanel(self)
|
||||
end
|
||||
|
||||
self:RoundedBox("Background", 3, 0, 0, w, h, SUI.GetColor("frame"))
|
||||
end
|
||||
|
||||
function PANEL:SetTitleFont(font)
|
||||
self.m_bTitleFont = font
|
||||
self.title:SetFont(font)
|
||||
end
|
||||
|
||||
function PANEL:SetTitle(text)
|
||||
self.title:SetText(text)
|
||||
self.title:SizeToContents()
|
||||
end
|
||||
|
||||
function PANEL:AddHeaderButton(image_name, name, callback)
|
||||
local button = self.header:Add("DButton")
|
||||
button:SetText("")
|
||||
button:Dock(RIGHT)
|
||||
button:DockMargin(0, 2, #self.header:GetChildren() == 1 and 4 or 2, 2)
|
||||
|
||||
local hover = name .. "_hover"
|
||||
local press = name .. "_press"
|
||||
local circle = {}
|
||||
button.Paint = function(s, w, h)
|
||||
if s:IsHovered() then
|
||||
TDLib.DrawCircle(circle, w / 2, h / 2, w / 2, SUI.GetColor(hover))
|
||||
end
|
||||
|
||||
if s.Depressed then
|
||||
TDLib.DrawCircle(circle, w / 2, h / 2, w / 2, SUI.GetColor(press))
|
||||
end
|
||||
end
|
||||
button.DoClick = callback
|
||||
|
||||
local image = button:Add(NAME .. ".Image")
|
||||
image:Dock(FILL)
|
||||
image:SetMouseInputEnabled(false)
|
||||
image:SetImage(image_name)
|
||||
|
||||
button.image = image
|
||||
|
||||
table.insert(self.header_buttons, button)
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(_, checking)
|
||||
if not self.m_bSizable then return end
|
||||
|
||||
local x, y = self:LocalToScreen(0, 0)
|
||||
local w, h = self:GetSize()
|
||||
if gui.MouseX() > (x + w - 20) and gui.MouseY() > (y + h - 20) then
|
||||
if not checking then
|
||||
self.sizing = {gui.MouseX() - w, gui.MouseY() - h}
|
||||
self:MouseCapture(true)
|
||||
end
|
||||
|
||||
self:SetCursor("sizenwse")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if checking then
|
||||
self:SetCursor("arrow")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased()
|
||||
if not self.m_bSizable then return end
|
||||
|
||||
self:MouseCapture(false)
|
||||
SUI.CallScaleChanged()
|
||||
self.sizing = nil
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
if not self.m_bSizable then return end
|
||||
|
||||
self:OnMousePressed(nil, true)
|
||||
|
||||
if not self.sizing then return end
|
||||
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
|
||||
local cx, cy = input.GetCursorPos()
|
||||
local mousex = math.Clamp(cx, 1, sw - 1)
|
||||
local mousey = math.Clamp(cy, 1, sh - 1)
|
||||
|
||||
local x = mousex - self.sizing[1]
|
||||
x = math.Clamp(x, self.m_iMinWidth, sw - 10)
|
||||
|
||||
local y = mousey - self.sizing[2]
|
||||
y = math.Clamp(y, self.m_iMinHeight, sh - 10)
|
||||
|
||||
self.real_w, self.real_h = x, y
|
||||
SetSize(self, x, y)
|
||||
self:InvalidateLayout(true)
|
||||
self:SetCursor("sizenwse")
|
||||
end
|
||||
|
||||
function PANEL:OnPosChanged()
|
||||
end
|
||||
|
||||
local SetVisible = Panel.SetVisible
|
||||
local Remove = Panel.Remove
|
||||
|
||||
local anim_speed = 0.2
|
||||
|
||||
local show = function(s)
|
||||
local w, h = s.real_w, s.real_h
|
||||
|
||||
if s.anim_scale then
|
||||
w, h = SUI.Scale(w), SUI.Scale(h)
|
||||
end
|
||||
|
||||
SetVisible(s, true)
|
||||
|
||||
SetSize(s, w * 1.1, h * 1.1)
|
||||
s:Center()
|
||||
|
||||
s:Stop()
|
||||
s:SizeTo(w, h, anim_speed, 0, -1)
|
||||
s:MoveTo((ScrW() / 2) - (w / 2), (ScrH() / 2) - (h / 2), anim_speed, 0, -1)
|
||||
s:AlphaTo(255, anim_speed + 0.02, 0)
|
||||
s:MakePopup()
|
||||
end
|
||||
|
||||
local remove = function(s, hide)
|
||||
if not hide and not s:IsVisible() then
|
||||
Remove(s)
|
||||
return
|
||||
end
|
||||
|
||||
local w, h = s.real_w, s.real_h
|
||||
|
||||
if s.anim_scale then
|
||||
w, h = SUI.Scale(w), SUI.Scale(h)
|
||||
end
|
||||
|
||||
w, h = w * 1.1, h * 1.1
|
||||
|
||||
s:Stop()
|
||||
s:SizeTo(w, h, anim_speed, 0, -1)
|
||||
s:MoveTo((ScrW() / 2) - (w / 2), (ScrH() / 2) - (h / 2), anim_speed, 0, -1)
|
||||
s:SetMouseInputEnabled(false)
|
||||
s:SetKeyboardInputEnabled(false)
|
||||
s:AlphaTo(0, anim_speed + 0.02, 0, function()
|
||||
if hide then
|
||||
SetVisible(s, false)
|
||||
else
|
||||
Remove(s)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local hide = function(s)
|
||||
remove(s, true)
|
||||
end
|
||||
|
||||
function PANEL:AddAnimations(w, h, no_scale)
|
||||
self.anim_scale = not no_scale
|
||||
self.real_w, self.real_h = w, h
|
||||
|
||||
self:SetAlpha(0)
|
||||
show(self)
|
||||
|
||||
self.Remove = remove
|
||||
self.Hide = hide
|
||||
self.Show = show
|
||||
end
|
||||
|
||||
sui.register("Frame", PANEL, "EditablePanel")
|
||||
334
lua/sui/vgui/sui_image.lua
Normal file
334
lua/sui/vgui/sui_image.lua
Normal file
@@ -0,0 +1,334 @@
|
||||
--[[
|
||||
| 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 table = table
|
||||
local file = file
|
||||
local coroutine = coroutine
|
||||
local surface = surface
|
||||
|
||||
local UnPredictedCurTime = UnPredictedCurTime
|
||||
local pairs = pairs
|
||||
|
||||
local color_white = color_white
|
||||
|
||||
local sui = sui
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local read_gif = include("sui/libs/gif_loader.lua")
|
||||
local generate_png = include("sui/libs/png_encoder.lua")
|
||||
|
||||
local images_path = (NAME .. "/images/"):lower()
|
||||
file.CreateDir(images_path)
|
||||
|
||||
local get_image_path = function(url)
|
||||
return images_path .. (url:gsub("%W", "_") .. ".png")
|
||||
end
|
||||
|
||||
local Write = file.Write
|
||||
local gif_to_png; do
|
||||
local internal_gif_to_png = function(file_path, chunk)
|
||||
local gif = read_gif(chunk)
|
||||
local frames = gif:get_frames()
|
||||
local w, h = gif.width, gif.height
|
||||
|
||||
local path = file_path .. "/"
|
||||
file.CreateDir(path)
|
||||
|
||||
for frame_id = 1, #frames do
|
||||
local frame = frames[frame_id]
|
||||
local data = frame.data
|
||||
local png = generate_png(w, h, data)
|
||||
Write(("%s%d_%d.png"):format(path, frame_id, frame.delay), png)
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
local delay = 0.01
|
||||
local next_run = 0
|
||||
|
||||
local coroutines = {}
|
||||
local callbacks = {}
|
||||
gif_to_png = function(file_path, data, callback)
|
||||
local co = coroutine.create(internal_gif_to_png)
|
||||
local i = table.insert(coroutines, co)
|
||||
callbacks[i] = callback
|
||||
coroutine.resume(co, file_path, data)
|
||||
next_run = UnPredictedCurTime()
|
||||
end
|
||||
|
||||
hook.Add("Think", NAME .. "ProcessGIFs", function()
|
||||
local co = coroutines[1]
|
||||
if not co then return end
|
||||
if UnPredictedCurTime() < next_run then return end
|
||||
|
||||
if coroutine.status(co) == "suspended" then
|
||||
coroutine.resume(co)
|
||||
else
|
||||
callbacks[1]()
|
||||
table.remove(coroutines, 1)
|
||||
table.remove(callbacks, 1)
|
||||
end
|
||||
|
||||
next_run = UnPredictedCurTime() + delay
|
||||
end)
|
||||
|
||||
hook.Add(NAME .. "ImagesCleared", "ClearCoroutines", function()
|
||||
table.Empty(coroutines)
|
||||
table.Empty(callbacks)
|
||||
end)
|
||||
end
|
||||
|
||||
local download_image, is_downloading_image; do
|
||||
-- https://stackoverflow.com/questions/25959386/how-to-check-if-a-file-is-a-valid-image
|
||||
local valid_images = {
|
||||
["\xff\xd8\xff"] = "jpeg",
|
||||
["\x89PNG\r\n\x1a\n"] = "png",
|
||||
["GIF87a"] = "gif",
|
||||
["GIF89a"] = "gif",
|
||||
}
|
||||
|
||||
local get_image_type = function(data)
|
||||
for k, v in pairs(valid_images) do
|
||||
if data:StartWith(k) then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local downloading_images = {}
|
||||
|
||||
local process_callbacks = function(url)
|
||||
local callbacks = downloading_images[url] or {}
|
||||
downloading_images[url] = nil
|
||||
|
||||
for _, func in ipairs(callbacks) do
|
||||
func()
|
||||
end
|
||||
end
|
||||
|
||||
download_image = function(url, callback)
|
||||
if downloading_images[url] then
|
||||
table.insert(downloading_images[url], callback)
|
||||
return
|
||||
end
|
||||
|
||||
downloading_images[url] = {callback}
|
||||
|
||||
http.Fetch(url, function(data)
|
||||
local image_type = get_image_type(data)
|
||||
if not image_type then
|
||||
downloading_images[url] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local image_path = get_image_path(url)
|
||||
|
||||
if image_type == "gif" then
|
||||
gif_to_png(image_path, data, function()
|
||||
process_callbacks(url)
|
||||
end)
|
||||
else
|
||||
file.Write(image_path, data)
|
||||
process_callbacks(url)
|
||||
end
|
||||
end, function(err)
|
||||
print("(SUI) Failed to download an image, error: " .. err)
|
||||
downloading_images[url] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
is_downloading_image = function(url)
|
||||
return downloading_images[url] ~= nil
|
||||
end
|
||||
|
||||
hook.Add(NAME .. "ImagesCleared", "ClearDownloadingImages", function()
|
||||
table.Empty(downloading_images)
|
||||
end)
|
||||
end
|
||||
|
||||
local images_panels = {}
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
local err_mat = SUI.Material("error")
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetMouseInputEnabled(false)
|
||||
|
||||
self.minus = 0
|
||||
self.rotation = 0
|
||||
self.image = err_mat
|
||||
self.image_col = color_white
|
||||
|
||||
table.insert(images_panels, self)
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
for k, v in ipairs(images_panels) do
|
||||
if v == self then
|
||||
table.remove(images_panels, k)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetMinus(minus)
|
||||
self.minus = minus
|
||||
end
|
||||
|
||||
function PANEL:SetRotation(rotation)
|
||||
self.rotation = rotation
|
||||
end
|
||||
|
||||
function PANEL:SetImageColor(col)
|
||||
self.image_col = col
|
||||
end
|
||||
|
||||
local cached_files = {}
|
||||
local get_files = function(image_path)
|
||||
local f = cached_files[image_path]
|
||||
if f then return f end
|
||||
|
||||
cached_files[image_path] = file.Find(image_path .. "/*", "DATA")
|
||||
|
||||
return cached_files[image_path]
|
||||
end
|
||||
|
||||
function PANEL:SetImage(url)
|
||||
self.image = err_mat
|
||||
|
||||
self.pos = nil
|
||||
self.delay = nil
|
||||
|
||||
self.images = nil
|
||||
self.delays = nil
|
||||
self.url = url
|
||||
|
||||
if url:sub(1, 4) ~= "http" then
|
||||
self.image = SUI.Material(url)
|
||||
return
|
||||
end
|
||||
|
||||
local image_path = get_image_path(url)
|
||||
if not file.Exists(image_path, "DATA") or is_downloading_image(url) then
|
||||
download_image(url, function()
|
||||
if self:IsValid() then
|
||||
self:SetImage(url)
|
||||
end
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
local is_gif = file.IsDir(image_path, "DATA")
|
||||
if is_gif then
|
||||
local images = {}
|
||||
local delays = {}
|
||||
|
||||
local files = get_files(image_path)
|
||||
for i = 1, #files do
|
||||
local v = files[i]
|
||||
local id, delay = v:match("(.*)_(.*)%.png")
|
||||
id = tonumber(id)
|
||||
local img_path = "../data/" .. image_path .. "/" .. v
|
||||
images[id] = img_path
|
||||
delays[id] = delay
|
||||
end
|
||||
|
||||
self.frame = 1
|
||||
self.delay = (UnPredictedCurTime() * 100) + delays[1]
|
||||
|
||||
self.images = images
|
||||
self.delays = delays
|
||||
|
||||
self.max_images = #files
|
||||
else
|
||||
self.image = SUI.Material("../data/" .. image_path)
|
||||
end
|
||||
end
|
||||
|
||||
local SetMaterial = surface.SetMaterial
|
||||
function PANEL:PaintGIF(w, h, images)
|
||||
local frame = self.frame
|
||||
|
||||
-- SUI.Material() caches materials by default
|
||||
local mat = SUI.Material(images[frame], true)
|
||||
if not mat then
|
||||
if frame > 1 then
|
||||
mat = SUI.Material(images[frame - 1])
|
||||
else
|
||||
mat = err_mat
|
||||
end
|
||||
|
||||
SetMaterial(mat)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
SetMaterial(mat)
|
||||
|
||||
local curtime = UnPredictedCurTime() * 100
|
||||
if curtime < self.delay then return end
|
||||
frame = frame + 1
|
||||
if frame > self.max_images then
|
||||
frame = 1
|
||||
end
|
||||
|
||||
self.frame = frame
|
||||
self.delay = curtime + self.delays[frame]
|
||||
end
|
||||
|
||||
local PaintGIF = PANEL.PaintGIF
|
||||
local SetDrawColor = surface.SetDrawColor
|
||||
local DrawTexturedRectRotated = surface.DrawTexturedRectRotated
|
||||
function PANEL:Paint(w, h)
|
||||
SetDrawColor(self.image_col)
|
||||
|
||||
local images = self.images
|
||||
if images then
|
||||
PaintGIF(self, w, h, images)
|
||||
else
|
||||
SetMaterial(self.image)
|
||||
end
|
||||
|
||||
if self.Draw then
|
||||
self:Draw(w, h, true)
|
||||
else
|
||||
local minus = self.minus
|
||||
DrawTexturedRectRotated(w * 0.5, h * 0.5, w - minus, h - minus, self.rotation)
|
||||
end
|
||||
end
|
||||
|
||||
sui.register("Image", PANEL, "PANEL")
|
||||
|
||||
function SUI.ClearImages()
|
||||
local files, dirs = file.Find(images_path .. "/*", "DATA")
|
||||
for _, f in ipairs(files) do
|
||||
file.Delete(images_path .. f)
|
||||
end
|
||||
|
||||
for _, d in ipairs(dirs) do
|
||||
for _, f in ipairs(file.Find(images_path .. d .. "/*", "DATA")) do
|
||||
file.Delete(images_path .. (d .. "/" .. f))
|
||||
end
|
||||
file.Delete(images_path .. d)
|
||||
end
|
||||
|
||||
table.Empty(SUI.materials)
|
||||
table.Empty(cached_files)
|
||||
|
||||
hook.Call(NAME .. "ImagesCleared")
|
||||
|
||||
for k, v in ipairs(images_panels) do
|
||||
if v.url then
|
||||
v:SetImage(v.url)
|
||||
end
|
||||
end
|
||||
end
|
||||
220
lua/sui/vgui/sui_label.lua
Normal file
220
lua/sui/vgui/sui_label.lua
Normal file
@@ -0,0 +1,220 @@
|
||||
--[[
|
||||
| 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, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
local MOUSE_LEFT = MOUSE_LEFT
|
||||
|
||||
local SysTime = SysTime
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "m_colText", "TextColor")
|
||||
AccessorFunc(PANEL, "m_colTextStyle", "TextStyleColor")
|
||||
AccessorFunc(PANEL, "m_FontName", "Font")
|
||||
|
||||
AccessorFunc(PANEL, "m_bDoubleClicking", "DoubleClickingEnabled", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bAutoStretchVertical", "AutoStretchVertical", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bIsMenuComponent", "IsMenu", FORCE_BOOL)
|
||||
|
||||
AccessorFunc(PANEL, "m_bBackground", "PaintBackground", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bBackground", "DrawBackground", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bDisabled", "Disabled", FORCE_BOOL)
|
||||
|
||||
AccessorFunc(PANEL, "m_bIsToggle", "IsToggle", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bToggle", "Toggle", FORCE_BOOL)
|
||||
|
||||
AccessorFunc(PANEL, "m_bBright", "Bright", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bDark", "Dark", FORCE_BOOL)
|
||||
AccessorFunc(PANEL, "m_bHighlight", "Highlight", FORCE_BOOL)
|
||||
|
||||
PANEL:SetIsToggle(false)
|
||||
PANEL:SetToggle(false)
|
||||
PANEL:SetDisabled(false)
|
||||
PANEL:SetDoubleClickingEnabled(true)
|
||||
|
||||
local Panel = FindMetaTable("Panel")
|
||||
local SetMouseInputEnabled = Panel.SetMouseInputEnabled
|
||||
local SetPaintBackgroundEnabled = Panel.SetPaintBackgroundEnabled
|
||||
local SetPaintBorderEnabled = Panel.SetPaintBorderEnabled
|
||||
local InvalidateLayout = Panel.InvalidateLayout
|
||||
local SetFGColor = Panel.SetFGColor
|
||||
function PANEL:Init()
|
||||
SetMouseInputEnabled(self, false)
|
||||
SetPaintBackgroundEnabled(self, false)
|
||||
SetPaintBorderEnabled(self, false)
|
||||
end
|
||||
|
||||
function PANEL:AllowScale()
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
end
|
||||
|
||||
function PANEL:ScaleChanged()
|
||||
self:SizeToContents()
|
||||
end
|
||||
|
||||
function PANEL:SetFont(font)
|
||||
if self.m_FontName == font then return end
|
||||
|
||||
self.m_FontName = font
|
||||
self:SetFontInternal(self.m_FontName)
|
||||
end
|
||||
|
||||
function PANEL:SetTextColor(col)
|
||||
if self.m_colText == col then return end
|
||||
|
||||
self.m_colText = col
|
||||
SetFGColor(self, col.r, col.g, col.b, col.a)
|
||||
end
|
||||
PANEL.SetColor = PANEL.SetTextColor
|
||||
|
||||
function PANEL:GetColor()
|
||||
return self.m_colText or self.m_colTextStyle
|
||||
end
|
||||
|
||||
function PANEL:Toggle()
|
||||
if not self:GetIsToggle() then return end
|
||||
|
||||
self:SetToggle(not self:GetToggle())
|
||||
self:OnToggled(self:GetToggle())
|
||||
end
|
||||
|
||||
function PANEL:SetDisabled(bDisabled)
|
||||
self.m_bDisabled = bDisabled
|
||||
InvalidateLayout(self)
|
||||
end
|
||||
|
||||
function PANEL:SetEnabled(bEnabled)
|
||||
self:SetDisabled(not bEnabled)
|
||||
end
|
||||
|
||||
function PANEL:IsEnabled()
|
||||
return not self:GetDisabled()
|
||||
end
|
||||
|
||||
function PANEL:ApplySchemeSettings()
|
||||
local col = self:GetColor()
|
||||
if not col then return end
|
||||
|
||||
self:SetFGColor(col.r, col.g, col.b, col.a)
|
||||
end
|
||||
|
||||
function PANEL:AutoStretchVerticalThink()
|
||||
self:SizeToContentsY()
|
||||
end
|
||||
|
||||
function PANEL:SetAutoStretchVertical(enable)
|
||||
self.m_bAutoStretchVertical = enable
|
||||
self.Think = enable and self.AutoStretchVerticalThink or nil
|
||||
end
|
||||
|
||||
function PANEL:OnCursorEntered()
|
||||
InvalidateLayout(self, true)
|
||||
end
|
||||
|
||||
function PANEL:OnCursorExited()
|
||||
InvalidateLayout(self, true)
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(mousecode)
|
||||
if self:GetDisabled() then return end
|
||||
|
||||
if mousecode == MOUSE_LEFT and not dragndrop.IsDragging() and self.m_bDoubleClicking then
|
||||
if self.LastClickTime and SysTime() - self.LastClickTime < 0.2 then
|
||||
|
||||
self:DoDoubleClickInternal()
|
||||
self:DoDoubleClick()
|
||||
return
|
||||
end
|
||||
|
||||
self.LastClickTime = SysTime()
|
||||
end
|
||||
|
||||
if self:IsSelectable() and mousecode == MOUSE_LEFT and input.IsShiftDown() then
|
||||
return self:StartBoxSelection()
|
||||
end
|
||||
|
||||
self:MouseCapture(true)
|
||||
self.Depressed = true
|
||||
self:OnDepressed()
|
||||
InvalidateLayout(self, true)
|
||||
|
||||
self:DragMousePress(mousecode)
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(mousecode)
|
||||
self:MouseCapture(false)
|
||||
|
||||
if self:GetDisabled() then return end
|
||||
if not self.Depressed and dragndrop.m_DraggingMain ~= self then return end
|
||||
|
||||
if self.Depressed then
|
||||
self.Depressed = nil
|
||||
self:OnReleased()
|
||||
InvalidateLayout(self, true)
|
||||
end
|
||||
|
||||
if self:DragMouseRelease(mousecode) then return end
|
||||
|
||||
if self:IsSelectable() and mousecode == MOUSE_LEFT then
|
||||
local canvas = self:GetSelectionCanvas()
|
||||
if canvas then
|
||||
canvas:UnselectAll()
|
||||
end
|
||||
end
|
||||
|
||||
if not self.Hovered then return end
|
||||
|
||||
self.Depressed = true
|
||||
|
||||
if mousecode == MOUSE_RIGHT then
|
||||
self:DoRightClick()
|
||||
end
|
||||
|
||||
if mousecode == MOUSE_LEFT then
|
||||
self:DoClickInternal()
|
||||
self:DoClick()
|
||||
end
|
||||
|
||||
if mousecode == MOUSE_MIDDLE then
|
||||
self:DoMiddleClick()
|
||||
end
|
||||
|
||||
self.Depressed = nil
|
||||
end
|
||||
|
||||
function PANEL:OnReleased()
|
||||
end
|
||||
|
||||
function PANEL:OnDepressed()
|
||||
end
|
||||
|
||||
function PANEL:OnToggled(bool)
|
||||
end
|
||||
|
||||
function PANEL:DoClick()
|
||||
self:Toggle()
|
||||
end
|
||||
|
||||
function PANEL:DoRightClick()
|
||||
end
|
||||
|
||||
function PANEL:DoMiddleClick()
|
||||
end
|
||||
|
||||
function PANEL:DoClickInternal()
|
||||
end
|
||||
|
||||
function PANEL:DoDoubleClick()
|
||||
end
|
||||
|
||||
function PANEL:DoDoubleClickInternal()
|
||||
end
|
||||
|
||||
sui.register("Label", PANEL, "Label")
|
||||
81
lua/sui/vgui/sui_label_panel.lua
Normal file
81
lua/sui/vgui/sui_label_panel.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
--[[
|
||||
| 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, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local LABEL_FONT = SUI.CreateFont("LabelPanel", "Roboto", 18)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
local add = function(s, c)
|
||||
if IsValid(s.pnl) then
|
||||
s.pnl:Remove()
|
||||
end
|
||||
|
||||
local pnl = vgui.Create(c, s)
|
||||
s.pnl = pnl
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self.title = ""
|
||||
|
||||
local label = self:Add(NAME .. ".Label")
|
||||
label:Dock(LEFT)
|
||||
self.label = label
|
||||
|
||||
self:SetFont(LABEL_FONT)
|
||||
|
||||
self:Dock(TOP)
|
||||
self:InvalidateLayout(true)
|
||||
self.Add = add
|
||||
end
|
||||
|
||||
function PANEL:SetPanel(pnl)
|
||||
if IsValid(self.pnl) then
|
||||
self.pnl:Remove()
|
||||
end
|
||||
|
||||
pnl:SetParent(self)
|
||||
self.pnl = pnl
|
||||
end
|
||||
|
||||
function PANEL:SetLabel(lbl)
|
||||
self.title = lbl
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:SetFont(font)
|
||||
self.font = font
|
||||
self.label:SetFont(font)
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
local label = self.label
|
||||
local pnl = self.pnl
|
||||
|
||||
local pnl_w, pnl_h = 0, 0
|
||||
if pnl then
|
||||
pnl_w, pnl_h = pnl:GetSize()
|
||||
end
|
||||
|
||||
label:SetWide(w - pnl_w - 4)
|
||||
label:SetText(sui.wrap_text(self.title, self.font, w - pnl_w - 4))
|
||||
|
||||
local _, _h = label:GetTextSize()
|
||||
self:SetTall(math.max(_h, pnl_h))
|
||||
|
||||
if pnl then
|
||||
pnl:SetPos(w - pnl_w, h / 2 - pnl_h / 2)
|
||||
end
|
||||
end
|
||||
|
||||
sui.register("LabelPanel", PANEL, "PANEL")
|
||||
45
lua/sui/vgui/sui_number_slider.lua
Normal file
45
lua/sui/vgui/sui_number_slider.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
--[[
|
||||
| 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 surface = surface
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local NUMBER_SLIDER_FONT = SUI.CreateFont("NumberSlider", "Roboto Regular", 14)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
sui.scaling_functions(PANEL)
|
||||
|
||||
function PANEL:Init()
|
||||
self:ScaleInit()
|
||||
|
||||
local slider = vgui.Create(NAME .. ".Slider", self, "NumberSlider")
|
||||
slider:Dock(FILL)
|
||||
|
||||
self.slider = slider
|
||||
|
||||
local label = self:Add(NAME .. ".Label")
|
||||
label:Dock(RIGHT)
|
||||
label:DockMargin(3, 0, 0, 0)
|
||||
label:SetFont(NUMBER_SLIDER_FONT)
|
||||
self.label = label
|
||||
|
||||
function label:Think()
|
||||
self:SetText(slider:GetValue())
|
||||
|
||||
self:SizeToContents()
|
||||
end
|
||||
|
||||
self:SetSize(100, 12)
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
sui.register("NumberSlider", PANEL, "Panel")
|
||||
19
lua/sui/vgui/sui_panel.lua
Normal file
19
lua/sui/vgui/sui_panel.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
sui.scaling_functions(PANEL)
|
||||
|
||||
function PANEL:Init()
|
||||
self:ScaleInit()
|
||||
end
|
||||
|
||||
sui.register("Panel", PANEL, "Panel")
|
||||
171
lua/sui/vgui/sui_property_sheet.lua
Normal file
171
lua/sui/vgui/sui_property_sheet.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
--[[
|
||||
| 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 draw = draw
|
||||
local surface = surface
|
||||
local vgui = vgui
|
||||
|
||||
local TYPE_MATERIAL = TYPE_MATERIAL
|
||||
|
||||
local RealFrameTime = RealFrameTime
|
||||
local IsValid = IsValid
|
||||
local Lerp = Lerp
|
||||
local pairs = pairs
|
||||
local TypeID = TypeID
|
||||
|
||||
local TDLib_Classes = sui.TDLib.LibClasses
|
||||
local TextColor = TDLib_Classes.TextColor
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local PROPERTY_SHEET_FONT = SUI.CreateFont("PropertySheet", "Roboto Regular", 18)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "m_FontName", "Font", FORCE_STRING)
|
||||
|
||||
function PANEL:Init()
|
||||
self.tabs = {}
|
||||
|
||||
self:SetFont(PROPERTY_SHEET_FONT)
|
||||
|
||||
local tab_scroller = self:Add("DHorizontalScroller")
|
||||
tab_scroller:Dock(TOP)
|
||||
|
||||
self.tabs_tall = 26
|
||||
self.tab_scroller = tab_scroller
|
||||
|
||||
self:ScaleChanged()
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
end
|
||||
|
||||
function PANEL:ScaleChanged()
|
||||
self.tab_scroller:SetTall(SUI.Scale(self.tabs_tall))
|
||||
|
||||
for k, v in pairs(self.tab_scroller.Panels) do
|
||||
if v:IsValid() then
|
||||
if v.Material then
|
||||
v:SetWide(self.tab_scroller:GetTall())
|
||||
else
|
||||
v:SizeToContentsX()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
self:RoundedBox("Background", 1, 0, 0, w, self.tab_scroller:GetTall(), SUI.GetColor("property_sheet_bg"))
|
||||
end
|
||||
|
||||
function PANEL:PaintOver(w, h)
|
||||
local active_tab = self:GetActiveTab()
|
||||
if not IsValid(active_tab) then return end
|
||||
|
||||
local tab_scroller = self.tab_scroller
|
||||
local offset = tab_scroller:GetTall() - SUI.Scale(1)
|
||||
|
||||
local x = active_tab:LocalToScreen(0) - self:LocalToScreen(0)
|
||||
|
||||
if not self.activeTabX then
|
||||
self.activeTabX = x
|
||||
self.activeTabW = active_tab:GetWide()
|
||||
end
|
||||
|
||||
local delta = RealFrameTime() * 6
|
||||
if delta then
|
||||
self.activeTabX = Lerp(delta, self.activeTabX, x)
|
||||
self.activeTabW = Lerp(delta, self.activeTabW, active_tab:GetWide())
|
||||
end
|
||||
|
||||
self:RoundedBox("Background2", 1, self.activeTabX, tab_scroller.y + offset, self.activeTabW, SUI.Scale(1), SUI.GetColor("property_sheet_tab_active"))
|
||||
end
|
||||
|
||||
local tab_Paint = function(s, w, h)
|
||||
s.circle_click_color = SUI.GetColor("property_sheet_tab_click")
|
||||
if s.property_sheet:GetActiveTab() == s then
|
||||
TextColor(s, SUI.GetColor("property_sheet_tab_active"))
|
||||
else
|
||||
TextColor(s, SUI.GetColor("property_sheet_tab"))
|
||||
end
|
||||
end
|
||||
|
||||
local tab_DoClick = function(s)
|
||||
s.parent:SetActiveTab(s)
|
||||
end
|
||||
|
||||
local image_paint = function(s, w, h)
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(s.Material)
|
||||
surface.DrawTexturedRectRotated(w * 0.5, h * 0.5, w - 10, h - 10, 0)
|
||||
end
|
||||
|
||||
function PANEL:AddSheet(name, load_func)
|
||||
local tab = vgui.Create("DButton")
|
||||
if TypeID(name) == TYPE_MATERIAL then
|
||||
tab:SetText("")
|
||||
tab.Material = name
|
||||
tab.Paint = image_paint
|
||||
tab:SetWide(self.tab_scroller:GetTall())
|
||||
else
|
||||
tab:SetFont(self:GetFont())
|
||||
tab:SetText(name)
|
||||
tab:SetTextInset(10, 0)
|
||||
tab:SizeToContentsX()
|
||||
|
||||
tab.Paint = tab_Paint
|
||||
end
|
||||
|
||||
tab.parent = self
|
||||
tab.DoClick = tab_DoClick
|
||||
|
||||
tab.load_func = load_func
|
||||
tab.property_sheet = self
|
||||
|
||||
tab.On = TDLib_Classes.On
|
||||
TDLib_Classes.CircleClick(tab)
|
||||
|
||||
self.tab_scroller:AddPanel(tab)
|
||||
|
||||
if not self:GetActiveTab() then
|
||||
self:SetActiveTab(tab)
|
||||
end
|
||||
|
||||
table.insert(self.tabs, tab)
|
||||
|
||||
return tab
|
||||
end
|
||||
|
||||
function PANEL:GetActiveTab()
|
||||
return self.active_tab
|
||||
end
|
||||
|
||||
function PANEL:SetActiveTab(new_tab)
|
||||
if IsValid(new_tab) and not IsValid(new_tab.panel) then
|
||||
local panel = new_tab.load_func(self)
|
||||
panel:SetParent(self)
|
||||
panel:SetVisible(false)
|
||||
|
||||
panel.tab = new_tab
|
||||
new_tab.panel = panel
|
||||
end
|
||||
|
||||
if self.active_tab and IsValid(self.active_tab.panel) then
|
||||
self.active_tab.panel:SetVisible(false)
|
||||
end
|
||||
|
||||
if IsValid(new_tab) then
|
||||
new_tab.panel:SetVisible(true)
|
||||
end
|
||||
|
||||
self.active_tab = new_tab
|
||||
end
|
||||
|
||||
sui.register("PropertySheet", PANEL, "EditablePanel")
|
||||
140
lua/sui/vgui/sui_query_box.lua
Normal file
140
lua/sui/vgui/sui_query_box.lua
Normal file
@@ -0,0 +1,140 @@
|
||||
--[[
|
||||
| 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 ScrW, ScrH = ScrW, ScrH
|
||||
local DisableClipping = DisableClipping
|
||||
local SetDrawColor = surface.SetDrawColor
|
||||
local DrawRect = surface.DrawRect
|
||||
local BlurPanel = sui.TDLib.BlurPanel
|
||||
local lerp_color = sui.lerp_color
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:SetCallback(callback)
|
||||
self.callback = callback
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(0, 0)
|
||||
|
||||
local bottom = self:Add("Panel")
|
||||
bottom:Dock(BOTTOM)
|
||||
bottom:DockMargin(4, 10, 4, 4)
|
||||
bottom:SetZPos(100)
|
||||
|
||||
local save = bottom:Add(NAME .. ".Button")
|
||||
save:SetText("SAVE")
|
||||
save:Dock(RIGHT)
|
||||
save:SetEnabled(false)
|
||||
self.save = save
|
||||
|
||||
function save.DoClick()
|
||||
self.callback()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
local cancel = bottom:Add(NAME .. ".Button")
|
||||
cancel:Dock(RIGHT)
|
||||
cancel:DockMargin(0, 0, 4, 0)
|
||||
cancel:SetContained(false)
|
||||
cancel:SetColors(GetColor("query_box_cancel"), GetColor("query_box_cancel_text"))
|
||||
cancel:SetText("CANCEL")
|
||||
self.cancel = cancel
|
||||
|
||||
function cancel.DoClick()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
bottom:SetSize(save:GetWide() * 2 + 4, SUI.Scale(30))
|
||||
|
||||
local body = self:Add("Panel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(4, 4, 4, 4)
|
||||
body:DockPadding(3, 3, 3, 3)
|
||||
body:InvalidateLayout(true)
|
||||
body:InvalidateParent(true)
|
||||
|
||||
local added = 1
|
||||
function body.OnChildAdded(s, child)
|
||||
added = added + 1
|
||||
child:Dock(TOP)
|
||||
child:SetZPos(added)
|
||||
child:InvalidateLayout(true)
|
||||
s:InvalidateLayout(true)
|
||||
end
|
||||
self.body = body
|
||||
|
||||
function self:Add(name)
|
||||
return body:Add(name)
|
||||
end
|
||||
|
||||
local old_Paint = self.Paint
|
||||
local trans = Color(0, 0, 0, 0)
|
||||
local new_col = Color(70, 70, 70, 100)
|
||||
function self:Paint(w, h)
|
||||
lerp_color(trans, new_col)
|
||||
|
||||
local x, y = self:LocalToScreen(0, 0)
|
||||
DisableClipping(true)
|
||||
BlurPanel(self)
|
||||
SetDrawColor(trans)
|
||||
DrawRect(x * -1, y * -1, ScrW(), ScrH())
|
||||
DisableClipping(false)
|
||||
|
||||
old_Paint(self, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:ChildrenHeight()
|
||||
local body = self.body
|
||||
|
||||
self.header:InvalidateLayout(true)
|
||||
local height = self.header:GetTall()
|
||||
|
||||
body:InvalidateLayout(true)
|
||||
self:InvalidateLayout(true)
|
||||
height = height + select(2, body:ChildrenSize())
|
||||
|
||||
height = height + SUI.Scale(30) + 14 + 6
|
||||
|
||||
return height
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if GetColor("frame_blur") then
|
||||
BlurPanel(self)
|
||||
end
|
||||
|
||||
self:RoundedBox("Background", 8, 0, 0, w, h, GetColor("query_box_bg"))
|
||||
end
|
||||
|
||||
function PANEL:Done()
|
||||
self:InvalidateChildren(true)
|
||||
|
||||
self.size_to_children = function()
|
||||
local h = self:ChildrenHeight()
|
||||
self:RealSetSize(self:GetWide(), h)
|
||||
self.real_h = h
|
||||
end
|
||||
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
self:DoModal(true)
|
||||
|
||||
timer.Simple(0.08, function()
|
||||
self:AddAnimations(self:GetWide(), self:ChildrenHeight(), true)
|
||||
end)
|
||||
end
|
||||
|
||||
sui.register("QueryBox", PANEL, NAME .. ".Frame")
|
||||
208
lua/sui/vgui/sui_scroll_panel.lua
Normal file
208
lua/sui/vgui/sui_scroll_panel.lua
Normal file
@@ -0,0 +1,208 @@
|
||||
--[[
|
||||
| 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 math = math
|
||||
local table = table
|
||||
|
||||
local pairs = pairs
|
||||
local RealFrameTime = RealFrameTime
|
||||
|
||||
local TDLib = sui.TDLib
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
local RoundedBox = sui.TDLib.LibClasses.RoundedBox
|
||||
|
||||
local Panel = {}
|
||||
|
||||
AccessorFunc(Panel, "m_bFromBottom", "FromBottom", FORCE_BOOL)
|
||||
AccessorFunc(Panel, "m_bVBarPadding", "VBarPadding", FORCE_NUMBER)
|
||||
|
||||
Panel:SetVBarPadding(0)
|
||||
|
||||
Panel.NoOverrideClear = true
|
||||
|
||||
-- VBar
|
||||
local starting_scroll_speed = 3
|
||||
|
||||
local vbar_OnMouseWheeled = function(s, delta)
|
||||
s.scroll_speed = s.scroll_speed + (14 * RealFrameTime() --[[ slowly increase scroll speed ]])
|
||||
s:AddScroll(delta * -s.scroll_speed)
|
||||
end
|
||||
|
||||
-- default set scroll clamps amount
|
||||
local vbar_SetScroll = function(s, amount)
|
||||
if not s.Enabled then s.Scroll = 0 return end
|
||||
|
||||
s.scroll_target = amount
|
||||
s:InvalidateLayout()
|
||||
end
|
||||
|
||||
-- ¯\_(ツ)_/¯ https://github.com/Facepunch/garrysmod/blob/cd3d894288b847e3d081570129963d4089e36261/garrysmod/lua/vgui/dvscrollbar.lua#L234
|
||||
local vbar_OnCursorMoved = function(s, _, y)
|
||||
if s.Dragging then
|
||||
y = y - s.HoldPos
|
||||
y = y / (s:GetTall() - s:GetWide() * 2 - s.btnGrip:GetTall())
|
||||
s.scroll_target = y * s.CanvasSize
|
||||
end
|
||||
end
|
||||
|
||||
local vbar_Think = function(s)
|
||||
local frame_time = RealFrameTime() * 14
|
||||
local scroll_target = s.scroll_target
|
||||
|
||||
s.Scroll = Lerp(frame_time, s.Scroll, scroll_target)
|
||||
|
||||
if not s.Dragging then
|
||||
s.scroll_target = Lerp(frame_time, scroll_target, math.Clamp(scroll_target, 0, s.CanvasSize))
|
||||
end
|
||||
|
||||
-- now start slowing it down!!!
|
||||
s.scroll_speed = Lerp(frame_time / 14, s.scroll_speed, starting_scroll_speed)
|
||||
end
|
||||
|
||||
local vbar_Paint = function(s, w, h)
|
||||
TDLib.RoundedBox(s.vertices, 3, 0, 0, w, h, SUI.GetColor("scroll"))
|
||||
end
|
||||
|
||||
local vbarGrip_Paint = function(s, w, h)
|
||||
TDLib.RoundedBox(s.vertices, 3, 0, 0, w, h, SUI.GetColor("scroll_grip"))
|
||||
end
|
||||
|
||||
local vbar_PerformLayout = function(s, w, h)
|
||||
local scroll = s:GetScroll() / s.CanvasSize
|
||||
local bar_size = math.max(s:BarScale() * h, 10)
|
||||
|
||||
local track = (h - bar_size) + 1
|
||||
scroll = scroll * track
|
||||
|
||||
s.btnGrip.y = scroll
|
||||
s.btnGrip:SetSize(w, bar_size)
|
||||
end
|
||||
--
|
||||
|
||||
function Panel:Init()
|
||||
local canvas = self:GetCanvas()
|
||||
canvas:SUI_TDLib()
|
||||
|
||||
local children = {}
|
||||
function canvas:OnChildAdded(child)
|
||||
table.insert(children, child)
|
||||
end
|
||||
function canvas:OnChildRemoved(child)
|
||||
for i = 1, #children do
|
||||
local v = children[i]
|
||||
if v == child then
|
||||
table.remove(children, i)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
canvas.GetChildren = function()
|
||||
return children
|
||||
end
|
||||
canvas.children = children
|
||||
|
||||
local vbar = self.VBar
|
||||
vbar:SetHideButtons(true)
|
||||
vbar.btnUp:SetVisible(false)
|
||||
vbar.btnDown:SetVisible(false)
|
||||
|
||||
vbar.vertices = {}
|
||||
vbar.scroll_target = 0
|
||||
vbar.scroll_speed = starting_scroll_speed
|
||||
|
||||
vbar.OnMouseWheeled = vbar_OnMouseWheeled
|
||||
vbar.SetScroll = vbar_SetScroll
|
||||
vbar.OnCursorMoved = vbar_OnCursorMoved
|
||||
vbar.Think = vbar_Think
|
||||
vbar.Paint = vbar_Paint
|
||||
vbar.PerformLayout = vbar_PerformLayout
|
||||
|
||||
vbar.btnGrip.vertices = {}
|
||||
vbar.btnGrip.Paint = vbarGrip_Paint
|
||||
|
||||
self:ScaleChanged()
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
end
|
||||
|
||||
function Panel:OnChildAdded(child)
|
||||
self:AddItem(child)
|
||||
self:ChildAdded(child)
|
||||
end
|
||||
|
||||
function Panel:ChildAdded()
|
||||
end
|
||||
|
||||
function Panel:ScaleChanged()
|
||||
local w = SUI.Scale(4)
|
||||
|
||||
self.VBar:SetWide(w)
|
||||
self.VBar.btnDown:SetSize(w, 0)
|
||||
self.VBar.btnUp:SetSize(w, 0)
|
||||
end
|
||||
|
||||
function Panel:Paint(w, h)
|
||||
local outline = SUI.GetColor("scroll_panel_outline")
|
||||
if outline then
|
||||
TDLib.DrawOutlinedBox(3, 0, 0, w, h, SUI.GetColor("scroll_panel"), outline, 1)
|
||||
else
|
||||
RoundedBox(self, "Background", 3, 0, 0, w, h, SUI.GetColor("scroll_panel"))
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:ScrollToBottom()
|
||||
local vbar = self.VBar
|
||||
for k, anim in pairs(vbar.m_AnimList or {}) do
|
||||
anim:Think(vbar, 1)
|
||||
vbar.m_AnimList[k] = nil
|
||||
end
|
||||
|
||||
self:InvalidateParent(true)
|
||||
self:InvalidateChildren(true)
|
||||
|
||||
vbar:SetScroll(vbar.CanvasSize)
|
||||
end
|
||||
|
||||
function Panel:PerformLayoutInternal(w, h)
|
||||
w = w or self:GetWide()
|
||||
h = h or self:GetTall()
|
||||
|
||||
local canvas = self.pnlCanvas
|
||||
|
||||
self:Rebuild()
|
||||
|
||||
local vbar = self.VBar
|
||||
vbar:SetUp(h, canvas:GetTall())
|
||||
|
||||
if vbar.Enabled then
|
||||
w = w - vbar:GetWide() - self.m_bVBarPadding
|
||||
end
|
||||
|
||||
canvas:SetWide(w)
|
||||
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
function Panel:Think()
|
||||
local canvas = self.pnlCanvas
|
||||
|
||||
local vbar = self.VBar
|
||||
if vbar.Enabled then
|
||||
canvas.y = -vbar.Scroll
|
||||
else
|
||||
if self:GetFromBottom() then
|
||||
canvas._y = Lerp(14 * RealFrameTime(), canvas._y or canvas.y, self:GetTall() - canvas:GetTall())
|
||||
else
|
||||
canvas._y = Lerp(14 * RealFrameTime(), canvas._y or canvas.y, -vbar.Scroll)
|
||||
end
|
||||
canvas.y = canvas._y
|
||||
end
|
||||
end
|
||||
|
||||
sui.register("ScrollPanel", Panel, "DScrollPanel")
|
||||
91
lua/sui/vgui/sui_slider.lua
Normal file
91
lua/sui/vgui/sui_slider.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
--[[
|
||||
| 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, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
local TDLib = sui.TDLib
|
||||
|
||||
local Panel = {}
|
||||
|
||||
sui.scaling_functions(Panel)
|
||||
|
||||
AccessorFunc(Panel, "m_bValue", "Value", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "m_bMin", "Min", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "m_bMax", "Max", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "m_bDecimals", "Decimals", FORCE_NUMBER)
|
||||
|
||||
function Panel:Init()
|
||||
self:ScaleInit()
|
||||
|
||||
self:SetMin(0)
|
||||
self:SetMax(10)
|
||||
self:SetValue(1)
|
||||
self:SetDecimals(1)
|
||||
|
||||
self:SetSize(100, 12)
|
||||
|
||||
self.rounded_box = {}
|
||||
|
||||
self.Knob.circle = {}
|
||||
self.Knob.Paint = self.KnobPaint
|
||||
self:SetTrapInside(true)
|
||||
end
|
||||
|
||||
function Panel:SetMinMax(min, max)
|
||||
self:SetMin(min)
|
||||
self:SetMax(max)
|
||||
end
|
||||
|
||||
function Panel:TranslateValues(x, y)
|
||||
self:SetValue(self:GetMin() + (x * self:GetRange()))
|
||||
return self:GetFraction(), y
|
||||
end
|
||||
|
||||
function Panel:GetFraction()
|
||||
return (self:GetValue() - self:GetMin()) / self:GetRange()
|
||||
end
|
||||
|
||||
function Panel:SetValue(val)
|
||||
val = math.Clamp(val, self:GetMin(), self:GetMax())
|
||||
val = math.Round(val, self:GetDecimals())
|
||||
|
||||
self.m_bValue = val
|
||||
self:SetSlideX((val - self:GetMin()) / self:GetRange())
|
||||
|
||||
self:OnValueChanged(val)
|
||||
end
|
||||
|
||||
function Panel:OnValueChanged(val)
|
||||
end
|
||||
|
||||
function Panel:GetRange()
|
||||
return self:GetMax() - self:GetMin()
|
||||
end
|
||||
|
||||
function Panel:Paint(w, h)
|
||||
local _h = SUI.Scale(2)
|
||||
TDLib.RoundedBox(self.rounded_box, 3, 0, h / 2 - _h / 2, w, _h, SUI.GetColor("slider_track"))
|
||||
end
|
||||
|
||||
function Panel:KnobPaint(w, h)
|
||||
if self.Depressed then
|
||||
TDLib.DrawCircle(self.circle, w / 2, h / 2, h / 1.1, SUI.GetColor("slider_pressed"))
|
||||
elseif self.Hovered then
|
||||
TDLib.DrawCircle(self.circle, w / 2, h / 2, h / 1.1, SUI.GetColor("slider_hover"))
|
||||
end
|
||||
|
||||
TDLib.DrawCircle(self.circle, w / 2, h / 2, h / 2, SUI.GetColor("slider_knob"))
|
||||
end
|
||||
|
||||
function Panel:PerformLayout(w, h)
|
||||
self.Knob:SetSize(SUI.Scale(12), SUI.Scale(12))
|
||||
DSlider.PerformLayout(self, w, h)
|
||||
end
|
||||
|
||||
sui.register("Slider", Panel, "DSlider")
|
||||
389
lua/sui/vgui/sui_text_entry.lua
Normal file
389
lua/sui/vgui/sui_text_entry.lua
Normal file
@@ -0,0 +1,389 @@
|
||||
--[[
|
||||
| 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")
|
||||
99
lua/sui/vgui/sui_threegrid.lua
Normal file
99
lua/sui/vgui/sui_threegrid.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
| 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 math = math
|
||||
local table = table
|
||||
local ipairs = ipairs
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local Panel = {}
|
||||
|
||||
AccessorFunc(Panel, "horizontalMargin", "HorizontalMargin", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "verticalMargin", "VerticalMargin", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "columns", "Columns", FORCE_NUMBER)
|
||||
AccessorFunc(Panel, "Wide2", "Wide2", FORCE_NUMBER)
|
||||
|
||||
function Panel:Init()
|
||||
self:SetHorizontalMargin(0)
|
||||
self:SetVerticalMargin(0)
|
||||
self.Rows = {}
|
||||
self.Cells = {}
|
||||
end
|
||||
|
||||
function Panel:AddCell(pnl)
|
||||
local cols = self:GetColumns()
|
||||
local idx = math.floor(#self.Cells / cols) + 1
|
||||
|
||||
local rows = self.Rows[idx]
|
||||
if not rows then
|
||||
rows = self:CreateRow()
|
||||
self.Rows[idx] = rows
|
||||
end
|
||||
|
||||
local margin = self:GetHorizontalMargin()
|
||||
|
||||
local dockl, dockt, _, dockb = pnl:GetDockMargin()
|
||||
pnl:SetParent(rows)
|
||||
pnl:Dock(LEFT)
|
||||
pnl:DockMargin(dockl, dockt, #rows.Items + 1 < cols and self:GetHorizontalMargin() or 0, dockb)
|
||||
pnl:SetWide(((self:GetWide2() or self:GetWide()) - margin * (cols - 1)) / cols)
|
||||
|
||||
table.insert(rows.Items, pnl)
|
||||
table.insert(self.Cells, pnl)
|
||||
|
||||
self:CalculateRowHeight(rows)
|
||||
end
|
||||
|
||||
function Panel:CreateRow()
|
||||
local row = self:Add("Panel")
|
||||
row:Dock(TOP)
|
||||
row:DockMargin(0, 0, 0, self:GetVerticalMargin())
|
||||
row.Items = {}
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
function Panel:CalculateRowHeight(row)
|
||||
local height = 0
|
||||
|
||||
for k, v in ipairs(row.Items) do
|
||||
local _, t, _, b = v:GetDockMargin()
|
||||
height = math.max(height, v:GetTall() + t + b)
|
||||
end
|
||||
|
||||
row:SetTall(height)
|
||||
end
|
||||
|
||||
function Panel:Skip()
|
||||
local cell = vgui.Create("Panel")
|
||||
self:AddCell(cell)
|
||||
end
|
||||
|
||||
function Panel:CalculateRowHeights()
|
||||
for _, row in ipairs(self.Rows) do
|
||||
self:CalculateRowHeight(row)
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:Clear()
|
||||
for _, row in ipairs(self.Rows) do
|
||||
for _, cell in ipairs(row.Items) do
|
||||
cell:Remove()
|
||||
end
|
||||
|
||||
row:Remove()
|
||||
end
|
||||
|
||||
self.Cells, self.Rows = {}, {}
|
||||
end
|
||||
|
||||
Panel.OnRemove = Panel.Clear
|
||||
sui.register("ThreeGrid", Panel, NAME .. ".ScrollPanel")
|
||||
48
lua/sui/vgui/sui_toggle_button.lua
Normal file
48
lua/sui/vgui/sui_toggle_button.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
--[[
|
||||
| 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 Lerp = Lerp
|
||||
local FrameTime = FrameTime
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
local TDLib = sui.TDLib
|
||||
|
||||
local Panel = {}
|
||||
|
||||
sui.scaling_functions(Panel)
|
||||
|
||||
function Panel:Init()
|
||||
self:ScaleInit()
|
||||
|
||||
local rounded_box = {}
|
||||
local switch_circle = {}
|
||||
function self:Paint(w, h)
|
||||
local is_checked = self:GetChecked()
|
||||
|
||||
local _h = SUI.Scale(14)
|
||||
TDLib.RoundedBox(rounded_box, _h, 0, h / 2 - _h / 2, w, _h, is_checked and SUI.GetColor("toggle_button_active") or SUI.GetColor("toggle_button"))
|
||||
|
||||
local size = h - 2
|
||||
do
|
||||
local pos = is_checked and (w - (size / 2)) or (h / 2 - 1)
|
||||
if self.pos then
|
||||
self.pos = Lerp(FrameTime() * 12, self.pos, pos)
|
||||
else
|
||||
self.pos = pos
|
||||
end
|
||||
end
|
||||
|
||||
TDLib.DrawCircle(switch_circle, self.pos, h / 2, size / 2, is_checked and SUI.GetColor("toggle_button_switch_active") or SUI.GetColor("toggle_button_switch"))
|
||||
end
|
||||
|
||||
self:SetSize(34, 20)
|
||||
end
|
||||
|
||||
sui.register("ToggleButton", Panel, "DCheckBox")
|
||||
147
lua/sui/vgui/sui_zbutton.lua
Normal file
147
lua/sui/vgui/sui_zbutton.lua
Normal file
@@ -0,0 +1,147 @@
|
||||
--[[
|
||||
| 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 draw = draw
|
||||
local render = render
|
||||
|
||||
local TDLib = sui.TDLib
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local lerp_color = sui.lerp_color
|
||||
local contrast_color = sui.contrast_color
|
||||
|
||||
local BUTTON_FONT = SUI.CreateFont("Button", "Roboto Medium", 16)
|
||||
|
||||
local color_white = color_white
|
||||
local color_transparent = Color(0, 0, 0, 0)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "m_Background", "Background")
|
||||
AccessorFunc(PANEL, "m_bContained", "Contained", FORCE_BOOL)
|
||||
|
||||
sui.TDLib.Install(PANEL)
|
||||
sui.scaling_functions(PANEL)
|
||||
|
||||
PANEL:ClearPaint()
|
||||
PANEL:SetContained(true)
|
||||
|
||||
local Panel = FindMetaTable("Panel")
|
||||
local SetMouseInputEnabled = Panel.SetMouseInputEnabled
|
||||
local IsMouseInputEnabled = Panel.IsMouseInputEnabled
|
||||
local SetCursor = Panel.SetCursor
|
||||
local SetContentAlignment = Panel.SetContentAlignment
|
||||
function PANEL:Init()
|
||||
self:ScaleInit()
|
||||
|
||||
self.vertices, self.vertices_2 = {}, {}
|
||||
|
||||
SetMouseInputEnabled(self, true)
|
||||
SetCursor(self, "hand")
|
||||
SetContentAlignment(self, 5)
|
||||
|
||||
self:SetSize(90, 30)
|
||||
self:SetFont(BUTTON_FONT)
|
||||
|
||||
self:CircleClick(nil, 7)
|
||||
|
||||
self.OldPaint, self.Paint = self.Paint, self.Paint2
|
||||
|
||||
self.cur_col = Color(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
function PANEL:SetEnabled(b)
|
||||
SetMouseInputEnabled(self, b)
|
||||
end
|
||||
|
||||
function PANEL:IsEnabled()
|
||||
return IsMouseInputEnabled(self)
|
||||
end
|
||||
|
||||
function PANEL:ContainedPaint(w, h)
|
||||
local enabled = self:IsEnabled()
|
||||
local col
|
||||
if enabled then
|
||||
col = self:GetBackground() or SUI.GetColor("button")
|
||||
self:SetTextColor(SUI.GetColor("button_text"))
|
||||
else
|
||||
col = SUI.GetColor("button_disabled")
|
||||
self:SetTextColor(SUI.GetColor("button_disabled_text"))
|
||||
end
|
||||
self:RoundedBox("Background", 4, 0, 0, w, h, col)
|
||||
|
||||
if not enabled then return end
|
||||
|
||||
self.circle_click_color = SUI.GetColor("button_click")
|
||||
|
||||
if self.Hovered or self.Selected then
|
||||
self:RoundedBox("Hover", 4, 0, 0, w, h, SUI.GetColor("button_hover"))
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetColors(hover_color, text_color)
|
||||
self.hover = hover_color
|
||||
self.text_color = text_color
|
||||
end
|
||||
|
||||
function PANEL:Paint2(w, h)
|
||||
if self:GetContained() then
|
||||
self:ContainedPaint(w, h)
|
||||
self:OldPaint(w, h)
|
||||
return
|
||||
end
|
||||
|
||||
render.ClearStencil()
|
||||
render.SetStencilEnable(true)
|
||||
|
||||
render.SetStencilWriteMask(1)
|
||||
render.SetStencilTestMask(1)
|
||||
|
||||
render.SetStencilFailOperation(STENCILOPERATION_REPLACE)
|
||||
render.SetStencilPassOperation(STENCILOPERATION_ZERO)
|
||||
render.SetStencilZFailOperation(STENCILOPERATION_ZERO)
|
||||
render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER)
|
||||
render.SetStencilReferenceValue(1)
|
||||
|
||||
TDLib.RoundedBox(self.vertices, 4, 0, 0, w, h, color_white)
|
||||
|
||||
render.SetStencilFailOperation(STENCILOPERATION_ZERO)
|
||||
render.SetStencilPassOperation(STENCILOPERATION_REPLACE)
|
||||
render.SetStencilZFailOperation(STENCILOPERATION_ZERO)
|
||||
render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL)
|
||||
render.SetStencilReferenceValue(1)
|
||||
|
||||
local cur_col = self.cur_col
|
||||
if self.Selected then
|
||||
lerp_color(cur_col, SUI.GetColor("button2_selected"))
|
||||
elseif self.Hovered then
|
||||
lerp_color(cur_col, self.hover or SUI.GetColor("button2_hover"))
|
||||
else
|
||||
lerp_color(cur_col, color_transparent)
|
||||
end
|
||||
|
||||
TDLib.RoundedBox(self.vertices_2, 4, 0, 0, w, h, cur_col)
|
||||
|
||||
if self.text_color then
|
||||
self.circle_click_color = self.text_color
|
||||
self:SetTextColor(self.text_color)
|
||||
else
|
||||
local col = contrast_color(cur_col)
|
||||
self.circle_click_color = col
|
||||
self:SetTextColor(col)
|
||||
end
|
||||
|
||||
self:OldPaint(w, h)
|
||||
|
||||
render.SetStencilEnable(false)
|
||||
render.ClearStencil()
|
||||
end
|
||||
|
||||
sui.register("Button", PANEL, NAME .. ".Label")
|
||||
254
lua/sui/vgui/sui_zcollapse_category.lua
Normal file
254
lua/sui/vgui/sui_zcollapse_category.lua
Normal file
@@ -0,0 +1,254 @@
|
||||
--[[
|
||||
| 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 draw_material = sui.draw_material
|
||||
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
|
||||
local RoundedBox = sui.TDLib.LibClasses.RoundedBox
|
||||
local TextColor = sui.TDLib.LibClasses.TextColor
|
||||
|
||||
local TABS_FONT = SUI.CreateFont("CategoryListTabs", "Roboto Bold", 13)
|
||||
local ITEMS_FONT = SUI.CreateFont("CategoryListItems", "Roboto Medium", 14)
|
||||
|
||||
local Panel = {}
|
||||
|
||||
local item_OnRemove = function(s)
|
||||
local parent = s.parent
|
||||
|
||||
local items = parent.items
|
||||
for k, v in ipairs(items) do
|
||||
if v == s then
|
||||
table.remove(items, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if #items == 0 then
|
||||
local category = s.category
|
||||
category:Remove()
|
||||
parent.categories[category.name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local item_DoClick = function(s)
|
||||
local parent = s.parent
|
||||
parent:select_item(s)
|
||||
end
|
||||
|
||||
function Panel:Init()
|
||||
local categories = {}
|
||||
local items = {}
|
||||
|
||||
self.categories = categories
|
||||
self.items = items
|
||||
|
||||
self:SetVBarPadding(1)
|
||||
|
||||
local get_category = function(name)
|
||||
local category = categories[name]
|
||||
if category then return category end
|
||||
|
||||
local expanded = false
|
||||
|
||||
category = self:Add("Panel")
|
||||
category:Dock(TOP)
|
||||
category:DockMargin(0, 0, 0, 3)
|
||||
category.name = name
|
||||
|
||||
local header = category:Add("DButton")
|
||||
header:Dock(TOP)
|
||||
header:DockMargin(0, 0, 0, 3)
|
||||
header:SetFont(TABS_FONT)
|
||||
header:SetContentAlignment(4)
|
||||
header:SetTextInset(6, 0)
|
||||
header:SetText(name)
|
||||
header:SizeToContentsY(SUI.Scale(14))
|
||||
|
||||
local cur_col
|
||||
local cur_col_text = Color(GetColor("collapse_category_header_text"):Unpack())
|
||||
function header:Paint(w, h)
|
||||
if expanded then
|
||||
cur_col = GetColor("collapse_category_header_active")
|
||||
cur_col_text = GetColor("collapse_category_header_text_active")
|
||||
elseif self.Hovered then
|
||||
cur_col = GetColor("collapse_category_header_hover")
|
||||
cur_col_text = GetColor("collapse_category_header_text_hover")
|
||||
else
|
||||
cur_col = GetColor("collapse_category_header")
|
||||
cur_col_text = GetColor("collapse_category_header_text")
|
||||
end
|
||||
|
||||
RoundedBox(self, "Background", 3, 0, 0, w, h, cur_col)
|
||||
TextColor(self, cur_col_text)
|
||||
end
|
||||
|
||||
local image = header:Add(NAME .. ".Image")
|
||||
image:Dock(FILL)
|
||||
image:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sui/arrow.png")
|
||||
|
||||
function image:Draw(w, h)
|
||||
local size = SUI.ScaleEven(10)
|
||||
draw_material(nil, w - (size / 2) - 6, h / 2, size, cur_col_text, expanded and 180)
|
||||
end
|
||||
|
||||
local current_h
|
||||
function category.RefreshHeight()
|
||||
local h
|
||||
if expanded then
|
||||
local _
|
||||
_, h = category:ChildrenSize()
|
||||
if self.searching and h == header:GetTall() then
|
||||
h = 0
|
||||
end
|
||||
else
|
||||
h = header:GetTall()
|
||||
end
|
||||
|
||||
if current_h == h then return end
|
||||
|
||||
if h > 0 then
|
||||
category:SetVisible(true)
|
||||
end
|
||||
|
||||
current_h = h
|
||||
|
||||
category:Stop()
|
||||
category:SizeTo(-1, h, 0.2, 0, -1, function()
|
||||
if h == 0 then
|
||||
category:SetVisible(false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function category.SetExpanded(_, set_expanded)
|
||||
if expanded == set_expanded then return end
|
||||
|
||||
if sam.isbool(set_expanded) then
|
||||
expanded = set_expanded
|
||||
else
|
||||
expanded = not expanded
|
||||
end
|
||||
|
||||
category.RefreshHeight()
|
||||
|
||||
if expanded then
|
||||
self:OnCategoryExpanded(category)
|
||||
end
|
||||
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
header.DoClick = category.SetExpanded
|
||||
|
||||
category:SetTall(header:GetTall())
|
||||
categories[name] = category
|
||||
|
||||
return category
|
||||
end
|
||||
|
||||
function self:add_item(name, category_name)
|
||||
local category = get_category(category_name)
|
||||
|
||||
local item = category:Add("DButton")
|
||||
item:Dock(TOP)
|
||||
item:DockMargin(0, 0, 0, 3)
|
||||
item:SetFont(ITEMS_FONT)
|
||||
item:SetText(name)
|
||||
item:SizeToContentsY(SUI.Scale(3 * 2))
|
||||
item.name = name
|
||||
item.parent = self
|
||||
item.category = category
|
||||
|
||||
local cur_col
|
||||
local cur_col_text = Color(GetColor("collapse_category_item_text"):Unpack())
|
||||
function item:Paint(w, h)
|
||||
if self.selected then
|
||||
cur_col = GetColor("collapse_category_item_active")
|
||||
cur_col_text = GetColor("collapse_category_item_text_active")
|
||||
elseif self.Hovered then
|
||||
cur_col = GetColor("collapse_category_item_hover")
|
||||
cur_col_text = GetColor("collapse_category_item_text_hover")
|
||||
else
|
||||
cur_col = GetColor("collapse_category_item")
|
||||
cur_col_text = GetColor("collapse_category_item_text")
|
||||
end
|
||||
|
||||
RoundedBox(self, "Background", 4, 0, 0, w, h, cur_col)
|
||||
TextColor(self, cur_col_text)
|
||||
end
|
||||
|
||||
item.DoClick = item_DoClick
|
||||
item.OnRemove = item_OnRemove
|
||||
|
||||
table.insert(items, item)
|
||||
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:OnCategoryExpanded(category)
|
||||
end
|
||||
|
||||
function Panel:select_item(item)
|
||||
if self.selected_item ~= item then
|
||||
if IsValid(self.selected_item) then
|
||||
self.selected_item.selected = false
|
||||
end
|
||||
item.selected = true
|
||||
self.selected_item = item
|
||||
self:item_selected(item)
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:item_selected()
|
||||
end
|
||||
|
||||
function Panel:Search(text, names)
|
||||
local items = self.items
|
||||
self.searching = true
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
local category = item.category
|
||||
category:SetExpanded(true)
|
||||
|
||||
if not names then
|
||||
if item.name:find(text, nil, true) then
|
||||
item:SetVisible(true)
|
||||
else
|
||||
item:SetVisible(false)
|
||||
end
|
||||
else
|
||||
local found = false
|
||||
for _, name in ipairs(item.names) do
|
||||
if name:find(text, nil, true) then
|
||||
found = true
|
||||
item:SetVisible(true)
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
item:SetVisible(false)
|
||||
end
|
||||
end
|
||||
|
||||
if text == "" then
|
||||
category:SetExpanded(false)
|
||||
end
|
||||
|
||||
category:RefreshHeight()
|
||||
category:InvalidateLayout(true)
|
||||
end
|
||||
self.searching = false
|
||||
end
|
||||
|
||||
sui.register("CollapseCategory", Panel, NAME .. ".ScrollPanel")
|
||||
142
lua/sui/vgui/sui_zcolumn_sheet.lua
Normal file
142
lua/sui/vgui/sui_zcolumn_sheet.lua
Normal file
@@ -0,0 +1,142 @@
|
||||
--[[
|
||||
| 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 IsValid = IsValid
|
||||
|
||||
local TDLib_Classes = sui.TDLib.LibClasses
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
|
||||
local Panel = {}
|
||||
|
||||
function Panel:Init()
|
||||
self.tabs = {}
|
||||
|
||||
local tab_scroller = self:Add(NAME .. ".ScrollPanel")
|
||||
tab_scroller:Dock(LEFT)
|
||||
|
||||
function tab_scroller:Paint(w, h)
|
||||
self:RoundedBox("Background", 1, 0, 0, w, h, GetColor("column_sheet_bar"))
|
||||
end
|
||||
|
||||
self.tabs_wide = 48
|
||||
self.tab_scroller = tab_scroller
|
||||
|
||||
self:ScaleChanged()
|
||||
SUI.OnScaleChanged(self, self.ScaleChanged)
|
||||
end
|
||||
|
||||
function Panel:ScaleChanged()
|
||||
local tabs_wide = SUI.Scale(self.tabs_wide)
|
||||
self.tab_scroller:SetWide(tabs_wide)
|
||||
|
||||
self:InvalidateLayout(true)
|
||||
|
||||
local tabs = self.tabs
|
||||
for i = 1, #self.tabs do
|
||||
tabs[i].img:SetMinus(SUI.Scale(20))
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:Paint(w, h)
|
||||
self:RoundedBox("Background", 1, 0, 0, w, h, GetColor("column_sheet"))
|
||||
end
|
||||
|
||||
local tab_DoClick = function(s)
|
||||
s.parent:SetActiveTab(s)
|
||||
end
|
||||
|
||||
local tab_Paint = function(s, w, h)
|
||||
local cur_col
|
||||
if s.active then
|
||||
cur_col = GetColor("column_sheet_tab_active")
|
||||
elseif s.Hovered then
|
||||
cur_col = GetColor("column_sheet_tab_hover")
|
||||
else
|
||||
cur_col = GetColor("column_sheet_tab")
|
||||
end
|
||||
|
||||
s:RoundedBox("Backgrounds", 0, 0, 0, w, h, cur_col)
|
||||
end
|
||||
|
||||
local tab_OnRemove = function(s)
|
||||
table.RemoveByValue(s.parent.tabs, s)
|
||||
end
|
||||
|
||||
function Panel:AddSheet(mat, load_func)
|
||||
local tab = self.tab_scroller:Add(NAME .. ".Button")
|
||||
tab:Dock(TOP)
|
||||
tab:SetText("")
|
||||
tab:SetTall(self.tabs_wide)
|
||||
|
||||
tab.On = TDLib_Classes.On
|
||||
|
||||
tab.DoClick = tab_DoClick
|
||||
tab.Paint = tab_Paint
|
||||
tab:On("OnRemove", tab_OnRemove)
|
||||
|
||||
tab.parent = self
|
||||
tab.load_func = load_func
|
||||
|
||||
local img = tab:Add(NAME .. ".Image")
|
||||
img:Dock(FILL)
|
||||
img:SetImage(mat)
|
||||
img:SetMinus(SUI.Scale(20))
|
||||
|
||||
tab.img = img
|
||||
|
||||
self.tab_scroller:AddItem(tab)
|
||||
|
||||
if not self:GetActiveTab() then
|
||||
self:SetActiveTab(tab)
|
||||
end
|
||||
|
||||
table.insert(self.tabs, tab)
|
||||
|
||||
return tab
|
||||
end
|
||||
|
||||
function Panel:GetActiveTab()
|
||||
return self.active_tab
|
||||
end
|
||||
|
||||
function Panel:SetActiveTab(new_tab)
|
||||
if new_tab == self.active_tab then return end
|
||||
|
||||
if not IsValid(new_tab.panel) then
|
||||
local panel = new_tab.load_func(self)
|
||||
panel:SetParent(self)
|
||||
panel:SetVisible(false)
|
||||
panel:SetAlpha(0)
|
||||
|
||||
panel.tab = new_tab
|
||||
new_tab.panel = panel
|
||||
end
|
||||
|
||||
local old_active_tab = self.active_tab
|
||||
local delay = 0
|
||||
if old_active_tab and IsValid(old_active_tab.panel) then
|
||||
old_active_tab.active = false
|
||||
delay = 0.2
|
||||
old_active_tab.panel:AlphaTo(0, delay, 0, function(_, p)
|
||||
if p:IsValid() then
|
||||
p:SetVisible(false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
new_tab.active = true
|
||||
new_tab.panel:SetVisible(true)
|
||||
new_tab.panel:AlphaTo(255, 0.2, delay)
|
||||
self.active_tab = new_tab
|
||||
end
|
||||
|
||||
sui.register("ColumnSheet", Panel, "EditablePanel")
|
||||
146
lua/sui/vgui/sui_zmenu.lua
Normal file
146
lua/sui/vgui/sui_zmenu.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
--[[
|
||||
| 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 BSHADOWS = sui.BSHADOWS
|
||||
local SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
|
||||
local RoundedBox = sui.TDLib.LibClasses.RoundedBox
|
||||
local TextColor = sui.TDLib.LibClasses.TextColor
|
||||
|
||||
local OPTION_FONT = SUI.CreateFont("MenuOption", "Roboto Medium", 15, 500)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "m_bIsMenuComponent", "IsMenu")
|
||||
AccessorFunc(PANEL, "m_bDeleteSelf", "DeleteSelf")
|
||||
AccessorFunc(PANEL, "m_iMinimumWidth", "MinimumWidth")
|
||||
AccessorFunc(PANEL, "m_SetInternal", "Internal")
|
||||
|
||||
PANEL:SetIsMenu(true)
|
||||
PANEL:SetDeleteSelf(true)
|
||||
|
||||
local pad = 4
|
||||
local max_height = 300
|
||||
|
||||
local PerformLayout = function(s)
|
||||
local w, h = s:ChildrenSize()
|
||||
if h > SUI.Scale(max_height) then
|
||||
h = SUI.Scale(max_height)
|
||||
end
|
||||
s:SetSize(math.max(s:GetMinimumWidth(), w), h)
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self:GetCanvas():DockPadding(0, pad, 0, pad)
|
||||
self:SetMinimumWidth(SUI.Scale(100))
|
||||
self:SetKeyboardInputEnabled(false)
|
||||
self:SetTall(pad * 2)
|
||||
self:SetAlpha(0)
|
||||
self.tall = pad * 2
|
||||
RegisterDermaMenuForClose(self)
|
||||
self:On("PerformLayoutInternal", PerformLayout)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
local x, y = self:LocalToScreen()
|
||||
|
||||
BSHADOWS.BeginShadow()
|
||||
self:RoundedBox("Background", pad, x, y, w, h, GetColor("menu"))
|
||||
BSHADOWS.EndShadow(1, 3, 3)
|
||||
|
||||
self:MoveToFront()
|
||||
end
|
||||
|
||||
function PANEL:Open(x, y)
|
||||
self:SizeToChildren(true, false)
|
||||
|
||||
local w, h = self:GetSize()
|
||||
if h > SUI.Scale(max_height) then
|
||||
h = SUI.Scale(max_height)
|
||||
end
|
||||
|
||||
local internal = self:GetInternal()
|
||||
internal:On("OnRemove", function()
|
||||
self:Remove()
|
||||
end)
|
||||
if not x then
|
||||
x, y = internal:LocalToScreen(0, 0)
|
||||
y = y + (internal:GetTall() + 2)
|
||||
end
|
||||
|
||||
if y + h > ScrH() then
|
||||
y = y - h
|
||||
end
|
||||
|
||||
if x + w > ScrW() then
|
||||
x = x - w
|
||||
end
|
||||
|
||||
if y < 1 then
|
||||
y = 1
|
||||
end
|
||||
|
||||
if x < 1 then
|
||||
x = 1
|
||||
end
|
||||
|
||||
self:SetPos(x, y)
|
||||
self:MakePopup()
|
||||
self:AlphaTo(255, 0.23)
|
||||
self:SetDrawOnTop(true)
|
||||
self:MoveToFront()
|
||||
end
|
||||
|
||||
local option_OnMouseReleased = function(s, mousecode)
|
||||
if s.Depressed and mousecode == MOUSE_LEFT then
|
||||
CloseDermaMenus()
|
||||
end
|
||||
DButton.OnMouseReleased(s, mousecode)
|
||||
end
|
||||
|
||||
function PANEL:AddOption(str, callback)
|
||||
local option = self:Add("DButton")
|
||||
option:Dock(TOP)
|
||||
option:SetFont(OPTION_FONT)
|
||||
option:SetText(str)
|
||||
option:SizeToContentsX(20)
|
||||
option:SizeToContentsY(10)
|
||||
option:InvalidateLayout(true)
|
||||
option.OnMouseReleased = option_OnMouseReleased
|
||||
|
||||
function option:Paint(w, h)
|
||||
RoundedBox(self, "Background", 0, 0, 0, w, h, self.Hovered and GetColor("menu_option_hover") or GetColor("menu_option"))
|
||||
TextColor(self, self.Hovered and GetColor("menu_option_hover_text") or GetColor("menu_option_text"))
|
||||
end
|
||||
|
||||
option.DoClick = callback
|
||||
|
||||
self.tall = self.tall + option:GetTall()
|
||||
self:SetTall(self.tall)
|
||||
|
||||
return option
|
||||
end
|
||||
|
||||
function PANEL:AddSpacer()
|
||||
local spacer = self:Add("Panel")
|
||||
spacer:Dock(TOP)
|
||||
spacer:DockMargin(0, 1, 0, 1)
|
||||
spacer:SetTall(2)
|
||||
|
||||
function spacer:Paint(w, h)
|
||||
RoundedBox(self, "Background", 0, 0, 0, w, h, GetColor("menu_spacer"))
|
||||
end
|
||||
|
||||
spacer:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
sui.register("Menu", PANEL, NAME .. ".ScrollPanel")
|
||||
Reference in New Issue
Block a user