jeopardy/gui/docs/gui-library-docs.md
2026-05-12 21:10:14 -07:00

29 KiB
Raw Blame History

GUI Library Documentation

A LÖVE2D-based GUI framework with a dual-dimension layout system, event-driven architecture, theming, transitions, and a rich set of built-in widgets.


Table of Contents

  1. Setup & Integration
  2. The Dual-Dimension Layout System
  3. Core Widget Reference
  4. Base Object API
  5. Events & Connections
  6. Theming
  7. Color Module
  8. Transitions & Animation
  9. Add-on Widgets
  10. Canvas
  11. Simulation (Testing)
  12. Scheduler Probe (Load Monitoring)
  13. Task Manager
  14. Tips & Patterns

1. Setup & Integration

Require the library at the top of your project. The library hooks into LÖVE's callbacks automatically.

local gui = require("gui")

function love.update(dt)
    gui.update(dt)
end

function love.draw()
    gui.draw()
end

That is all that is required. The library installs its own hooks for love.keypressed, love.mousepressed, love.resize, etc., so you do not need to forward those manually.

Creating Additional Processors

The library runs on the multi scheduler. If you need background work to integrate with the GUI update cycle, use gui:newProcessor:

local proc = gui:newProcessor("MyProcessor")
proc:newThread(function() ... end)

2. The Dual-Dimension Layout System

Every object's size and position is described by eight numbers that combine pixel offsets with fractional (01) scale values relative to the parent. This is the library's central concept.

setDualDim(x, y, w, h, sx, sy, sw, sh)
Parameter Meaning
x Pixel offset from parent's left edge
y Pixel offset from parent's top edge
w Pixel width
h Pixel height
sx Fractional X position (0 = left, 1 = right of parent)
sy Fractional Y position (0 = top, 1 = bottom of parent)
sw Fractional width (0 = 0px, 1 = full parent width)
sh Fractional height (0 = 0px, 1 = full parent height)

The resolved absolute values are calculated as:

absolute_x = parent.w * sx + x + parent.x
absolute_y = parent.h * sy + y + parent.y
absolute_w = parent.w * sw + w
absolute_h = parent.h * sh + h

Examples

-- Full-screen frame (fills parent completely)
frame:setDualDim(0, 0, 0, 0,   0, 0, 1, 1)

-- Fixed 200×50 button in the top-left corner
btn:setDualDim(10, 10, 200, 50)

-- Centered horizontally, 300px wide, 5% from the top
panel:setDualDim(-150, 0, 300, 0,   0.5, 0.05, 0, 0.4)
-- sx=0.5 puts the left edge at the parent's midpoint;
-- x=-150 shifts it left by half the panel's own width.

-- Right-aligned sidebar, 20% of parent width, full height
sidebar:setDualDim(0, 0, 0, 0,   0.8, 0, 0.2, 1)

-- Anchored to bottom-right corner, fixed 100×30
btn:setDualDim(-110, -40, 100, 30,   1, 1)

Helper: fullFrame()

Sets the object to fill its parent completely (equivalent to setDualDim(0,0,0,0,0,0,1,1)).

frame:fullFrame()

Reading Position

local x, y, w, h = obj:getAbsolutes()  -- resolved pixel values

local x, y, w, h, sx, sy, sw, sh = obj:getDualDim()  -- raw dual-dim values

Squaring

Setting obj.square = "w" forces h = w (width-driven square). Setting obj.square = "h" forces w = h (height-driven square). Useful for icon buttons and circle elements.


3. Core Widget Reference

All constructors follow the same signature pattern:

parent:newXxx(x, y, w, h, sx, sy, sw, sh)

where parent is either gui (the root) or another object. Children are drawn on top of and clipped by (if clipDescendants is set) their parent.


3.1 Frame

A plain rectangular container. The base building block.

local panel = gui:newFrame(x, y, w, h, sx, sy, sw, sh)
-- Example: a full-screen dark overlay
local overlay = gui:newFrame(0, 0, 0, 0, 0, 0, 1, 1)
overlay.color = {0, 0, 0}
overlay.visibility = 0.6

Virtual Frame — exists in memory and updates but is never drawn. Used to move objects off-screen without destroying them.

local vframe = gui:newVirtualFrame(...)

Visual Frame — a frame that does not participate in mouse hit-testing. Children of a visual frame are also non-interactive.

local display = gui:newVisualFrame(...)

3.2 TextLabel

A non-interactive frame that renders text.

local label = parent:newTextLabel("Hello, World!", x, y, w, h, sx, sy, sw, sh)

Key properties:

Property Type Description
text string The displayed text
textColor color Text color (default: black)
font Font LÖVE font object
align constant gui.ALIGN_LEFT, gui.ALIGN_CENTER, or gui.ALIGN_RIGHT
textOffsetX/Y number Pixel nudge for text rendering
textScaleX/Y number Scale multiplier for text
textVisibility number 01 alpha for text only

Font methods:

label:setFont(16)                      -- set by size (default font)
label:setFont("fonts/myfont.ttf", 18)  -- set by file + size
label:setFont(myLoveFont)              -- set by existing font object

label:fitFont(minSize, maxSize)        -- auto-fit text to the element's bounds
label:centerFont()                     -- vertically center text within element

3.3 TextButton

An interactive button with text. Automatically shows a hand cursor on hover.

local btn = parent:newTextButton("Click Me", x, y, w, h, sx, sy, sw, sh)
btn.OnReleased(function(self, x, y, button, istouch)
    print("Button clicked!")
end)

Buttons automatically integrate with the theming system inside a newWindow — they receive button colors, hover highlights, and the button font.


3.4 TextBox (Input)

An editable single-line text input field. Gains focus on click and shows a blinking cursor.

local input = parent:newTextBox("placeholder", x, y, w, h, sx, sy, sw, sh)

Key properties:

Property Type Description
text string Current text value
cur_pos number Cursor position (character index)
blink boolean Whether cursor blinks (default: true)

Selection API:

input:HasSelection()      -- boolean
input:GetSelection()      -- start, stop (always start <= stop)
input:GetSelectedText()   -- string
input:ClearSelection()

Events:

input.OnReturn(function(self, text)
    print("Submitted:", text)
end)

Built-in hotkeys (active when the textbox has focus):

  • Ctrl+A — select all
  • Ctrl+C — copy selection
  • Ctrl+V — paste
  • Ctrl+X — cut selection
  • Left/Right — move cursor
  • Backspace/Delete — delete character

3.5 ImageLabel

Displays an image. Supports PNG, JPG, and GIF.

local img = parent:newImageLabel("path/to/image.png", x, y, w, h, sx, sy, sw, sh)

The image is stretched to fill the element's bounds. GIFs animate automatically.

Changing the image:

img:setImage("path/to/other.png")
img:setImage(loveImageObject)
-- Tile from a spritesheet:
img:setImage("spritesheet.png", srcX, srcY, srcW, srcH)

Flipping:

img:flip()        -- flip horizontally
img:flip(true)    -- flip vertically

Gradient fill (applies a gradient image to any frame or image element):

panel:applyGradient("vertical", color1, color2, color3)
panel:applyGradient("horizontal", {1,0,0,1}, {0,0,1,1})

Pre-loading images into cache:

gui.cacheImage(gui, "assets/hero.png")
gui.cacheImage(gui, {"assets/a.png", "assets/b.png"})

3.6 ImageButton

An image that responds to click events. Shows a hand cursor on hover.

local btn = parent:newImageButton("icon.png", x, y, w, h, sx, sy, sw, sh)

btn.OnReleased(function(self)
    self:setImage("icon_pressed.png")
end)

3.7 Video

Plays a LÖVE-supported video file inside an element.

local vid = parent:newVideo("movie.ogv", x, y, w, h, sx, sy, sw, sh)

Playback control:

vid:play()
vid:pause()
vid:stop()      -- pause + rewind
vid:rewind()
vid:seek(t)     -- seek to time in seconds
vid:tell()      -- returns current time in seconds
vid:getDuration()
vid:setVolume(0.8)

Events:

vid.OnVideoFinished(function(self)
    print("Video ended")
end)

4. Base Object API

Every object in the hierarchy inherits the following API.


4.1 Positioning & Sizing

obj:setDualDim(x, y, w, h, sx, sy, sw, sh)  -- update position/size; nil preserves current value
obj:getAbsolutes()                             -- returns resolved x, y, w, h in pixels
obj:getDualDim()                               -- returns all 8 raw values
obj:move(dx, dy)                               -- increment pixel offset (fires OnPositionChanged)
obj:size(dx, dy)                               -- increment pixel size (fires OnSizeChanged)
obj:moveInBounds(dx, dy)                       -- move but keep within parent bounds
obj:fullFrame()                                -- shorthand for 100%×100% fill
obj:centerX(true)                              -- auto-center horizontally within parent
obj:centerY(true)                              -- auto-center vertically within parent

4.2 Appearance

obj.color = {r, g, b}              -- background color (01 range)
obj.borderColor = {r, g, b}        -- border color
obj.drawBorder = true/false        -- show/hide border
obj.visibility = 0.8               -- overall opacity 01
obj.rotation = 45                  -- rotation in degrees

obj:setRoundness(rx, ry, segments) -- round all corners
obj:setRoundness(rx, ry, seg, "top")    -- round top corners only
obj:setRoundness(rx, ry, seg, "bottom") -- round bottom corners only

obj.shader = myShader              -- apply a LÖVE shader (image elements only)
obj.clipDescendants = true         -- clip child rendering to this element's bounds

4.3 Visibility & Lifecycle

obj.visible = false   -- hide (and stop receiving events)
obj.active  = false   -- deactivate without hiding

obj:isActive()        -- true if active and not in the virtual tree
obj:isOffScreen()     -- true if fully outside the window

obj:topStack()        -- move to top of parent's draw order (drawn last = on top)
obj:bottomStack()     -- move to bottom of draw order

obj:destroy()         -- remove from tree, disconnect all events, free resources
obj:removeChildren()  -- destroy all children but keep the object itself

4.4 Interaction

-- Drag support
obj:enableDragging(gui.MOUSE_PRIMARY)    -- enable drag with left mouse button
obj:enableDragging(gui.MOUSE_SECONDARY)  -- enable drag with right button
obj:enableDragging(false)                -- disable dragging

-- Hierarchy hit-testing: only fires events when not occluded by a sibling
obj:respectHierarchy(true)

-- Tag system (used for identifying objects and filtering events)
obj:tag("myTag")          -- set the primary tag (accessible via :getTag())
obj:setTag("category")    -- set an arbitrary tag key
obj:hasTag("category")    -- boolean
obj:parentHasTag("visual") -- checks the ancestor chain

-- Tree queries
obj:getChildren()         -- immediate children table
obj:getAllChildren()       -- flat list of all visible descendants
obj:isDescendantOf(other) -- boolean
obj:canPress(mx, my)      -- boolean: would a click at mx,my hit this object?

-- Cloning
local copy = obj:clone()
local copy = obj:clone({copyTo = parent, connections = true})

4.5 Shape & Form Factor

By default elements are rectangles. You can change an element's form factor:

-- Circle
obj:makeCircle(x, y, radius, sx, sy, sr, segments)

-- Arc
obj:makeArc(arcType, x, y, radius, sx, sy, sr, angle1, angle2, segments)
-- arcType is a LÖVE arc type string: "open", "closed", or "pie"

The formFactor property can also be set directly:

obj.formFactor = gui.FORM_RECTANGLE  -- default
obj.formFactor = gui.FORM_CIRCLE
obj.formFactor = gui.FORM_ARC

5. Events & Connections

The library uses the multi connection system. Connections are called with the syntax:

obj.OnSomeEvent(function(self, ...)
    -- handler
end)

Multiple handlers can be attached to a single event. Connections can be combined:

-- OR: fires the handler if either event fires
(obj.OnReleased + obj.OnReleasedOuter)(function() ... end)

-- AND: fires the handler only when both have fired
(conn1 * conn2)(function() ... end)

5.1 Per-Object Events

Event Arguments Description
OnPressed self, x, y, dx, dy, istouch Mouse pressed inside the element
OnReleased self, x, y, button, istouch, presses Mouse released inside the element
OnReleasedOuter same Released after a press, but outside the element
OnReleasedOther same Released with no previous press on this element
OnPressedOuter self, x, y, button, istouch, presses Pressed outside this element
OnEnter self, x, y Mouse moved onto the element
OnExit self, x, y Mouse moved off the element
OnMoved self, x, y, dx, dy, istouch Mouse moved while over the element
OnDragStart self, dx, dy, x, y, istouch Drag began
OnDragging self, dx, dy, x, y, istouch Drag in progress
OnDragEnd self, dx, dy, x, y, istouch, presses Drag ended
OnWheelMoved x, y Scroll wheel moved while cursor is over element
OnSizeChanged self, ... Element size changed
OnPositionChanged self, ... Element position changed
OnDestroy self Element is being destroyed
OnCreated element Fires for any descendant created under this element
OnLoad Fires once when the element is first set up
OnUpdate self, dt Fires every update frame

Gamepad / joystick events are also available on every object:

obj.OnLeftStickUp / Down / Left / Right
obj.OnRightStickUp / Down / Left / Right

TextLabel / TextButton / TextBox extras:

obj.OnFontUpdated(function(self) end)  -- font changed
input.OnReturn(function(self, text) end) -- Enter key pressed in textbox

Video extras:

vid.OnVideoFinished(function(self) end)

5.2 Global Events

All global events live under gui.Events:

gui.Events.OnKeyPressed(function(key, scancode, isrepeat) end)
gui.Events.OnKeyReleased(function(key, scancode) end)
gui.Events.OnTextInputed(function(text) end)
gui.Events.OnMouseMoved(function(x, y, dx, dy, istouch) end)
gui.Events.OnMousePressed(function(x, y, button, istouch, presses) end)
gui.Events.OnMouseReleased(function(x, y, button, istouch, presses) end)
gui.Events.OnWheelMoved(function(x, y) end)
gui.Events.OnResized(function(w, h) end)
gui.Events.OnQuit(function() end)
gui.Events.OnFilesDropped(function(x, y, files) end)
gui.Events.OnFocus(function(focus) end)
gui.Events.OnObjectFocusChanged(function(previous, current) end)

-- Gamepad / joystick
gui.Events.OnGamepadPressed(function(joystick, button) end)
gui.Events.OnGamepadAxis(function(joystick, axis, value) end)

5.3 Hotkeys

Register a hotkey and get back a connection object:

local conn = obj:setHotKey({"lctrl", "s"})
conn(function(ref)
    print("Save triggered from", ref)
end)

Multiple key combinations for the same action:

local onSave = gui:setHotKey({"lctrl", "s"}) + gui:setHotKey({"rctrl", "s"})
onSave(function() save() end)

Built-in hotkeys:

Hotkey Connection
Ctrl+A gui.HotKeys.OnSelectAll
Ctrl+C gui.HotKeys.OnCopy
Ctrl+V gui.HotKeys.OnPaste
Ctrl+X gui.HotKeys.OnCut
Ctrl+Z gui.HotKeys.OnUndo
Ctrl+Y / Ctrl+Shift+Z gui.HotKeys.OnRedo
Ctrl+T Toggle Task Manager

6. Theming

The theme module generates consistent color palettes for use with newWindow and other themed widgets.

local theme = require("gui.core.theme")

Creating a Theme

From explicit colors:

local t = theme:new(primaryColor, primaryText, buttonText, buttonNormal, primaryFont, buttonFont)

-- Using hex strings (most convenient):
local t = theme:new("#2d6a9f", "#f0f0f0", "#ffffff")

From a table (preferred for full control):

local t = theme:new({
    primary          = "#124559",
    primaryDark      = "#01161E",
    primaryText      = "#AEC3B0",
    buttonNormal     = "#1e6f8a",
    buttonHighlight  = "#2a9bbf",
    buttonText       = "#ffffff",
    textFont         = myFont,
    buttonTextFont   = myBoldFont,
    -- Any extra keys are kept and accessible on the theme object
})

Random harmonious theme:

local t = theme:random()              -- any brightness
local t = theme:random(nil, "dark")   -- dark palette
local t = theme:random(nil, "light")  -- light palette
local t = theme:random(12345)         -- reproducible from a seed
print(t:getSeed())                    -- retrieve the seed
print(t:dump())                       -- export as hex string

Theme Properties

Property Description
colorPrimary Main background color
colorPrimaryDark Darker variant (headers, accents)
colorPrimaryText Text on primary backgrounds
colorButtonNormal Button resting color
colorButtonHighlight Button hover color
colorButtonText Text on buttons
fontPrimary Font for labels
fontButton Font for buttons

Applying a Theme to a Window

Pass the theme as the last argument to newWindow — the window will automatically style all child buttons and labels that are created inside it:

local win = gui:newWindow(x, y, w, h, "Title", draggable, myTheme)

7. Color Module

The color module handles color creation, conversion, and manipulation. All colors in the library are {r, g, b} or {r, g, b, a} tables with values in the 01 range.

local color = require("gui.core.color")

Creating Colors

-- Hex string
color.new("#ff5500")
color.new("#ff550088")      -- with alpha

-- CSS-style strings
color.new("rgb(255,85,0)")
color.new("rgba(255,85,0,0.5)")
color.new("hsl(20,100,50)")
color.new("hsla(20,100,50,0.8)")

-- Raw 01 floats
color.new(1, 0.33, 0)

-- HSL (hue 0360, sat 0100, light 0100)
color.new(color.hsl(200, 60, 40))

-- HSV (hue 0360, sat 01, val 01)
color.new(color.hsv(200, 0.6, 0.8))

Manipulation

color.lighten(c, amount)         -- amount is 01 factor
color.darken(c, amount)
color.saturate(c, amount)
color.desaturate(c, amount)
color.invert(c)
color.lerp(c1, c2, t)            -- blend; t is 01
color.mix(c1, c2, t)             -- alias for lerp

Queries

color.isLight(c)                 -- boolean
color.getAverageLightness(c)     -- 01 float
color.rgbToHex(c)                -- returns hex string without "#"

Arithmetic

Color objects support +, -, *, /, %, ^, and unary - operators, applied component-wise.

Named Colors

color.white
color.black
color.red
color.green
color.blue
color.highlighter_blue
-- (and more — check core_color.lua for the full list)

8. Transitions & Animation

The transitions module provides smooth interpolated animations for numeric values.

local transition = require("gui.elements.transitions")

Built-in Transitions

Currently transition.glide is provided — a linear glide from a start value to a stop value.

Using a Transition

A transition factory is created by calling transition.glide(start, stop, duration). This returns a factory function that, when called, starts the animation and returns a handle.

-- Animate a panel sliding in from the left
local slideIn = transition.glide(-200, 0, 0.3)   -- from -200 to 0 in 0.3 seconds

local t = slideIn()   -- start the animation
t.OnStep(function(position)
    panel:setDualDim(position)
end)
t.OnStop(function()
    print("Animation complete")
end)

Overriding Values at Runtime

The factory function accepts optional overrides:

local t = slideIn(newStart, newStop, newDuration)

Stopping Early

t.Kill()

Custom Transitions

local myTransition = transition:newTransition(function(t, start, stop, time)
    -- t.fps is the target FPS for this transition
    local steps = t.fps * time
    local piece = time / steps
    t.running = true
    for i = 0, steps do
        if not t.kill then
            thread.sleep(piece)
            -- push the current interpolated value as a status update
            thread.pushStatus(start + i * ((stop - start) / steps))
        end
    end
    t.running = false
    t.kill = false
end)

Changing FPS

transition.glide:SetFPS(30)   -- lower for performance-sensitive animations

9. Add-on Widgets

These widgets live in gui/addons and extend the core library.


9.1 Window

A resizable, draggable floating window with a title bar and a close button.

require("gui.addons")  -- loads addons

local win = gui:newWindow(x, y, width, height, "Window Title", draggable, theme)

The returned object is the inner content frame (inside the title bar). Add children directly to win.

API:

win:setTitle("New Title")
win:close()           -- moves the window to the virtual tree (hides it)
win:open()            -- brings the window back to the main tree
win:setTheme(theme)   -- re-apply a different theme
win:getTheme()        -- returns current theme

win.OnClose(function(self)
    -- fires when the X button is pressed
end)

Minimum dimensions: 200px wide, 100px tall (enforced by resize handles).

Children auto-styled: Any TYPE_BUTTON or TYPE_TEXT created inside the window automatically receives the theme's colors and fonts via the OnCreated event.


9.2 ScrollFrame

A viewport with automatic vertical and horizontal scrollbars. Returns the content frame — add children to that.

require("gui.addons")

local content = gui:newScrollFrame(x, y, w, h, sx, sy, sw, sh)
-- Add children to `content`:
local row = content:newFrame(0, rowY, 0, 30, 0, 0, 1)

Scrolling API (on the content frame):

content:scrollTo(scrollY, scrollX)    -- jump to absolute scroll position
content:scrollBy(deltaY, deltaX)      -- scroll by a relative amount
content:scrollToTop()
content:scrollToBottom()
content:setScrollSpeed(speed)         -- default is 40 pixels per wheel tick
content:getScrollPos()                -- returns scrollX, scrollY
content:getMaxScroll()                -- returns maxScrollX, maxScrollY
content:setContentSize(width, height) -- explicitly set the content dimensions

The scrollbars appear automatically when the content overflows the viewport and hide when it does not.


9.3 Slide-in Menu

A panel that slides in from the left, right, or top with an animated transition.

local transition = require("gui.elements.transitions")

local menu = gui:newMenu(title, size, position, trans)
-- title    : string label (required)
-- size     : fractional width/height (e.g. 0.25 for 25% of screen)
-- position : gui.ALIGN_LEFT (default), gui.ALIGN_RIGHT, or gui.ALIGN_CENTER
-- trans    : transition factory (default: transition.glide)

API:

menu:Open(true)    -- slide open
menu:Open(false)   -- slide closed
menu:isOpen()      -- boolean

Example:

local sidebar = gui:newMenu("Navigation", 0.2, gui.ALIGN_LEFT)

-- Add content to the menu frame
local navBtn = sidebar:newTextButton("Home", 10, 60, 180, 40)

-- Wire toggle
toggleBtn.OnReleased(function()
    sidebar:Open(not sidebar:isOpen())
end)

9.4 Video Player

A pre-built media player widget with play/pause toggle and a seek bar.

require("gui.addons")

gui:newVideoPlayer(source, x, y, w, h, sx, sy, sw, sh)

The player creates its own themed window containing the video, a play/pause button, and a progress bar.


10. Canvas

The canvas module creates full-screen root frames.

local newCanvas = require("gui.core.canvas")

local visual  = newCanvas("visual")   -- visual frame: non-interactive, for backgrounds/effects
local regular = newCanvas()           -- regular interactive frame

Swapping child trees between canvases:

visual:swap(frameA, frameB)
-- Exchanges the children of frameA and frameB, re-parenting correctly.
-- Useful for scene transitions.

11. Simulation (Testing)

The simulate module lets you programmatically fire mouse events, useful for automated testing or scripted UI demos.

local simulate = require("gui.core.simulate")

Methods

-- Immediate (synchronous) mouse press and release
simulate:Press(button, x, y, istouch)
simulate:Release(button, x, y, istouch)

-- Async click (press then release after one scheduler tick)
simulate.Click(obj, button, x, y, istouch)

-- Animated mouse movement
simulate.Move(obj, dx, dy, x, y, istouch)

When called on an object (obj:Press()), the position defaults to the object's center. When called on simulate directly, x and y default to the current mouse position.

Example:

-- Programmatically click a button
simulate.Click(myButton)

-- Simulate a drag from one point to another
simulate.Move(nil, 100, 0, startX, startY)  -- move 100px to the right

12. Scheduler Probe (Load Monitoring)

Measures scheduler responsiveness using tick-slip detection. Gives a 0100% load estimate without blocking.

local probe = require("gui.core.probe")
probe:install(multi)

Options:

probe:install(multi, {
    interval = 0.05,   -- probe fires every N seconds (default: 0.05)
    alpha    = 0.15,   -- EMA smoothing factor 01 (default: 0.15, lower = smoother)
    maxLag   = 0.5,    -- seconds of lag that equals 100% load (default: 0.5)
})

Reading load:

-- Both are non-blocking — safe to call every frame
local load, lagMs = multi:getLoad()
-- load  : integer 0100
-- lagMs : smoothed scheduler lag in milliseconds

local lagMs, lagRatio = multi:getSchedulerLag()

The probe is automatically installed when gui:showTaskManager() is called.


13. Task Manager

A built-in debug overlay showing all active scheduler tasks, their state, uptime, and priority.

require("gui.addons")
gui:showTaskManager()

Default hotkey: Ctrl+T toggles it open/closed.

The task manager window provides:

  • A list of all processors, threads, and tasks with live state
  • Pause/Resume buttons for individual tasks
  • Kill buttons to terminate tasks
  • Clickable priority column to cycle a task's scheduler priority
  • An Error Log tab that captures all thread errors in real-time
  • A load bar showing current scheduler utilization

14. Tips & Patterns

Aspect Ratio Locking

-- Lock the root to a 16:9 canvas; letterbox on resize
gui:setAspectSize(1920, 1080)
gui.aspect_ratio = true

Clipping Children

local container = gui:newFrame(x, y, w, h)
container.clipDescendants = true  -- children are scissored to container bounds

Tag-Based Queries

-- Mark a group of elements and query by tag
for _, child in ipairs(parent:getAllChildren()) do
    if child:hasTag("card") then
        child.color = selectedColor
    end
end

Using gui.apply for Bulk Property Setting

gui.apply sets properties on multiple objects at once. It understands connection names (C_ prefix), invoke-style functions (I_ prefix), and plain properties.

gui.apply({
    color = {0.2, 0.2, 0.2},
    drawBorder = false,
    I_enableDragging = {gui.MOUSE_PRIMARY},
    OnReleased = function(self) print("clicked", self:getTag()) end,
}, btn1, btn2, btn3)

Stacking Order

Objects are drawn in the order they appear in their parent's children table. The last child is drawn on top.

obj:topStack()     -- draw on top of siblings
obj:bottomStack()  -- draw below all siblings

Responsive Layouts

Combine fractional scale with negative pixel offsets for padding:

-- A frame inset 10px on all sides within its parent
inner:setDualDim(10, 10, -20, -20, 0, 0, 1, 1)

Intersection Testing

local ix, iy, iw, ih = obj:intersecpt(x, y, w, h)
-- Returns the overlapping rectangle, or 0,0,0,0 if no overlap

Programmatic Focus

local focused = gui:getObjectFocus()   -- the currently focused object

OnUpdate (per-frame callback)

obj:OnUpdate(function(self, dt)
    -- runs every frame while the object exists
    self.rotation = self.rotation + 90 * dt
end)