layout

package
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package layout divides a region among the things that go in it.

It is geometry and nothing else: it decides where boxes go and hands back the views to draw them in, and it never draws. That is what lets the same rules place a widget, a string, or a hole left deliberately empty — and what keeps the sizing rules testable by asking for numbers rather than by reading a screen.

Measuring

A slot whose size follows from its content says so with Measured and supplies a Measurer. The measurer is asked about the axis being divided, given how much room there is across the other one: a row of text asked how tall it is at a width, a column of labels asked how wide it is at a height. One question, either axis, which is why Measured means the same thing in Rows and in Columns.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Columns

func Columns(v grid.View, slots ...Slot) []grid.View

Columns is Rows across, for panes side by side.

func Divide

func Divide(total, across int, slots []Slot) []int

Divide splits total among slots, measuring against across, and returns each slot's size. The sizes always add up to at most total.

It is exported because a caller aligning something to the same grid — a header over a table, a ruler beside a pane — needs the numbers without the views.

func Rows

func Rows(v grid.View, slots ...Slot) []grid.View

Rows divides v into horizontal bands down the region and returns the view for each, in order.

Nothing is drawn. The caller draws into the views it is given, which is what lets a slot be left empty, be drawn conditionally, or be measured now and drawn later.

The order of business is measure, then arrange: the only order that works when one slot's size depends on its content and another's depends on what is left. Slots that end up with no height still get a view — an empty one — because a caller's draw code runs every frame, and code that only breaks when it is squeezed to nothing breaks in front of the user.

Types

type Align

type Align uint8

Align is how content sits in a space wider than itself.

const (
	Start Align = iota
	Center
	End
)

func (Align) Offset

func (a Align) Offset(space, width int) int

Offset is where content of the given width starts inside space columns.

type Anchor

type Anchor uint8

Anchor is where a floating layer sits in the space it floats over.

const (
	// Middle is the centre, which is where a modal belongs: it is the one position
	// that does not imply the thing it covers is still reachable.
	Middle Anchor = iota
	TopLeft
	Top
	TopRight
	Left
	Right
	BottomLeft
	Bottom
	BottomRight
)

type Inset

type Inset struct{ Top, Right, Bottom, Left int }

Inset is space held clear on each side.

func Symmetric

func Symmetric(vertical, horizontal int) Inset

Symmetric is one inset above and below, another to the left and right — the common case, because a terminal cell is about twice as tall as it is wide and even padding does not look even.

func Uniform

func Uniform(n int) Inset

Uniform is the same inset on every side.

func (Inset) Apply

func (i Inset) Apply(r image.Rectangle) image.Rectangle

Apply is what is left of r after the inset is held clear, and nothing at all when the inset is larger than the region.

The rectangle is built by hand rather than with image.Rect, which puts a backwards rectangle the right way round: an inset that overran its region would come back as a real region somewhere else instead of as no region at all.

func (Inset) Size

func (i Inset) Size() Size

Size is how many columns and rows the inset takes.

type MeasureFunc

type MeasureFunc func(across int) int

MeasureFunc adapts a function to Measurer.

func (MeasureFunc) Measure

func (f MeasureFunc) Measure(across int) int

type Measurer

type Measurer interface {
	Measure(across int) int
}

Measurer reports how much of one axis something wants, given how much room it has across the other.

Which axis is which is decided by whoever is asking: Rows divides height and asks for a height at a width, Columns divides width and asks for a width at a height. A type that can only answer for one axis is a type that belongs in only one of them, and saying so is the caller's business rather than this package's.

type Placement

type Placement struct {
	Anchor Anchor
	// Width and Height are the layer's size in cells. Zero means as large as the
	// space allows, less Margin.
	Width, Height int
	// Margin is kept clear between the layer and the edges of the space, so a layer
	// anchored to a corner does not look stuck to it.
	Margin int
}

Placement is where a floating layer goes in the space it floats over.

It is placement and nothing else — no drawing, no dimming, no idea what is going to be put there. Keeping it that way is what lets a hit test a frame later ask exactly the question the frame asked, and get exactly the same answer.

The layer is clamped to the space rather than allowed to hang off the edge. A dialog whose buttons are past the right margin is a dialog nobody can answer.

func (Placement) In

func (p Placement) In(space Size) image.Rectangle

In is where the layer goes inside a space of the given size, in that space's own coordinates.

type Size

type Size struct{ W, H int }

Size is a width and a height in cells.

type Sizing

type Sizing struct {
	// Fixed is an exact number of rows or columns. It wins over everything else.
	Fixed int
	// Flex is a share of what is left after the fixed and measured slots have taken
	// theirs. Two slots with flex 1 and 2 split the remainder one third to two
	// thirds.
	Flex int
	// Measured asks the slot's [Measurer] how much it wants.
	Measured bool
	// Min is a floor on a flex or measured slot, so a pane cannot be squeezed into a
	// size where it shows nothing useful. It is honoured while there is room for it:
	// several floors can add up to more than the space there is.
	Min int
	// Max caps a measured slot, so content that grew without bound does not take the
	// whole region.
	Max int
}

Sizing says how much of an axis a slot wants.

func Fixed

func Fixed(n int) Sizing

Fixed is a slot of an exact size.

func Flex

func Flex(share int) Sizing

Flex is a slot taking a share of what is left.

func Measured

func Measured(minimum, maximum int) Sizing

Measured is a slot as big as its Measurer asks to be, within bounds. A zero maximum means no cap.

type Slot

type Slot struct {
	Size Sizing
	// Of is asked how much of the divided axis this slot wants, and is only
	// consulted when Size says the slot is measured. A measured slot with nothing to
	// ask gets its floor, which is zero unless one was set.
	Of Measurer
}

Slot is one division of a region: how much room it gets, and what to ask when that follows from its content.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL