- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
CI / ci (pull_request) Successful in 1m20s
|
||
| .forgejo/workflows | ||
| examples/demo | ||
| internal | ||
| vendor | ||
| widget | ||
| .gitignore | ||
| .golangci.yml | ||
| .pre-commit-config.yaml | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
| ROADMAP.md | ||
| screen.go | ||
Kao
Kao is a widget library for the Plan 9 draw library, written in Go. It provides a collection of interactive GUI components—buttons, text inputs, checklists, radio buttons, progress bars, sparklines, tab bars, file browsers, and more—that can be used to build native Plan 9 applications with a classic, lightweight look and feel. The library is currently in active development, but all core widgets are implemented and ready for use.
The library is designed around a simple, event‑driven architecture where each widget implements a common Widget interface, making it easy to compose, extend, and embed custom components.
Features
- Core Widgets – Button, Input, Checklist, Radiobutton, ProgressBar, Sparkline, TabBar, Container, FileBrowser, FileViewer.
- Plan 9 draw Integration – Uses the
9fans.net/go/drawpackage for low‑level graphics and input. - Event‑Driven – Unified event system for mouse, keyboard, and resize events.
- Flexible Theming – Color palettes (default and dark) with per‑widget overrides.
- Easy Embedding – All widgets embed a
BaseWidgetthat provides common geometry, visibility, and enablement logic. - Demo Application – A fully‑functional example (
examples/demo/) that showcases every widget and interactive wiring.
Installation
Kao requires Go 1.24.11 or later and the 9fans.net/go/draw package.
go get git.lan.thwap.org/thwap/kao
Because the library depends on Plan 9’s draw library, you must have a Plan 9 environment (or a compatible Unix system with the draw device) to run applications. On Linux/Unix, you can use drawterm to connect to a Plan 9 CPU server, or run a local Plan 9 instance via 9vx or similar.
Quick Start
Create a new Go file (e.g., main.go) with the following minimal example:
package main
import (
"fmt"
"git.lan.thwap.org/thwap/kao"
"git.lan.thwap.org/thwap/kao/widget"
"image"
)
func main() {
// Initialize a screen (window)
screen, err := kao.NewScreen("Hello Kao", "400x300")
if err != nil {
fmt.Printf("Failed to create screen: %v\n", err)
return
}
defer screen.Close()
// Create a button with a click handler
btn := widget.NewButton("Click Me!", image.Rect(50, 50, 200, 100), func() {
fmt.Println("Button clicked!")
})
// Set the button as the root widget
screen.SetRoot(btn)
// Start the event loop
screen.Run()
}
Run the program:
go run main.go
A window titled “Hello Kao” will appear with a clickable button.
Widgets
| Widget | Description |
|---|---|
Button |
Clickable button with label and handler. |
Input |
Single‑line text field with cursor and change callback. |
Checklist |
List of checkable items with keyboard navigation. |
Radiobutton / RadioGroup |
Mutually‑exclusive radio buttons. |
ProgressBar |
Horizontal/vertical progress bar with thresholds. |
Sparkline |
Scrolling time‑series graph with multiple lanes and rendering modes. |
TabBar |
Tabbed container switching between child widgets. |
Container |
Simple container for grouping widgets. |
FileBrowser |
File‑system browser with directory navigation and integrated viewer. |
FileViewer |
Displays file contents as text or hex. |
Button (widget.Button)
A clickable button with configurable label, padding, and alignment.
btn := widget.NewButton("Label", image.Rect(x0, y0, x1, y1), func() {
// handler
})
Input (widget.Input)
A single‑line text field with cursor, focus management, and an optional OnChange callback.
input := widget.NewInput(image.Rect(x0, y0, x1, y1))
input.SetOnChange(func(text string) {
fmt.Printf("Text changed: %s\n", text)
})
Checklist (widget.Checklist)
A list of checkable items with keyboard navigation.
cl := widget.NewChecklist(image.Rect(x0, y0, x1, y1))
cl.AddItem("Item 1", false)
cl.AddItem("Item 2", true)
cl.OnChange = func() {
// called when any item’s checked state changes
}
Radiobutton (widget.Radiobutton) and RadioGroup (widget.RadioGroup)
Mutually‑exclusive radio buttons grouped by a RadioGroup.
group := widget.NewRadioGroup()
rb1 := widget.NewRadiobutton("Option 1", image.Rect(...))
rb1.SetGroup(group)
rb1.SetOnSelect(func() { fmt.Println("Option 1 selected") })
ProgressBar (widget.ProgressBar)
Horizontal or vertical progress bar with optional thresholds and color changes.
pb := widget.NewProgressBar(image.Rect(...), 100) // max = 100
pb.SetValue(30)
pb.SetOrientation(widget.Vertical)
Sparkline (widget.Sparkline)
A scrolling time‑series graph supporting multiple lanes, different rendering modes (line, bar, area, step), and dynamic scaling.
sp := widget.NewSparkline(image.Rect(...), 1000) // 1000‑point window
sp.SetMode(widget.ModeLine)
sp.AddLane("CPU", widget.DefaultPalette().Accent, 1000)
sp.AddValue(42) // add to default lane
sp.AddValueToLane(1, 75) // add to lane 1
TabBar (widget.TabBar)
A tabbed container that switches between child widgets.
tb := widget.NewTabBar(screen.Size())
tb.AddTab("First", child1)
tb.AddTab("Second", child2)
Container (widget.Container)
A simple container that holds and draws multiple child widgets.
c := widget.NewContainer(image.Rectangle{})
c.AddChild(btn)
c.AddChild(input)
FileBrowser (widget.FileBrowser)
A file‑system browser with directory navigation, file selection, and an integrated file viewer.
fb := widget.NewFileBrowser(image.Rectangle{}, "/home/user")
fb.OnSelect = func(path string, isDir bool) {
fmt.Printf("Selected: %s\n", path)
}
FileViewer (widget.FileViewer)
Displays file contents as text or hex; invoked automatically by the file browser.
fv := widget.NewFileViewer(image.Rectangle{}, "/path/to/file.txt")
fv.OnClose = func() {
// close the viewer
}
Events
Events are defined in internal/event and include:
MouseEvent– button press/release, movement, drag.KeyboardEvent– rune and key code.ResizeEvent– window size change.ExposeEvent– region needing redraw.
Widgets receive events via the Handle(interface{}) (bool, error) method. The Screen’s event loop forwards events to the root widget, which can propagate them to children.
Theming
Color palettes are defined in widget.Palette. Two built‑in palettes are provided:
widget.DefaultPalette()– light theme.widget.DarkPalette()– dark theme.
Set a widget’s palette with SetPalette():
btn.SetPalette(widget.DarkPalette())
A PaletteManager is available for application‑wide palette management with per‑widget overrides.
Architecture
Widget Interface
Every widget must implement the Widget interface:
type Widget interface {
Draw(r image.Rectangle, screen *draw.Image)
Handle(event interface{}) (bool, error)
Resize(r image.Rectangle)
Rect() image.Rectangle
}
BaseWidget
BaseWidget provides default implementations for Rect, Resize, SetVisible, SetEnabled, Contains, and palette management. Concrete widgets embed *BaseWidget and override Draw and Handle.
Screen
The Screen type (screen.go) manages the draw connection, mouse/keyboard controllers, and the main event loop. It creates a window, starts an event‑processing goroutine, and calls Redraw() after each event or periodically (for animations).
Examples
The repository includes a comprehensive demo in examples/demo/ that demonstrates every widget and wires them together:
- Button toggles auto‑increment of a progress bar.
- Input field updates a checklist item label.
- Checklist selection prints checked items.
- Radio buttons change the progress bar’s color theme.
- Sparkline displays two lanes of random‑walk data.
- Tab bar switches between three tabs (demo widgets, info text, file browser).
Run the demo:
cd examples/demo
go run main.go
Contributing
Contributions are welcome! Please follow the existing code style and add tests where applicable.
- Fork the repository.
- Create a feature branch.
- Implement your changes.
- Ensure the demo still works correctly.
- Submit a pull request.
For major changes, please open an issue first to discuss what you would like to change.
License
Kao is distributed under the same license as the 9fans.net/go/draw package (typically a permissive BSD‑style license). See the vendor/9fans.net/go/LICENSE file for details.
Roadmap
See ROADMAP.md for a detailed list of completed phases and future plans.
Kao – simple, native widgets for Plan 9 draw.