agg-gui
A Rust GUI framework built on Anti-Grain Geometry (AGG). Widgets rasterize directly via AGG paths — no retained scene graph, no DOM, no CSS. The rendering model is immediate-mode: full redraw every frame, deterministic layout, Y-up coordinates throughout.
Part of the rust-apps suite — a collection of Rust graphics and geometry libraries by Lars Brubaker.
Live Demo
Install
Optional features:
| Feature | Enables |
|---|---|
winit-adapter |
agg_gui::winit_adapter — maps winit MouseButton / Modifiers / Key / CursorIcon to the crate's input types |
clipboard |
arboard-backed system clipboard integration |
[]
= { = "0.1", = ["winit-adapter", "clipboard"] }
Widget Library
| Widget | Description |
|---|---|
Label |
Static text, theme-aware color, left/center/right alignment |
Button |
Themeable background, focus ring, disabled state, click callback |
Checkbox |
Animated check mark, shared state cell for two-way binding |
Slider |
Linear value control with focus ring and keyboard nudge |
DragValue |
Click-drag to increment/decrement numeric values |
RadioGroup |
Single-selection group with shared state |
ProgressBar |
Filled track with optional label |
ToggleSwitch |
Animated on/off toggle |
TextField |
Full text editing: cursor, selection, clipboard, undo/redo |
Hyperlink |
Underlined link text with click callback |
ScrollView |
Vertical scroll with drag-thumb and mouse-wheel support |
Window |
Floating panel: draggable title bar, close button, resize handles, collapse |
FlexColumn |
Vertical flex layout with gap, padding, fixed + growing children |
FlexRow |
Horizontal flex layout |
Stack |
Z-ordered overlay layout (for floating windows) |
SizedBox |
Fixed-size constraint wrapper |
Splitter |
Draggable divider between two panes |
TabView |
Tabbed panel switcher with persistable active-tab cell |
TreeView |
Hierarchical list with expand/collapse and drag-and-drop |
Container |
Border + background decorator |
MarkdownView |
Markdown renderer: headings, paragraphs, lists, code blocks, images |
Separator |
Horizontal or vertical rule |
Spacer / Padding |
Layout utility widgets |
Features
- Theme system — dark / light / system themes, runtime-switchable; every widget reads
colors from
ctx.visuals()(no hardcoded colors). - Flex layout — fixed + growing children, per-child margins, cross-axis anchoring, min/max constraints, inner padding.
- Event system — Y-up mouse events routed by hit-test with proper Z-order. Capture semantics for drag. Keyboard focus with Tab navigation and focus rings.
- Multi-touch — gesture aggregator (
current_multi_touch()) exposes per-frame zoom / rotation / translation / pressure deltas. Works on mobile browsers and touchscreen laptops. - Drawing API —
DrawCtxcovers paths, fills, strokes, rounded rects, circles, arcs, Bézier curves, text, transforms, clipping, compositing layers, image blitting, and inline GL content. Two implementations: software AGG rasterizer + halo-AA GL path. - Inspector — built-in widget-tree inspector overlay highlighting hovered widgets, showing bounds and properties, reporting hover position.
Quick Start
use ;
use Arc;
let font = new;
let root = new
.with_gap
.with_padding
.add
.add;
let mut app = new;
// Feed OS events via `app.on_mouse_*` / `app.on_key_down`;
// call `app.layout(size)` + `app.paint(&mut ctx)` each frame.
See the demo shell for a complete example covering 28+ demo windows, themes, persistence, and a GL 3-D cube.
Design Principles
- Y-up coordinates everywhere — origin at bottom-left, positive Y upward. One conversion at event ingestion; no per-widget flipping.
- Direct-to-surface rendering — AGG paths rasterize straight to the target surface. No retained scene graph, no layout cache to invalidate.
- Full redraw every frame — no dirty regions, no incremental update complexity.
- Theme via thread-local —
set_visuals()writes to a thread-local read by everyDrawCtx::visuals()call. Zero plumbing required in widget constructors. - Two-way state binding —
Rc<Cell<T>>shared between widgets keeps UI in sync without callbacks. - No unsafe, no
RefCellpervasion — the widget tree is owned byApp; mutable traversal uses index-based child access to satisfy the borrow checker cleanly.
License
MIT
