Skip to content

Look, and the window

The keywords every element takes, the palette, and what the window itself brings.

The keywords every element takes

Fifteen properties mean the same on every element, and are methods on every one:

Method Type What it does
Width(v), Height(v) float64 a fixed size; written even when 0
MinWidth(v), MaxWidth(v) float64 bounds
Disabled(v) bool inert, and drawn so
Theme(v) string the palette its subtree resolves colors in: "dark" or "light"
Animate(ms), Easing(v) float64, string a tween on change, and its curve ("out", "inOut")
Enter(v), Exit(v) bool animate its arrival, its departure
ColSpan(v), RowSpan(v) int how many of a grid's tracks it covers
Role(v), A11yLabel(v) string what a screen reader is told
Tooltip(v) string what the pointer shows

An element whose own keyword has one of these names keeps its own meaning: a Button's Width is the button's, sized as the button wants, where a Column's is a box around it. The elements whose own label is already the name a screen reader reads — Checkbox, Switch, Progress — refuse A11yLabel, since there is no second name to give.

Themes and animation

Colors are the engine's tokens — "accent", "panel", "text", "textDim" — or a hex value. A token is resolved in the palette of the nearest Theme above it, so one method flips a whole panel:

package main

import (
    "fmt"

    . "github.com/i2y/yokan/gomamochi"
)

func key(b *ButtonEl) *ButtonEl { return b.Background("#313244").HoverBackground("#45475a") }

type Styled struct {
    mode string
    n    int
}

func (s *Styled) flip() {
    if s.mode == "dark" {
        s.mode = "light"
    } else {
        s.mode = "dark"
    }
}

func (s *Styled) View() Element {
    return Column(
        Text(fmt.Sprintf("n=%d", s.n)).Size(18).Color("accent"),
        Row(
            key(Button("+1")).OnClick(func() { s.n += 1 }),
            key(Button("flip")).Background("#fab387").OnClick(func() { s.flip() }),
        ).Spacing(6),
    ).Spacing(8).Padding(12).Background("panel").Theme(s.mode)
}

func main() {
    Run(&Styled{mode: "dark"}, Title("styled"))
}

A look kept in one place is a function that sets the same properties on every element handed to it, like key above: the element's type comes back, so the chain goes on. Animate(120).Easing("out") on an element tweens the change to it over 120 milliseconds; Enter and Exit do the same for its arrival and departure.

The window

Run takes the app and its options: Title, Size(width, height), and Padding, the space between the window's edge and the tree. Quit() closes the window on the engine's next frame; a headless run never takes it, so a script runs to its end.

The elements that arrange or cover: Grid lays its children on Columns tracks, and GridCell(child).ColSpan(2) spans them; Stack puts its children on top of one another; ScrollView and HScrollView are panes that scroll; Modal is a panel over the rest of the window while Open. Spacer takes the space its parent has left over, Divider draws a rule, Link opens a page, Spinner and Progress show work, Image and Svg show a file.

package main

import (
    "fmt"

    . "github.com/i2y/yokan/gomamochi"
)

type Panels struct {
    open bool
}

func (p *Panels) View() Element {
    var lines []Element
    for n := 1; n <= 12; n++ {
        lines = append(lines, Text(fmt.Sprintf("line %d", n)))
    }
    return Stack(
        Column(
            Row(Text("Panels").Size(18), Spacer(), Spinner().Size(14)).Spacing(8),
            Grid(Text("one"), Text("two"), GridCell(Text("across both").Align("center")).ColSpan(2)).Columns(2).Spacing(6),
            ScrollView(Column(lines...).Spacing(2)).Height(90),
            Button("about").OnClick(func() { p.open = true }),
        ).Spacing(10).Padding(14),
        Modal(
            Column(
                Text("A panel over the rest of it.").Size(14),
                Button("close").OnClick(func() { p.open = false }),
            ).Spacing(8).Padding(12).Background("panel"),
        ).Open(p.open),
    )
}

func main() {
    Run(&Panels{}, Title("panels"), Size(420, 360))
}