🏛️ Architecture & Core Philosophy
Vela's standard GUI library (std/gui) is built around three
foundational design principles:
1. Software 2D Rasterizer
A fast, pixel-accurate CPU rasterizer drawing directly into a linear 32-bit RGBA frame buffer. No external rendering engine or heavyweight GPU stack required.
2. Native Presentation Backend
Direct presentation to native platform windows (X11 on Linux) when a display server is present, with automatic graceful fallback to virtual headless operation in CI.
3. Per-Frame Region Discipline
All transient frame allocations (formatted strings, layout calculations, temporary bounds) live in a single frame region that resets each tick with zero heap fragmentation.
📐 Geometry & Color Primitives
All widgets and canvas operations are positioned using straightforward value shapes:
; Geometry primitives
fixed pt = Point { x: 30, y: 50 }
fixed sz = Size { width: 200, height: 40 }
fixed box = Rect { x: 20, y: 30, width: 160, height: 36 }
; Check if a point falls within a rectangle (e.g. for mouse clicks)
when box.contains(mouse_pos)
show("clicked inside box")
fi
Color Representation
Colors are represented as 32-bit RGBA values with convenient constructor helpers:
fixed c1 = Color.rgb(37, 99, 235) ; Solid RGB (alpha = 255)
fixed c2 = Color.rgba(15, 23, 42, 180) ; RGBA with transparency
fixed c3 = Color.hex(0x38BDF8) ; Hexadecimal literal (0xRRGGBB)
; Built-in color constants
fixed white = Color.white()
fixed black = Color.black()
fixed trans = Color.transparent()
🎨 Styling System (Themes & Modifiers)
The Style shape provides a clean, unified way to theme any
widget. You can use standard presets or customize individual properties
fluently:
| Preset | Background | Border | Intended Usage |
|---|---|---|---|
Style.primary() |
Royal Blue (0x2563EB) |
Light Blue | Primary call-to-action buttons, submit actions |
Style.secondary() |
Muted Slate (0x475569) |
Slate 400 | Secondary actions, auxiliary controls |
Style.success() |
Emerald Green (0x059669) |
Green 400 | Confirmations, add items, positive feedback |
Style.danger() |
Crimson Red (0xDC2626) |
Red 400 | Deletions, resets, critical cancellations |
Style.dark() |
Obsidian (0x0F172A) |
Slate 700 | Dark mode panels, text inputs, list boxes |
Style.light() |
Crisp Gray (0xF1F5F9) |
Slate 300 | Light mode elements and high-contrast containers |
Fluent Style Customization
Modify presets without rewriting full shapes using modifier methods:
fixed custom_style = Style.primary()
.with_bg(Color.hex(0x6366F1)) ; Change background to Indigo
.with_border(Color.hex(0xA5B4FC), 2) ; 2px lighter border
.with_padding(8) ; Adjust internal padding
🧩 Rich Widget Gallery
Vela provides out-of-the-box widgets designed for common desktop application tasks:
Button
Interactive push button with automated hover, active pressed state, border styling, and centered label drawing.
Label
Crisp typography display supporting custom text colors and optional background pill pill rendering.
TextBox
Single-line input with focus outline glow, placeholder support, cursor pipe indicator, and backspace handling.
ListBox
Selectable list holding items with mouse hover highlighting, row click selection, and active item queries.
CheckBox
Interactive toggle box with animated check fill, hover feedback, and paired text description.
ProgressBar
Horizontal progress indicator bounded between 0% and 100% with customizable fill color.
ImageView
Graphic container with framed borders and built-in checkerboard pattern for media previews.
MessageBox & InputBox
Modal dialog overlays with darkened backdrop dimming, title bar, message/input fields, and OK buttons.
VBox & HBox
Flow layout helpers that automatically advance cursor coordinates with consistent widget spacing.
🔄 The Window & Event Loop
Desktop applications run on an event loop. Opening a
Window creates the native window and gives access to its
Canvas:
include std/gui { Window, Canvas, GuiEvent, Point, Rect, Color, Button, is_interactive }
task main()
flex win = Window.open("My Application", 400, 300) { failure err
show("Failed to open window")
<-
}
fixed canvas = win.canvas()
loop win.is_open
region frame default
fixed ev = win.poll_event()
match ev
case GuiEvent.Redraw
canvas.clear(Color.hex(0x0F172A))
canvas.draw_text("Hello, Vela GUI!" at frame, Point { x: 40, y: 50 }, Color.white())
win.present()
case GuiEvent.MouseDown x y btn
show("Mouse clicked at: ", x, ", ", y)
case GuiEvent.CloseRequested
win.close()
break
case _
fi
fi
fi
🤖 Headless Automated Testing
Deterministic GUI Testing Without an X Server
In CI environments or server runners where DISPLAY is
unavailable, is_interactive() returns false.
You can push synthetic events using
win.push_event(...) to simulate exact user interactions
deterministically!
when not is_interactive()
; Synthetic event sequence:
win.push_event(1, 50, 140, 1) ; Simulate MouseDown at (50, 140)
win.push_event(7) ; Simulate CloseRequested
fi
🚀 Complete Example: Modern Counter Application
Here is the complete code for an interactive counter featuring custom button styling, a progress bar, and automated test dispatching:
; An interactive counter application built with std/gui.
include std/gui { Window, Canvas, GuiEvent, Point, Rect, Color, Style, Button, Label, ProgressBar, is_interactive }
task draw_scene(canvas: Canvas, flex& win: Window, count: int, btn_inc: Button, btn_dec: Button, btn_rst: Button, progress: ProgressBar, title_label: Label, r: region default)
canvas.clear(Color.hex(0x0F172A))
title_label.draw(canvas)
canvas.draw_text("Current count: {count}" at r, Point { x: 30, y: 55 }, Color.hex(0x38BDF8))
; Draw progress bar reflecting count percentage (0 - 100%)
progress.draw(canvas)
; Draw styled action buttons
btn_inc.draw(canvas, Point { x: 0, y: 0 }, false)
btn_dec.draw(canvas, Point { x: 0, y: 0 }, false)
btn_rst.draw(canvas, Point { x: 0, y: 0 }, false)
win.present()
fi
task main()
flex win = Window.open("Rich GUI Counter", 340, 220) { failure _
show("failed opening window")
<-
}
fixed title_label = Label { pos: Point { x: 30, y: 25 }, text: "Vela Modern Counter", color: Color.white() }
fixed btn_inc = Button { rect: Rect { x: 30, y: 130, width: 85, height: 36 }, label: "+ Add", style: Style.success() }
fixed btn_dec = Button { rect: Rect { x: 125, y: 130, width: 85, height: 36 }, label: "- Sub", style: Style.primary() }
fixed btn_rst = Button { rect: Rect { x: 220, y: 130, width: 85, height: 36 }, label: "Reset", style: Style.danger() }
flex progress = ProgressBar { rect: Rect { x: 30, y: 85, width: 275, height: 18 }, fill_color: Color.hex(0x10B981) }
flex count = 0
fixed canvas = win.canvas()
; Simulate user clicks when running in automated headless testing
when not is_interactive()
win.push_event(1, 50, 140, 1) ; click "+ Add"
win.push_event(1, 50, 140, 1) ; click "+ Add"
win.push_event(1, 50, 140, 1) ; click "+ Add"
win.push_event(1, 145, 140, 1) ; click "- Sub"
win.push_event(1, 240, 140, 1) ; click "Reset"
win.push_event(7) ; CloseRequested
fi
loop win.is_open
region frame default
fixed ev = win.poll_event()
match ev
case GuiEvent.Redraw
progress.set_value(count * 20)
draw_scene(canvas, &win, count, btn_inc, btn_dec, btn_rst, progress, title_label, frame)
case GuiEvent.MouseDown x y btn
fixed click_pos = Point { x: x, y: y }
when btn_inc.rect.contains(click_pos)
count++
progress.set_value(count * 20)
show("count incremented: ", count, " (progress: ", progress.value, "%)")
draw_scene(canvas, &win, count, btn_inc, btn_dec, btn_rst, progress, title_label, frame)
else when btn_dec.rect.contains(click_pos)
count--
progress.set_value(count * 20)
show("count decremented: ", count, " (progress: ", progress.value, "%)")
draw_scene(canvas, &win, count, btn_inc, btn_dec, btn_rst, progress, title_label, frame)
else when btn_rst.rect.contains(click_pos)
count = 0
progress.set_value(0)
show("count reset: ", count, " (progress: ", progress.value, "%)")
draw_scene(canvas, &win, count, btn_inc, btn_dec, btn_rst, progress, title_label, frame)
fi
case GuiEvent.CloseRequested
win.close()
show("counter app closed; final count: ", count)
break
case _
fi
fi
fi