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 { return this.padding.Apply(this.innerClippingBounds()) } func (this *box) MinimumSize () image.Point { return this.minSize } func (this *box) borderSum () tomo.Inset { sum := tomo.Inset { } for _, border := range this.border { sum[0] += border.Width[0] sum[1] += border.Width[1] sum[2] += border.Width[2] sum[3] += border.Width[3] } return sum } func (this *box) innerClippingBounds () image.Rectangle { return this.borderSum().Apply(this.bounds) } 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() this.recalculateMinimumSize() } func (this *box) SetMinimumSize (size image.Point) { if this.minSize == size { return } this.minSize = size if this.parent != nil { this.parent.notifyMinimumSizeChange(this) } } func (this *box) SetPadding (padding tomo.Inset) { if this.padding == padding { return } this.padding = padding this.invalidateLayout() this.recalculateMinimumSize() } 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.parent == nil { return } window := this.parent.window() if window == nil { return } if !this.focusable { return } 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 { if this.parent == nil { return false } parentWindow := this.parent.window() if parentWindow == nil { return false } return this.outer == parentWindow.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) { if can == nil { return } pen := can.Pen() pen.Fill(this.color) pen.Rectangle(this.bounds) } func (this *box) drawBorders (can canvas.Canvas) { if can == nil { return } 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.drawBorders(this.canvas) this.drawer.Draw(this.canvas.Clip(this.innerClippingBounds())) } } func (this *box) doLayout () { if this.parent == nil { this.canvas = nil; return } parentCanvas := this.parent.canvas() if parentCanvas == nil { this.canvas = nil; return } this.canvas = parentCanvas.Clip(this.bounds) } func (this *box) setParent (parent parent) { if this.parent != parent && this.Focused() { this.SetFocused(false) } 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) } func (this *box) recalculateMinimumSize () { if this.outer != anyBox(this) { this.outer.recalculateMinimumSize() } } func (this *box) canBeFocused () bool { return this.focusable } func (this *box) boxUnder (point image.Point) anyBox { if point.In(this.bounds) { return this.outer } else { return nil } } func (this *box) handleFocusEnter () { this.on.focusEnter.Broadcast() } func (this *box) handleFocusLeave () { this.on.focusLeave.Broadcast() } func (this *box) handleMouseEnter () { this.on.mouseEnter.Broadcast() } func (this *box) handleMouseLeave () { this.on.mouseLeave.Broadcast() } func (this *box) handleMouseMove () { this.on.mouseMove.Broadcast() } func (this *box) handleMouseDown (button input.Button) { if this.focusable { this.SetFocused(true) } else { if this.parent == nil { return } window := this.parent.window() if window == nil { return } window.focus(nil) } for _, listener := range this.on.mouseDown.Listeners() { listener(button) } } func (this *box) handleMouseUp (button input.Button) { for _, listener := range this.on.mouseUp.Listeners() { listener(button) } } func (this *box) handleKeyDown (key input.Key, numberPad bool) { for _, listener := range this.on.keyDown.Listeners() { listener(key, numberPad) } } func (this *box) handleKeyUp (key input.Key, numberPad bool) { for _, listener := range this.on.keyUp.Listeners() { listener(key, numberPad) } } func (this *box) propagate (callback func (anyBox) bool) bool { return callback(this) } func (this *box) propagateAlt (callback func (anyBox) bool) bool { return callback(this) }