Widget Basics
In Wild Pockets, GUI widgets like buttons, scrollbars, menus, and the like are simply subroutines that use the simpler primitives to draw a complex object. Consider, for example, this subroutine:
function Rect:drawSimpleButton(clip, label)
if (self:leftClickInProgress()) then
self:drawPict(clip, { background="josh/buttonDownImage" } )
elseif (self:hasMouse()) then
self:drawPict(clip, { background="josh/buttonHoverImage" } )
else
self:drawPict(clip, { background="josh/buttonStandardImage" } )
end
self:drawText(clip, { halign="center" }, label)
self:leftMouseClick(fn)
end
So this is just a drawing routine like drawPict and drawText - the only difference is, it draws one of three different pictures depending on what the mouse is doing, and it also sets up a mouse handler.
This is a very important concept: our GUI system has no "widget" objects. All we have are subroutines like drawSimpleButton above that draw several drawPict/drawText elements all at once, and which may set up some mouse handlers as well.
Rect Userdata
Some widgets have state. For example, a check-box widget has a single on/off bit. A scrollbar widget has state that indicates how high/low the thumb is in the bar. When designing a routine that draws a checkbox or a scrollbar, you will need someplace to store the checkbox state, or the thumb position. For this, you will need Rect:userdata.
The function Rect:userdata will return a Lua table where you can put anything you want. The data is specifically associated with a particular rectangle.
The interesting thing about userdata is that your GUI may be discarded and recreated every frame, but the userdata is not discarded. Instead, the system manages to carry over userdata from the previous frame.
The way this works is as follows: the userdata is cached according to the rectangle's ID string. When the GUI is rebuilt, if you construct a rectangle whose ID string is the same as the previous frame, the userdata will be retrieved from the previous frame. This is one reason why it's important to use the predictable Rect naming conventions that are standard for Wild Pockets GUIs.
You must fetch the userdata every frame, or it will be discarded. This is how garbage collection of userdata works: if you stop drawing a given GUI element, its userdata will dissappear.
Here is how you would use userdata to implement a checkbox widget:
function Rect:drawSimpleCheckBox(clip)
local userdata = self:userdata()
if (userdata.__CHECKED) then
self:drawPict(clip, {background="josh/checkBoxChecked"})
else
self:drawPict(clip, {background="josh/checkBoxUnchecked"})
end
self:leftMouseClick(toggleCheckBox)
end
function toggleCheckBox(rect)
local userdata = rect:userdata()
userdata.__CHECKED = not userdata.__CHECKED
end
The checkbox state is stored in the userdata table. The routine that draws the checkbox draws either the checked or unchecked version, depending on the state stored in the userdata table. A mouse click in the region will call the toggle routine, which reverses the value stored in the table.
Keyboard Focus
To implement GUI elements that accept keyboard input, you will need to use the GUI system's keyboard focus handling.
At any time, the focus points at a particular rectangle (or it can be nil). To enable a given rectangle to receive the keyboard focus, use Rect:enableFocus. This takes a pointer to a keyboard-event handling function. If you type when a rectangle has focus, the events will go to that rectangle's event-handling function.
The GUI system will use a series of heuristics to guide the focus to the right rectangle. The heuristics are:
- If you click on a rectangle with enableFocus, the GUI will direct focus to that rectangle.
- If you click on a rectangle without enableFocus, it will search for a child rectangle that has enableFocus. It will pick the child rectangle which had focus most recently.
- If a new rectangle appears with enableFocus, then focus will go to the new rectangle. If multiple new rectangles appear with enableFocus, the focus will go to the earliest-painted of these.
It usually isn't necessary to understand these perfectly, suffice to say that these heuristics generally direct focus to the right place. You can force focus to a particular rectangle using GuiManager.setFocus.
Our Provided Widgets
Class Rect includes many 'draw' functions that draw all kinds of prefab widgets, including buttons, checkboxes, radio buttons, scroll bars, drop-down menus, and so forth. These are documented in the API reference for class Rect.
- Printer-friendly version
- Login or register to post comments
-



