Garry's Mod Wiki

Advanced Chatbox

Benefits over the Basic Chatbox

Making a chat box using this method is slightly harder than using the Basic Chatbox and requires a little more attention to detail but it gives you full control over the chatbox as opposed to just simply reskinning it. You can add buttons, image panels, and more by basically coding the chatbox from the bottom up.

Getting started

Setting up

Same set up as the basic chatbox... DFrame, DTextEntry, RichText and a table.

myChat = {} myChat.dFrame = ... myChat.dTextEntry = ... myChat.dRichText = ...

Here is a basic run down of how this type of chat box works by Facepunch user PortalGod.

  • Disable "messagemode" in GM:PlayerBindPress, open vgui chatbox and focus its text entry instead.
  • In myChat.dTextEntry:OnEnter (or OnKeyCodeTyped and check for enter), RunConsoleCommand( "say", myChat.dTextEntry:GetText() ) and hide the chatbox.
  • Hijack chat.AddText and parse the arguments yourself (see RichText), call the original chat.AddText inside it though for colored console text.

Adding hooks

The first thing you need to do is override function called when the player's chat binds are pressed, you do this in a PlayerBindPress hook. This will prevent the default chat box from appearing when the player presses their chat keys. In this function is where you would open up your chat box and I would highly recommend you add a boolean argument to it for team chat. It would be bad if your players tried to talk to their team and it went global.

hook.Add( "PlayerBindPress", "overrideChatbind", function( ply, bind, pressed ) local bTeam = false if bind == "messagemode" then print( "global chat" ) elseif bind == "messagemode2" then print( "team chat" ) bTeam = true else return end myChat.openChatbox( bTeam ) return true -- Doesn't allow any functions to be called for this bind end )

Now you want to display messages for stuff like sv_cheats being changed or a player leaving the game, that kind of stuff is important.

hook.Add( "ChatText", "serverNotifications", function( index, name, text, type ) if type == "joinleave" or type == "none" then myChat.dRichText:AppendText( text .. "\n" ) end end )

Lastly you should also hide the default chat messages since you will add your own to your chat box

hook.Add( "HUDShouldDraw", "noMoreDefault", function( name ) if name == "CHudChat" then return false end end )

chat.AddText

If you do not override chat.AddText, all the calls to that function will appear in the default chatbox which you have hidden. We want the player to see this so we will override the original function with one of our own. The source code for this function is not available, it likely isn't in Lua, so we will save the old function and just call that. You might notice that the arguments are ellipses (...), this is a deprecated Lua functionality that gLua has called varargs that basically means you can pass pretty much any number of arguments to that function.

local oldAddText = chat.AddText function chat.AddText( ... ) local args = {...} -- Create a table of varargs for _, obj in ipairs( args ) do if type( obj ) == "table" then -- We were passed a color object myChat.dRichText:InsertColorChange( obj.r, obj.g, obj.b, 255 ) elseif type( obj ) == "string" then -- This is just a string myChat.dRichText:AppendText( obj ) elseif obj:IsPlayer() then local col = GAMEMODE:GetTeamColor( obj ) -- Get the player's team color myChat.dRichText:InsertColorChange( col.r, col.g, col.b, 255 ) -- Make their name that color myChat.dRichText:AppendText( obj:Nick() ) end end -- Gotta end our line for this message myChat.dRichText:AppendText( "\n" ) -- Call the original function oldAddText( ... ) end

Restoring functionality

Now that we have overridden the default chat box with one of our own, the hooks that are called by the default chat box won't be called anymore. We need to fix this.

In your chat box's text entry, you need to tell the player to say the text so the proper gamemode hooks are called for that. It would also be a good idea to make sure that the Escape key closes your chat box so we account for that too

myChat.dTextEntry.OnKeyCodeTyped = function( self, code ) if code == KEY_ESCAPE then -- Work around to hide the chatbox when the client presses escape myChat.closeChatbox() gui.HideGameUI() elseif code == KEY_ENTER then -- Replicate the client pressing enter if string.Trim( self:GetText() ) != "" then LocalPlayer():ConCommand( "say " .. self:GetText() ) end myChat.closeChatbox() end end

When opening the chat box you are going to want to call the function for that even and then make sure your DTextEntry is available to be typed in

function myChat.openChatbox() -- Stuff -- MakePopup calls the input functions so we don't need to call those myChat.dFrame:MakePopup() myChat.dTextEntry:RequestFocus() hook.Run( "StartChat" ) -- More stuff end

When the player closes your chat box you need to make sure they can move around again and that once again the proper gamemode function are called.

function myChat.closeChatbox() -- Stuff -- Give the player control again myChat.dFrame:SetMouseInputEnabled( false ) myChat.dFrame:SetKeyboardInputEnabled( false ) gui.EnableScreenClicker( false ) -- We are done chatting hook.Run( "FinishChat" ) -- Clear the text entry myChat.dTextEntry:SetText( "" ) hook.Run( "ChatTextChanged", "" ) -- More stuff end

Finishing

Summary

Although this is quick and covers very little code wise, it should hopefully be a useful tool for anyone looking to code their own chatbox and add their own spin to it. It contains the basics of what is used to create a chatbox of this scale without any of the visual code, a backend of sorts. This doesn't do anything fancy by default like having embeddable images or any of that, but it is possible using this as a base

Examples