tomo/object.go

217 lines
5.9 KiB
Go

package tomo
import "image"
import "image/color"
import "golang.org/x/image/font"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/canvas"
// Side represents one side of a rectangle.
type Side int; const (
SideTop Side = iota
SideRight
SideBottom
SideLeft
)
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.")
}
}
// Apply returns the given 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]
}
type Border struct {
Width Inset
Color [4]color.Color
}
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
)
// Object is any obscreen object. Each object must be linked to a box, even if
// it is that box.
type Object interface {
Box () Box
}
// Box is a basic styled box.
type Box interface {
Object
Window () Window
Bounds () image.Rectangle
InnerBounds () image.Rectangle
SetBounds (image.Rectangle)
SetColor (color.Color)
SetBorder (...Border)
SetMinimumSize (int, int)
SetPadding (Inset)
SetDNDData (data.Data)
SetDNDAccept (...data.Mime)
SetFocused (bool)
SetFocusable (bool)
Focused () bool
Modifiers () input.Modifiers
MousePosition () image.Point
OnFocusEnter (func ()) event.Cookie
OnFocusLeave (func ()) event.Cookie
OnDNDEnter (func ()) event.Cookie
OnDNDLeave (func ()) event.Cookie
OnDNDDrop (func (data.Data)) event.Cookie
OnMouseEnter (func ()) event.Cookie
OnMouseLeave (func ()) event.Cookie
OnMouseMove (func ()) event.Cookie
OnMouseDown (func (input.Button)) event.Cookie
OnMouseUp (func (input.Button)) event.Cookie
OnScroll (func (deltaX, deltaY float64)) event.Cookie
OnKeyDown (func (key input.Key, numberPad bool)) event.Cookie
OnKeyUp (func (key input.Key, numberPad bool)) event.Cookie
}
// CanvasBox is a box that can be drawn to.
type CanvasBox interface {
Box
SetDrawer (canvas.Drawer)
Invalidate ()
}
// ContentBox is an abstract box that has some kind of content. Its only purpose
// is to be embedded into TextBox and ContainerBox.
type ContentBox interface {
Box
SetOverflow (horizontal, vertical bool)
ContentBounds () image.Rectangle
ScrollTo (image.Point)
OnContentBoundsChange (func ()) event.Cookie
}
// TextBox is a box that contains text content.
type TextBox interface {
ContentBox
SetTextColor (color.Color)
SetFace (font.Face)
SetHAlign (Align)
SetVAlign (Align)
}
// ContentBox is a box that can contain child objects. It arranges them
// according to a layout rule.
type ContainerBox interface {
ContentBox
SetGap (image.Point)
Add (Object)
Delete (Object)
Insert (child Object, before Object)
Clear ()
Length () int
At (int) Object
SetLayout (Layout)
}
// LayoutHints are passed to a layout to tell it how to arrange child boxes.
type LayoutHints struct {
Bounds image.Rectangle
OverflowX bool
OverflowY bool
Gap image.Point
}
// Layout can be given to a ContainerBox to arrange child objects.
type Layout interface {
MinimumSize (LayoutHints, []Box) image.Point
Arrange (LayoutHints, []Box)
}
// Window is an operating system window. It can contain one object.
type Window interface {
SetRoot (Object)
SetTitle (string)
SetIcon (sizes []image.Image)
NewMenu (image.Rectangle) (Window, error)
NewModal (image.Rectangle) (Window, error)
Widget () (Window, error)
Copy (data.Data)
Paste (callback func (data.Data, error), accept ...data.Mime)
Show ()
Hide ()
Close ()
OnClose (func ()) event.Cookie
}
// MainWindow is a top-level operating system window.
type MainWindow interface {
Window
NewChild (image.Rectangle) (Window, error)
}