Getting Started
This guide will walk you through creating your first MicroUI application, from basic setup to building interactive interfaces.
Installation
using Pkg
Pkg.add("MicroUI")Understanding Immediate Mode GUIs
MicroUI is an immediate mode GUI library, which means:
- No persistent widgets: UI elements are created fresh each frame
- Direct state management: You control all application state
- Simple mental model: UI code runs top-to-bottom, like a regular program
- Easy integration: No complex event handling or widget hierarchies
This is different from traditional retained mode GUIs where widgets persist between frames.
Your First MicroUI Application
Let's start with the absolute minimum:
Core API Approach
using MicroUI
# Create and initialize context
ctx = Context()
init!(ctx)
# Set up text measurement callbacks (required)
ctx.text_width = (font, str) -> length(str) * 8 # 8 pixels per character
ctx.text_height = font -> 16 # 16 pixels line height
# Main UI loop (you'd typically put this in a render loop)
begin_frame(ctx)
if begin_window(ctx, "Hello MicroUI", Rect(100, 100, 300, 200)) != 0
text(ctx, "Welcome to MicroUI!")
if button(ctx, "Click Me!") != 0
println("Button was clicked!")
end
end_window(ctx)
end
end_frame(ctx)
# At this point, ctx.command_list contains all rendering commands
# You would pass these to your rendering backendMacro DSL Approach (Recommended for Beginners)
using MicroUI
using MicroUI.Macros
ctx = @context begin
@window "Hello MicroUI" begin
@text welcome = "Welcome to MicroUI!"
@button click_btn = "Click Me!"
@onclick click_btn begin
@popup "Button was clicked!"
end
end
end
# ctx now contains all rendering commandsThe macro approach is much more concise and handles state management automatically!
Building Interactive Applications
Example 1: Counter Application
using MicroUI.Macros
# Application runs in a loop (simplified here)
function run_counter_app()
ctx = @context begin
@window "Counter App" begin
@var counter_value = 0
@text display = "Count: $counter_value"
@row [100, 100, 100] begin
@button increment_btn = "+"
@button decrement_btn = "-"
@button reset_btn = "Reset"
end
@onclick increment_btn begin
counter_value += 1
end
@onclick decrement_btn begin
counter_value -= 1
end
@onclick reset_btn begin
counter_value = 0
end
end
end
return ctx
end
# In a real application, you'd call this in your render loop
ctx = run_counter_app()Example 2: Settings Panel
using MicroUI.Macros
ctx = @context begin
@window "Application Settings" begin
@text title = "Settings"
@panel "Audio" begin
@checkbox enable_sound = true
@slider volume = 0.8 range(0.0, 1.0)
@when enable_sound begin
@reactive volume_percent = "Volume: $(round(Int, volume * 100))%"
@text volume_display = volume_percent
end
end
@panel "Graphics" begin
@checkbox fullscreen = false
@checkbox vsync = true
@slider brightness = 1.0 range(0.1, 2.0)
end
@panel "Controls" begin
@row [100, 100] begin
@button save_btn = "Save"
@button cancel_btn = "Cancel"
end
@onclick save_btn begin
@popup "Settings saved!"
end
end
end
endUnderstanding State Management
Automatic State Persistence
With the macro DSL, widget states automatically persist between frames:
# First frame
@context begin
@window "Persistent State" begin
@checkbox remember_me = false # Initial value
@slider volume = 0.5 range(0.0, 1.0)
end
end
# Second frame - values persist!
@context begin
@window "Persistent State" begin
@checkbox remember_me = false # This initial value is IGNORED
@slider volume = 0.5 range(0.0, 1.0) # User's actual value is used
# Access current values
@reactive status = remember_me ? "Remembered" : "Not remembered"
@text status_display = status
end
endMultiple Windows
You can create multiple windows easily:
@context begin
@window "Main Application" begin
@text title = "My App"
@button settings_btn = "Open Settings"
end
@window "Settings" begin
@text settings_title = "Application Settings"
@checkbox dark_mode = false
@button close_btn = "Close"
end
@window "About" begin
@text about_text = "MicroUI Application v1.0"
@button ok_btn = "OK"
end
endCommon Patterns
Conditional UI Elements
@context begin
@window "Conditional Demo" begin
@checkbox show_advanced = false
@when show_advanced begin
@panel "Advanced Options" begin
@slider precision = 0.01 range(0.001, 1.0)
@checkbox debug_mode = false
end
end
end
endDynamic Lists
@context begin
@window "Dynamic List" begin
@var items = ["Apple", "Banana", "Cherry"]
@foreach (i, item) in enumerate(items) begin
@row [200, 80] begin
@text "item_$i" = "$i. $item"
@button "delete_$i" = "Delete"
end
@onclick "delete_$i" begin
# In a real app, you'd modify the items list
@popup "Would delete: $item"
end
end
@button add_btn = "Add Item"
@onclick add_btn begin
# In a real app, you'd add to items list
@popup "Would add new item"
end
end
endInput Forms
@context begin
@window "User Registration" begin
@text title = "Create Account"
@textbox username = "" maxlength(32)
@textbox email = "" maxlength(100)
@textbox password = "" maxlength(64) # Note: real apps need password masking
@checkbox agree_terms = false
@row [100, 100] begin
@button register_btn = "Register"
@button cancel_btn = "Cancel"
end
@onclick register_btn begin
@when agree_terms begin
@when (length(username) > 0 && length(email) > 0) begin
@popup "Registration successful!"
end
end
end
end
endLayout System
MicroUI provides flexible layout options:
Row Layouts
@row [100, 200, -1] begin # Fixed, Fixed, Fill remaining
@button btn1 = "100px"
@button btn2 = "200px"
@button btn3 = "Remaining space"
endColumn Layouts
@column begin
@text header = "Column Header"
@button item1 = "First Item"
@button item2 = "Second Item"
@button item3 = "Third Item"
endPanels (Grouped Content)
@panel "Network Settings" begin
@checkbox enable_wifi = true
@textbox ssid = "MyNetwork"
@button connect_btn = "Connect"
endError Handling and Debugging
Debug Widget States
@context begin
@window "Debug Example" begin
@checkbox test_flag = true
@slider test_value = 0.5 range(0.0, 1.0)
# Debug all widget states for this window
@debug_types "Debug Example"
end
endCommon Issues
- Widget state not persisting: Make sure you're using the same variable names between frames
- Type errors with sliders: Use
Realtype or let the macro handle conversion - Events not firing: Remember that
@onclickchecks forRES_SUBMITflag - Layout issues: Check your row/column specifications
Integration with Rendering Backends
MicroUI generates rendering commands that you pass to your graphics backend:
ctx = @context begin
# ... your UI code ...
end
# Process rendering commands
iter = CommandIterator(ctx.command_list)
while true
(has_cmd, cmd_type, cmd_idx) = next_command!(iter)
if !has_cmd
break
end
if cmd_type == MicroUI.COMMAND_RECT
rect_cmd = read_command(ctx.command_list, cmd_idx, RectCommand)
# Draw rectangle: rect_cmd.rect, rect_cmd.color
elseif cmd_type == MicroUI.COMMAND_TEXT
text_cmd = read_command(ctx.command_list, cmd_idx, TextCommand)
text_str = get_string(ctx.command_list, text_cmd.str_index)
# Draw text: text_str at text_cmd.pos with text_cmd.color
end
# ... handle other command types
endTips for Success
- Start simple: Begin with basic windows and buttons, add complexity gradually
- Use the macro DSL: It's much easier than the core API for most applications
- Think in frames: Remember that your UI code runs every frame
- Embrace immediate mode: Don't try to cache or optimize prematurely
- Handle state explicitly: You control all application state, which gives you power and responsibility
Happy coding with MicroUI! 🚀