Zamin Docs

GUI Toolkit Guide

Build native desktop applications with Zamin's cross-platform GUI toolkits.

Overview

Zamin ships with two GUI toolkits that share the same API surface, so code written for one works on the other with no changes:

  • Sol (Windows) — A Win32-based toolkit that ships as a built-in module on Windows. No external dependencies are required.
  • Luna (Linux) — A GTK4-based toolkit available behind the luna feature flag. Requires GTK4 development libraries on the host system.

Both toolkits expose the same function names and widget constructors. You pick the one that matches your platform, and the rest of your code stays the same.

💡 Note

On Windows, sol() is always available. On Linux, use luna() and build with --features luna.

Quick Start

Here is a minimal GUI application that displays a window with a label:

let gui = sol()  // or luna() on Linux
let win = gui.Leo("My App", 400, 300)
let label = gui.Label(win, "Hello, Zamin!")
gui.pack(label)
gui.mainloop(win)

Save this as app.zamin and run it:

$ zamin run app.zamin

A window titled "My App" appears with the text "Hello, Zamin!" centered inside it.

Widget Reference

Leo (Window)

The top-level window that holds all other widgets.

SignatureDescription
Leo(title?, width?, height?) Creates a new top-level window. Defaults to 640×480 if width/height are omitted.
let gui = sol()
let win = gui.Leo("Settings", 800, 600)

Frame

A container widget used to group and organize child widgets.

SignatureDescription
Frame(parent) Creates a frame inside the given parent widget.
let gui = sol()
let win = gui.Leo("App", 400, 300)
let frame = gui.Frame(win)
gui.pack(frame)

Label

Displays static text. Does not accept user input.

SignatureDescription
Label(parent, text?) Creates a label with the given text inside the parent widget.
let gui = sol()
let win = gui.Leo("App", 400, 300)
let lbl = gui.Label(win, "Status: Ready")
gui.pack(lbl)

Button

A clickable button that triggers a callback when pressed.

SignatureDescription
Button(parent, text?, callback?) Creates a button with optional label and click handler.
let gui = sol()
let win = gui.Leo("App", 400, 300)

func on_click() {
    print("Button was clicked!");
}

let btn = gui.Button(win, "Click Me", on_click)
gui.pack(btn)
gui.mainloop(win)

Callbacks are plain Zamin functions. They can accept arguments or be closures that capture local state.

Entry

A single-line text input field for user input.

SignatureDescription
Entry(parent) Creates a text input field inside the parent widget.
let gui = sol()
let win = gui.Leo("App", 400, 300)
let entry = gui.Entry(win)
gui.pack(entry)

Layout Management

Zamin GUI toolkits provide two layout methods for arranging widgets:

pack

SignatureDescription
pack(widget, side?, padx?, pady?) Places a widget using the pack geometry manager. side can be "top", "bottom", "left", or "right". padx and pady add horizontal and vertical padding in pixels.
gui.pack(label, "top", 10, 5)
gui.pack(btn, "left", 5, 5)

place

SignatureDescription
place(widget, x, y, w?, h?) Places a widget at absolute coordinates. w and h set the width and height if desired.
gui.place(label, 20, 50, 200, 30)

Widget Operations

These functions modify or query widgets after they have been created.

FunctionDescription
config(widget, property, value) Set a property on a widget. Common properties: "text", "command", "font", "fg", "bg".
get(entry) Return the current text content of an Entry widget.
insert(entry, pos, text) Insert text at the given position in an Entry widget.
delete(entry, start, end?) Delete characters from start to end in an Entry widget. If end is omitted, deletes one character.
title(window, text) Change the title of a window.
geometry(window, w, h) Resize a window to the given width and height.
destroy(widget) Remove a widget from the layout and free its resources.
messagebox(text, title?) Show a modal message dialog with the given text and optional title.
gui.config(btn, "text", "Updated Label")
let value = gui.get(entry)
gui.title(win, "New Title")
gui.messagebox("Operation complete!", "Info")

Example: Calculator

A complete calculator application with a display field and arithmetic buttons:

let gui = sol()
let win = gui.Leo("Calculator", 280, 350)

let display = gui.Entry(win)
gui.pack(display, "top", 10, 10)

let expression = ""

func on_digit(d) {
    expression = expression + d;
    gui.config(display, "text", expression);
}

func on_operator(op) {
    expression = expression + " " + op + " ";
    gui.config(display, "text", expression);
}

func on_clear() {
    expression = "";
    gui.config(display, "text", "");
}

func on_equal() {
    let result = eval(expression);
    gui.config(display, "text", str(result));
    expression = str(result);
}

// Build the button row for digits 1-9
for row in 0..3 {
    let frame = gui.Frame(win);
    gui.pack(frame, "top");
    for col in 0..3 {
        let digit = str(row * 3 + col + 1);
        let d = digit;
        let btn = gui.Button(frame, digit, func() { on_digit(d) });
        gui.pack(btn, "left", 2, 2);
    }
}

// Bottom row: 0, clear, equals
let bottom = gui.Frame(win)
gui.pack(bottom, "top")
let b0 = gui.Button(bottom, "0", func() { on_digit("0") })
gui.pack(b0, "left", 2, 2)
let bc = gui.Button(bottom, "C", on_clear)
gui.pack(bc, "left", 2, 2)
let be = gui.Button(bottom, "=", on_equal)
gui.pack(be, "left", 2, 2)

// Operator buttons
let ops = gui.Frame(win)
gui.pack(ops, "top")
for op in ["+", "-", "*", "/"] {
    let o = op;
    let b = gui.Button(ops, op, func() { on_operator(o) });
    gui.pack(b, "left", 2, 2);
}

gui.mainloop(win)

Example: Text Editor

A basic text editor with an input field, open button, and save button:

let gui = sol()
let win = gui.Leo("Text Editor", 600, 400)

// File path entry
let top_frame = gui.Frame(win)
gui.pack(top_frame, "top", 5, 5)

gui.Label(top_frame, "File:")
let path_entry = gui.Entry(top_frame)
gui.pack(path_entry, "left", 5, 5)

// Text area (simulated with a large Entry)
let text_area = gui.Entry(win)
gui.pack(text_area, "top", 10, 10)

// Button bar
let btn_frame = gui.Frame(win)
gui.pack(btn_frame, "top", 5, 5)

func on_open() {
    let path = gui.get(path_entry);
    if path == "" {
        gui.messagebox("Enter a file path first.", "Error");
        return;
    }
    let content = read(path);
    gui.config(text_area, "text", content);
    gui.title(win, "Text Editor - {path}");
}

func on_save() {
    let path = gui.get(path_entry);
    if path == "" {
        gui.messagebox("Enter a file path first.", "Error");
        return;
    }
    let content = gui.get(text_area);
    write(path, content);
    gui.messagebox("File saved!", "Success");
}

let open_btn = gui.Button(btn_frame, "Open", on_open)
gui.pack(open_btn, "left", 5, 5)

let save_btn = gui.Button(btn_frame, "Save", on_save)
gui.pack(save_btn, "left", 5, 5)

gui.mainloop(win)

Platform Notes

Sol (Windows)

  • Built on top of the Win32 API directly — no external dependencies or runtime libraries required.
  • Ships as a built-in module on Windows builds of Zamin.
  • Supports standard Windows look-and-feel with native widget rendering.

Luna (Linux)

  • Built on GTK4 for modern Linux desktop environments.
  • Requires GTK4 development libraries to be installed:
# Debian / Ubuntu
sudo apt install libgtk-4-dev

# Fedora
sudo dnf install gtk4-devel

# Arch
sudo pacman -S gtk4
  • Build Zamin with the luna feature enabled:
cargo build --release --features luna

macOS

  • macOS support is planned for a future release. On macOS today, you can use the standard library modules to build command-line tools.
✔ Tip

Since both Sol and Luna share the same API, you can write your GUI code once and compile it for different platforms by selecting the appropriate toolkit constructor at the top of your file.

Edit this page on GitHub Last updated: July 2026