Home

Lokasenna_GUI

Important: This is adapted for use in Ultraschall-API, which makes initializing the GUI a little easier to do.

This wiki will (hopefully) provide a thorough explanation of how everything in Lokasenna_GUI works.

Please let me know if anything is missing, incomplete, or vague.


1. Getting started

Usage

Just add these lines to your script:

dofile(reaper.GetResourcePath().."/UserPlugins/ultraschall_api.lua")
ultraschall.Lokasenna_LoadGuiLib_v2()
and it will load the core-features and all classes available.

Setting up the window:

GUI.name = "My script's window"
GUI.x, GUI.y = 128, 128
GUI.w, GUI.h = 384, 96

Adding a few elements to it:

GUI.New("my_txtbox", "Textbox", 1, 96,  28, 96, 20, "Track name:")
GUI.New("my_slider", "Slider",  1, 256, 32, 48,     "Track number:", 1,   10,  10,    1)
GUI.New("my_button", "Button",  1, 160,  64, 64, 24, "Set name", set_track_name)

Making the elements do something. _set_track_name_ would, in this case, be a function declared earlier in the script:

local function set_track_name()

    local name = GUI.Val("my_txtbox")
    local num =  GUI.Val("my_slider")

    -- Do something with that information. Presumably renaming a track.

end

Opening the script window:

GUI.Init()

And starting the main loop:

GUI.Main()

That's it - not even two dozen lines of code and you've got a window, it has things in it, and you can get the values from those things for your script to work with.


2. Parts of a basic script

Calling the GUI library

First, you need to add Ultraschall-API and the Gui-Lib to your script:

dofile(reaper.GetResourcePath().."/UserPlugins/ultraschall_api.lua")
ultraschall.Lokasenna_LoadGuiLib_v2()
and it will load the core-features and all classes available.

The script window and anchors

Next you set up the script window. This doesn't have to be done now; just make sure the values are set prior to calling GUI.Init. These values are passed directly to gfx.init, but GUI will hold on to them in case they're wanted later. For example, you might check to see if the window has been resized and then force it back to your original dimensions.

GUI.name = "My script's window"
GUI.x, GUI.y = 128, 128
GUI.w, GUI.h = 384, 96

You can also have the window positioned dynamically. "Centered on the mouse cursor" is always a popular choice:

GUI.anchor, GUI.corner = "mouse", "C"

Possible anchor settings are as follows. The corner strings refer to the corners and sides of the window - that is, "screen" and "TR" would place the window's top-right corner in the top-right corner of the screen.

    anchor    "screen" or "mouse"
    corner    "TL"
        "T"
        "TR"
        "R"
        "BR"
        "B"
        "BL"
        "L"
        "C"

In this case the _x_ and _y_ above are relative to the specified anchor oint.

Adding elements

Create all of your GUI elements. Only one parameter, [_z_], is actually required - the rest are up to individual classes. See the individual class pages for their parameters.

        name         class      z  x    y   w,  h,  caption     
GUI.New("my_txtbox", "Textbox", 1, 96,  28, 96, 20, "Track name:")

                                                                     min  max  steps  default
GUI.New("my_slider", "Slider",  1, 256, 32, 48,     "Track number:", 1,   10,  10,    1)

                                                                 function
GUI.New("my_button", "Button",  1, 160,  64, 64, 24, "Set name", set_track_name)

Note that the elements must be created via GUI.New in order for the GUI to properly track them. Don't call their :new methods directly.

Since this is Lua, creating elements can make use of whatever helper functions or variables you like. For instance, elements can use a common reference for coordinates to facilitate placing or moving them as a group:

local ref1 = {x = 32, y = 32}
GUI.New("my_txtbox", "Textbox", 1, ref1.x, ref1.y....
GUI.New("my_txtbox", "Textbox", 1, ref1.x, ref1.y + 24....
GUI.New("my_txtbox", "Textbox", 1, ref1.x, ref1.y + 48....

Elements can created at run-time as well.

Communicating with your script

Every element's state can be checked by name using GUI.Val.

local function set_track_name()
    local name = GUI.Val("my_txtbox")
    local num =  GUI.Val("my_slider")
    -- Do something with that information. Presumably renaming a track.
end

Changing the value of an element is just as simple - call GUI.Val with the new value as a parameter:

local function get_track_name()
    local name = ...get the current track's name somehow...
    GUI.Val("my_txtbox", name)
end

Each class will accept/return its value in a particular format. Some of the more adventurous classes will even accept multiple value types and sort it out for themselves. See each element's Class page.

Opening the window

GUI.Init()

...well that was underwhelming. To be fair, Init does set up a lot of stuff under the hood.

User functions

_Lokasenna_GUI_ includes two hooks for your own functions to be run as part of the main loop.

GUI.func = my_function will be run on every update, or with a scheduled frequency (in seconds) by adding GUI.freq = 3 - the latter is probably a good idea if your functions are particularly CPU-intensive.

GUI.exit = save_my_data will be run when the script exits using reaper.atexit.

Starting the GUI

GUI.Main()

As described above, this will kick off the GUI library's logic and keep everything going until the user exits. Scripts can also request an early exit (after performing a one-time task, say) by setting GUI.quit = true.


3. How it works

The main loop

  • All of the elements you create are stored in a table, GUI.elms.
  • On each update cycle for the script, GUI.Main gets the state of the mouse and keyboard and compares them with all of your elements to see if the user has done anything and, if so, what they did it to.
  • Assuming the user did do something, the appropriate element is told "hey, the mouse is being dragged on you", and the element does its thing.
  • If the parent script has asked to run its own function on each update, it gets a turn now.
  • Main then checks to see if any of the elements have asked to have their layer redrawn, and does so.
  • All of the elements are copied to the script window.
  • Life continues.

The library

All of the core functionality for _Lokasenna_GUI_ lives in, er, Core.lua. Strictly speaking it's the only file that's necessary for a GUI script, although it would be a pretty boring script if it just opened a window and couldn't do anything with it.

The various bells and whistles are provided by classes in the - duh - /Classes folder. Labels, text boxes, sliders, even a Slurpee machine - they're all there. You can also go without the stock classes entirely and write your own instead; a blank template is provided.

Z-layers

_z_ is used to establish a front-to-back hierarchy for the various elements. This concept will be familiar to most as "layers", in Photoshop for instance. A _z_ value of 1 would be "front", with no practical limit (okay, roughly a thousand) on how many layers are allowed.

When GUI.Main checks for user input, it updates the elements on each z-layer from front to back so that elements in front will always catch the input first.

Likewise, when elements have changed and request a redraw, they use their z-layer. Scripts can also call for redraws on either specific layers or all the visible ones.

GUI.elms

All of the elements are stored in a table, GUI.elms. Basic scripts may be perfectly fine just using GUI.Val here and there to get things done, but all of the classes have additional properties and methods that can be accessed directly if necessary.

GUI.elms.my_slider.caption = "Pan"
GUI.elms.my_button:exec()

Any visual changes, such as to fonts or colors, will probably necessitate a redraw for that element. _Lokasena_GUI_ only redraws them when asked, either by the element itself or manually:

GUI.redraw_z[ GUI.elms.my_slider.z ] = true

Also, it's best to avoid tinkering with an element's value directly. GUI.Val will prompt a redraw, make sure the element updates various internal values, etc, so it should be used unless absolutely necessary.


4. Fonts and colors

Fonts

In the interest of making _Lokasenna_GUI_ as straightforward as possible, and since Reaper's API does the opposite, fonts are handled by way of presets.

1   -- Title
2   -- Header
3   -- Label
4   -- Value
GUI.font(2)

Most classes will have one or two font parameters:

GUI.elms.my_label.font = 3

Presets can be overridden or added to at any point:

                font     size  flags
GUI.fonts[4] = {"Calibri", 12, "bi"}
GUI.fonts[5] = {"Arial",   10}
GUI.fonts["meme"] = {"Impact", 20, "b"}

GUI.font will also directly accept a table in the same format.

Note: Sizes are internally multiplied by 0.7 on OSX, because Mac and Windows apparently can't agree on something as simple as font sizes.

Colors

Colors are dealt with in a similar fashion.

wnd_bg      -- Window BG
tab_bg      -- Tabs BG
elm_bg      -- Element BG
elm_frame   -- Element Frame
elm_fill    -- Element Fill
elm_outline -- Element Outline
txt         -- Text
GUI.color("elm_fill")
GUI.elms.my_label.color = "cyan"

As with fonts, the color presets can be added to or amended:

                     R   G    B    A
GUI.colors["red"] = {16, 103, 192, 255}
GUI.colors["my_ugly_button"] = {96, 128, 192, 255}

And GUI.color can likewise be given a color table directly.

The 16 standard web colors are also available, by name: GUI.color("magenta")

Important: Prior to calling GUI.Init, the color presets are stored as 0-255 values. Init converts them to 0-1, since that's what REAPER's gfx functions use. Be aware of this if you need to work with any color values directly after the script has started. In all honesty, though, you probably won't need to.

Setting the color yourself

Some scripts may need to tinker with the gfx color values directly, for instance to create the Label class' :fade. This is perfectly fine, but make sure you set gfx.a = 1 when you're finished. GUI.Main checks it a couple of times, but forgetting to reset _a_ can mess up the appearance of the entire GUI.


5. Element methods

All elements have the following methods available for scripts to work with. When overriding a method, be aware of the whether or not the class is doing anything with it - if it is, you'll have to include a call to the base class' method:

function GUI.elms.my_slider:onmouseup()

    -- Add your own code here...

    GUI.Slider.onmouseup(self)

    -- ...or here

end

Basic methods

init

Called when a) GUI.Init opens the script window or b) once the window is open, immediately after an element is created. Elements that use blitting should open a buffer and draw whatever they need here.

draw

Called when GUI asks the element's z layer to redraw itself.

onupdate

Called on every update loop. Return true if the element doesn't need to check for user input (i.e. an overlay that doesn't do anything on its own and shouldn't "intercept" the mouse or keyboard).

ondelete

Runs when an element is deleted, either automatically by GUI if its _z_ has been set to _-1_, or if a script calls my_element:delete().

onresize

Runs for each element if the script window has been resized on a given update loop, allowing for elements that automatically redraw themselves to scale up and down with the window.

Input methods

onmouseover

Called on every update loop, if the mouse is over this element.

onmousedown

Called when the left mouse button is first pressed down on this element.

onmouseup

Called when the left mouse button is released, if it was originally clicked on this element and is still over it.

ondoubleclick

Called when a left double-click is detected on this element. (Time is hard-coded at 100ms)

ondrag

Called on each update loop if the left mouse button was pressed down on this element and the mouse is moved. Will continue being called on each loop even if the mouse moves elsewhere.

onmouser_down

onmouser_up

onr_doubleclick

onr_drag

Right mouse button methods.

onmousem_down

onmousem_up

onm_doubleclick

onm_drag

Middle mouse button equivalents of the above.

onwheel(inc)

Called when the mousewheel is scrolled over this element. Passes a positive integer if the wheel was scrolled up, and a negative integer if the wheel was scrolled down.

ontype

Called if this element currently has focus (was clicked, or given .focus = true) and keyboard input is detected.

lostfocus

Called when the element loses focus (mouse was clicked elsewhere, or given .focus = false, or by Textbox if Return is pressed).

Script methods

redraw

Redraws the element's z layer on the next round of drawing. Generally needs to be called if a script makes any changes to an element's visual properties, or if an element's value is changed directly rather than via GUI.Val.

delete

Deletes the element.

val(newval)

Gets or sets the element's value. Normally accessed via GUI.Val, but can be called directly if you want, I suppose.

Msg("property1", "property2", ...)

For development/debugging purposes. Returns a string with the specified properties. If called with no arguments, returns all of the Element's properties.

Misc. Variables

tooltip

String. If specified for an element (GUI.elms.my_button.toolip = "I am a button!"), GUI will display a tooltip when the mouse is hovered over it. The delay time is specified by GUI.tooltip_time, and defaults to 0.8s.


1. Button

It's a button. You click on it. Things happen.

Parameters

z, x, y, w, h, caption, func[, ...]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Button size
caption            Label
func            Function to perform when clicked. 

Note that you only need give a reference to the function:

GUI.New("my_button", "Button", 1, 32, 32, 64, 32, "Button", my_func)

Unless the function is returning a function (hey, Lua is weird), you don't want to actually run it:

GUI.New("my_button", "Button", 1, 32, 32, 64, 32, "Button", my_func())

Optional

...             Any parameters to pass to that function, separated by commas as they
                would be if calling the function directly.

Additional parameters

r_func          Function to perform when right-clicked
r_params        If provided, any parameters to pass to that function
font            Button label's font
col_txt         Button label's color

col_fill        Button color. 

                If you change this at any time after having run GUI.Init(), call 
                GUI.elms.my_button:init() so the buffer can be redrawn.

Methods

:exec([r])

Programmatically force a button-click and run func, i.e. for allowing buttons to have an associated hotkey.

r               Boolean, optional. r = true will run r_func instead.

2. Frame

This class draws a basic frame for visually organizing the other elements in your GUI. It can also display text, wrapped to fit, for things like a Help or About screen.

Frame is also a good choice for scripts that want to display information in a way that isn't covered by the other classes - just overwrite the frame's :draw() method with your own code.



Parameters

z, x, y, w, h[, shadow, fill, color, round]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Frame size

Optional

shadow          Boolean. Draw a shadow beneath the frame?    Defaults to False.
fill            Boolean. Fill in the frame?    Defaults to False.
color           Frame (and fill) color.    Defaults to "elm_frame".
round           Radius of the frame's corners. Defaults to 0.

Additional parameters

text            Text to be written inside the frame. Will automatically be wrapped
                to fit (self.w - 2 * self.pad).
txt_indent      Number of spaces to indent the first line of each paragraph
txt_pad         Number of spaces to indent wrapped lines (to match up with bullet
                points, etc)
pad             Padding between the frame's edges and text. Defaults to 0.                
bg              Color to be drawn underneath the text. Defaults to "wnd_bg",
                but will use the frame's fill color instead if Fill = True
font            Text font. Defaults to preset 4.
col_txt         Text color. Defaults to "txt".

Methods

GUI.Val("my_frame")

Returns the frame's text.

GUI.Val("my_frame", "this is a new string")

Sets the frame's text and formats it to fit within the frame, as above.


3. Knob

I really hope we all know what a Knob is.

  • Adjustable via mouse-drag or mousewheel
  • Ctrl+click for finer control
  • Doubleclick to reset it to its default value



Parameters

z, x, y, w, caption, min, max, default[, inc, vals]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.    
x, y, w            Coordinates of top-left corner, width. Height is fixed.
caption            Label
min, max        Minimum and maximum values
default         Default step for the knob. Steps are counted from min to max starting at 0.

Optional

inc             Amount to increment the value per step. Defaults to 1.

vals            Boolean. Display value labels?
                For knobs with a lot of steps, i.e. Pan from -100 to +100, set this
                to false and manually update the Knob's caption instead


                Example:

                    A knob from 0 to 11, defaulting to 11, with a step size of 0.25:

                        min     = 0
                        max     = 11
                        default = 44
                        inc     = 0.25

Additional parameters

bg              Color to be drawn underneath the label. Defaults to "wnd_bg"
font_a          Caption font
font_b          Value font
col_txt         Text color
col_head        Knob head color
col_body        Knob body color    
cap_x, cap_y    Offset values for the knob's caption.    
output          Allows the value labels to be modified; accepts several different types:

                table       Replaces each value label with output[step], with the steps
                            being numbered as above

                            Useful for a knob with named settings:
                            {"Clean", "Dirty", Aggressive", "XXXTREME"}

                functions   Replaces each value with the returned value from
                            output(step), numbered as above

                            Useful for appending text to the knob values:
                            output(step) = function return step.."db" end

                Output will always count steps starting from 0, so you'll have to account for minimum
                values in the final string yourself.  

Methods

GUI.Val("my_knob")

Returns the current value of the knob. i.e. for a Pan knob, -100 to +100, Val will return an integer from -100 to +100.

GUI.Val("my_knob", -28)

Sets the value of the knob, as above.


4. Label

The most basic element, though not without some neat tricks.

Parameters

z, x, y, caption[, shadow, font, color, bg]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
caption         Label text

Optional

shadow          Boolean. Draw a shadow?
font            Which of the GUI's font values to use
color           Use one of the GUI.colors keys to override the standard text color
bg              Color to be drawn underneath the label. Defaults to "wnd_bg"

Additional parameters

Read only

w, h            These are set when the Label is initially drawn, and updated any time the 
                label's text is changed via GUI.Val().

Methods

:fade(len, z_new, z_end[, curve])

Allows a label to fade out and disappear. Nice for briefly displaying a status message like "Saved to disk..."

len             Length of the fade, in seconds
z_new           z layer to move the label to when called
                i.e. popping up a tooltip
z_end           z layer to move the label to when finished
                i.e. putting the tooltip label back in a
                frozen layer until you need it again

                Set to -1 to have the label deleted instead

curve           Optional. Sets the "shape" of the fade.

                1     will produce a linear fade
                >1    will keep the text at full-strength longer,
                but with a sharper fade at the end
                <1    will drop off very steeply

                Defaults to 3 if not specified                            

                Use negative values to fade in on z_new, rather
                than fading out. In this case, the value of
                z_end doesn't matter.

Note: While fading, the label's z layer will be redrawn on every update loop, which may impact CPU usage for scripts with many elements. If this is the case, try to put the label on a layer with as few other elements as possible.


5. Listbox

Provides a scrolling list of options, from which the user can select one or more.

Parameters

z, x, y, w, h[, list, multi, caption, pad]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Width and height of the text editor

Optional

list            Accepts either a comma-separated string of options or a table

                If you change the list at run-time using a comma-separated string,
                you'll need to call GUI.elms.my_list:init() afterward to have the
                string parsed into a table.

multi           Boolean. Can the user select multiple items (Ctrl, Shift)?
caption         Label shown to the left of the text editor
pad                Padding between the label and the text editor

Additional parameters

cap_bg          Color to be drawn underneath the label. Defaults to "wnd_bg"
bg              List background. Defaults to "elm_bg"
shadow          Boolean. Draw a shadow beneath the label?
color           Text color
font_a          Caption font
font_b          List font

Methods

GUI.Val("my_list")

Returns either a single item number (multi = false), or a table (multi = true) with the indices of each selected item.

GUI.Val("my_list", new_vals)

Accepts a table of boolean values for items to be selected or not selected.

:wnd_recalc()

If your script needs to resize the listbox, move it around, etc, run this afterward so it can update a few internal values.


6. Menubar

Ye olde menu bar. If you don't recognize this I'm not really sure how to help you.

Parameters

z, x, y, menus[, w, h, pad])

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Width and height of the Menubar
menus           Accepts a specifically formatted table.

                menus = {


                    -- Menu title
                    {title = "File", options = {

                        -- Menu item            Function to run when clicked
                        {"New",                 mnu_file.new},
                        {""},
                        {"Open",                mnu_file.open},
                        {">Recent Files"},
                            {"blah.txt",        mnu_file.recent_blah},
                            {"stuff.txt",       mnu_file.recent_stuff},
                            {"<readme.md",      mnu_file.recent_readme},
                        {"Save",                mnu_file.save},
                        {"Save As",             mnu_file.save_as},
                        {""},
                        {"#Print",               mnu_file.print},
                        {"#Print Preview",       mnu_file.print_preview},
                        {""},
                        {"Exit",                mnu_file.exit}

                    }},

                    {title = "Edit", options = {....}},

                    ...etc...

                }

Menu options can be prefixed with the following:

! : Checked
# : grayed out
> : this menu item shows a submenu
< : last item in the current submenu   

An empty item ({""},) will appear as a separator in the menu.

Functions don't need to be in any particular format or even associated with each other; this would also work:

{"New",                 new_func),
{"Open",                open),
{"Save",                savestuff),

Optional

w, h            Specify an overall width and height. If omitted, these will be calculated
                automatically from the menu titles
pad             Extra width added between menus. Defaults to 0.

Additional parameters

font            Font for the menu titles
col_txt         Color the menu titles
col_bg          Color for the menu bar
col_over        Color for the highlighted menu
fullwidth       Boolean. Extends the menubar to the right edge of the script window.
                Defaults to true.

Methods

GUI.Val("my_menubar")

Returns the menu table.

GUI.Val("my_menubar", new_menus)

Accepts a new menu table and reinitializes some internal values.

This should only be necessary if the menu titles themselves need to be changed; for things like checking off/graying out menu items, or even updating dynamic menus like "Recent Files", it would probably be easier to just edit the options = {....} yourself and directly replace it:

local new_file_options = GUI.elms.my_menubar.menus[1].options
...edit the names or whatever...
GUI.elms.my_menubar.menus[1].options = new_file_options    

7. Menubox

Provides a standard dropdown menu, with the usual separators and folders.

Parameters

z, x, y, w, h, caption, opts[, pad, noarrow])

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner, element size.
w, h            
caption            Label displayed to the left of the menu box
opts            Comma-separated string of options. As with gfx.showmenu, there are
                a few special symbols that can be added at the beginning of an option:

                    # : grayed out
                    > : this menu item shows a submenu
                    < : last item in the current submenu
                    An empty field will appear as a separator in the menu.

Optional

pad                Padding between the label and the box
noarrow         Boolean. Removes the arrow from the menubox.

Additional parameters

bg              Color to be drawn underneath the label. Defaults to "wnd_bg"
font_a          Font for the menu's label
font_b          Font for the menu's current value
col_cap         Caption color
col_txt         Text color
align           Flags for gfx.drawstr:

                    flags&1: center horizontally
                    flags&2: right justify
                    flags&4: center vertically
                    flags&8: bottom justify
                    flags&256: ignore right/bottom, 
                    otherwise text is clipped to (gfx.x, gfx.y, right, bottom)

Methods

GUI.Val("my_menu")

Returns the current menu option, numbered from 1. Numbering includes separators and submenus:

New                1
--                    
Open            3
Save            4
--                    
Recent    >    a.txt     7
            b.txt        8
            c.txt        9
--
Options         11
Quit            12

GUI.Val("my_menu", num)

Sets the current menu option, numbered as above.


8. Options

This class provides two separate, but very similar, element classes:

Radio Checklist

Both classes take the same parameters on creation, and offer the same parameters afterward - their usage only differs when it comes to how their :val() methods behave.

Parameters

z, x, y, w, h, caption, opts[, dir, pad]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
caption         Element title. Feel free to just use a blank string: ""
opts            Accepts either a table* or a comma-separated string of options.

                Options can be skipped to create a gap in the list by using "_":

                opts = "Alice,Bob,Charlie,_,Edward,Francine"
                ->
                Alice       1
                Bob         2
                Charlie     3

                Edward      5
                Francine    6


                * Must be indexed contiguously, starting from 1.

Optional

dir             "h"        Options will extend to the right, with labels above them
                "v"        Options will extend downward, with labels to their right
pad             Separation in px between options. Defaults to 4.

Additional parameters

bg              Color to be drawn underneath the caption. Defaults to "wnd_bg"
frame           Boolean. Draw a frame around the options.
size            Width of the unfilled options in px. Defaults to 20.
                * Changing this might mess up the spacing *
col_txt         Text color
col_fill        Filled option color
font_a          List title font
font_b          List option font
shadow          Boolean. Draw a shadow under the text? Defaults to true.
swap            Causes the options and their text to swap places. Purely visual.

Methods - Radio

GUI.Val("my_radio")

Returns the current option, numbered from 1.

GUI.Val("my_radio", 3)

Sets the current option, numbered from 1.

Methods - Checklist

GUI.Val("my_check")

Returns a table of boolean values for each option. Indexed from 1.

GUI.Val("my_check", {1 = true, 4 = true})

Accepts a table of boolean values for each option. Indexed from 1.


9. Slider

Provides a slider with one or more handles for the user to select values.

  • Adjustable via mouse-drag or mousewheel
  • Ctrl+click for finer control
  • Doubleclick to reset it to its default value(s)



Parameters

z, x, y, w, caption, min, max, defaults[, inc, dir]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w               Width of the slider track. Height is fixed.
caption         Label shown above the slider track.
min, max        Minimum and maximum values
defaults        Table of default steps for each slider handle.

                - Steps are inclusive, and start from 0.

                - If only one handle is needed, it can be given as a number rather than a table.

Optional

inc             Amount to increment the value per step. Defaults to 1.

dir             "h"        Horizontal slider (default)
                "v"        Vertical slider


                Slider values are counted in steps from min to max. The total number of 
                steps will be:

                        inc * |max - min|


                Examples:

                    A slider from 1 to 10, defaulting to 5:
                        min      = 1
                        max      = 10
                        defaults = 4     <-- 0,1,2,3,[4]

                    A pan slider from -100 to 100, defaulting to 0:
                        min            = -100
                        max            = 100
                        defaults = 100

                    Five sliders from 0 to 30, with a step size of 0.25,
                    defaulting to 5, 10, 15, 20, 25:

                        min            = 0
                        max            = 30
                        defaults = {20, 40, 60, 80, 100}
                        inc      = 0.25

Additional parameters

bg              Color to be drawn underneath the label. Defaults to "wnd_bg"
font_a          Label font
font_b          Value font
col_txt         Text color
col_hnd         Handle color
col_fill        Fill bar color
show_handles    Boolean. If false, will hide the slider handles.
                i.e. displaying a VU meter
show_values     Boolean. If false, will hide the handles' value labels.
cap_x, cap_y    Offset values for the slider's caption

output          Allows the value labels to be modified; accepts several different types:

                table       Replaces each value label with output[step], with the steps
                            being numbered as above

                            Useful for a slider with named settings:
                            {"Clean", "Dirty", Aggressive", "XXXTREME"}

                functions   Replaces each value with the returned value from
                            output(step), numbered as above

                            Useful for appending text to the slider values:
                            output(step) = function return step.."db" end

                Output will always count steps starting from 0, so you'll have to account for minimum
                values in the final string yourself. 

Methods

GUI.Val("my_slider")

Returns a table of values for each handle, sorted from smallest to largest

GUI.Val("my_slider", {8, 12, 16})

Accepts a table of values for each handle, as above

Slider.init_handles()

Recalculates the number of steps and resets the handles to their default positions. Must be called after changing inc, min, or max.


10. Tabs

Provides a set of tabs that swap between different sets of z-layers, allowing for a tabbed view.

Parameters

z, x, y, tab_w, tab_h, opts[, pad]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
tab_w, tab_h    Size of individual tabs
opts            Comma-separated string, or a table, of tab names

Optional

pad             Padding between tabs. Defaults to 8.

Additional parameters

w, h            Overall width and height of the tab strip. These are determined
                automatically based on the number of tabs and their size.

bg              Color to be drawn underneath the tabs. Defaults to "elm_bg".
col_txt         Text color. Defaults to "txt".
col_tab_a       Active tab color. Defaults to "wnd_bg"
col_tab_b       Inactive tab color. Defaults to "tab_bg"
font_a          Active tab font. Defaults to 3.
font_b          Inactive tab font. Defaults to 4.
fullwidth       Boolean. Extends the tab background to the right edge of the script window.
                Defaults to true.    

state           The current tab. Numbered from left to right, starting at 1.

Methods

GUI.Val("my_tabs")

Returns the active tab. Numbered from 1.

GUI.Val("my_tabs", 2)

Sets the active tab. Numbered from 1.

:update_sets(init)

Assigns the z layers that are shown for each tab. Sets are defined thusly:
(Only needs to be done once, at startup)

            GUI.elms.my_tabs:update_sets(
                {
                z-layers shown on that tab
                   tab     /
                  /       |
                 |        |
                 v        v 
                [1] = {2, 3, 4}, 
                [2] = {2, 5, 6}, 
                [3] = {2, 7, 8},
                }
            )

_update_sets()_ will be called automatically when a tab is clicked, or if a new tab is set using GUI.Val.

  • z-layers not included in any set (1, in the example above) will always be active unless frozen/hidden manually
  • z-layers in multiple sets (2, above) will be active on all of those tabs
  • Elements can have their z changed at any time - handy if you want to hide specific bits rather than the whole layer. Make sure to redraw any appropriate layers afterward
  • Likewise, the sets can be changed atany time by calling _update_sets()_ with a new layer table

Notes


11. Textbox

Provides a single-line textbox.

  • Forces a monospace font, because it makes things way easier.
  • Supports most common commands - copy, paste, selections, navigation, etc.

Parameters

z, x, y, w, h[, caption, pad]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Width and height of the textbox

Optional

caption         Label shown to the left of the textbox
pad             Padding between the label and the textbox

Additional parameters

bg              Color to be drawn underneath the label. Defaults to "wnd_bg"
shadow          Boolean. Draw a shadow beneath the label?
color           Text color
font_a          Label font
font_b          Text font
cap_pos         Position of the text box's label.
                "left", "right", "top", "bottom"

focus           Boolean. Whether the textbox is "in focus" or not, allowing users to
                type. This setting is automatically updated, so you shouldn't need to
                change it yourself in most cases.

Methods

GUI.Val("my_txt")

Returns the contents of the textbox.

GUI.Val("my_txt", "Hello, world!")

Sets the contents of the textbox.


12. TextEditor

Provides a multiline text editor.

  • Forces a monospace font, because it makes things way easier.
  • Supports most common commands - copy, paste, selections, navigation, tab, backtab, etc.
  • Due to Reaper limitations, tab characters are automatically replaced with four spaces.
  • Does not support word wrapping yet, unfortunately.

Parameters

z, x, y, w, h[, text, caption, pad]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.
x, y            Coordinates of top-left corner
w, h            Width and height of the text editor

Optional

text            Multiline string of text
caption         Label shown to the left of the text editor
pad             Padding between the label and the text editor

Additional parameters

cap_bg          Color to be drawn underneath the label. Defaults to "wnd_bg"
bg              Editor background. Defaults to "elm_bg"
shadow          Boolean. Draw a shadow beneath the label?
color           Text color
font_a          Label font

font_b          Text font.
                *** Only monospaced fonts will work properly ***

focus           Whether the text editor is "in focus" or not, allowing users to type.
                This setting is automatically updated, so you shouldn't need to
                change it yourself in most cases.

undo_limit      How many undo states can be stored before the first is erased.
                Defaults to 20.

Methods

GUI.Val("my_txt")

Returns self.retval as a string.

GUI.Val("my_txt", "In a hole in the ground, there lived a Hobbit.")

Accepts a either a table or a multiline string.

:wnd_recalc()

If your script needs to resize the text editor, move it around, etc, run this afterward so it can update a few internal values.


13. Window

Allows scripts to open subwindow for preferences, dialog messages, etc.



Parameters

z, x, y, w, h, caption, z_set[, center]

Required

z               Element depth, used for hiding and disabling layers. 1 is the highest.    
x, y, w, h      Coordinates of top-left corner, width, and height.
caption            Label
z_set           A table of z layers to display. Must include the window's own layer.

                i.e. {4, 5, 6}

Optional

center          Boolean. If true, will center the window element in the script window,
                ignoring the window's given x,y coordinates. Defaults to true.

Additional parameters

title_height    Height, in pixels, of the window's title bar. Defaults to 20.
noclose         Boolean. If true, will hide the X button. Defaults to false.

Methods

:open()

Opens the window. Any arguments given are passed directly to :onopen

:onopen(?)

Hook for a function to perform when the window is opened. This is where you might have the window's elements populated with information from the main script, for instance.

:close()

Closes the window. Any arguments given are passed directly to :onclose

:onclose(?)

Hook for a function to perform when the window is closed. This is where you might apply or revert values from the window to the main script, depending on what argument is given.

:adjustelm(elm)

Accepts a GUI element. The element is repositioned with its given x,y coordinates relative to the window element, i.e. a button created at 48,48 will be adjusted to Window.x + 48, Window.y + Window.title_height + 48. The original coordinates are stored as elm.ox, elm.oy.


1. Element creation

GUI.New(name, type, params...)

Creates a new GUI element, overwriting any existing element of the same name. Can be used in two different ways:

Function Arguments

The element's required and/or optional parameters are passed as function arguments:

        name        type     z  x   y   caption  shadow font
GUI.New("my_label", "Label", 2, 16, 16, "Hello!", true, 1)

These parameters, and any parameters listed as Additional, can be accessed afterward via GUI.elms.my_label.parameter.

Keyed table

The element's required, optional, and any additional parameters are given as a table:

GUI.New({
    name = "my_label",
    type = "Label"
    z = 2,
    x = 16,
    y = 16,
    caption = "Hello!",
    shadow = true,
    font = 1
})

GUI.CreateElms(elms)

Allows for the creation of multiple elements from a set of keyed tables at once. First, a table containing the element tables described above is created:

local elements = {}
elements.my_label = {
    type = "Label"
    z = 2,
    ...
}
elements.my_button = {
    type = "Button",
    z = 2,
    ...
}

And then the table is passed to our function:

GUI.CreateElms(elements)

CreateElms doesn't do anything special; it simply iterates through all of the keyed tables and passes them individually to GUI.New.


2. GUI variables

GUI.mouse.x, GUI.mouse.y

Updated on every loop with the mouse coordinates, relative to the script window. Identical to gfx.mouse_x, gfx.mouse_y.

GUI.mouse.cap

Updated on every loop with the state of Ctrl, Shift, Alt, etc. Identical to gfx.mouse_cap.

1       Left mouse button
2       Right mouse button
4       Control key
8       Shift key
16      Alt key
32      Windows key
64      Middle mouse button

Values can be checked using bitwise comparison: if GUI.mouse.cap & 8 == 8 then shift_is_pressed end.

GUI.mouse.wheel

Updated on every loop with the position (relative to the last value) of the mouse wheel. Shouldn't need to be accessed directly; all GUI elements receive a value, inc, when :onwheel is called. Identical to gfx.mouse_wheel.

GUI.mouse_down_elm

While a mouse button is down, these store the element that was clicked.

Also: rmouse_down_elm, mmouse_down_elm.

GUI.mouse.ox, GUI.mouse.oy

When the mouse is being dragged (i.e. an element's :ondrag logic is being called), these store the the mouse's original coordinates in the script window.

Also: r_ox, r_oy, m_ox, m_oy.

GUI.mouse.off_x, GUI.mouse.off_y

When the mouse is being dragged these store the mouse's original position relative to the element being dragged. i.e. If the element was at 32,32 , and the mouse was pressed down at 64,48, _off_x_ and _off_y_ would be 32,16.

Also: r_off_x, r_off_y, m_off_x, m_off_y.

GUI.char

Updated on every loop with the keyboard state. Identical to calling gfx.getchar().

GUI.escape_bypass

Boolean. When set to true, pressing Esc will not close the script window. Use this if your script needs to use Esc for something itself.

GUI.SWS_exists

Boolean. True if the SWS extension is available (currently only SWS v2.9.7 or newer).

GUI.script_restricted

Boolean. True if the script is running in "restricted permissions" mode.

GUI.tooltip_time

Number. Specifies the delay time when the mouse is hovered over an element before a tooltip is shown (if GUI.elms.my_elm.tooltip = "Hello!" is specified).


3. Script hooks

_Lokasenna_GUI_ provides several hooks for user functions, to be run at appropriate times as part of the update loop. All of them are set the same way: GUI.func = my_func

GUI.func

Called on every update loop, after updating elements and checking for user input but prior to redrawing the elements and window. For functions that would be CPU-heavy if run on every loop, it can be set to run every _X_ seconds by adding GUI.freq = X.

GUI.onresize

Called whenever the window has been resized. Can be used to reposition elements (i.e. "glueing" them to the right side) or to simply force the window to stay at a certain size:

local function force_size()
    gfx.quit()
    gfx.init(GUI.name, GUI.w, GUI.h, GUI.dock, GUI.x, GUI.y)
    -- So .onresize isn't called again on the next loop
    GUI.cur_w, GUI.cur_h = GUI.w, GUI.h
end
GUI.onresize = force_size

GUI.onmousemove

Called whenever the user moves the mouse. I had a cool reason for doing this once, but I'll be damned if I can remember what it was now.


4. Table functions

GUI.table_copy(source[, base])

Copies the contents of one table to another, including metatables and subtables.

Required

source      A table

Optional

base        Will use this as the primary source for copying and then grab any additional keys 
            from 'source' didn't exist here.

Returns a table.

GUI.table_list(t[, max_depth, cur_depth])

For development/debugging purposes. Returns a table's contents as a string, indented to show nesting, etc.

Required

t           A table

Optional

max_depth   How many levels of nesting to read through.
cur_depth   Will "pad" the indenting by this amount of tabs.

Returns a string.

GUI.table_compare(t_a, t_b)

Recursively compares the contents of two tables, since Lua doesn't do this on its own.

Required

t_a         A table
t_b         Another table

Returns a boolean.

GUI.table_find(t, find[, f])

Looks through a table and returns the key of the first matching value.

Required

t           A table
find        Search term - a string, a number, a boolean

Optional

f           Sorting function. Defaults to ```ipairs```.

If you need to find multiple values in the same table, and each is known to only occur once, it will be much more efficient to copy the table with GUI.table_invert and just check for keys as normal.

Returns a string or number.

GUI.table_invert(t)

Returns a table with the keys and values swapped.

Required

t           A table

Returns a table.

GUI.kpairs(t[, f])

For use in place of pairs or ipairs - iterates through a table in alphabetical/numerical order.

Required

t           A table

Optional

f           An iterator function, as with 'pairs' or 'ipairs'. Specify f = "full" to perform 
            a comparison across types. i.e.

                12 > "6 apples" -> true

5. Text functions

Text functions

GUI.init_txt_width()

Creates a table with the widths of every character in every font specified in GUI.fonts.

GUI.get_txt_width(str, font)

Uses the table above to measure a given string at a given font size. For scripts that need to work with a lot of text, this can be more than ten times faster than gfx.measurestr.

Required

str         A string
font        A GUI font preset

Returns a width in pixels.

GUI.fit_txt_width(str, font, w)

Measures a string to see how much of it will fit in the given width.

Required

str         A string
font        A GUI font preset
w           Width in pixels

Returns two strings - the text that will fit, and the excess.

GUI.word_wrap(str, font, w[, indent, pad])

Measures a string and adds line breaks where appropriate to fit within a specified width.

Required

str         String. Can include line breaks/paragraphs; they should be preserved.
font        A GUI font preset
w           Pixel width

Optional

indent      Number of spaces to indent the first line of each paragraph. Defaults to 0.

            *The algorithm skips tab characters and leading spaces, so
            use this parameter instead*

i.e.        Blah blah blah blah        -> indent = 2 ->      Blah blah blah blah
            blah blah blah blah                                        blah blah blah blah


pad         Indent wrapped lines by the first __ characters of the paragraph. Defaults to 0.

            (For use with bullet points, etc)

i.e.        - Blah blah blah blah    -> pad = 2 ->    - Blah blah blah blah

blah blah blah blah blah blah blah blah

Returns a string.

(This function expands on the "greedy" algorithm found here: https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap#Algorithm)

GUI.shadow(str, col1[, col2])

Draws a string with a shadow effect.

Required

str         A string
col1        Text color

Optional

col2        Shadow color. If not specified, will use "shadow".

GUI.outline(str, col1, col2)

Draws a string with a colored outline.

Required

str         A string
col1        Text color
col2        Outline color

GUI.textbg(str, col)

Draws a string over a solid background, slightly larger than the string itself. This is necessary if drawing on an "empty" buffer where antialiasing will cause the text to look thin and pixellated. This should be called with gfx.x/y, GUI.font, and GUI.color already set appropriately for the text.

Required

str         A string
col         Background color

6. Color functions

GUI.hex2rgb(num)

Converts a hex color to 8-bit RGB.

Required

num         A color in hex format - RRGGBB or 0xRRGGBB

Returns R, G, B as values from 0-255.

GUI.rgb2hsv(r, g, b[, a])

Converts from RGB (optionally _A_) to HSV. Arguments and returns are values from 0-1.

Required

r, g, b     Color values from 0-1.

Optional

a           Alpha value from 0-1. Defaults to 1.

Returns H, S, V, A as values from 0-1.

GUI.hsv2rgb(h, s, v[, a])

And back the other way.

Required

h, s, v     Color values from 0-1.

Optional

a           Alpha value from 0-1. Defaults to 1.

Returns R, G, B, A as values from 0-1.

GUI.gradient(col_a, col_b, pos)

Accepts two RGB color tables and calculates the color at a specified position on a gradient between them.

Required

col_a       Color tables: {R, G, B[, A]}, values from 0-1
col_b
pos         Position along the gradient from 0 (col_a) to 1 (col_b)

Returns R, G, B, A as values from 0-1.


7. Math and Logic functions

GUI.round(num[, places])

Rounds a number to the nearest integer.

Required

num         A number

Optional

places      How many decimal places to round to. Defaults to 0.

Returns a number.

GUI.nearestmultiple(val, snap)

Rounds a number to the nearest multiple of another.

Required

num         A number
snap        Number to "snap" to multiples of.

Returns a number.

GUI.clamp(num, min, max)

Used to limit a value to a specified range, hence the function name. The order of arguments doesn't actually matter.

Required

num         A number
min
max

Returns a number.

GUI.ordinal(num)

Returns an ordinal - "1st, 2nd, 3rd", etc.

Required

num         A number

Returns a string.

GUI.polar2cart(angle, radius[, ox, oy])

Converts an angle from polar coordinates to Cartesian.

Required

angle       An angle in radians. Omit Pi, i.e.  pi/4 -> 0.25
radius      A number.

Optional

ox          Origin point. Defaults to 0, 0.
oy

Returns x, y relative to the origin.

GUI.cart2polar(x, y[, ox, oy])

Converts a point from Cartesian to polar coordinates.

Required

x           A point.
y

Optional

ox          Origin point. Defaults to 0, 0.
oy

Returns angle, radius. As above, the angle is given without reference to Pi, i.e. pi/4 -> 0.25.

GUI.xor(a, b)

Compares two values using a boolean exclusive or.

Required

a,b         Values. 

Returns true if one value is true, but not both.


8. Graphics functions

GUI.roundrect(x, y, w, h, r[, antialias, fill])

Draws a rounded rectangle. This function is an improved version of gfx.roundrect, adapted from an EEL example by mwe.

Required

x,y,w,h     Coordinates and dimensions of the roundrect
r           Radius of the corners

Optional

antialias   Boolean. Whether to antialias the corners. Defaults to true.
fill        Boolean. Whether to fill in the roundrect.

GUI.triangle(fill, x1, y1, x2, y2, x3, y3, ...)

Draws a triangle (or any polygon), with optional fill.

Required

fill        Boolean. Whether to fill in the triangle.
x1,y1       Each point of the triangle.
x2,y2
x3,y3

Optional

x4,y4       Additional points, for larger polygons. The last point will always be connected
...         back to the first point.

GUI.GetBuffer(num)

Assigns one or more image buffers, so elements have their own space to draw without inteference.

Required

num         Number of buffers required

Returns either a buffer number, or a table of buffer numbers: {1020,1021,1022}.

GUI.FreeBuffer(num)

Lets the GUI know that a given buffer, or buffers, are no longer in use.

Required

num         A buffer number, or a table of buffer numbers as above.

9. Element functions

GUI.Val(elm_name[, newval])

Gets or sets an element's value. It's preferred to access element values this way rather than GUI.elms.my_element.retval = 5, as many of classes will need to format their output or sanitize their input.

Required

elm_name    Element name

Optional

newval      New value. Type is determined by each element class. If not specified, the function
            will simply return the current value.

Returned values are of varying types, depending on the element class. See each class's documentation.

GUI.IsInside(elm[, x, y])

Determines if a given position is within the element specified.

Required

elm         A GUI element, or a table containing:
                {x = _, y = _, w = _, h = _}

Optional

x,y         A coordinate pair. If not specified, the mouse position will be used.

Returns a boolean.

GUI.center(elm1[, elm2])

Gives x,y coordinates that would center elm1 within elm2.

Required

elm1        A GUI element, or a table as above.

Optional

elm2        A GUI element, or a table as above. 
            If not specified, elm1 will be centered in the script window.

Returns x,y.


1. Building a class

What is a class?

Classes, in Lua, are really just tables that have been set up to reference a different table in the event that they don't contain a requested value. That is:

  • A Person is human, with skin but no wings
  • Susan is female, with blonde hair and green eyes

Does Susan have wings? We don't know, because Susan's description doesn't tell us. We can do this instead:

  • Susan is a Person, female, with blonde hair and green eyes

Susan's description still doesn't tell us, but since we know that she's a Person we can look and see that people do not, in fact, have wings.

(If you'd like a more detailed description of this behavior, do a Google search for "lua classes metatables" and try not to fall asleep.)

For our purposes, your class just needs to reference GUI.Element like so:

GUI.Thing = GUI.Element:new()

GUI.Element doesn't do much of anything; it exists simply to keep the script from crashing when it goes to look for a method you didn't add.

Methods

'Methods' refer to all of the actions a class can perform. i.e.

Person:walk("2 feet", "east")
Person:sleep("4 hours")
Person:thinkabout("N'Sync")

The first method a class needs is a way to store all of its parameters, and for it know that it is, in fact, part of a class:

function GUI.Thing:new(name, z, x, y, ...whatever parameters you want...)

    local Thing = {}

    Thing.name = name
    Thing.z = z
    Thing.x, Thing.y = x, y

    Thing.optional_parameter = optional_parameter or 5

    Thing.internal_value = human_readable_value * (2 / math.pi)

    Thing.retval = human_readable_value


    setmetatable(Thing, self)
    self.__index = self
    return Thing

end

That last bit tells our temporary Thing to use 'self' as a backup if it can't find a value or method. Because this function uses a : up top, self is included as a hidden variable referring to whatever called it. i.e. In GUI.Thing:new() self refers to GUI.Thing.

By giving the temporary Thing instructions to look up GUI.Thing, we've created a new, independent copy of our base class. It can be modified, told to do stuff, even rewritten, and other Things won't be affected. If any of them are asked for a value they don't have, they'll look at their class for an answer.

:init

Many elements will want to do a bit of prep work when first created, such as getting a buffer and drawing themselves to it for blitting later. init is called:

  • When GUI.Init() opens the window, for all elements that have been created at the time.
  • When any element is created after that.
function GUI.Thing:init()

    self.buffers = GUI.GetBuffer(3)

    ...draw some stuff to the buffers...

    if self.caption ~= "" then

        GUI.font(self.font)
        self.cap_w, cap_h = gfx.measurestr(self.caption)

    end

end

The call to GUI.GetBuffer simply asks the GUI logic to assign 3 graphics buffers to this element so that there's no risk of other elements drawing in them. Classes can also have a common buffer for all child elements, for instance if your buttons are all going to be the same color and size. In this case, you would assign the buffer to the class as a whole:

GUI.Thing = GUI.Element:new()
GUI.Thing.buffers = GUI.GetBuffer(2)

function GUI.Thing:new(....)
    ...etc...
end

Because of the metatable stuff, an element looking for 'self.buffer' will come up blank and go looking to see if GUI.Thing.buffer exists.

:draw

Classes probably need to be able to draw themselves, right? This method is called any time the element's z layer is asked to redraw.

function GUI.Thing:draw()

    gfx.rect(self.x, self.y, self.w, self.h)

    gfx.x, gfx.y = self.x, self.y
    gfx.drawstr(self.caption)        

end

If your class made use of a buffer to pre-draw a few things, you instead have to copy ('blit' being the computer term) the buffer's contents (or simply part of it) to the main drawing area.

function GUI.Thing:draw()

    gfx.blit(self.buff[1], 1, 0)
    gfx.blit(self.buff[2], 1, 0)        

    gfx.x, gfx.y = self.x, self.y
    gfx.drawstr(self.caption)       
end

It's also perfectly fine to use a combination of blitting and drawing - most of the stock classes do. In the Slider class, for instance:

init...

  • Draws the background "track" to self.buffs[1]
  • Draws a single slider handle and its shadow to self.buffs[2]

draw...

  • Blits the track
  • Draws the green 'fill'
  • Blits a copy of the original handle and shadow for each handle on the slider
  • Draws the text values next to them

(I apologize if the Slider class has been updated at some point and no longer works that way by the time you read this)

There are also more complicated ways to use blitting such as scaling, rotation, cropping, and different blending modes. See the ReaScript API's gfx functions for more detail on that topic.

:val

Most classes will also need a way to return their value, or have a new value assigned to them. In some cases this can be done directly, like so:

local label_caption = GUI.elms.my_label.retval
GUI.elms.my_label.retval = "Ha! I changed the text!"

but many classes will want to do some processing to convert their internal parameters to a human-readable string, and vice versa. In this case, use:

function GUI.Thing:val(newval)

    if newval then

        self.retval = (newval - self.min_value) / (self.number_of_values)

        self:redraw()

    else

        return ((self.retval * self.number_of_values) + self.min_value)

    end

end

All elements' :val() methods can be accessed via GUI.Val():

-- Get the value
local thing_value = GUI.Val("my_thing")

-- Set a new one
GUI.Val("my_thing", 12)

Input

Another thing most classes might need is a way to respond to the user's input. We'll use :onclick as an example.

function GUI.Thing:onclick()

    local x_val = (GUI.mouse.x - self.x) / self.w
    local y_val = (GUI.mouse.y - self.y) / self.h

    self.caption("x_val = "..x_val.."\ny_val = "..y_val)

    self:redraw()

end

Note the last line there, and remember that elements are only redrawn if their z layer is told to redraw. If the method didn't include that line, the new caption would never be displayed because the GUI would just keep blitting the previous copy.


Classes are by no means limited to these methods, although the GUI logic will only pass input to an element based on the existing input methods.

You can also add as many extra methods as you like - the stock classes have dozens, particularly for complicated class behaviors that would leave you with a three-hundred-line function if you tried to do it all in one function.


For a full list of the input methods provided by Lokasenna_GUI, see the comments for GUI.Element in Core.lua.