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
This commit is contained in:
@@ -0,0 +1,191 @@
|
|||||||
|
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
|
||||||
Reference in New Issue
Block a user