· 7 years ago · Jan 18, 2019, 07:14 PM
1--XGUI: A GUI for ULX -- by Stickly Man!
2xgui = xgui or {}
3
4--Make a spot for modules to store data and hooks
5xgui.data = xgui.data or {}
6xgui.hook = xgui.hook or { onProcessModules={}, onOpen={}, onClose={} }
7--Call this function in your client-side module code to ensure the data types have been instantiated on the client.
8function xgui.prepareDataType( dtype, location )
9 if not xgui.data[dtype] then
10 xgui.data[dtype] = location or {}
11 xgui.hook[dtype] = { clear={}, process={}, done={}, add={}, update={}, remove={}, data={} }
12 end
13end
14
15--Set up various hooks modules can "hook" into.
16function xgui.hookEvent( dtype, event, func, name )
17 if not xgui.hook[dtype] or ( event and not xgui.hook[dtype][event] ) then
18 Msg( "XGUI: Attempted to add to invalid type or event to a hook! (" .. dtype .. ", " .. ( event or "nil" ) .. ")\n" )
19 else
20 if not name then name = "FixMe" .. math.floor(math.random()*10000) end -- Backwards compatibility for older XGUI modules
21 if not event then
22 xgui.hook[dtype][name] = func
23 else
24 xgui.hook[dtype][event][name] = func
25 end
26 end
27end
28
29--Set up tables and functions for creating and storing modules
30xgui.modules = xgui.modules or {}
31
32xgui.modules.tab = xgui.modules.tab or {}
33function xgui.addModule( name, panel, icon, access, tooltip )
34 local refreshModules = false
35 for i = #xgui.modules.tab, 1, -1 do
36 if xgui.modules.tab[i].name == name then
37 xgui.modules.tab[i].panel:Remove()
38 xgui.modules.tab[i].tabpanel:Remove()
39 xgui.modules.tab[i].xbutton:Remove()
40 table.remove(xgui.modules.tab, i)
41 refreshModules = true
42 end
43 end
44 table.insert( xgui.modules.tab, { name=name, panel=panel, icon=icon, access=access, tooltip=tooltip } )
45 if refreshModules then xgui.processModules() end
46end
47
48xgui.modules.setting = xgui.modules.setting or {}
49function xgui.addSettingModule( name, panel, icon, access, tooltip )
50 local refreshModules = false
51 for i = #xgui.modules.setting, 1, -1 do
52 if xgui.modules.setting[i].name == name then
53 xgui.modules.setting[i].panel:Remove()
54 xgui.modules.setting[i].tabpanel:Remove()
55 table.remove(xgui.modules.setting, i)
56 refreshModules = true
57 end
58 end
59 table.insert( xgui.modules.setting, { name=name, panel=panel, icon=icon, access=access, tooltip=tooltip } )
60 if refreshModules then xgui.processModules() end
61end
62
63xgui.modules.submodule = xgui.modules.submodule or {}
64function xgui.addSubModule( name, panel, access, mtype )
65 local refreshModules = false
66 for i = #xgui.modules.submodule, 1, -1 do
67 if xgui.modules.submodule[i].name == name then
68 xgui.modules.submodule[i].panel:Remove()
69 table.remove(xgui.modules.submodule, i)
70 refreshModules = true
71 end
72 end
73 table.insert( xgui.modules.submodule, { name=name, panel=panel, access=access, mtype=mtype } )
74 if refreshModules then xgui.processModules() end
75end
76--Set up a spot to store entries for autocomplete.
77xgui.tabcompletes = xgui.tabcompletes or {}
78xgui.ulxmenucompletes = xgui.ulxmenucompletes or {}
79
80
81--Set up XGUI clientside settings, load settings from file if it exists
82xgui.settings = xgui.settings or {}
83if ULib.fileExists( "data/ulx/xgui_settings.txt" ) then
84 local input = ULib.fileRead( "data/ulx/xgui_settings.txt" )
85 input = input:match( "^.-\n(.*)$" )
86 xgui.settings = ULib.parseKeyValues( input )
87end
88--Set default settings if they didn't get loaded
89if not xgui.settings.moduleOrder then xgui.settings.moduleOrder = { "Cmds", "Groups", "Maps", "Settings", "Bans" } end
90if not xgui.settings.settingOrder then xgui.settings.settingOrder = { "Sandbox", "Server", "Client" } end
91if not xgui.settings.animTime then xgui.settings.animTime = 0.22 else xgui.settings.animTime = tonumber( xgui.settings.animTime ) end
92if not xgui.settings.infoColor then
93 --Default color
94 xgui.settings.infoColor = Color( 100, 255, 255, 128 )
95else
96 --Ensure that the color contains numbers, not strings
97 xgui.settings.infoColor = Color(xgui.settings.infoColor.r, xgui.settings.infoColor.g, xgui.settings.infoColor.b, xgui.settings.infoColor.a)
98end
99if not xgui.settings.showLoadMsgs then xgui.settings.showLoadMsgs = true else xgui.settings.showLoadMsgs = ULib.toBool( xgui.settings.showLoadMsgs ) end
100if not xgui.settings.skin then xgui.settings.skin = "Default" end
101if not xgui.settings.xguipos then xgui.settings.xguipos = { pos=5, xoff=0, yoff=0 } end
102if not xgui.settings.animIntype then xgui.settings.animIntype = 1 end
103if not xgui.settings.animOuttype then xgui.settings.animOuttype = 1 end
104
105
106function xgui.init( ply )
107 xgui.load_helpers()
108
109 --Initiate the base window (see xgui_helpers.lua for code)
110 xgui.makeXGUIbase{}
111
112 --Create the bottom infobar
113 xgui.infobar = xlib.makepanel{ x=10, y=399, w=580, h=20, parent=xgui.anchor }
114 xgui.infobar:NoClipping( true )
115 xgui.infobar.Paint = function( self, w, h )
116 draw.RoundedBoxEx( 4, 0, 1, 580, 20, xgui.settings.infoColor, false, false, true, true )
117 end
118 local infoLabel = string.format( "\nULX Admin Mod :: XGUI - Team Ulysses | ULX %s | ULib %s", ULib.pluginVersionStr("ULX"), ULib.pluginVersionStr("ULib") )
119 xlib.makelabel{ x=5, y=-10, label=infoLabel, parent=xgui.infobar }:NoClipping( true )
120 xgui.thetime = xlib.makelabel{ x=515, y=-10, label="", parent=xgui.infobar }
121 xgui.thetime:NoClipping( true )
122 xgui.thetime.check = function()
123 xgui.thetime:SetText( os.date( "\n%I:%M:%S %p" ) )
124 xgui.thetime:SizeToContents()
125 timer.Simple( 1, xgui.thetime.check )
126 end
127 xgui.thetime.check()
128
129 --Create an offscreen place to parent modules that the player can't access
130 xgui.null = xlib.makepanel{ x=-10, y=-10, w=0, h=0 }
131 xgui.null:SetVisible( false )
132
133 --Load modules
134 local sm = xgui.settings.showLoadMsgs
135 if sm then
136 Msg( "\n///////////////////////////////////////\n" )
137 Msg( "// ULX GUI -- Made by Stickly Man! //\n" )
138 Msg( "///////////////////////////////////////\n" )
139 Msg( "// Loading GUI Modules... //\n" )
140 end
141 for _, file in ipairs( file.Find( "ulx/xgui/*.lua", "LUA" ) ) do
142 include( "ulx/xgui/" .. file )
143 if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
144 end
145 if sm then Msg( "// Loading Setting Modules... //\n" ) end
146 for _, file in ipairs( file.Find( "ulx/xgui/settings/*.lua", "LUA" ) ) do
147 include( "ulx/xgui/settings/" .. file )
148 if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
149 end
150 if sm then Msg( "// Loading Gamemode Module(s)... //\n" ) end
151 if ULib.isSandbox() and GAMEMODE.FolderName ~= "sandbox" then -- If the gamemode sandbox-derived (but not sandbox, that will get added later), then add the sandbox Module
152 include( "ulx/xgui/gamemodes/sandbox.lua" )
153 if sm then Msg( "// sandbox.lua //\n" ) end
154 end
155 for _, file in ipairs( file.Find( "ulx/xgui/gamemodes/*.lua", "LUA" ) ) do
156 if string.lower( file ) == string.lower( GAMEMODE.FolderName .. ".lua" ) then
157 include( "ulx/xgui/gamemodes/" .. file )
158 if sm then Msg( "// " .. file .. string.rep( " ", 32 - file:len() ) .. "//\n" ) end
159 break
160 end
161 if sm then Msg( "// No module found! //\n" ) end
162 end
163 if sm then Msg( "// Modules Loaded! //\n" ) end
164 if sm then Msg( "///////////////////////////////////////\n\n" ) end
165
166 --Find any existing modules that aren't listed in the requested order.
167 local function checkModulesOrder( moduleTable, sortTable )
168 for _, m in ipairs( moduleTable ) do
169 local notlisted = true
170 for _, existing in ipairs( sortTable ) do
171 if m.name == existing then
172 notlisted = false
173 break
174 end
175 end
176 if notlisted then
177 table.insert( sortTable, m.name )
178 end
179 end
180 end
181 checkModulesOrder( xgui.modules.tab, xgui.settings.moduleOrder )
182 checkModulesOrder( xgui.modules.setting, xgui.settings.settingOrder )
183
184 --Check if the server has XGUI installed
185 RunConsoleCommand( "_xgui", "getInstalled" )
186
187 xgui.initialized = true
188
189 xgui.processModules()
190end
191hook.Add( ULib.HOOK_LOCALPLAYERREADY, "InitXGUI", xgui.init, HOOK_MONITOR_LOW )
192
193function xgui.saveClientSettings()
194 if not ULib.fileIsDir( "data/ulx" ) then
195 ULib.fileCreateDir( "data/ulx" )
196 end
197 local output = "// This file stores clientside settings for XGUI.\n"
198 output = output .. ULib.makeKeyValues( xgui.settings )
199 ULib.fileWrite( "data/ulx/xgui_settings.txt", output )
200end
201
202function xgui.checkModuleExists( modulename, moduletable )
203 for k, v in ipairs( moduletable ) do
204 if v.name == modulename then
205 return k
206 end
207 end
208 return false
209end
210
211function xgui.processModules()
212 local activetab = nil
213 if xgui.base:GetActiveTab() then
214 activetab = xgui.base:GetActiveTab():GetValue()
215 end
216
217 local activesettingstab = nil
218 if xgui.settings_tabs:GetActiveTab() then
219 activesettingstab = xgui.settings_tabs:GetActiveTab():GetValue()
220 end
221
222 xgui.base:Clear() --We need to remove any existing tabs in the GUI
223 xgui.tabcompletes = {}
224 xgui.ulxmenucompletes = {}
225 for _, modname in ipairs( xgui.settings.moduleOrder ) do
226 local module = xgui.checkModuleExists( modname, xgui.modules.tab )
227 if module then
228 module = xgui.modules.tab[module]
229 if module.xbutton == nil then
230 module.xbutton = xlib.makebutton{ x=555, y=-5, w=32, h=32, btype="close", parent=module.panel }
231 module.xbutton.DoClick = function()
232 xgui.hide()
233 end
234 end
235 if LocalPlayer():query( module.access ) then
236 xgui.base:AddSheet( module.name, module.panel, module.icon, false, false, module.tooltip )
237 module.tabpanel = xgui.base.Items[#xgui.base.Items].Tab
238 table.insert( xgui.tabcompletes, "xgui show " .. modname )
239 table.insert( xgui.ulxmenucompletes, "ulx menu " .. modname )
240 else
241 module.tabpanel = nil
242 module.panel:SetParent( xgui.null )
243 end
244 end
245 end
246
247 xgui.settings_tabs:Clear() --Clear out settings tabs for reprocessing
248 for _, modname in ipairs( xgui.settings.settingOrder ) do
249 local module = xgui.checkModuleExists( modname, xgui.modules.setting )
250 if module then
251 module = xgui.modules.setting[module]
252 if LocalPlayer():query( module.access ) then
253 xgui.settings_tabs:AddSheet( module.name, module.panel, module.icon, false, false, module.tooltip )
254 module.tabpanel = xgui.settings_tabs.Items[#xgui.settings_tabs.Items].Tab
255 table.insert( xgui.tabcompletes, "xgui show " .. modname )
256 table.insert( xgui.ulxmenucompletes, "ulx menu " .. modname )
257 else
258 module.tabpanel = nil
259 module.panel:SetParent( xgui.null )
260 end
261 end
262 end
263
264 --Call any functions that requested to be called when permissions change
265 xgui.callUpdate( "onProcessModules" )
266 table.sort( xgui.tabcompletes )
267 table.sort( xgui.ulxmenucompletes )
268
269 local hasFound = false
270 if activetab then
271 for _, v in pairs( xgui.base.Items ) do
272 if v.Tab:GetValue() == activetab then
273 xgui.base:SetActiveTab( v.Tab, true )
274 hasFound = true
275 break
276 end
277 end
278 if not hasFound then
279 xgui.base.m_pActiveTab = "none"
280 xgui.base:SetActiveTab( xgui.base.Items[1].Tab, true )
281 end
282 end
283
284 hasFound = false
285 if activesettingstab then
286 for _, v in pairs( xgui.settings_tabs.Items ) do
287 if v.Tab:GetValue() == activesettingstab then
288 xgui.settings_tabs:SetActiveTab( v.Tab, true )
289 hasFound = true
290 break
291 end
292 end
293 if not hasFound then
294 xgui.settings_tabs.m_pActiveTab = "none"
295 xgui.settings_tabs:SetActiveTab( xgui.settings_tabs.Items[1].Tab, true )
296 end
297 end
298end
299
300function xgui.checkNotInstalled( tabname )
301 if xgui.notInstalledWarning then return end
302
303 gui.EnableScreenClicker( true )
304 RestoreCursorPosition()
305 xgui.notInstalledWarning = xlib.makeframe{ label="XGUI Warning!", w=375, h=110, nopopup=true, showclose=false, skin=xgui.settings.skin }
306 xlib.makelabel{ x=10, y=30, wordwrap=true, w=365, label="XGUI has not initialized properly with the server. This could be caused by a heavy server load after a mapchange, a major error during XGUI server startup, or XGUI not being installed.", parent=xgui.notInstalledWarning }
307
308 xlib.makebutton{ x=37, y=83, w=80, label="Offline Mode", parent=xgui.notInstalledWarning }.DoClick = function()
309 xgui.notInstalledWarning:Remove()
310 xgui.notInstalledWarning = nil
311 offlineWarning = xlib.makeframe{ label="XGUI Warning!", w=375, h=110, nopopup=true, showclose=false, skin=xgui.settings.skin }
312 xlib.makelabel{ x=10, y=30, wordwrap=true, w=365, label="XGUI will run locally in offline mode. Some features will not work, and information will be missing. You can attempt to reconnect to the server using the 'Refresh Server Data' button in the XGUI client menu.", parent=offlineWarning }
313 xlib.makebutton{ x=77, y=83, w=80, label="OK", parent=offlineWarning }.DoClick = function()
314 offlineWarning:Remove()
315 xgui.offlineMode = true
316 xgui.show( tabname )
317 end
318 xlib.makebutton{ x=217, y=83, w=80, label="Cancel", parent=offlineWarning }.DoClick = function()
319 offlineWarning:Remove()
320 RememberCursorPosition()
321 gui.EnableScreenClicker( false )
322 end
323 end
324
325 xlib.makebutton{ x=257, y=83, w=80, label="Close", parent=xgui.notInstalledWarning }.DoClick = function()
326 xgui.notInstalledWarning:Remove()
327 xgui.notInstalledWarning = nil
328 RememberCursorPosition()
329 gui.EnableScreenClicker( false )
330 end
331
332 xlib.makebutton{ x=147, y=83, w=80, label="Try Again", parent=xgui.notInstalledWarning }.DoClick = function()
333 xgui.notInstalledWarning:Remove()
334 xgui.notInstalledWarning = nil
335 RememberCursorPosition()
336 gui.EnableScreenClicker( false )
337 local reattempt = xlib.makeframe{ label="XGUI: Attempting reconnection...", w=200, h=20, nopopup=true, showclose=false, skin=xgui.settings.skin }
338 timer.Simple( 1, function()
339 RunConsoleCommand( "_xgui", "getInstalled" )
340 reattempt:Remove()
341 timer.Simple( 0.5, function() xgui.show( tabname ) end )
342 end )
343 end
344end
345
346function xgui.show( tabname )
347 if not xgui.anchor then return end
348 if not xgui.initialized then return end
349
350 --Check if XGUI is not installed, display the warning if hasn't been shown yet.
351 if not xgui.isInstalled and not xgui.offlineMode then
352 xgui.checkNotInstalled( tabname )
353 return
354 end
355
356 if not game.SinglePlayer() and not ULib.ucl.authed[LocalPlayer():UniqueID()] then
357 local unauthedWarning = xlib.makeframe{ label="XGUI Error!", w=250, h=90, showclose=true, skin=xgui.settings.skin }
358 xlib.makelabel{ label="Your ULX player has not been Authed!", x=10, y=30, parent=unauthedWarning }
359 xlib.makelabel{ label="Please wait a couple seconds and try again.", x=10, y=45, parent=unauthedWarning }
360 xlib.makebutton{ x=50, y=63, w=60, label="Try Again", parent=unauthedWarning }.DoClick = function()
361 unauthedWarning:Remove()
362 xgui.show( tabname )
363 end
364 xlib.makebutton{ x=140, y=63, w=60, label="Close", parent=unauthedWarning }.DoClick = function()
365 unauthedWarning:Remove()
366 end
367 return
368 end
369
370 if xgui.base.refreshSkin then
371 xgui.base:SetSkin( xgui.settings.skin )
372 xgui.base.refreshSkin = nil
373 end
374
375 --In case the string name had spaces, it sent the whole argument table. Convert it to a string here!
376 if type( tabname ) == "table" then
377 tabname = table.concat( tabname, " " )
378 end
379 --Sets the active tab to tabname if it was specified
380 if tabname and tabname ~= "" then
381 local found, settingsTab
382 for _, v in ipairs( xgui.modules.tab ) do
383 if string.lower( v.name ) == "settings" then settingsTab = v.tabpanel end
384 if string.lower( v.name ) == string.lower( tabname ) and v.panel:GetParent() ~= xgui.null then
385 xgui.base:SetActiveTab( v.tabpanel )
386 if xgui.anchor:IsVisible() then return end
387 found = true
388 break
389 end
390 end
391 if not found then
392 for _, v in ipairs( xgui.modules.setting ) do
393 if string.lower( v.name ) == string.lower( tabname ) and v.panel:GetParent() ~= xgui.null then
394 xgui.base:SetActiveTab( settingsTab )
395 xgui.settings_tabs:SetActiveTab( v.tabpanel )
396 if xgui.anchor:IsVisible() then return end
397 found = true
398 break
399 end
400 end
401 end
402 if not found then return end --If invalid input was taken, then do nothing.
403 end
404
405 xgui.base.animOpen()
406 gui.EnableScreenClicker( true )
407 RestoreCursorPosition()
408 xgui.anchor:SetMouseInputEnabled( true )
409
410 --Calls the functions requesting to hook when XGUI is opened
411 xgui.callUpdate( "onOpen" )
412end
413
414function xgui.hide()
415 if not xgui.anchor then return end
416 if not xgui.anchor:IsVisible() then return end
417 RememberCursorPosition()
418 gui.EnableScreenClicker( false )
419 xgui.anchor:SetMouseInputEnabled( false )
420 xgui.base.animClose()
421 CloseDermaMenus()
422
423 --Calls the functions requesting to hook when XGUI is closed
424 xgui.callUpdate( "onClose" )
425end
426
427function xgui.toggle( tabname )
428 if xgui.anchor and ( not xgui.anchor:IsVisible() or ( tabname and #tabname ~= 0 ) ) then
429 xgui.show( tabname )
430 else
431 xgui.hide()
432 end
433end
434
435--New XGUI Data stuff
436function xgui.expectChunks( numofchunks )
437 if xgui.isInstalled then
438 xgui.expectingdata = true
439 xgui.chunkbox.max = numofchunks
440 xgui.chunkbox.value = 0
441 xgui.chunkbox:SetFraction( 0 )
442 xgui.chunkbox.Label:SetText( "Getting data: Waiting for server..." )
443 xgui.chunkbox:SetVisible( true )
444 xgui.chunkbox:SetSkin( xgui.settings.skin )
445 xgui.flushQueue( "chunkbox" ) --Remove the queue entry that would hide the chunkbox
446 end
447end
448
449function xgui.getChunk( flag, datatype, data )
450 if xgui.expectingdata then
451 --print( datatype, flag ) --Debug
452 xgui.chunkbox:Progress( datatype )
453 if flag == -1 then return --Ignore these chunks
454 elseif flag == 0 then --Data should be purged
455 if xgui.data[datatype] then
456 table.Empty( xgui.data[datatype] )
457 end
458 xgui.flushQueue( datatype )
459 xgui.callUpdate( datatype, "clear" )
460 elseif flag == 1 then
461 if not xgui.mergeData then --A full data table is coming in
462 if not data then data = {} end --Failsafe for no table being sent
463 xgui.flushQueue( datatype )
464 table.Empty( xgui.data[datatype] )
465 table.Merge( xgui.data[datatype], data )
466 xgui.callUpdate( datatype, "clear" )
467 xgui.callUpdate( datatype, "process", data )
468 xgui.callUpdate( datatype, "done" )
469 else --A chunk of data is coming in
470 table.Merge( xgui.data[datatype], data )
471 xgui.callUpdate( datatype, "process", data )
472 end
473 elseif flag == 2 or flag == 3 then --Add/Update a portion of data
474 table.Merge( xgui.data[datatype], data )
475 xgui.callUpdate( datatype, flag == 2 and "add" or "update", data )
476 elseif flag == 4 then --Remove a key from the table
477 xgui.removeDataEntry( xgui.data[datatype], data ) --Needs to be called recursively!
478 xgui.callUpdate( datatype, "remove", data )
479 elseif flag == 5 then --Begin a set of chunks (Clear the old data, then flag to merge incoming data)
480 table.Empty( xgui.data[datatype] )
481 xgui.mergeData = true
482 xgui.flushQueue( datatype )
483 xgui.callUpdate( datatype, "clear" )
484 elseif flag == 6 then --End a set of chunks (Clear the merge flag)
485 xgui.mergeData = nil
486 xgui.callUpdate( datatype, "done" )
487 elseif flag == 7 then --Pass the data directly to the module to be handled.
488 xgui.callUpdate( datatype, "data", data )
489 end
490 end
491end
492
493function xgui.removeDataEntry( data, entry )
494 for k, v in pairs( entry ) do
495 if type( v ) == "table" then
496 xgui.removeDataEntry( data[k], v )
497 else
498 if type(v) == "number" then
499 table.remove( data, v )
500 else
501 data[v] = nil
502 end
503 end
504 end
505end
506
507function xgui.callUpdate( dtype, event, data )
508 --Run any functions that request to be called when "curtable" is updated
509 if not xgui.hook[dtype] or ( event and not xgui.hook[dtype][event] ) then
510 Msg( "XGUI: Attempted to call non-existent type or event to a hook! (" .. dtype .. ", " .. ( event or "nil" ) .. ")\n" )
511 else
512 if not event then
513 for name, func in pairs( xgui.hook[dtype] ) do func( data ) end
514 else
515 for name, func in pairs( xgui.hook[dtype][event] ) do func( data ) end
516 end
517 end
518end
519
520--If the player's group is changed, reprocess the XGUI modules for permissions, and request for extra data if needed
521function xgui.PermissionsChanged( ply )
522 if ply == LocalPlayer() and xgui.isInstalled then
523 xgui.processModules()
524 local types = {}
525 for dtype, data in pairs( xgui.data ) do
526 if table.Count( data ) > 0 then table.insert( types, dtype ) end
527 end
528 RunConsoleCommand( "xgui", "refreshdata", unpack( types ) )
529 end
530end
531hook.Add( "UCLAuthed", "XGUI_PermissionsChanged", xgui.PermissionsChanged )
532
533function xgui.getInstalled()
534 if not xgui.isInstalled then
535 if xgui.notInstalledWarning then
536 xgui.notInstalledWarning:Remove()
537 xgui.notInstalledWarning = nil
538 end
539 xgui.isInstalled = true
540 xgui.offlineMode = false
541 RunConsoleCommand( "xgui", "getdata" )
542 end
543end
544
545function xgui.cmd_base( ply, func, args )
546 if not args[ 1 ] then
547 xgui.toggle()
548 elseif xgui.isInstalled then --First check that it's installed
549 RunConsoleCommand( "_xgui", unpack( args ) )
550 end
551end
552
553function xgui.tab_completes()
554 return xgui.tabcompletes
555end
556
557function xgui.ulxmenu_tab_completes()
558 return xgui.ulxmenucompletes
559end
560
561ULib.cmds.addCommandClient( "xgui", xgui.cmd_base )
562ULib.cmds.addCommandClient( "xgui show", function( ply, cmd, args ) xgui.show( args ) end, xgui.tab_completes )
563ULib.cmds.addCommandClient( "xgui hide", xgui.hide )
564ULib.cmds.addCommandClient( "xgui toggle", function() xgui.toggle() end )
565
566--local ulxmenu = ulx.command( CATEGORY_NAME, "ulx menu", ulx.menu, "!menu" )
567ULib.cmds.addCommandClient( "ulx menu", function( ply, cmd, args ) xgui.toggle( args ) end, xgui.ulxmenu_tab_completes )