1123 lines
29 KiB
Markdown
1123 lines
29 KiB
Markdown
# 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](#1-setup--integration)
|
||
2. [The Dual-Dimension Layout System](#2-the-dual-dimension-layout-system)
|
||
3. [Core Widget Reference](#3-core-widget-reference)
|
||
- [Frame](#31-frame)
|
||
- [TextLabel](#32-textlabel)
|
||
- [TextButton](#33-textbutton)
|
||
- [TextBox (Input)](#34-textbox-input)
|
||
- [ImageLabel](#35-imagelabel)
|
||
- [ImageButton](#36-imagebutton)
|
||
- [Video](#37-video)
|
||
4. [Base Object API](#4-base-object-api)
|
||
- [Positioning & Sizing](#41-positioning--sizing)
|
||
- [Appearance](#42-appearance)
|
||
- [Visibility & Lifecycle](#43-visibility--lifecycle)
|
||
- [Interaction](#44-interaction)
|
||
- [Shape & Form Factor](#45-shape--form-factor)
|
||
5. [Events & Connections](#5-events--connections)
|
||
- [Per-Object Events](#51-per-object-events)
|
||
- [Global Events](#52-global-events)
|
||
- [Hotkeys](#53-hotkeys)
|
||
6. [Theming](#6-theming)
|
||
7. [Color Module](#7-color-module)
|
||
8. [Transitions & Animation](#8-transitions--animation)
|
||
9. [Add-on Widgets](#9-add-on-widgets)
|
||
- [Window](#91-window)
|
||
- [ScrollFrame](#92-scrollframe)
|
||
- [Slide-in Menu](#93-slide-in-menu)
|
||
- [Video Player](#94-video-player)
|
||
10. [Canvas](#10-canvas)
|
||
11. [Simulation (Testing)](#11-simulation-testing)
|
||
12. [Scheduler Probe (Load Monitoring)](#12-scheduler-probe-load-monitoring)
|
||
13. [Task Manager](#13-task-manager)
|
||
14. [Tips & Patterns](#14-tips--patterns)
|
||
|
||
---
|
||
|
||
## 1. Setup & Integration
|
||
|
||
Require the library at the top of your project. The library hooks into LÖVE's callbacks automatically.
|
||
|
||
```lua
|
||
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`:
|
||
|
||
```lua
|
||
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 (0–1) 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
|
||
|
||
```lua
|
||
-- 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)`).
|
||
|
||
```lua
|
||
frame:fullFrame()
|
||
```
|
||
|
||
### Reading Position
|
||
|
||
```lua
|
||
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:
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local panel = gui:newFrame(x, y, w, h, sx, sy, sw, sh)
|
||
```
|
||
|
||
```lua
|
||
-- 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.
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local display = gui:newVisualFrame(...)
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 TextLabel
|
||
|
||
A non-interactive frame that renders text.
|
||
|
||
```lua
|
||
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 | 0–1 alpha for text only |
|
||
|
||
**Font methods:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local btn = parent:newTextButton("Click Me", x, y, w, h, sx, sy, sw, sh)
|
||
```
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
input:HasSelection() -- boolean
|
||
input:GetSelection() -- start, stop (always start <= stop)
|
||
input:GetSelectedText() -- string
|
||
input:ClearSelection()
|
||
```
|
||
|
||
**Events:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
img:setImage("path/to/other.png")
|
||
img:setImage(loveImageObject)
|
||
-- Tile from a spritesheet:
|
||
img:setImage("spritesheet.png", srcX, srcY, srcW, srcH)
|
||
```
|
||
|
||
**Flipping:**
|
||
|
||
```lua
|
||
img:flip() -- flip horizontally
|
||
img:flip(true) -- flip vertically
|
||
```
|
||
|
||
**Gradient fill** (applies a gradient image to any frame or image element):
|
||
|
||
```lua
|
||
panel:applyGradient("vertical", color1, color2, color3)
|
||
panel:applyGradient("horizontal", {1,0,0,1}, {0,0,1,1})
|
||
```
|
||
|
||
**Pre-loading images into cache:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local vid = parent:newVideo("movie.ogv", x, y, w, h, sx, sy, sw, sh)
|
||
```
|
||
|
||
**Playback control:**
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
obj.color = {r, g, b} -- background color (0–1 range)
|
||
obj.borderColor = {r, g, b} -- border color
|
||
obj.drawBorder = true/false -- show/hide border
|
||
obj.visibility = 0.8 -- overall opacity 0–1
|
||
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
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
-- 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:
|
||
|
||
```lua
|
||
-- 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:
|
||
|
||
```lua
|
||
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:
|
||
|
||
```lua
|
||
obj.OnSomeEvent(function(self, ...)
|
||
-- handler
|
||
end)
|
||
```
|
||
|
||
Multiple handlers can be attached to a single event. Connections can be combined:
|
||
|
||
```lua
|
||
-- 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:
|
||
|
||
```lua
|
||
obj.OnLeftStickUp / Down / Left / Right
|
||
obj.OnRightStickUp / Down / Left / Right
|
||
```
|
||
|
||
**TextLabel / TextButton / TextBox extras:**
|
||
|
||
```lua
|
||
obj.OnFontUpdated(function(self) end) -- font changed
|
||
input.OnReturn(function(self, text) end) -- Enter key pressed in textbox
|
||
```
|
||
|
||
**Video extras:**
|
||
|
||
```lua
|
||
vid.OnVideoFinished(function(self) end)
|
||
```
|
||
|
||
---
|
||
|
||
### 5.2 Global Events
|
||
|
||
All global events live under `gui.Events`:
|
||
|
||
```lua
|
||
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:
|
||
|
||
```lua
|
||
local conn = obj:setHotKey({"lctrl", "s"})
|
||
conn(function(ref)
|
||
print("Save triggered from", ref)
|
||
end)
|
||
```
|
||
|
||
Multiple key combinations for the same action:
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local theme = require("gui.core.theme")
|
||
```
|
||
|
||
### Creating a Theme
|
||
|
||
**From explicit colors:**
|
||
|
||
```lua
|
||
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):**
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
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:
|
||
|
||
```lua
|
||
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 0–1 range.
|
||
|
||
```lua
|
||
local color = require("gui.core.color")
|
||
```
|
||
|
||
### Creating Colors
|
||
|
||
```lua
|
||
-- 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 0–1 floats
|
||
color.new(1, 0.33, 0)
|
||
|
||
-- HSL (hue 0–360, sat 0–100, light 0–100)
|
||
color.new(color.hsl(200, 60, 40))
|
||
|
||
-- HSV (hue 0–360, sat 0–1, val 0–1)
|
||
color.new(color.hsv(200, 0.6, 0.8))
|
||
```
|
||
|
||
### Manipulation
|
||
|
||
```lua
|
||
color.lighten(c, amount) -- amount is 0–1 factor
|
||
color.darken(c, amount)
|
||
color.saturate(c, amount)
|
||
color.desaturate(c, amount)
|
||
color.invert(c)
|
||
color.lerp(c1, c2, t) -- blend; t is 0–1
|
||
color.mix(c1, c2, t) -- alias for lerp
|
||
```
|
||
|
||
### Queries
|
||
|
||
```lua
|
||
color.isLight(c) -- boolean
|
||
color.getAverageLightness(c) -- 0–1 float
|
||
color.rgbToHex(c) -- returns hex string without "#"
|
||
```
|
||
|
||
### Arithmetic
|
||
|
||
Color objects support `+`, `-`, `*`, `/`, `%`, `^`, and unary `-` operators, applied component-wise.
|
||
|
||
### Named Colors
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
-- 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:
|
||
|
||
```lua
|
||
local t = slideIn(newStart, newStop, newDuration)
|
||
```
|
||
|
||
### Stopping Early
|
||
|
||
```lua
|
||
t.Kill()
|
||
```
|
||
|
||
### Custom Transitions
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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):**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
menu:Open(true) -- slide open
|
||
menu:Open(false) -- slide closed
|
||
menu:isOpen() -- boolean
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
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:**
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
local simulate = require("gui.core.simulate")
|
||
```
|
||
|
||
### Methods
|
||
|
||
```lua
|
||
-- 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:**
|
||
|
||
```lua
|
||
-- 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 0–100% load estimate without blocking.
|
||
|
||
```lua
|
||
local probe = require("gui.core.probe")
|
||
probe:install(multi)
|
||
```
|
||
|
||
**Options:**
|
||
|
||
```lua
|
||
probe:install(multi, {
|
||
interval = 0.05, -- probe fires every N seconds (default: 0.05)
|
||
alpha = 0.15, -- EMA smoothing factor 0–1 (default: 0.15, lower = smoother)
|
||
maxLag = 0.5, -- seconds of lag that equals 100% load (default: 0.5)
|
||
})
|
||
```
|
||
|
||
**Reading load:**
|
||
|
||
```lua
|
||
-- Both are non-blocking — safe to call every frame
|
||
local load, lagMs = multi:getLoad()
|
||
-- load : integer 0–100
|
||
-- 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.
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
-- Lock the root to a 16:9 canvas; letterbox on resize
|
||
gui:setAspectSize(1920, 1080)
|
||
gui.aspect_ratio = true
|
||
```
|
||
|
||
### Clipping Children
|
||
|
||
```lua
|
||
local container = gui:newFrame(x, y, w, h)
|
||
container.clipDescendants = true -- children are scissored to container bounds
|
||
```
|
||
|
||
### Tag-Based Queries
|
||
|
||
```lua
|
||
-- 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.
|
||
|
||
```lua
|
||
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.
|
||
|
||
```lua
|
||
obj:topStack() -- draw on top of siblings
|
||
obj:bottomStack() -- draw below all siblings
|
||
```
|
||
|
||
### Responsive Layouts
|
||
|
||
Combine fractional scale with negative pixel offsets for padding:
|
||
|
||
```lua
|
||
-- A frame inset 10px on all sides within its parent
|
||
inner:setDualDim(10, 10, -20, -20, 0, 0, 1, 1)
|
||
```
|
||
|
||
### Intersection Testing
|
||
|
||
```lua
|
||
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
|
||
|
||
```lua
|
||
local focused = gui:getObjectFocus() -- the currently focused object
|
||
```
|
||
|
||
### OnUpdate (per-frame callback)
|
||
|
||
```lua
|
||
obj:OnUpdate(function(self, dt)
|
||
-- runs every frame while the object exists
|
||
self.rotation = self.rotation + 90 * dt
|
||
end)
|
||
```
|