This repository has been archived on 2024-06-02. You can view files and clone it, but cannot push or open issues or pull requests.
x/box.go
2023-07-05 03:25:50 -04:00

258 lines
6.5 KiB
Go

package x
import "image"
import "image/color"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
type box struct {
backend *Backend
parent parent
outer anyBox
bounds image.Rectangle
minSize image.Point
padding tomo.Inset
border []tomo.Border
color color.Color
dndData data.Data
dndAccept []data.Mime
focused bool
focusable bool
canvas canvas.Canvas
drawer canvas.Drawer
on struct {
focusEnter event.FuncBroadcaster
focusLeave event.FuncBroadcaster
dndEnter event.FuncBroadcaster
dndLeave event.FuncBroadcaster
dndDrop event.Broadcaster[func (data.Data)]
mouseEnter event.FuncBroadcaster
mouseLeave event.FuncBroadcaster
mouseMove event.FuncBroadcaster
mouseDown event.Broadcaster[func (input.Button)]
mouseUp event.Broadcaster[func (input.Button)]
scroll event.Broadcaster[func (float64, float64)]
keyDown event.Broadcaster[func (input.Key, bool)]
keyUp event.Broadcaster[func (input.Key, bool)]
}
}
func (backend *Backend) NewBox() tomo.Box {
box := &box {
backend: backend,
}
box.drawer = box
box.outer = box
return box
}
func (this *box) Box () tomo.Box {
return this
}
func (this *box) Window () tomo.Window {
return this.parent.window()
}
func (this *box) Bounds () image.Rectangle {
return this.bounds
}
func (this *box) InnerBounds () image.Rectangle {
innerBounds := this.padding.Apply(this.bounds)
for _, border := range this.border {
innerBounds = border.Width.Apply(innerBounds)
}
return innerBounds
}
func (this *box) SetBounds (bounds image.Rectangle) {
if this.bounds == bounds { return }
this.bounds = bounds
this.invalidateLayout()
}
func (this *box) SetColor (c color.Color) {
if this.color == c { return }
this.color = c
this.invalidateDraw()
}
func (this *box) SetBorder (border ...tomo.Border) {
this.border = border
this.invalidateLayout()
}
func (this *box) SetMinimumSize (width, height int) {
minSize := image.Pt(width, height)
if this.minSize == minSize { return }
this.minSize = minSize
if this.bounds.Dx() < width || this.bounds.Dy() < height {
this.invalidateLayout()
}
// TODO: alert the parent
}
func (this *box) SetPadding (padding tomo.Inset) {
if this.padding == padding { return }
this.padding = padding
this.invalidateLayout()
}
func (this *box) SetDNDData (dat data.Data) {
this.dndData = dat
}
func (this *box) SetDNDAccept (types ...data.Mime) {
this.dndAccept = types
}
func (this *box) SetFocused (focused bool) {
if this.Focused () && !focused {
this.parent.window().focus(nil)
} else if !this.Focused() && focused {
this.parent.window().focus(this.outer)
}
}
func (this *box) SetFocusable (focusable bool) {
if this.focusable == focusable { return }
this.focusable = focusable
if !focusable {
this.SetFocused(false)
}
}
func (this *box) Focused () bool {
return this.outer == this.parent.window().focused
}
func (this *box) Modifiers () input.Modifiers {
return this.parent.window().modifiers
}
func (this *box) MousePosition () image.Point {
return this.parent.window().mousePosition
}
// ----- event handler setters ---------------------------------------------- //
func (this *box) OnFocusEnter (callback func()) event.Cookie {
return this.on.focusEnter.Connect(callback)
}
func (this *box) OnFocusLeave (callback func()) event.Cookie {
return this.on.focusLeave.Connect(callback)
}
func (this *box) OnDNDEnter (callback func()) event.Cookie {
return this.on.dndEnter.Connect(callback)
}
func (this *box) OnDNDLeave (callback func()) event.Cookie {
return this.on.dndLeave.Connect(callback)
}
func (this *box) OnDNDDrop (callback func(data.Data)) event.Cookie {
return this.on.dndDrop.Connect(callback)
}
func (this *box) OnMouseEnter (callback func()) event.Cookie {
return this.on.mouseEnter.Connect(callback)
}
func (this *box) OnMouseLeave (callback func()) event.Cookie {
return this.on.mouseLeave.Connect(callback)
}
func (this *box) OnMouseMove (callback func()) event.Cookie {
return this.on.mouseMove.Connect(callback)
}
func (this *box) OnMouseDown (callback func(input.Button)) event.Cookie {
return this.on.mouseDown.Connect(callback)
}
func (this *box) OnMouseUp (callback func(input.Button)) event.Cookie {
return this.on.mouseUp.Connect(callback)
}
func (this *box) OnScroll (callback func(deltaX, deltaY float64)) event.Cookie {
return this.on.scroll.Connect(callback)
}
func (this *box) OnKeyDown (callback func(key input.Key, numberPad bool)) event.Cookie {
return this.on.keyDown.Connect(callback)
}
func (this *box) OnKeyUp (callback func(key input.Key, numberPad bool)) event.Cookie {
return this.on.keyUp.Connect(callback)
}
// -------------------------------------------------------------------------- //
func (this *box) Draw (can canvas.Canvas) {
this.drawBorders(can)
pen := can.Pen()
pen.Fill(this.color)
pen.Rectangle(this.bounds)
}
func (this *box) drawBorders (can canvas.Canvas) {
pen := can.Pen()
bounds := this.bounds
for _, border := range this.border {
pen.Fill(border.Color[tomo.SideTop])
pen.Rectangle(image.Rect (
bounds.Min.X,
bounds.Min.Y,
bounds.Max.X,
bounds.Min.Y + border.Width[tomo.SideTop]))
pen.Fill(border.Color[tomo.SideBottom])
pen.Rectangle(image.Rect (
bounds.Min.X,
bounds.Max.Y - border.Width[tomo.SideBottom],
bounds.Max.X,
bounds.Max.Y))
pen.Fill(border.Color[tomo.SideLeft])
pen.Rectangle(image.Rect (
bounds.Min.X,
bounds.Min.Y + border.Width[tomo.SideTop],
bounds.Min.X + border.Width[tomo.SideLeft],
bounds.Max.Y - border.Width[tomo.SideBottom]))
pen.Fill(border.Color[tomo.SideRight])
pen.Rectangle(image.Rect (
bounds.Max.X - border.Width[tomo.SideRight],
bounds.Min.Y + border.Width[tomo.SideTop],
bounds.Max.X,
bounds.Max.Y - border.Width[tomo.SideBottom]))
bounds = border.Width.Apply(bounds)
}
}
func (this *box) doDraw () {
if this.canvas == nil { return }
if this.drawer != nil {
this.drawer.Draw(this.canvas)
}
}
func (this *box) doLayout () {
this.canvas = this.parent.canvas().Clip(this.bounds)
}
func (this *box) setParent (parent parent) {
this.parent = parent
}
func (this *box) recursiveRedo () {
this.doLayout()
this.doDraw()
}
func (this *box) invalidateLayout () {
if this.parent == nil || this.parent.window() == nil { return }
this.parent.window().invalidateLayout(this.outer)
}
func (this *box) invalidateDraw () {
if this.parent == nil || this.parent.window() == nil { return }
this.parent.window().invalidateDraw(this.outer)
}