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

940 lines
19 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local 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