tomo/unit.go

226 lines
6.5 KiB
Go

package tomo
import "fmt"
import "image"
import "image/color"
// Side represents one side of a rectangle.
type Side int; const (
SideTop Side = iota
SideRight
SideBottom
SideLeft
)
func (side Side) String () string {
switch side {
case SideTop: return "SideTop"
case SideRight: return "SideRight"
case SideBottom: return "SideBottom"
case SideLeft: return "SideLeft"
default: return fmt.Sprintf("Side(%d)", side)
}
}
// Inset represents a rectangle inset that can have a different value for each
// side.
type Inset [4]int
// I allows you to create an inset in a CSS-ish way:
//
// - One argument: all sides are set to this value
// - Two arguments: the top and bottom sides are set to the first value, and
// the left and right sides are set to the second value.
// - Three arguments: the top side is set by the first value, the left and
// right sides are set by the second vaue, and the bottom side is set by the
// third value.
// - Four arguments: each value corresponds to a side.
//
// This function will panic if an argument count that isn't one of these is
// given.
func I (sides ...int) Inset {
switch len(sides) {
case 1: return Inset { sides[0], sides[0], sides[0], sides[0] }
case 2: return Inset { sides[0], sides[1], sides[0], sides[1] }
case 3: return Inset { sides[0], sides[1], sides[2], sides[1] }
case 4: return Inset { sides[0], sides[1], sides[2], sides[3] }
default: panic("I: illegal argument count.")
}
}
func (inset Inset) String () string {
return fmt.Sprintf("%d %d %d %d", inset[0], inset[1], inset[2], inset[3])
}
// Apply returns the given image.Rectangle, shrunk on all four sides by the
// given Inset. If a measurment of the Inset is negative, that side will instead
// be expanded outward. If the rectangle's dimensions cannot be reduced any
// further, an empty rectangle near its center will be returned.
func (inset Inset) Apply (bigger image.Rectangle) (smaller image.Rectangle) {
smaller = bigger
if smaller.Dx() < inset[3] + inset[1] {
smaller.Min.X = (smaller.Min.X + smaller.Max.X) / 2
smaller.Max.X = smaller.Min.X
} else {
smaller.Min.X += inset[3]
smaller.Max.X -= inset[1]
}
if smaller.Dy() < inset[0] + inset[2] {
smaller.Min.Y = (smaller.Min.Y + smaller.Max.Y) / 2
smaller.Max.Y = smaller.Min.Y
} else {
smaller.Min.Y += inset[0]
smaller.Max.Y -= inset[2]
}
return
}
// Inverse returns a negated version of the Inset.
func (inset Inset) Inverse () (prime Inset) {
return Inset {
inset[0] * -1,
inset[1] * -1,
inset[2] * -1,
inset[3] * -1,
}
}
// Horizontal returns the sum of SideRight and SideLeft.
func (inset Inset) Horizontal () int {
return inset[SideRight] + inset[SideLeft]
}
// Vertical returns the sum of SideTop and SideBottom.
func (inset Inset) Vertical () int {
return inset[SideTop] + inset[SideBottom]
}
// Border represents a single border of a Box.
type Border struct {
Width Inset
Color [4]color.Color
}
func (border Border) String () string {
return fmt.Sprintf("%v %v %v %v / %v",
border.Color[0], border.Color[1], border.Color[2], border.Color[3],
border.Width)
}
// Align lists basic alignment types.
type Align int; const (
AlignStart Align = iota // similar to left-aligned text
AlignMiddle // similar to center-aligned text
AlignEnd // similar to right-aligned text
AlignEven // similar to justified text
)
func (align Align) String () string {
switch align {
case AlignStart: return "AlignStart"
case AlignMiddle: return "AlignMiddle"
case AlignEnd: return "AlignEnd"
case AlignEven: return "AlignEven"
default: return fmt.Sprintf("Align(%d)", align)
}
}
// TextureMode lists texture rendering modes.
type TextureMode int; const (
TextureModeTile TextureMode = iota
TextureModeCenter
// TODO more modes: fit, fill, and stretch
// Of course canvas will need to have a concept of this.
)
func (mode TextureMode) String () string {
switch mode {
case TextureModeTile: return "TextureModeTile"
case TextureModeCenter: return "TextureModeCenter"
default: return fmt.Sprintf("TextureMode(%d)", mode)
}
}
// Face represents a typeface.
type Face struct {
// Font specifies the font name. This should be searched for in a list
// of installed fonts.
Font string
// Size is the point size of the face.
Size float64
// Weight is the weight of the face. If zero, it should be interpreted
// as normal (equivalent to 400).
Weight int
// Italic is how italicized the face is. It ranges from 0 to 1. It is
// different from Slant in that it may alter the design of the glyphs
// instead of simply skewing them.
Italic float64
// Slant is how slanted the face is. It ranges from 0 to 1. It is
// different from Italic in that it simply skews the glyphs without
// altering their design.
Slant float64
}
func (face Face) String () string {
return fmt.Sprintf (
"%s %fpt W%d I%f S%f",
face.Font, face.Size, face.Weight, face.Italic, face.Slant)
}
// Role describes the role of an Object.
type Role struct {
// Package is an optional namespace field. If specified, it should be
// the package name or module name the object is from.
Package string
// Object specifies what type of Object it is. For example:
// - TextInput
// - Table
// - Label
// - Dial
// This should correspond directly to the type name of the Object.
Object string
}
// String satisfies the fmt.Stringer interface. It follows the format of:
// Package.Object
func (r Role) String () string {
return fmt.Sprintf("%s.%s", r.Package, r.Object)
}
// R is a convenience constructor for Role.
func R (pack, object string) Role {
return Role { Package: pack, Object: object }
}
// Color represents a color ID.
type Color int; const (
ColorBackground Color = iota
ColorForeground
ColorRaised
ColorSunken
ColorAccent
)
func (id Color) String () string {
switch id {
case ColorBackground: return "ColorBackground"
case ColorForeground: return "ColorForeground"
case ColorRaised: return "ColorRaised"
case ColorSunken: return "ColorSunken"
case ColorAccent: return "ColorAccent"
default: return fmt.Sprintf("Color(%d)", id)
}
}
// RGBA satisfies the color.Color interface. The result of this method may be
// rendered invalid if the visual style changes, so it is often necessary to
// subscribe to the StyleChange event in order to get new RGBA values when this
// happens.
func (id Color) RGBA () (r, g, b, a uint32) {
assertBackend()
return backend.ColorRGBA(id)
}