diff --git a/lua/cbox/cl_chatbox.lua b/lua/cbox/cl_chatbox.lua index e184f79..e5a02e2 100644 --- a/lua/cbox/cl_chatbox.lua +++ b/lua/cbox/cl_chatbox.lua @@ -1,12 +1,32 @@ -local DisableClipping = DisableClipping local ScrW = ScrW local ScrH = ScrH +local Color = Color +local STENCIL_EQUAL = STENCIL_EQUAL +local STENCIL_KEEP = STENCIL_KEEP +local STENCIL_NEVER = STENCIL_NEVER +local STENCIL_REPLACE = STENCIL_REPLACE + +local draw = draw +local math = math local render = render -local string = string local surface = surface +local draw_NoTexture = draw.NoTexture +local math_rad = math.rad +local math_sin = math.sin +local math_cos = math.cos +local render_ClearStencil = render.ClearStencil +local render_SetStencilCompareFunction = render.SetStencilCompareFunction +local render_SetStencilEnable = render.SetStencilEnable +local render_SetStencilFailOperation = render.SetStencilFailOperation +local render_SetStencilPassOperation = render.SetStencilPassOperation +local render_SetStencilReferenceValue = render.SetStencilReferenceValue +local render_SetStencilTestMask = render.SetStencilTestMask +local render_SetStencilWriteMask = render.SetStencilWriteMask +local render_SetStencilZFailOperation = render.SetStencilZFailOperation local render_UpdateScreenEffectTexture = render.UpdateScreenEffectTexture +local surface_DrawPoly = surface.DrawPoly local surface_DrawRect = surface.DrawRect local surface_DrawTexturedRect = surface.DrawTexturedRect local surface_SetDrawColor = surface.SetDrawColor @@ -19,6 +39,7 @@ cbox.chatbox.panels = cbox.chatbox.panels or {} local CHATBOX_COLOR = CreateClientConVar("cbox_chatbox_color", "160 160 160", true, false, "Chatbox background color") local CHATBOX_ALPHA = CreateClientConVar("cbox_chatbox_alpha", "128", true, false, "Chatbox background alpha") local CHATBOX_BLUR = CreateClientConVar("cbox_chatbox_blur", "0", true, false, "Chatbox background is blurred") +local CHATBOX_FADE = CreateClientConVar("cbox_chatbox_fade", "1", true, false, "Chatbox fades in and out") ---@param height number ---@return number @@ -50,6 +71,55 @@ end local MATERIAL_BLUR = Material("pp/blurscreen") +local function add_rounded_poly(poly, x, y, rad, seg, offset) + offset = offset or 0 + + for i = 0, seg do + local r = math_rad(((i + offset) / seg) * -90) + poly[#poly + 1] = { + x = x + math_sin(r) * rad, + y = y + math_cos(r) * rad, + u = math_sin(r) / 2 + 0.5, + v = math_cos(r) / 2 + 0.5, + } + end +end + +local function RoundedBoxPoly(x, y, w, h, rad, seg) + local poly = {} + + add_rounded_poly(poly, x + rad, y + rad, rad, seg, seg) + + poly[#poly + 1] = { + x = x + (w - rad), + y = y, + u = 0.5, + v = 0.5, + } + + add_rounded_poly(poly, x + (w - rad), y + rad, rad, seg, seg * 2) + + poly[#poly + 1] = { + x = x + w, + y = y + (h - rad), + u = 0.5, + v = 0.5, + } + + add_rounded_poly(poly, x + (w - rad), y + (h - rad), rad, seg, seg * 3) + + poly[#poly + 1] = { + x = x + rad, + y = y + h, + u = 0.5, + v = 0.5, + } + + add_rounded_poly(poly, x + rad, y + (h - rad), rad, seg) + + surface_DrawPoly(poly) +end + local function CreateChatbox() if IsValid(cbox.chatbox.panels.frame) then cbox.chatbox.panels.frame:Remove() @@ -60,15 +130,85 @@ local function CreateChatbox() frame:SetDeleteOnClose(false) frame:SetSizable(true) frame:SetScreenLock(true) - frame:DockPadding(4, 4, 4, 4) + frame:DockPadding(8, 8, 8, 8) - frame.btnMinim:SetVisible(false) - frame.btnMaxim:SetVisible(false) - frame.lblTitle:SetVisible(false) + local dx, dy, dw, dh = GetDefaultBounds() + + -- TODO: make this configurable + frame:SetMinWidth(dw) + frame:SetMinWidth(dh) + + -- TODO: save resizing/moving + frame:SetPos(dx, dy) + frame:SetSize(dw, dh) + + frame:SetVisible(false) + + function frame:PerformLayout() end + + frame.btnClose:Remove() + frame.btnMinim:Remove() + frame.btnMaxim:Remove() + frame.lblTitle:Remove() + + function frame:CrossFade(anim, delta, out) + if anim.Finished then + if out then + self:Close() + else + self:MakePopup() + end + end + + if anim.Started then + if out then + self:SetAlpha(255) + else + self:SetAlpha(0) + end + end + + self:SetAlpha(out and 255 * (1 - delta) or 255 * delta) + end + + frame.animFade = Derma_Anim("Fade", frame, frame.CrossFade) + + frame.oldThink = frame.Think + + function frame:Think() + self:oldThink() + + self.animFade:Run() + + -- TODO: save position and size + end function frame:Paint(w, h) local alpha = CHATBOX_ALPHA:GetInt() + -- Reset everything to known good + render_SetStencilWriteMask( 0xFF ) + render_SetStencilTestMask( 0xFF ) + render_SetStencilReferenceValue( 0 ) + render_SetStencilPassOperation( STENCIL_KEEP ) + render_SetStencilZFailOperation( STENCIL_KEEP ) + render_ClearStencil() + + -- Enable stencils + render_SetStencilEnable( true ) + -- Set everything up everything draws to the stencil buffer instead of the screen + render_SetStencilReferenceValue( 1 ) + render_SetStencilCompareFunction( STENCIL_NEVER ) + render_SetStencilFailOperation( STENCIL_REPLACE ) + + draw_NoTexture() + surface_SetDrawColor(255, 255, 255) + RoundedBoxPoly(0, 0, w, h, 8, 24) + + -- Only draw things that are in the stencil buffer + render_SetStencilCompareFunction( STENCIL_EQUAL ) + render_SetStencilFailOperation( STENCIL_KEEP ) + if CHATBOX_BLUR:GetBool() and alpha ~= 255 then local x, y = self:LocalToScreen(0, 0) @@ -83,32 +223,26 @@ local function CreateChatbox() end end - local color = string.Explode(" ", CHATBOX_COLOR:GetString()) - surface_SetDrawColor(tonumber(color[1]), tonumber(color[2]), tonumber(color[3]), alpha) + local r, g, b = CHATBOX_COLOR:GetString():match("(%d+) (%d+) (%d+)") + surface_SetDrawColor(r, g, b, alpha) surface_DrawRect(0, 0, w, h) + + -- Let everything render normally again + render_SetStencilEnable(false) end - local dx, dy, dw, dh = GetDefaultBounds() - - -- TODO: make this configurable - frame:SetMinWidth(dw) - frame:SetMinWidth(dh) - - -- TODO: save resizing/moving - frame:SetPos(dx, dy) - frame:SetSize(dw, dh) - - frame:SetVisible(false) - cbox.chatbox.panels.frame = frame local tabs = vgui.Create("DPropertySheet", frame, "cbox.chatbox.tabs") tabs:Dock(FILL) tabs:SetPadding(0) - function tabs:Paint(w, h) - surface_SetDrawColor(0, 0, 0, 128) - surface_DrawRect(0, 20, w, h - 20) + function tabs:Paint(w, h) end + + function tabs:OnActiveTabChanged( old, new ) + if new:GetPanel().cbox_id == "\1chat" then + cbox.chatbox.panels.input:RequestFocus() + end end for id, tab in next, cbox.chatbox.tabs do @@ -123,10 +257,9 @@ local function CreateChatbox() continue end - tabs:AddSheet(tab.name, ret, tab.icon) + local sheet = tabs:AddSheet(tab.name, ret, tab.icon) + sheet.Panel.cbox_id = id end - - frame.btnClose:SetZPos(99) end local function Init() @@ -142,6 +275,7 @@ local function Init() hook.Add("PlayerBindPress", "cbox.chatbox", function(ply, bind, pressed) if bind ~= "messagemode" and bind ~= "messagemode2" then return end + if not pressed then return end cbox.chatbox.Open(bind == "messagemode2") @@ -154,25 +288,32 @@ end function cbox.chatbox.Open(alt) alt = alt ~= nil and alt or false - if not IsValid(cbox.chatbox.panels.frame) then + local frame = cbox.chatbox.panels.frame + + if not IsValid(frame) then CreateChatbox() end - cbox.chatbox.panels.frame:SetVisible(true) - cbox.chatbox.panels.frame:MakePopup() + frame:SetVisible(true) + + if frame.animFade and CHATBOX_FADE:GetBool() then + frame.animFade:Start(0.1, false) + else + frame:MakePopup() + end if not IsValid(cbox.chatbox.panels.input) then -- attempt to reinit - if IsValid(cbox.chatbox.panels.frame) then - cbox.chatbox.panels.frame:Remove() + if IsValid(frame) then + frame:Remove() end Init() end if not IsValid(cbox.chatbox.panels.input) then cbox.utils.RealmError("Input isn't valid, chat tab failed to load, bailing!") - if IsValid(cbox.chatbox.panels.frame) then - cbox.chatbox.panels.frame:Remove() + if IsValid(frame) then + frame:Remove() end hook.Remove("PlayerBindPress", "cbox.chatbox") return @@ -185,7 +326,13 @@ end ---Closes the chatbox function cbox.chatbox.Close() - cbox.chatbox.panels.frame:Close() + local frame = cbox.chatbox.panels.frame + + if frame.animFade and CHATBOX_FADE:GetBool() then + frame.animFade:Start(0.1, true) + else + frame:Close() + end hook.Run("FinishChat") diff --git a/lua/cbox/tabs/chat.lua b/lua/cbox/tabs/chat.lua index 37063de..0b1d8c4 100644 --- a/lua/cbox/tabs/chat.lua +++ b/lua/cbox/tabs/chat.lua @@ -1,9 +1,23 @@ +local Color = Color + +local surface = surface + +local surface_DrawRect = surface.DrawRect +local surface_SetDrawColor = surface.SetDrawColor + +local INPUT_TEXT_COLOR = Color(221, 221, 221) +local INPUT_HIGHLIGHT_COLOR = Color(192, 28, 0, 140) + cbox.chatbox.AddTab("\1chat", "Chat", "icon16/comments.png", function() local wrapper = vgui.Create("EditablePanel") -- TODO: custom richtext panel local history = vgui.Create("RichText", wrapper) history:Dock(FILL) + function history:Paint(w, h) + surface_SetDrawColor(0, 0, 0, 128) + surface_DrawRect(0, 0, w, h) + end cbox.chatbox.panels.history = history function history:PerformLayout() @@ -13,18 +27,35 @@ cbox.chatbox.AddTab("\1chat", "Chat", "icon16/comments.png", function() local input_wrapper = vgui.Create("EditablePanel", wrapper) input_wrapper:SetHeight(20) + input_wrapper:DockMargin(0, 8, 0, 0) input_wrapper:Dock(BOTTOM) local input = vgui.Create("DTextEntry", input_wrapper) + input:DockMargin(4, 0, 0, 0) input:Dock(FILL) + input:SetFont("ChatFont") cbox.chatbox.panels.input = input + function input:Paint(w, h) + surface_SetDrawColor(0, 0, 0, 128) + surface_DrawRect(0, 0, w, h) + + self:DrawTextEntryText(INPUT_TEXT_COLOR, INPUT_HIGHLIGHT_COLOR, INPUT_TEXT_COLOR) + end + local mode_switch = vgui.Create("DButton", input_wrapper) + mode_switch:SetFont("ChatFont") + mode_switch:SetTextColor(INPUT_TEXT_COLOR) mode_switch:SetText("Say") mode_switch:SizeToContents() mode_switch:Dock(LEFT) cbox.chatbox.panels.mode_switch = mode_switch + function mode_switch:Paint(w, h) + surface_SetDrawColor(0, 0, 0, 128) + surface_DrawRect(0, 0, w, h) + end + function mode_switch:DoClick() -- TODO end diff --git a/lua/cbox/tabs/settings.lua b/lua/cbox/tabs/settings.lua new file mode 100644 index 0000000..3de4997 --- /dev/null +++ b/lua/cbox/tabs/settings.lua @@ -0,0 +1,14 @@ +local surface = surface + +local surface_DrawRect = surface.DrawRect +local surface_SetDrawColor = surface.SetDrawColor + +cbox.chatbox.AddTab("zzzzzzzsettings", "Settings", "icon16/cog.png", function() + local wrapper = vgui.Create("EditablePanel") + function wrapper:Paint(w, h) + surface_SetDrawColor(0, 0, 0, 128) + surface_DrawRect(0, 0, w, h) + end + + return wrapper +end)