Slide 64 of 74

Views

A page, served directly
proc main(): void {
	view := hive.ui.column([hive.ui.pad(24), hive.ui.gap(12)], [
		hive.ui.text([hive.ui.size(hive.ui.TextSize.Title()), hive.ui.heading(1)], "Hive Tour"),
		hive.ui.row([hive.ui.gap(8)], [
			hive.ui.input([hive.ui.placeholder("your name")], ""),
			hive.ui.button([hive.ui.tone(hive.ui.Tone.Good())], "Say hi"),
		]),
		hive.ui.link([hive.ui.background(hive.ui.Tone.Good())], "/1", "Start →"),
	])

	body := hive.ui.page("Hive", view)   // a whole document, served directly
	echo len(body)
}
An interface you can test
type Model {
	posts: Str[dyn]
	draft: Str
}

func view(model: Model): ui.View {
	mut ui.View[dyn] kids = [
		ui.input([ui.placeholder("say something")], model.draft),
		ui.button([ui.disabled(model.draft == "")], "Post"),
	]
	for each post in model.posts {
		append(kids, ui.text([], post))
	}
	return ui.column([ui.pad(24), ui.gap(8)], kids)
}

test "the post button is disabled until something is typed" {
	empty := ui.html(view(Model([], "")))
	assert indexOf(empty, "disabled") is Result.Ok(_)

	typed := ui.html(view(Model([], "hi")))
	assert indexOf(typed, "disabled") is Result.Error(_)
}

test "a post body cannot smuggle markup into the page" {
	page := ui.html(view(Model(["<script>steal()</script>"], "")))
	assert indexOf(page, "<script>") is Result.Error(_)
}

hive.ui builds an interface as a value: a View is a tree of widgets, each taking its attributes first and then what it shows — except spacer() and none(), which take nothing, and spinner(attrs), which takes only attributes.

  • Layout: row(attrs, views), column(attrs, views), and overlay(attrs, child) — a centred, dimmed dialog, or with an align or justify a panel pinned to an edge that lets clicks through.
  • Showing: text(attrs, content), image(attrs, src, alt), icon(attrs, Icon), link(attrs, href, label), table(attrs, rows) for a Table, and canvas for drawing, on its own slide.
  • Asking: button(attrs, label), input(attrs, value), textarea(attrs, value), checkbox(attrs, label, checked), select(attrs, options, chosen).

Attributes are a closed set too: gap, pad, width, height, grow, align, justify and scroll lay out — a width is capped at the room there is — size, heading, tone and background style, and disabled, busy (which disables as well), placeholder, hint and kind describe state. One that does not apply to a widget is ignored. The enumerations behind them are closed sets reached like any library type: Align is Start, Center, End or Stretch; Justify is Start, Center, End or Between; TextSize is Title, Subtitle, Body or Caption; Axis is Horizontal, Vertical or Both; an InputKind is Text, Number, Date, Password, Search or Range, a slider from 0 to 100; a Tone is Normal, Muted, Good, Warn or Danger, or an exact colour through HEX and RGBA — a malformed HEX is Normal and an RGBA channel is clamped; and an Icon is one of eighteen, so a name that is not one is a compile error.

html(view) renders a tree as an HTML fragment and page(title, view) as a whole document with its stylesheet built in, light and dark alike — what an httpServe handler can answer with directly, and what every screen of this tour but its code samples is. On a touch screen, controls grow to a thumb's width and a field's text to 16 pixels. Because a view is a value, an interface is testable without a browser: a test renders one with html and reads the markup, which is the cheapest place to check that a disabled button is disabled and that somebody else's text cannot smuggle a script into the page.