Files
bladwin-RP/lua/bladw_announcement/client/admin_menu/cl_menu.lua
T
nocode b183517e9f feat(admin_menu): add announcement admin menu interface
Implement a custom GUI menu for administrators to send announcements with the following features:
- Support for announcement presets that auto-fill title and message fields
- Real-time character count validation with visual feedback
- Warning indicator when message exceeds max length limit
- Custom styled text input fields with placeholder text
- Rounded corners and custom color scheme matching announcement styling
- Close button and ESC key handling
- Network message handling to send announcements to server
2026-07-20 13:48:05 +02:00

192 lines
7.2 KiB
Lua

local rndx = include("lib/rndx.lua")
local logo = Material("nocode/announcement/logo.png")
BladW = BladW or {}
BladW.Announcement = BladW.Announcement or {}
-- mêmes couleurs que les annonces
local COL_BG = Color(0, 0, 0, 200)
local COL_PANEL = Color(0, 0, 0, 160)
local COL_ACCENT = Color(76, 119, 200)
local COL_ACCENT_DIM = Color(60, 90, 150)
local COL_TEXT = Color(255, 255, 255)
local COL_RED = Color(255, 80, 80)
local COL_GRAY = Color(54, 54, 54, 255)
local COL_MUTED = Color(160, 160, 160)
local COL_WARN = Color(255, 200, 50)
-- nb de caractères d'un champ (UTF-8, un accent = 1)
local function EntryLen(entry)
local v = entry:GetValue()
return utf8.len(v) or #v
end
-- petit triangle warning dessiné à la main (comme ça pas besoin de font emoji)
local function DrawWarning(x, y, size)
draw.NoTexture()
surface.SetDrawColor(COL_WARN)
surface.DrawPoly({
{ x = x + size * 0.5, y = y },
{ x = x + size, y = y + size },
{ x = x, y = y + size },
})
draw.SimpleText("!", "DermaDefaultBold", x + size * 0.5, y + size * 0.62, Color(30, 30, 30), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
-- mon style pour les champs de texte
local function StyleEntry(entry, placeholder, default)
entry:SetFont("bladw_text_Medium")
entry:SetText(default or "")
entry:SetPlaceholderText(placeholder or "")
entry:SetTextColor(COL_TEXT)
entry:SetPaintBackground(false)
entry.Paint = function(self, w, h)
rndx.Draw(10, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA)
local outline = self:IsEditing() and COL_ACCENT or Color(255, 255, 255, 60)
rndx.DrawOutlined(10, 0, 0, w, h, outline, 2, rndx.SHAPE_FIGMA)
self:DrawTextEntryText(COL_TEXT, COL_ACCENT, COL_TEXT)
end
end
local function OpenAAdminMenu()
-- si le menu est déjà ouvert je le vire
if IsValid(BladW_AdminMenu) then BladW_AdminMenu:Remove() end
local selectedPreset = ""
local maxLen = (BladW.Announcement.Conf or {}).announcement_max_length or 120
local frame = vgui.Create("DFrame")
BladW_AdminMenu = frame
frame:SetSize(500, 460)
frame:Center()
frame:SetTitle("")
frame:ShowCloseButton(false)
frame:MakePopup()
frame.Paint = function(self, w, h)
-- fond flou + sombre + contour blanc (comme la box d'annonce)
rndx.Draw(20, 0, 0, w, h, nil, rndx.SHAPE_FIGMA + rndx.BLUR)
rndx.Draw(20, 0, 0, w, h, COL_BG, rndx.SHAPE_FIGMA)
rndx.DrawOutlined(20, 0, 0, w, h, COL_TEXT, 3, rndx.SHAPE_FIGMA)
-- logo + titre
rndx.DrawMaterial(0, w * 0.5 - 30, 20, 60, 60, COL_TEXT, logo)
draw.SimpleText("Envoyer une annonce", "bladw_text", w * 0.5, 94, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
draw.SimpleText("Presets", "bladw_text_Medium", 40, 118, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
-- ESC ferme mon menu sans laisser le menu pause s'afficher.
-- le moteur ouvre le menu pause sur ESC avant que je puisse l'intercepter, donc je le recache direct et je ferme.
frame.Think = function(self)
if gui.IsGameUIVisible() then
gui.HideGameUI()
self:Remove()
end
end
-- bouton pour fermer
local close = vgui.Create("DButton", frame)
close:SetSize(30, 30)
close:SetPos(frame:GetWide() - 42, 14)
close:SetText("")
close.Paint = function(self, w, h)
rndx.Draw(8, 0, 0, w, h, self:IsHovered() and COL_RED or COL_GRAY, rndx.SHAPE_FIGMA)
draw.SimpleText("X", "bladw_text_Medium", w * 0.5, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
close.DoClick = function() frame:Remove() end
-- les champs (créés avant les boutons preset pour être captés dans leurs closures)
local titleEntry = vgui.Create("DTextEntry", frame)
titleEntry:SetPos(40, 240)
titleEntry:SetSize(420, 40)
StyleEntry(titleEntry, "Titre de l'annonce", "Annonce")
local msgEntry = vgui.Create("DTextEntry", frame)
msgEntry:SetPos(40, 288)
msgEntry:SetSize(420, 40)
StyleEntry(msgEntry, "Message de l'annonce")
-- bord rouge quand ça dépasse la limite
msgEntry.Paint = function(self, w, h)
local over = EntryLen(self) > maxLen
rndx.Draw(10, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA)
local outline = over and COL_RED or (self:IsEditing() and COL_ACCENT or Color(255, 255, 255, 60))
rndx.DrawOutlined(10, 0, 0, w, h, outline, over and 3 or 2, rndx.SHAPE_FIGMA)
self:DrawTextEntryText(COL_TEXT, COL_ACCENT, COL_TEXT)
end
-- ligne du bas : warning à gauche, compteur à droite
local status = vgui.Create("DPanel", frame)
status:SetPos(40, 332)
status:SetSize(420, 26)
status:SetPaintBackground(false)
status.Paint = function(self, w, h)
local len = EntryLen(msgEntry)
local over = len > maxLen
if over then
DrawWarning(0, h * 0.5 - 8, 16)
draw.SimpleText("Trop de caractères", "bladw_text_Medium", 24, h * 0.5, COL_RED, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
draw.SimpleText(len .. " / " .. maxLen, "bladw_text_Medium", w, h * 0.5,
over and COL_RED or COL_MUTED, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
-- boutons preset : remplissent les champs et retiennent le preset choisi
local presets = vgui.Create("DIconLayout", frame)
presets:SetPos(40, 134)
presets:SetSize(420, 90)
presets:SetSpaceX(8)
presets:SetSpaceY(8)
for key, preset in SortedPairs(BladW.Announcement.Presets or {}) do
local b = presets:Add("DButton")
b:SetSize(134, 34)
b:SetText("")
b.Paint = function(self, w, h)
local on = (selectedPreset == key) or self:IsHovered()
rndx.Draw(8, 0, 0, w, h, on and COL_ACCENT or COL_GRAY, rndx.SHAPE_FIGMA)
draw.SimpleText(preset.title or key, "bladw_text_Medium", w * 0.5, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
b.DoClick = function()
selectedPreset = key
titleEntry:SetText(preset.title or "")
msgEntry:SetText(preset.text or "")
end
end
-- bouton envoyer (grisé + bloqué si ça dépasse)
local send = vgui.Create("DButton", frame)
send:SetPos(40, 372)
send:SetSize(420, 50)
send:SetText("")
send.Paint = function(self, w, h)
local over = EntryLen(msgEntry) > maxLen
local col = over and COL_GRAY or (self:IsHovered() and COL_ACCENT or COL_ACCENT_DIM)
rndx.Draw(12, 0, 0, w, h, col, rndx.SHAPE_FIGMA)
draw.SimpleText("Envoyer", "bladw_text", w * 0.5, h * 0.5, over and COL_MUTED or COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
send.DoClick = function()
if EntryLen(msgEntry) > maxLen then
surface.PlaySound("buttons/button10.wav")
return
end
if msgEntry:GetValue() == "" and selectedPreset == "" then
surface.PlaySound("buttons/button10.wav")
return
end
net.Start("bladWSendAnnouncement")
net.WriteString(selectedPreset)
net.WriteString(titleEntry:GetValue())
net.WriteString(msgEntry:GetValue())
net.SendToServer()
frame:Remove()
end
end
return OpenAAdminMenu