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) innerClippingBounds () image.Rectangle { innerBounds := 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 (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() } 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) { 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 () { 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) }