add sborka
This commit is contained in:
138
garrysmod/addons/Binder/lua/binder/vgui/cl_frame.lua
Normal file
138
garrysmod/addons/Binder/lua/binder/vgui/cl_frame.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
-- Garry's Mod Binder - Custom Frame
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(ScrW() * 0.8, ScrH() * 0.8)
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
|
||||
self.AccentColor = Binder.Config.AccentColor
|
||||
|
||||
self.CurrentTab = "Profiles"
|
||||
|
||||
self.HeaderHeight = 60
|
||||
self.TabHeight = 40
|
||||
|
||||
-- Tabs definitions
|
||||
self.Tabs = {
|
||||
{id = "Profiles", name = Binder.GetPhrase("profiles")},
|
||||
{id = "Keybinds", name = Binder.GetPhrase("keybinds")},
|
||||
{id = "Radial", name = Binder.GetPhrase("radial")},
|
||||
{id = "Settings", name = Binder.GetPhrase("settings")},
|
||||
}
|
||||
|
||||
-- Close Button
|
||||
self.CloseBtn = vgui.Create("DButton", self)
|
||||
self.CloseBtn:SetText("✕")
|
||||
self.CloseBtn:SetFont("Binder_Main")
|
||||
self.CloseBtn:SetTextColor(Color(255, 255, 255, 150))
|
||||
self.CloseBtn.DoClick = function() self:Remove() end
|
||||
self.CloseBtn.Paint = function(s, w, h)
|
||||
if s:IsHovered() then
|
||||
s:SetTextColor(Color(255, 255, 255, 255))
|
||||
else
|
||||
s:SetTextColor(Color(255, 255, 255, 150))
|
||||
end
|
||||
end
|
||||
|
||||
-- Tab Content Container
|
||||
self.Content = vgui.Create("DPanel", self)
|
||||
self.Content:Dock(FILL)
|
||||
self.Content:DockMargin(0, self.HeaderHeight + self.TabHeight, 0, 0)
|
||||
self.Content.Paint = function(s, w, h) end
|
||||
|
||||
self:SwitchTab("Profiles")
|
||||
end
|
||||
|
||||
include("binder/vgui/cl_profiles_tab.lua")
|
||||
include("binder/vgui/cl_keybind_tab.lua")
|
||||
include("binder/vgui/cl_radial_tab.lua")
|
||||
include("binder/vgui/cl_settings_tab.lua")
|
||||
|
||||
function PANEL:SwitchTab(id)
|
||||
self.CurrentTab = id
|
||||
self.Content:Clear()
|
||||
|
||||
if id == "Profiles" then
|
||||
self.ActiveTab = vgui.Create("Binder_ProfilesTab", self.Content)
|
||||
elseif id == "Keybinds" then
|
||||
self.ActiveTab = vgui.Create("Binder_KeybindsTab", self.Content)
|
||||
elseif id == "Radial" then
|
||||
self.ActiveTab = vgui.Create("Binder_RadialTab", self.Content)
|
||||
elseif id == "Settings" then
|
||||
self.ActiveTab = vgui.Create("Binder_SettingsTab", self.Content)
|
||||
else
|
||||
local label = vgui.Create("DLabel", self.Content)
|
||||
|
||||
|
||||
label:SetText("Content for " .. id)
|
||||
label:SetFont("Binder_Title")
|
||||
label:SizeToContents()
|
||||
label:Center()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:RefreshActiveTab()
|
||||
if IsValid(self.ActiveTab) and self.ActiveTab.Refresh then
|
||||
self.ActiveTab:Refresh()
|
||||
elseif self.CurrentTab == "Profiles" and IsValid(self.ActiveTab) then
|
||||
-- Fallback if the tab logic doesn't have a specific refresh method,
|
||||
-- though we should add :Refresh to all of them.
|
||||
self:SwitchTab(self.CurrentTab)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
if IsValid(self.CloseBtn) then
|
||||
self.CloseBtn:SetSize(30, 30)
|
||||
self.CloseBtn:SetPos(w - 40, 15)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
-- Background with blur
|
||||
draw.RoundedBox(8, 0, 0, w, h, Color(30, 30, 30, 200))
|
||||
|
||||
-- Header
|
||||
draw.RoundedBoxEx(8, 0, 0, w, self.HeaderHeight, self.AccentColor, true, true, false, false)
|
||||
draw.SimpleText(Binder.GetPhrase("title"), "Binder_Title", 20, self.HeaderHeight / 2, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
-- Tabs Bar
|
||||
draw.RoundedBox(0, 0, self.HeaderHeight, w, self.TabHeight, Color(45, 45, 45))
|
||||
|
||||
local tabWidth = w / #self.Tabs
|
||||
for i, tab in ipairs(self.Tabs) do
|
||||
local x = (i - 1) * tabWidth
|
||||
local isActive = self.CurrentTab == tab.id
|
||||
|
||||
if isActive then
|
||||
draw.RoundedBox(0, x, self.HeaderHeight, tabWidth, self.TabHeight, Color(255, 255, 255, 10))
|
||||
draw.RoundedBox(0, x + 20, self.HeaderHeight + self.TabHeight - 3, tabWidth - 40, 3, Color(255, 255, 255))
|
||||
end
|
||||
|
||||
local isHovered = gui.MouseX() >= self:GetPos() + x and gui.MouseX() < self:GetPos() + x + tabWidth and
|
||||
gui.MouseY() >= self:GetY() + self.HeaderHeight and gui.MouseY() < self:GetY() + self.HeaderHeight + self.TabHeight
|
||||
|
||||
if isHovered and not isActive then
|
||||
draw.RoundedBox(0, x, self.HeaderHeight, tabWidth, self.TabHeight, Color(255, 255, 255, 5))
|
||||
end
|
||||
|
||||
draw.SimpleText(tab.name, "Binder_Tab", x + tabWidth / 2, self.HeaderHeight + self.TabHeight / 2,
|
||||
isActive and Color(255, 255, 255) or Color(150, 150, 150),
|
||||
TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
-- Hidden button for tab click
|
||||
if not self.TabButtons then self.TabButtons = {} end
|
||||
if not self.TabButtons[i] then
|
||||
local btn = vgui.Create("DButton", self)
|
||||
btn:SetText("")
|
||||
btn.Paint = function() end
|
||||
btn.DoClick = function() self:SwitchTab(tab.id) end
|
||||
self.TabButtons[i] = btn
|
||||
end
|
||||
self.TabButtons[i]:SetSize(tabWidth, self.TabHeight)
|
||||
self.TabButtons[i]:SetPos(x, self.HeaderHeight)
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("Binder_Frame", PANEL, "EditablePanel")
|
||||
204
garrysmod/addons/Binder/lua/binder/vgui/cl_keybind_tab.lua
Normal file
204
garrysmod/addons/Binder/lua/binder/vgui/cl_keybind_tab.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
-- Garry's Mod Binder - Keybinds Tab (Virtual Keyboard)
|
||||
local PANEL = {}
|
||||
|
||||
local KEY_LAYOUT = {
|
||||
{"ESC", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"},
|
||||
{"~", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BKSP"},
|
||||
{"TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"},
|
||||
{"CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER"},
|
||||
{"SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT"},
|
||||
{"CTRL", "WIN", "ALT", "SPACE", "ALT", "WIN", "MENU"}
|
||||
}
|
||||
|
||||
local KEY_SPECIAL = {
|
||||
{"PRSC", "SCRLK", "PAUSE"},
|
||||
{"INS", "HOME", "PGUP"},
|
||||
{"DEL", "END", "PGDN"},
|
||||
{"↑"},
|
||||
{"←", "↓", "→"}
|
||||
}
|
||||
|
||||
local NUM_PAD = {
|
||||
{"NUM", "/", "*", "-"},
|
||||
{"7", "8", "9", "+"},
|
||||
{"4", "5", "6"},
|
||||
{"1", "2", "3", "ENT"},
|
||||
{"0", "."}
|
||||
}
|
||||
|
||||
local STRING_TO_KEY = {
|
||||
["ESC"] = KEY_ESCAPE, ["~"] = KEY_TILDE, ["-"] = KEY_MINUS, ["="] = KEY_EQUAL, ["BKSP"] = KEY_BACKSPACE, ["TAB"] = KEY_TAB,
|
||||
["["] = KEY_LBRACKET, ["]"] = KEY_RBRACKET, ["\\"] = KEY_BACKSLASH, ["CAPS"] = KEY_CAPSLOCK, [";"] = KEY_SEMICOLON,
|
||||
["'"] = KEY_APOSTROPHE, ["ENTER"] = KEY_ENTER, ["SHIFT"] = KEY_LSHIFT, [","] = KEY_COMMA, ["."] = KEY_PERIOD,
|
||||
["/"] = KEY_SLASH, ["CTRL"] = KEY_LCONTROL, ["WIN"] = KEY_LWIN, ["ALT"] = KEY_LALT, ["SPACE"] = KEY_SPACE,
|
||||
["MENU"] = KEY_APP,
|
||||
["PRSC"] = KEY_SYSRQ, ["SCRLK"] = KEY_SCROLLLOCK, ["PAUSE"] = KEY_BREAK, ["INS"] = KEY_INSERT, ["HOME"] = KEY_HOME,
|
||||
["PGUP"] = KEY_PAGEUP, ["DEL"] = KEY_DELETE, ["END"] = KEY_END, ["PGDN"] = KEY_PAGEDOWN,
|
||||
["↑"] = KEY_UP, ["←"] = KEY_LEFT, ["↓"] = KEY_DOWN, ["→"] = KEY_RIGHT,
|
||||
["NUM"] = KEY_NUMLOCK, ["/"] = KEY_PAD_DIVIDE, ["*"] = KEY_PAD_MULTIPLY, ["-"] = KEY_PAD_MINUS, ["+"] = KEY_PAD_PLUS,
|
||||
["ENT"] = KEY_PAD_ENTER, ["."] = KEY_PAD_DECIMAL,
|
||||
["1"] = KEY_1, ["2"] = KEY_2, ["3"] = KEY_3, ["4"] = KEY_4, ["5"] = KEY_5, ["6"] = KEY_6, ["7"] = KEY_7, ["8"] = KEY_8, ["9"] = KEY_9, ["0"] = KEY_0,
|
||||
}
|
||||
|
||||
-- Add letters and F-keys
|
||||
for i = 1, 26 do STRING_TO_KEY[string.char(64 + i)] = _G["KEY_" .. string.char(64 + i)] end
|
||||
for i = 1, 12 do STRING_TO_KEY["F" .. i] = _G["KEY_F" .. i] end
|
||||
|
||||
function PANEL:Init()
|
||||
self:Dock(FILL)
|
||||
self:DockMargin(20, 20, 20, 20)
|
||||
self.Paint = function() end
|
||||
|
||||
self.InfoBar = vgui.Create("DPanel", self)
|
||||
self.InfoBar:Dock(TOP)
|
||||
self.InfoBar:SetHeight(40)
|
||||
self.InfoBar.Paint = function(s, w, h)
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(255, 255, 255, 5))
|
||||
|
||||
local activeProf = self:GetActiveProfile()
|
||||
local name = activeProf and activeProf.Name or "No Active Profile"
|
||||
local bindCount = activeProf and table.Count(activeProf.Binds or {}) or 0
|
||||
|
||||
draw.SimpleText(name, "Binder_Main", 50, h / 2, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(Binder.GetPhrase("binds_count", bindCount), "Binder_Small", w - 20, h / 2, Color(255, 255, 255, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
self.KeyboardContainer = vgui.Create("DPanel", self)
|
||||
self.KeyboardContainer:Dock(FILL)
|
||||
self.KeyboardContainer:DockMargin(0, 40, 0, 0)
|
||||
self.KeyboardContainer.Paint = function() end
|
||||
|
||||
self.KeyButtons = {}
|
||||
self:BuildKeyboard()
|
||||
self:Refresh()
|
||||
end
|
||||
|
||||
function PANEL:GetActiveProfile()
|
||||
for _, p in ipairs(Binder.Profiles or {}) do
|
||||
if p.Active then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function PANEL:Refresh()
|
||||
local profile = self:GetActiveProfile()
|
||||
local binds = profile and profile.Binds or {}
|
||||
|
||||
for _, btnData in ipairs(self.KeyButtons) do
|
||||
local isBound = binds[btnData.enum] != nil
|
||||
btnData.btn.IsActiveNode = isBound
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PromptBind(keyStr, keyEnum)
|
||||
local profile = self:GetActiveProfile()
|
||||
if not profile then return end
|
||||
|
||||
local currentCmd = profile.Binds[keyEnum] or ""
|
||||
|
||||
Derma_StringRequest(
|
||||
"Bind " .. keyStr,
|
||||
"Enter console command to execute on press (e.g., 'say /raid'):\nLeave blank to unbind.",
|
||||
currentCmd,
|
||||
function(text)
|
||||
if text == "" then
|
||||
profile.Binds[keyEnum] = nil
|
||||
else
|
||||
profile.Binds[keyEnum] = text
|
||||
end
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function PANEL:CreateKey(parent, text, x, y, w, h)
|
||||
local btn = vgui.Create("DButton", parent)
|
||||
btn:SetSize(w or 35, h or 35)
|
||||
btn:SetPos(x, y)
|
||||
btn:SetText("")
|
||||
btn.IsActiveNode = false
|
||||
|
||||
local keyEnum = STRING_TO_KEY[text]
|
||||
|
||||
btn.Paint = function(s, bw, bh)
|
||||
local bgColor = s.IsActiveNode and Binder.Config.AccentColor or Color(40, 40, 40)
|
||||
if s:IsHovered() then
|
||||
bgColor = Color(bgColor.r + 20, bgColor.g + 20, bgColor.b + 20)
|
||||
end
|
||||
draw.RoundedBox(4, 0, 0, bw, bh, bgColor)
|
||||
draw.SimpleText(text, "Binder_Key", bw / 2, bh / 2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
btn.DoClick = function()
|
||||
if keyEnum then
|
||||
self:PromptBind(text, keyEnum)
|
||||
else
|
||||
print("Binder: Unmapped key - " .. text)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(self.KeyButtons, {btn = btn, sysName = text, enum = keyEnum})
|
||||
end
|
||||
|
||||
function PANEL:BuildKeyboard()
|
||||
local startX, startY = 10, 10
|
||||
local keySize = 38
|
||||
local spacing = 4
|
||||
|
||||
-- Main Block
|
||||
for r, row in ipairs(KEY_LAYOUT) do
|
||||
local rx = startX
|
||||
local ry = startY + (r - 1) * (keySize + spacing)
|
||||
|
||||
-- Adjustments
|
||||
if r == 1 then ry = ry - 5 end
|
||||
|
||||
for k, key in ipairs(row) do
|
||||
local kw = keySize
|
||||
if key == "BKSP" then kw = keySize * 2 + spacing
|
||||
elseif key == "TAB" then kw = keySize * 1.5
|
||||
elseif key == "CAPS" then kw = keySize * 1.8
|
||||
elseif key == "ENTER" then kw = keySize * 1.8
|
||||
elseif key == "SHIFT" then kw = keySize * 2.3
|
||||
elseif key == "SPACE" then kw = keySize * 5 + spacing * 4
|
||||
elseif key == "CTRL" or key == "ALT" or key == "WIN" then kw = keySize * 1.2
|
||||
end
|
||||
|
||||
self:CreateKey(self.KeyboardContainer, key, rx, ry, kw, keySize)
|
||||
rx = rx + kw + spacing
|
||||
end
|
||||
end
|
||||
|
||||
-- Special Keys Block
|
||||
local specX = startX + 15 * (keySize + spacing)
|
||||
for r, row in ipairs(KEY_SPECIAL) do
|
||||
local rx = specX
|
||||
local ry = startY + (r - 1) * (keySize + spacing)
|
||||
if r == 4 then rx = rx + keySize + spacing end
|
||||
|
||||
for k, key in ipairs(row) do
|
||||
self:CreateKey(self.KeyboardContainer, key, rx, ry, keySize, keySize)
|
||||
rx = rx + keySize + spacing
|
||||
end
|
||||
end
|
||||
|
||||
-- Num Pad
|
||||
local numX = specX + 4 * (keySize + spacing)
|
||||
for r, row in ipairs(NUM_PAD) do
|
||||
local rx = numX
|
||||
local ry = startY + (r - 1) * (keySize + spacing)
|
||||
for k, key in ipairs(row) do
|
||||
local kh = keySize
|
||||
if key == "+" or key == "ENT" then kh = keySize * 2 + spacing end
|
||||
|
||||
local kw = keySize
|
||||
if key == "0" then kw = keySize * 2 + spacing end
|
||||
|
||||
self:CreateKey(self.KeyboardContainer, key, rx, ry, kw, kh)
|
||||
rx = rx + kw + spacing
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("Binder_KeybindsTab", PANEL, "EditablePanel")
|
||||
104
garrysmod/addons/Binder/lua/binder/vgui/cl_profiles_tab.lua
Normal file
104
garrysmod/addons/Binder/lua/binder/vgui/cl_profiles_tab.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
-- Garry's Mod Binder - Profiles Tab
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:Dock(FILL)
|
||||
self:DockMargin(40, 20, 40, 20)
|
||||
self.Paint = function() end
|
||||
|
||||
-- Title and Description
|
||||
self.Header = vgui.Create("DPanel", self)
|
||||
self.Header:Dock(TOP)
|
||||
self.Header:SetHeight(50)
|
||||
self.Header.Paint = function(s, w, h)
|
||||
draw.SimpleText(Binder.GetPhrase("profile_management"), "Binder_Title", 0, 0, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
-- Profile Grid
|
||||
self.Grid = vgui.Create("DIconLayout", self)
|
||||
self.Grid:Dock(FILL)
|
||||
self.Grid:SetSpaceX(20)
|
||||
self.Grid:SetSpaceY(20)
|
||||
|
||||
self:Refresh()
|
||||
end
|
||||
|
||||
function PANEL:Refresh()
|
||||
self.Grid:Clear()
|
||||
|
||||
local profiles = Binder.Profiles or {}
|
||||
|
||||
for i, profile in ipairs(profiles) do
|
||||
local card = self.Grid:Add("DButton")
|
||||
card:SetSize(200, 150)
|
||||
card:SetText("")
|
||||
card.Paint = function(s, w, h)
|
||||
local bgColor = profile.Active and Binder.Config.AccentColor or Color(40, 40, 40)
|
||||
draw.RoundedBox(8, 0, 0, w, h, bgColor)
|
||||
|
||||
draw.SimpleText(profile.Name, "Binder_Main", w / 2, h / 2 - 10, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
local bindCount = table.Count(profile.Binds or {})
|
||||
draw.SimpleText(Binder.GetPhrase("binds_count", bindCount), "Binder_Small", w / 2, h / 2 + 15, Color(255, 255, 255, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if profile.Active then
|
||||
draw.RoundedBox(4, w - 15, 10, 8, 8, Color(0, 255, 0)) -- Active dot
|
||||
end
|
||||
end
|
||||
card.DoClick = function()
|
||||
-- Set active
|
||||
for _, p in ipairs(Binder.Profiles) do p.Active = false end
|
||||
profile.Active = true
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
end
|
||||
card.DoRightClick = function()
|
||||
-- Delete profile
|
||||
if #Binder.Profiles > 1 then
|
||||
local menu = DermaMenu()
|
||||
menu:AddOption(Binder.GetPhrase("delete"), function()
|
||||
table.remove(Binder.Profiles, i)
|
||||
-- ensure one is active
|
||||
local hasActive = false
|
||||
for _, p in ipairs(Binder.Profiles) do if p.Active then hasActive = true break end end
|
||||
if not hasActive and #Binder.Profiles > 0 then Binder.Profiles[1].Active = true end
|
||||
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
end):SetIcon("icon16/delete.png")
|
||||
menu:Open()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create New Profile Buttons (to fill up to 6 as in screenshot)
|
||||
if #profiles < 6 then
|
||||
local card = self.Grid:Add("DButton")
|
||||
card:SetSize(200, 150)
|
||||
card:SetText("")
|
||||
card.Paint = function(s, w, h)
|
||||
draw.RoundedBox(8, 0, 0, w, h, Color(40, 40, 40))
|
||||
draw.SimpleText("+", "Binder_Title", w / 2, h / 2 - 15, Color(255, 255, 255, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(Binder.GetPhrase("create_profile"), "Binder_Main", w / 2, h / 2 + 20, Color(255, 255, 255, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
card.DoClick = function()
|
||||
Derma_StringRequest(
|
||||
Binder.GetPhrase("create_profile"),
|
||||
"Enter profile name:",
|
||||
"New Profile",
|
||||
function(text)
|
||||
table.insert(Binder.Profiles, {
|
||||
Name = text,
|
||||
Binds = {},
|
||||
Radial = {},
|
||||
Active = false
|
||||
})
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("Binder_ProfilesTab", PANEL, "EditablePanel")
|
||||
94
garrysmod/addons/Binder/lua/binder/vgui/cl_radial_hud.lua
Normal file
94
garrysmod/addons/Binder/lua/binder/vgui/cl_radial_hud.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
-- Garry's Mod Binder - Radial HUD
|
||||
local IsRadialOpen = false
|
||||
local SelectedSlot = 0
|
||||
|
||||
local function GetActiveProfile()
|
||||
for _, p in ipairs(Binder.Profiles or {}) do
|
||||
if p.Active then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
hook.Add("HUDPaint", "Binder_DrawRadial", function()
|
||||
if not IsRadialOpen then return end
|
||||
|
||||
local profile = GetActiveProfile()
|
||||
if not profile or not profile.Radial then return end
|
||||
|
||||
local cx, cy = ScrW() / 2, ScrH() / 2
|
||||
local radius = 150
|
||||
|
||||
-- Draw Center
|
||||
draw.NoTexture()
|
||||
surface.SetDrawColor(Color(30, 30, 30, 240))
|
||||
surface.DrawCircle(cx, cy, 50, 255, 255, 255, 255)
|
||||
draw.SimpleText(Binder.GetPhrase("radial_center"), "Binder_Main", cx, cy, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
local mouseX, mouseY = gui.MousePos()
|
||||
if mouseX == 0 and mouseY == 0 then
|
||||
mouseX, mouseY = cx, cy -- fallback if not capturing properly
|
||||
end
|
||||
|
||||
local dist = math.Dist(cx, cy, mouseX, mouseY)
|
||||
local angle = math.deg(math.atan2(mouseY - cy, mouseX - cx)) + 90
|
||||
if angle < 0 then angle = angle + 360 end
|
||||
|
||||
-- Determine selected segment based on angle (8 segments, 45 deg each)
|
||||
if dist > 50 then
|
||||
SelectedSlot = math.floor((angle + 22.5) / 45) + 1
|
||||
if SelectedSlot > 8 then SelectedSlot = 1 end
|
||||
else
|
||||
SelectedSlot = 0
|
||||
end
|
||||
|
||||
-- Draw segments
|
||||
for i = 1, 8 do
|
||||
local slotData = profile.Radial[i]
|
||||
if slotData then
|
||||
local segAngle = (i - 1) * 45 - 90
|
||||
local rad = math.rad(segAngle)
|
||||
local lx, ly = cx + math.cos(rad) * radius, cy + math.sin(rad) * radius
|
||||
|
||||
local boxColor = (SelectedSlot == i) and Binder.Config.AccentColor or Color(40, 40, 45, 240)
|
||||
|
||||
draw.RoundedBox(8, lx - 40, ly - 20, 80, 40, boxColor)
|
||||
draw.SimpleText(slotData.Label, "Binder_Main", lx, ly, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
-- Draw cursor line
|
||||
surface.SetDrawColor(255, 255, 255, 50)
|
||||
surface.DrawLine(cx, cy, mouseX, mouseY)
|
||||
end)
|
||||
|
||||
local PrevRadialState = false
|
||||
|
||||
hook.Add("Think", "Binder_RadialThink", function()
|
||||
local cvar = GetConVar("binder_radial_key")
|
||||
local radialKey = cvar and cvar:GetInt() or Binder.Config.DefaultRadialKey
|
||||
|
||||
local isDown = input.IsButtonDown(radialKey)
|
||||
local blockNewInput = gui.IsConsoleVisible() or gui.IsGameUIVisible() or (vgui.GetKeyboardFocus() ~= nil) or (LocalPlayer() and LocalPlayer():IsValid() and LocalPlayer():IsTyping())
|
||||
|
||||
if isDown and not PrevRadialState then
|
||||
if not blockNewInput then
|
||||
PrevRadialState = true
|
||||
IsRadialOpen = true
|
||||
gui.EnableScreenClicker(true)
|
||||
SelectedSlot = 0
|
||||
end
|
||||
elseif not isDown and PrevRadialState then
|
||||
PrevRadialState = false
|
||||
if IsRadialOpen then
|
||||
IsRadialOpen = false
|
||||
gui.EnableScreenClicker(false)
|
||||
|
||||
if SelectedSlot > 0 then
|
||||
local profile = GetActiveProfile()
|
||||
if profile and profile.Radial and profile.Radial[SelectedSlot] then
|
||||
LocalPlayer():ConCommand(profile.Radial[SelectedSlot].Command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
150
garrysmod/addons/Binder/lua/binder/vgui/cl_radial_tab.lua
Normal file
150
garrysmod/addons/Binder/lua/binder/vgui/cl_radial_tab.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
-- Garry's Mod Binder - Radial Menu Tab
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:Dock(FILL)
|
||||
self:DockMargin(40, 20, 40, 20)
|
||||
self.Paint = function() end
|
||||
|
||||
self.Header = vgui.Create("DPanel", self)
|
||||
self.Header:Dock(TOP)
|
||||
self.Header:SetHeight(80)
|
||||
self.Header.Paint = function(s, w, h)
|
||||
draw.SimpleText(Binder.GetPhrase("radial_config"), "Binder_Title", 0, 10, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(Binder.GetPhrase("radial_desc"), "Binder_Small", 0, 40, Color(255, 255, 255, 100), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
self.RadialContainer = vgui.Create("DPanel", self)
|
||||
self.RadialContainer:Dock(FILL)
|
||||
self.RadialContainer.Paint = function(s, w, h)
|
||||
local cx, cy = w / 2, h / 2
|
||||
|
||||
draw.NoTexture()
|
||||
surface.SetDrawColor(Color(255, 255, 255, 10))
|
||||
surface.DrawCircle(cx, cy, 40, 255, 255, 255, 255)
|
||||
draw.SimpleText(Binder.GetPhrase("radial_center"), "Binder_Small", cx, cy, Color(255, 255, 255, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
self.Segments = {}
|
||||
self:BuildSegments()
|
||||
self:Refresh()
|
||||
end
|
||||
|
||||
function PANEL:GetActiveProfile()
|
||||
for _, p in ipairs(Binder.Profiles or {}) do
|
||||
if p.Active then return p end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function PANEL:BuildSegments()
|
||||
local cx, cy = 0, 0 -- Will be updated in PerformLayout
|
||||
local radius = 120
|
||||
|
||||
for i = 1, 8 do
|
||||
local btn = vgui.Create("DButton", self.RadialContainer)
|
||||
btn:SetSize(80, 80)
|
||||
btn:SetText("")
|
||||
btn.Slot = i
|
||||
btn.Paint = function(s, w, h)
|
||||
local bgColor = s:IsHovered() and Binder.Config.AccentColor or Color(40, 40, 45)
|
||||
draw.RoundedBox(20, 0, 0, w, h, bgColor)
|
||||
|
||||
local activeProf = self:GetActiveProfile()
|
||||
local text = Binder.GetPhrase("slot", i)
|
||||
if activeProf and activeProf.Radial and activeProf.Radial[i] then
|
||||
text = activeProf.Radial[i].Label
|
||||
end
|
||||
|
||||
draw.SimpleText(text, "Binder_Small", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
btn.DoClick = function(s)
|
||||
self:PromptSegment(s.Slot)
|
||||
end
|
||||
|
||||
table.insert(self.Segments, btn)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Refresh()
|
||||
-- Nothing inherently needed here unless we drastically change profile
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
local cx, cy = w / 2, (h - 80) / 2
|
||||
local radius = 120
|
||||
|
||||
for i, btn in ipairs(self.Segments) do
|
||||
local angle = (i - 1) * (360 / 8) - 90 -- Start at top
|
||||
local rad = math.rad(angle)
|
||||
local lx, ly = cx + math.cos(rad) * radius, cy + math.sin(rad) * radius
|
||||
|
||||
btn:SetPos(lx - 40, ly - 40)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PromptSegment(slot)
|
||||
local profile = self:GetActiveProfile()
|
||||
if not profile then return end
|
||||
|
||||
profile.Radial = profile.Radial or {}
|
||||
local currentLabel = profile.Radial[slot] and profile.Radial[slot].Label or ""
|
||||
local currentCmd = profile.Radial[slot] and profile.Radial[slot].Command or ""
|
||||
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetTitle("Configure Radial Slot " .. slot)
|
||||
frame:SetSize(300, 150)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
|
||||
local lbl1 = vgui.Create("DLabel", frame)
|
||||
lbl1:SetPos(10, 30)
|
||||
lbl1:SetText("Label (Text):")
|
||||
lbl1:SizeToContents()
|
||||
|
||||
local txtLabel = vgui.Create("DTextEntry", frame)
|
||||
txtLabel:SetPos(10, 50)
|
||||
txtLabel:SetSize(280, 20)
|
||||
txtLabel:SetText(currentLabel)
|
||||
|
||||
local lbl2 = vgui.Create("DLabel", frame)
|
||||
lbl2:SetPos(10, 80)
|
||||
lbl2:SetText("Console Command:")
|
||||
lbl2:SizeToContents()
|
||||
|
||||
local txtCmd = vgui.Create("DTextEntry", frame)
|
||||
txtCmd:SetPos(10, 100)
|
||||
txtCmd:SetSize(280, 20)
|
||||
txtCmd:SetText(currentCmd)
|
||||
|
||||
local saveBtn = vgui.Create("DButton", frame)
|
||||
saveBtn:SetPos(10, 125)
|
||||
saveBtn:SetSize(135, 20)
|
||||
saveBtn:SetText("Save")
|
||||
saveBtn.DoClick = function()
|
||||
local l = txtLabel:GetValue()
|
||||
local c = txtCmd:GetValue()
|
||||
if l == "" or c == "" then
|
||||
profile.Radial[slot] = nil
|
||||
else
|
||||
profile.Radial[slot] = { Label = l, Command = c }
|
||||
end
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
frame:Remove()
|
||||
end
|
||||
|
||||
local clearBtn = vgui.Create("DButton", frame)
|
||||
clearBtn:SetPos(155, 125)
|
||||
clearBtn:SetSize(135, 20)
|
||||
clearBtn:SetText("Clear Slot")
|
||||
clearBtn.DoClick = function()
|
||||
profile.Radial[slot] = nil
|
||||
Binder.SaveToServer()
|
||||
self:Refresh()
|
||||
frame:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("Binder_RadialTab", PANEL, "EditablePanel")
|
||||
182
garrysmod/addons/Binder/lua/binder/vgui/cl_settings_tab.lua
Normal file
182
garrysmod/addons/Binder/lua/binder/vgui/cl_settings_tab.lua
Normal file
@@ -0,0 +1,182 @@
|
||||
-- Garry's Mod Binder - Settings Tab
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:Dock(FILL)
|
||||
self:DockMargin(40, 20, 40, 20)
|
||||
self.Paint = function() end
|
||||
|
||||
self.Scroll = vgui.Create("DScrollPanel", self)
|
||||
self.Scroll:Dock(FILL)
|
||||
|
||||
local sbar = self.Scroll:GetVBar()
|
||||
sbar:SetWide(6)
|
||||
sbar:SetHideButtons(true)
|
||||
sbar.Paint = function(s, w, h) end
|
||||
sbar.btnGrip.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(255, 255, 255, 30))
|
||||
end
|
||||
|
||||
self:AddCategory(Binder.GetPhrase("general_settings"), {
|
||||
{type = "check", label = Binder.GetPhrase("show_feedback"), convar = "binder_show_feedback"},
|
||||
{type = "check", label = Binder.GetPhrase("dark_theme"), convar = "binder_dark_theme"},
|
||||
})
|
||||
|
||||
self:AddCategory("Radial Menu Key", {
|
||||
{type = "keybind", label = "Activation Key", convar = "binder_radial_key"},
|
||||
})
|
||||
|
||||
self:AddCategory(Binder.GetPhrase("import_defaults"), {
|
||||
{type = "button", label = Binder.GetPhrase("import_btn"), btnText = Binder.GetPhrase("import_btn"),
|
||||
onClick = function()
|
||||
Binder.Profiles = {}
|
||||
table.insert(Binder.Profiles, {Name = Binder.GetPhrase("raiding_profile"), Binds = {}, Radial = {}, Active = true})
|
||||
Binder.SaveToServer()
|
||||
if Binder.Frame and IsValid(Binder.Frame) then Binder.Frame:RefreshActiveTab() end
|
||||
end},
|
||||
})
|
||||
|
||||
self:AddCategory(Binder.GetPhrase("appearance_settings"), {
|
||||
{type = "color", label = Binder.GetPhrase("accent_color")},
|
||||
{type = "presets", label = Binder.GetPhrase("preset_colors")},
|
||||
})
|
||||
end
|
||||
|
||||
function PANEL:AddCategory(title, items)
|
||||
local cat = self.Scroll:Add("DPanel")
|
||||
cat:Dock(TOP)
|
||||
cat:DockMargin(0, 0, 0, 20)
|
||||
cat:SetHeight(40 + #items * 40)
|
||||
cat.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, 30, Binder.Config.AccentColor)
|
||||
draw.SimpleText(title, "Binder_Main", 10, 15, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
draw.RoundedBoxEx(4, 0, 30, w, h - 30, Color(40, 40, 40), false, false, true, true)
|
||||
end
|
||||
|
||||
local itemContainer = vgui.Create("DPanel", cat)
|
||||
itemContainer:Dock(FILL)
|
||||
itemContainer:DockMargin(10, 35, 10, 5)
|
||||
itemContainer.Paint = function() end
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
local row = vgui.Create("DPanel", itemContainer)
|
||||
row:Dock(TOP)
|
||||
row:SetHeight(35)
|
||||
row.Paint = function() end
|
||||
|
||||
if item.type == "check" and item.convar then
|
||||
local chk = vgui.Create("DCheckBox", row)
|
||||
chk:SetPos(0, 10)
|
||||
chk:SetConVar(item.convar)
|
||||
|
||||
local lbl = vgui.Create("DLabel", row)
|
||||
lbl:SetText(item.label)
|
||||
lbl:SetFont("Binder_Small")
|
||||
lbl:SetPos(25, 8)
|
||||
lbl:SizeToContents()
|
||||
|
||||
elseif item.type == "keybind" and item.convar then
|
||||
local lbl = vgui.Create("DLabel", row)
|
||||
lbl:SetText(item.label)
|
||||
lbl:SetFont("Binder_Small")
|
||||
lbl:SetPos(0, 8)
|
||||
lbl:SizeToContents()
|
||||
|
||||
local binderBtn = vgui.Create("DBinder", row)
|
||||
binderBtn:SetSize(120, 20)
|
||||
binderBtn:SetPos(150, 8)
|
||||
binderBtn:SetConVar(item.convar)
|
||||
binderBtn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(60, 60, 60))
|
||||
draw.SimpleText(input.GetKeyName(s:GetValue()) or "NONE", "Binder_Small", w/2, h/2, Color(255,255,255), 1, 1)
|
||||
end
|
||||
|
||||
elseif item.type == "button" then
|
||||
local btn = vgui.Create("DButton", row)
|
||||
btn:SetText(item.btnText)
|
||||
btn:SetFont("Binder_Small")
|
||||
btn:SetSize(150, 25)
|
||||
btn:SetPos(0, 5)
|
||||
btn:SetTextColor(Color(255, 255, 255))
|
||||
btn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Binder.Config.AccentColor)
|
||||
end
|
||||
if item.onClick then btn.DoClick = item.onClick end
|
||||
|
||||
elseif item.type == "color" then
|
||||
local lbl = vgui.Create("DLabel", row)
|
||||
lbl:SetText(item.label)
|
||||
lbl:SetFont("Binder_Small")
|
||||
lbl:SetPos(0, 8)
|
||||
lbl:SizeToContents()
|
||||
|
||||
local colPreview = vgui.Create("DPanel", row)
|
||||
colPreview:SetSize(30, 20)
|
||||
colPreview:SetPos(150, 8)
|
||||
colPreview.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Binder.Config.AccentColor)
|
||||
end
|
||||
|
||||
local btn = vgui.Create("DButton", row)
|
||||
btn:SetText(Binder.GetPhrase("change_color"))
|
||||
btn:SetSize(100, 20)
|
||||
btn:SetPos(190, 8)
|
||||
btn:SetFont("Binder_Small")
|
||||
btn:SetTextColor(Color(255, 255, 255))
|
||||
btn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(60, 60, 60))
|
||||
end
|
||||
btn.DoClick = function()
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetTitle("Select Theme Color")
|
||||
frame:SetSize(300, 250)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
|
||||
local mixer = vgui.Create("DColorMixer", frame)
|
||||
mixer:Dock(FILL)
|
||||
mixer:SetPalette(true)
|
||||
mixer:SetAlphaBar(false)
|
||||
mixer:SetWangs(true)
|
||||
mixer:SetColor(Binder.Config.AccentColor)
|
||||
|
||||
local apply = vgui.Create("DButton", frame)
|
||||
apply:Dock(BOTTOM)
|
||||
apply:SetText("Apply")
|
||||
apply.DoClick = function()
|
||||
Binder.Config.AccentColor = mixer:GetColor()
|
||||
if IsValid(Binder.Frame) then Binder.Frame.AccentColor = Binder.Config.AccentColor end
|
||||
frame:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
elseif item.type == "presets" then
|
||||
local lbl = vgui.Create("DLabel", row)
|
||||
lbl:SetText(item.label)
|
||||
lbl:SetFont("Binder_Small")
|
||||
lbl:SetPos(0, 8)
|
||||
lbl:SizeToContents()
|
||||
|
||||
local presets = {Color(0, 67, 28), Color(52, 152, 219), Color(46, 204, 113), Color(231, 76, 60), Color(155, 89, 182), Color(230, 126, 34)}
|
||||
for i, col in ipairs(presets) do
|
||||
local p = vgui.Create("DButton", row)
|
||||
p:SetSize(20, 20)
|
||||
p:SetPos(150 + (i-1)*25, 8)
|
||||
p:SetText("")
|
||||
p.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, col)
|
||||
if Binder.Config.AccentColor == col then
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
end
|
||||
end
|
||||
p.DoClick = function()
|
||||
Binder.Config.AccentColor = col
|
||||
if IsValid(Binder.Frame) then Binder.Frame.AccentColor = col end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("Binder_SettingsTab", PANEL, "EditablePanel")
|
||||
Reference in New Issue
Block a user