2023-06-30 14:38:51 -06:00
|
|
|
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"
|
|
|
|
|
2023-07-01 17:23:33 -06:00
|
|
|
// Side represents one side of a rectangle.
|
|
|
|
type Side int; const (
|
|
|
|
SideTop Side = iota
|
|
|
|
SideRight
|
|
|
|
SideBottom
|
|
|
|
SideLeft
|
|
|
|
)
|
|
|
|
|
2023-06-30 14:38:51 -06:00
|
|
|
type Inset [4]int
|
2023-07-01 17:23:33 -06:00
|
|
|
|
|
|
|
// 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]
|
|
|
|
}
|
2023-06-30 14:38:51 -06:00
|
|
|
|
|
|
|
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
|
|
|
|
)
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// Object is any obscreen object. Each object must be linked to a box, even if
|
|
|
|
// it is that box.
|
2023-06-30 14:38:51 -06:00
|
|
|
type Object interface {
|
|
|
|
Box () Box
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// Box is a basic styled box.
|
2023-06-30 14:38:51 -06:00
|
|
|
type Box interface {
|
|
|
|
Object
|
2023-07-01 22:31:05 -06:00
|
|
|
|
|
|
|
Window () Window
|
2023-06-30 14:38:51 -06:00
|
|
|
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
|
2023-07-01 22:31:05 -06:00
|
|
|
Modifiers () input.Modifiers
|
|
|
|
MousePosition () image.Point
|
2023-06-30 14:38:51 -06:00
|
|
|
|
|
|
|
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
|
2023-07-02 00:46:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// CanvasBox is a box that can be drawn to.
|
|
|
|
type CanvasBox interface {
|
|
|
|
Box
|
|
|
|
SetDrawer (canvas.Drawer)
|
|
|
|
Invalidate ()
|
|
|
|
}
|
2023-06-30 14:38:51 -06:00
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// 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)
|
2023-06-30 14:38:51 -06:00
|
|
|
ContentBounds () image.Rectangle
|
|
|
|
ScrollTo (image.Point)
|
|
|
|
OnContentBoundsChange (func ()) event.Cookie
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// TextBox is a box that contains text content.
|
2023-06-30 14:38:51 -06:00
|
|
|
type TextBox interface {
|
2023-07-02 00:46:12 -06:00
|
|
|
ContentBox
|
2023-07-08 21:49:51 -06:00
|
|
|
|
|
|
|
SetText (string)
|
2023-06-30 14:38:51 -06:00
|
|
|
SetTextColor (color.Color)
|
|
|
|
SetFace (font.Face)
|
|
|
|
SetHAlign (Align)
|
|
|
|
SetVAlign (Align)
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// ContentBox is a box that can contain child objects. It arranges them
|
|
|
|
// according to a layout rule.
|
2023-06-30 14:38:51 -06:00
|
|
|
type ContainerBox interface {
|
2023-07-02 00:46:12 -06:00
|
|
|
ContentBox
|
2023-07-05 02:20:56 -06:00
|
|
|
SetGap (image.Point)
|
2023-06-30 14:38:51 -06:00
|
|
|
Add (Object)
|
|
|
|
Delete (Object)
|
|
|
|
Insert (child Object, before Object)
|
|
|
|
Clear ()
|
|
|
|
Length () int
|
|
|
|
At (int) Object
|
|
|
|
SetLayout (Layout)
|
|
|
|
}
|
|
|
|
|
2023-07-05 02:21:17 -06:00
|
|
|
// LayoutHints are passed to a layout to tell it how to arrange child boxes.
|
|
|
|
type LayoutHints struct {
|
2023-07-05 15:44:08 -06:00
|
|
|
Bounds image.Rectangle
|
|
|
|
OverflowX bool
|
|
|
|
OverflowY bool
|
2023-07-05 02:21:17 -06:00
|
|
|
Gap image.Point
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// Layout can be given to a ContainerBox to arrange child objects.
|
|
|
|
type Layout interface {
|
2023-07-05 02:21:17 -06:00
|
|
|
MinimumSize (LayoutHints, []Box) image.Point
|
|
|
|
Arrange (LayoutHints, []Box)
|
2023-07-02 00:46:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Window is an operating system window. It can contain one object.
|
2023-06-30 14:38:51 -06:00
|
|
|
type Window interface {
|
|
|
|
SetRoot (Object)
|
2023-06-30 20:08:17 -06:00
|
|
|
SetTitle (string)
|
2023-06-30 14:38:51 -06:00
|
|
|
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 ()
|
2023-06-30 20:08:17 -06:00
|
|
|
Close ()
|
2023-06-30 14:38:51 -06:00
|
|
|
OnClose (func ()) event.Cookie
|
|
|
|
}
|
|
|
|
|
2023-07-02 00:46:12 -06:00
|
|
|
// MainWindow is a top-level operating system window.
|
2023-06-30 14:38:51 -06:00
|
|
|
type MainWindow interface {
|
|
|
|
Window
|
|
|
|
NewChild (image.Rectangle) (Window, error)
|
|
|
|
}
|