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 window *window 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 drawClean bool layoutClean bool 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 return box } func (this *box) Box () tomo.Box { return this } func (this *box) Window () tomo.Window { return this.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) { this.minSize = image.Pt(width, height) if this.bounds.Dx() < width || this.bounds.Dy() < height { this.invalidateLayout() } } 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.window.focus(nil) } else if !this.Focused() && focused { this.window.focus(this) } } 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 == this.window.focused } func (this *box) Modifiers () input.Modifiers { return this.window.modifiers } func (this *box) MousePosition () image.Point { return this.window.mousePosition } // ----- event handlers ----------------------------------------------------- // 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) { 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) } pen.Fill(this.color) pen.Rectangle(bounds) } func (this *box) invalidateDraw () { this.drawClean = false } func (this *box) invalidateLayout () { this.invalidateDraw() this.layoutClean = false }