Leadwerks GUI
After a lot of research and development, Leadwerks GUI is almost ready to release. The goal with this system was to create an in-game GUI that was customizable, extendable, and could also serve as a windowed GUI for application development in the future.
Widgets
The GUI system is based on the Widget class. Once a GUI is created on a rendering context you can add widgets to it. Each widget is a rectangular container with a padding area. The widgets can be arranged in a hierarchy and their bounds will clip the contents of their child widgets, both for rendering and mouse interaction.
The GUI is automatically rendered onto the screen inside the call to Context:Sync(),
Widget Scripts
Each widget has a Lua script to control rendering and behavior, similar to the way Lua scripts work in our entity system. The script assigned to a widget controls what type of widget it is, how it looks, and how it interacts with mouse and keyboard input. A set of widget scripts are provided to create a variety of controls including buttons, checkboxes, text entry boxes, list boxes, text display areas, choice boxes, sliders, and more.
You can create your own widget scripts to add new types of controls, like for an RPG interface or something else. The script below shows how the tabber widget is implemented.
--Styles if Style==nil then Style={} end if Style.Panel==nil then Style.Panel={} end Style.Panel.Border=1 Style.Panel.Group=2 --Initial values Script.indent=1 Script.tabsize = iVec2(72,28) Script.textindent=6 Script.tabradius=5 function Script:Start() self.widget:SetPadding(self.indent,self.indent,self.tabsize.y+self.indent,self.indent) end function Script:MouseLeave() if self.hovereditem~=nil then self.hovereditem = nil local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) --self.widget:Redraw() end end function Script:Draw(x,y,width,height) local gui = self.widget:GetGUI() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) local scale = self.widget:GetGUI():GetScale() local n local sel = self.widget:GetSelectedItem() --Draw border gui:SetColor(0) gui:DrawRect(pos.x,pos.y+self.tabsize.y*scale,sz.width,sz.height-self.tabsize.y*scale,1) --Draw unselected tabs for n=0,self.widget:CountItems()-1 do if n~=sel then self:DrawTab(n) end end --Draw selected tab if sel>-1 then self:DrawTab(sel) end ---Panel background gui:SetColor(0.25) gui:DrawRect(pos.x+1,pos.y+self.tabsize.y*scale+1,sz.width-2,sz.height-self.tabsize.y*scale-2) end function Script:DrawTab(n) local gui = self.widget:GetGUI() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) local scale = self.widget:GetGUI():GetScale() local s = self.widget:GetItemText(n) local textoffset=2*scale if self.widget:GetSelectedItem()==n then textoffset=0 end local leftpadding=0 local rightpadding=0 if self.widget:GetSelectedItem()==n then gui:SetColor(0.25) if n>0 then leftpadding = scale*1 end rightpadding = scale*1 else gui:SetColor(0.2) end gui:DrawRect(-leftpadding+pos.x+n*(self.tabsize.x)*scale,textoffset+pos.y,rightpadding+leftpadding+self.tabsize.x*scale+1,self.tabsize.y*scale+self.tabradius*scale+1,0,self.tabradius*scale) gui:SetColor(0) gui:DrawRect(-leftpadding+pos.x+n*(self.tabsize.x)*scale,textoffset+pos.y,rightpadding+leftpadding+self.tabsize.x*scale+1,self.tabsize.y*scale+self.tabradius*scale+1,1,self.tabradius*scale) if self.widget:GetSelectedItem()~=n then gui:SetColor(0) gui:DrawLine(pos.x+n*self.tabsize.x*scale,pos.y+self.tabsize.y*scale,pos.x+n*self.tabsize.x*scale+self.tabsize.x*scale,pos.y+self.tabsize.y*scale) end if self.hovereditem==n and self.widget:GetSelectedItem()~=n then gui:SetColor(1) else gui:SetColor(0.7) end gui:DrawText(s,pos.x+(n*self.tabsize.x+self.textindent)*scale,textoffset+pos.y+self.textindent*scale,(self.tabsize.x-self.textindent*2)*scale-2,(self.tabsize.y-self.textindent*2)*scale-1,Text.VCenter+Text.Center) end function Script:MouseDown(button,x,y) if button==Mouse.Left then if self.hovereditem~=self.widget:GetSelectedItem() and self.hovereditem~=nil then self.widget.selection=self.hovereditem local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,self.hovereditem) end elseif button==Mouse.Right then if self.hovereditem~=self.widget:GetSelectedItem() and self.hovereditem~=nil then EventQueue:Emit(Event.WidgetMenu,self.widget,self.hovereditem,x,y) end end end function Script:KeyDown(keycode) if keycode==Key.Right or keycode==Key.Down then local item = self.widget:GetSelectedItem() + 1 if item<self.widget:CountItems() then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end elseif keycode==Key.Left or keycode==Key.Up then local item = self.widget:GetSelectedItem() - 1 if item>-1 and self.widget:CountItems()>0 then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end elseif keycode==Key.Tab then local item = self.widget:GetSelectedItem() + 1 if item>self.widget:CountItems()-1 then item=0 end if self.widget:CountItems()>1 then self.widget.selection=item local scale = self.widget:GetGUI():GetScale() local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) EventQueue:Emit(Event.WidgetAction,self.widget,item) end end end function Script:MouseMove(x,y) local prevhovereditem = self.hovereditem self.hovereditem = nil local scale = self.widget:GetGUI():GetScale() local sz = self.widget:GetSize(true) if x>=0 and y>=0 and x<sz.width and y<self.tabsize.y*scale then local item = math.floor(x / (self.tabsize.x*scale)) if item>=0 and item<self.widget:CountItems() then self.hovereditem=item end end if self.hovereditem==self.widget:GetSelectedItem() and prevhovereditem==nil then return end if self.hovereditem==nil and prevhovereditem==self.widget:GetSelectedItem() then return end if prevhovereditem~=self.hovereditem then local pos = self.widget:GetPosition(true) local sz = self.widget:GetSize(true) self.widget:GetGUI():Redraw(pos.x,pos.y,sz.width,self.tabsize.y*scale+1) end end
Widget Rendering
Widgets are buffered and rendered with an advanced system that draws only the portions of the screen that need to be updated. The GUI is rendered into a texture, and then the composite image is drawn onscreen. This means you can have very complex interfaces rendering in real-time game menus with virtually no performance cost.
By default, no images are used to render the UI so you don't have to include any extra files in your project.
Widget Items
Each widget stores a list of items you can add, remove, and edit. These are useful for list boxes, choice boxes, and other custom widgets.
GUI Events
Leadwerks 4.4 introduces a new concept into your code, the event queue. This stores a list of events that have occurred. When you retrieve an event it is removed from the stack:
while EventQueue:Peek() do local event = EventQueue:Wait() if event.source == widget then print("OK!") end end
Resolution Independence
Leadwerks GUI is designed to operate at any resolution. Creation and positioning of widgets uses a coordinate system based on a 1080p monitor, but the GUI can use a global scale to make the interface scale up or down to accommodate any DPI including 4K and 8K monitors. The image below is rendering the interface at 200% scaling on a 4K monitor.
A default script will be included that you can include from Main.lua to build up a menu system for starting and quitting games, and handling common graphical features and other settings.
Leadwerks GUI will be released in Leadwerks Game Engine 4.4.
- 1
- 10
10 Comments
Recommended Comments