Toolbar

Toolbar is a row of controls: what is written into it lines up horizontally, each item as wide as it needs to be, rather than taking a row of the page each. Sticky keeps that row at the top of the page while the rest of it scrolls under.

Usage

func Toolbar(c *tgframe.Container, conf ...*ToolbarConf) *tgframe.Container
  • c: Parent container.
  • conf: Optional configuration, at most one.
// ToolbarConf is the configuration for the Toolbar component.
type ToolbarConf struct {
	tgframe.Base // ID

	// Sticky keeps the row at the top of the page while the rest of it
	// scrolls under, on an opaque background so nothing shows through.
	Sticky bool

	// Justify is ToolbarJustifyStart (default), ToolbarJustifyEnd or
	// ToolbarJustifyBetween. Anything else panics.
	Justify string
}

The container a toolbar hands out derives its id from the toolbar's; give none and it carries none, and the components inside are still placed by position.

Justify says where the row's spare width goes:

ValueItems sit
ToolbarJustifyStartat the start of the row, the default
ToolbarJustifyEndat the end of the row
ToolbarJustifyBetweenspread out, first and last at the edges

A row too wide for the viewport wraps rather than pushing the page sideways, so a toolbar is as safe on a phone as a column is.

Example

	bar := tgcomp.Toolbar(p.Main, &tgcomp.ToolbarConf{ID: "toolbar"})

	run := tgcomp.Button(bar, "Run", &tgcomp.ButtonConf{ID: "toolbar_run"})
	tgcomp.Button(bar, "Stop", &tgcomp.ButtonConf{ID: "toolbar_stop"})
	modes := []string{"fast", "safe"}
	mode := tgcomp.Select(bar, "Mode", modes,
		(&tgcomp.SelectConf{ID: "toolbar_mode"}).SetDefault(0))

	tgcomp.Text(p.Main, fmt.Sprintf("Run: %v, mode: %s", run, modes[*mode]),
		&tgcomp.TextConf{ID: "toolbar_result"})

Sticky

A sticky toolbar stays at the top of the viewport while the page scrolls under it, on the page's own background and with a line under it, so what passes beneath does not show through:

	bar := tgcomp.Toolbar(p.Main, &tgcomp.ToolbarConf{
		ID:      "sticky_toolbar",
		Sticky:  true,
		Justify: tgcomp.ToolbarJustifyBetween,
	})

	tgcomp.Subtitle(bar, "Report")
	tgcomp.Button(bar, "Export", &tgcomp.ButtonConf{ID: "toolbar_export"})

	// Enough rows to scroll, so the row above stays put while they pass
	// under it.
	for i := range 40 {
		tgcomp.Text(p.Main, fmt.Sprintf("row-%d", i))
	}

It sticks to whatever scrolls the page, which for an app in the shell is the document. A toolbar written inside something that scrolls on its own — a dialog body — sticks to the top of that instead, and below anything that container already keeps there: a dialog's own header is sticky, so the row stops under it rather than sliding beneath it.

	d := tgcomp.Dialog(p.Main, "Rows", &tgcomp.DialogConf{ID: "toolbar_dialog"})

	if tgcomp.Button(p.Main, "Open", &tgcomp.ButtonConf{ID: "toolbar_dialog_open"}) {
		d.Open()
	}

	d.With(func(c *tgframe.Container) {
		bar := tgcomp.Toolbar(c, &tgcomp.ToolbarConf{
			ID:     "dialog_toolbar",
			Sticky: true,
		})
		tgcomp.Button(bar, "Export", &tgcomp.ButtonConf{ID: "toolbar_dialog_export"})

		// A dialog scrolls its own body, so the row sticks to the top of
		// that -- below the dialog's header rather than under it.
		for i := range 30 {
			tgcomp.Text(c, fmt.Sprintf("dialog-row-%d", i))
		}
	})

Toolbar or Column?

Column is for laying a page out in parts: each column is a share of the width, and what goes in one is a section of the page. A toolbar is for the controls above that page — it packs them together at their natural widths, wraps them on a narrow screen and, with Sticky, keeps them reachable however far down the page the reader is.