Skip to content
Caleb Bassi edited this page Jan 23, 2019 · 15 revisions

Initialization

termui is initialized with:

	if err := ui.Init(); err != nil {
		log.Fatalf("failed to initialize termui: %v", err)
	}
	defer ui.Close()

which basically wraps the termbox-go Init() function and sets some sane defaults like 256 colors and mouse support. Close also wraps the termbox-go Close function. Initializing termui sets up the terminal to display widgets that you create and Close resets terminal settings to their normal state.

Widgets

There are several built in widgets, and custom widgets can also be created. Each widget inherits from Block which provides utilities for managing position, size, a border, and a title. Widgets also have to implement a Draw(Buffer) method which draws its contents onto a provided Buffer object. Buffers represent a rectangular region of a terminal, and contain Cells which represent individual terminal cells. Cells hold a character and a Style, which contains foreground and background colors and any modifications like underline, etc.

Custom widgets can be created simply by inheriting from Block and implementing a custom Draw method. A widget's data is typically stored in its fields, and can be updated over time.

In a larger application, a modular approach could often be to create custom widgets that inherit from any of the built in widgets like LineChart, and also implement an update method that is unique to the widget and updates the widget fields used by Draw.

Layout

Absolute layout

To position a widget absolutely, call the SetRect(x0, y0, x1, y1) method which defines the area that the widget draws to.

Grid layout

To position widgets in a relative way, you can create a Grid which stores widgets in rows and columns that take up a percentage of the screen. The Grid sizes the widgets initially, and also resizes them when the terminal resizes.

Events

termui provides event handling for keypresses, mouse actions, and screen resizing. termui.PollEvents() returns a channel that converts and propogates events originating from termbox. Event types are detailed in events.go.

To update widget data on an interval, use Go's built in tickers.

Event loop example:

	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C
	for {
		select {
		case e := <-uiEvents:
			switch e.ID { // event string/identifier
			case "q", "<C-c>": // press 'q' or 'C-c' to quit
				return
			case "<MouseLeft>":
				payload := e.Payload.(ui.Mouse)
				x, y := payload.X, payload.Y
			case "<Resize>":
				payload := e.Payload.(ui.Resize)
				width, height := payload.Width, payload.Height
			}
			switch e.Type {
			case ui.KeyboardEvent: // handle all key presses
				eventID = e.ID // keypress string
			}
		// use Go's built-in tickers for updating and drawing data
		case <-ticker:
			drawFunction()
		}
	}

Rendering

Rendering widgets or a Grid works by calling the termui.Render(Drawable) function. Any struct that implements Drawable can be rendered. Drawable requires the GetRect and SetRect methods for widget sizing (which are implemented by Block so you usually don't have to worry about those since most widgets inherit from Block) and also Draw(Buffer), which is unique to the widget. Render works by creating a Buffer, filling it with empty Cells, passing the Buffer to the widget through its Draw method, and then iterating over the drawn Cells which are passed to termbox-go. Grid's Draw method works by having each of its widgets Draw to the Buffer that it is given.

Clone this wiki locally