diff --git a/box.go b/box.go index 039975a..a6d6615 100644 --- a/box.go +++ b/box.go @@ -251,7 +251,10 @@ func (this *box) doDraw () { } func (this *box) doLayout () { - this.canvas = this.parent.canvas().Clip(this.bounds) + 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) { @@ -274,7 +277,31 @@ func (this *box) invalidateDraw () { } func (this *box) recalculateMinimumSize () { - if this.outer != this { + 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) handleMouseDown (button input.Button) { + 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) + } +} diff --git a/containerbox.go b/containerbox.go index 53cd1c8..3aeee34 100644 --- a/containerbox.go +++ b/containerbox.go @@ -13,9 +13,10 @@ type containerBox struct { contentBounds image.Rectangle scroll image.Point - gap image.Point - children []tomo.Box - layout tomo.Layout + gap image.Point + children []tomo.Box + layout tomo.Layout + propagateEvents bool on struct { contentBoundsChange event.FuncBroadcaster @@ -24,7 +25,8 @@ type containerBox struct { func (backend *Backend) NewContainerBox() tomo.ContainerBox { box := &containerBox { - box: backend.NewBox().(*box), + box: backend.NewBox().(*box), + propagateEvents: true, } box.drawer = box box.outer = box @@ -63,6 +65,10 @@ func (this *containerBox) OnContentBoundsChange (callback func()) event.Cookie { return this.on.contentBoundsChange.Connect(callback) } +func (this *containerBox) PropagateEvents (propagate bool) { + this.propagateEvents = propagate +} + func (this *containerBox) SetGap (gap image.Point) { if this.gap == gap { return } this.gap = gap @@ -210,3 +216,14 @@ func (this *containerBox) recursiveRedo () { child.(anyBox).recursiveRedo() } } + +func (this *containerBox) boxUnder (point image.Point) anyBox { + if this.propagateEvents { + for _, box := range this.children { + candidate := box.(anyBox).boxUnder(point) + if candidate != nil { return candidate } + } + } + + return this.box.boxUnder(point) +} diff --git a/event.go b/event.go index 69b93a2..020d383 100644 --- a/event.go +++ b/event.go @@ -4,7 +4,9 @@ import "image" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" +import "git.tebibyte.media/tomo/xgbkb" import "github.com/jezek/xgbutil/xevent" +import "git.tebibyte.media/tomo/tomo/input" func (window *window) handleExpose ( connection *xgbutil.XUtil, @@ -72,6 +74,61 @@ func (window *window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (fo return false } + +func (window *window) handleButtonPress ( + connection *xgbutil.XUtil, + event xevent.ButtonPressEvent, +) { + if window.hasModal { return } + + buttonEvent := *event.ButtonPressEvent + point := image.Pt(int(buttonEvent.EventX), int(buttonEvent.EventY)) + insideWindow := point.In(window.xCanvas.Bounds()) + scrolling := buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 + window.updateModifiers(buttonEvent.State) + window.updateMousePosition(buttonEvent.EventX, buttonEvent.EventY) + + if !insideWindow && window.shy && !scrolling { + window.Close() + } else if scrolling { + // underneath := window.scrollTargetChildAt(point) + // if underneath != nil { + // if child, ok := underneath.element.(ability.ScrollTarget); ok { + // sum := scrollSum { } + // sum.add(buttonEvent.Detail, window, buttonEvent.State) + // window.compressScrollSum(buttonEvent, &sum) + // child.HandleScroll ( + // point, float64(sum.x), float64(sum.y), + // modifiers) + // } + // } + } else { + underneath := window.boxUnder(point) + window.drags[buttonEvent.Detail] = underneath + if underneath != nil { + underneath.handleMouseDown(input.Button(buttonEvent.Detail)) + } + } +} + +func (window *window) handleButtonRelease ( + connection *xgbutil.XUtil, + event xevent.ButtonReleaseEvent, +) { + if window.hasModal { return } + + buttonEvent := *event.ButtonReleaseEvent + if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { return } + window.updateModifiers(buttonEvent.State) + window.updateMousePosition(buttonEvent.EventX, buttonEvent.EventY) + dragging := window.drags[buttonEvent.Detail] + + if dragging != nil { + dragging.handleMouseUp(input.Button(buttonEvent.Detail)) + } +} + + func (window *window) compressExpose ( firstEvent xproto.ExposeEvent, ) ( @@ -136,3 +193,17 @@ func (window *window) compressConfigureNotify ( return } + +func (window *window) updateModifiers (state uint16) { + modifiers := xgbkb.StateToModifiers(state) + window.modifiers.Shift = modifiers.Shift || modifiers.ShiftLock + window.modifiers.Control = modifiers.Control + window.modifiers.Alt = modifiers.Alt + window.modifiers.Meta = modifiers.Meta + window.modifiers.Super = modifiers.Super + window.modifiers.Hyper = modifiers.Hyper +} + +func (window *window) updateMousePosition (x, y int16) { + window.mousePosition = image.Pt(int(x), int(y)) +} diff --git a/go.mod b/go.mod index 2790075..edc3f72 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.20 require ( git.tebibyte.media/tomo/ggfx v0.4.0 - git.tebibyte.media/tomo/tomo v0.14.0 - git.tebibyte.media/tomo/typeset v0.3.0 + git.tebibyte.media/tomo/tomo v0.16.0 + git.tebibyte.media/tomo/typeset v0.4.0 git.tebibyte.media/tomo/xgbkb v1.0.1 github.com/jezek/xgb v1.1.0 github.com/jezek/xgbutil v0.0.0-20230603163917-04188eb39cf0 diff --git a/go.sum b/go.sum index 16d7e74..9b63816 100644 --- a/go.sum +++ b/go.sum @@ -9,12 +9,18 @@ git.tebibyte.media/tomo/tomo v0.13.0 h1:xQvEia1vDUfq3QkpU3De72z53ON+y8I6HMP8TDtc git.tebibyte.media/tomo/tomo v0.13.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY= git.tebibyte.media/tomo/tomo v0.14.0 h1:cMCOLjBEH9OtF+CODn0XFWa9liE0keMnLWs3t66I+Zw= git.tebibyte.media/tomo/tomo v0.14.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY= +git.tebibyte.media/tomo/tomo v0.15.0 h1:xjHPB+nq1M2myjFeRU79p3DvuIIG/94hKwFr/OB8cGY= +git.tebibyte.media/tomo/tomo v0.15.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY= +git.tebibyte.media/tomo/tomo v0.16.0 h1:5tgN0Uj+3LSMzvXRLNE8AxgRUOTc/GEw8+TEJKmVGtQ= +git.tebibyte.media/tomo/tomo v0.16.0/go.mod h1:lTwjpiHbP4UN/kFw+6FwhG600B+PMKVtMOr7wpd5IUY= git.tebibyte.media/tomo/typeset v0.1.0 h1:ZLwQzy51vUskjg1nB4Emjag8VXn3ki2jEkE19kwVQ4c= git.tebibyte.media/tomo/typeset v0.1.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= git.tebibyte.media/tomo/typeset v0.2.0 h1:7DcnB0sW12eL+MxkEMv99eVG2IQxsZHDDK6pz6VE1O8= git.tebibyte.media/tomo/typeset v0.2.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= git.tebibyte.media/tomo/typeset v0.3.0 h1:9koJzy0bguBHjlesrHpXK8odIVEMmQRBIFIRXDhv7Bk= git.tebibyte.media/tomo/typeset v0.3.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= +git.tebibyte.media/tomo/typeset v0.4.0 h1:JdTmbqSJlMtdvqlwXC5AseM1GgG4tyfWcqPFGkHFRpU= +git.tebibyte.media/tomo/typeset v0.4.0/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE= git.tebibyte.media/tomo/xgbkb v1.0.1/go.mod h1:P5Du0yo5hUsojchW08t+Mds0XPIJXwMi733ZfklzjRw= github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA= diff --git a/system.go b/system.go index 7c34052..d3745fd 100644 --- a/system.go +++ b/system.go @@ -2,6 +2,7 @@ package x import "image" import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/input" import "git.tebibyte.media/tomo/tomo/canvas" type boxSet map[anyBox] struct { } @@ -43,7 +44,12 @@ type anyBox interface { doLayout () setParent (parent) recursiveRedo () + canBeFocused () bool + boxUnder (image.Point) anyBox recalculateMinimumSize () + + handleMouseDown (input.Button) + handleMouseUp (input.Button) } func assertAnyBox (unknown tomo.Box) anyBox { @@ -86,6 +92,8 @@ func (window *window) invalidateDraw (box anyBox) { } func (window *window) invalidateLayout (box anyBox) { + // TODO: use a priority queue for this and have the value be the amount + // of parents a box has window.needLayout.Add(box) window.invalidateDraw(box) } @@ -97,6 +105,11 @@ func (window *window) focus (box anyBox) { window.invalidateDraw(box) } +func (this *window) boxUnder (point image.Point) anyBox { + if this.root == nil { return nil } + return this.root.boxUnder(point) +} + func (window *window) afterEvent () { if window.xCanvas == nil { return } diff --git a/textbox.go b/textbox.go index d02af74..4688f21 100644 --- a/textbox.go +++ b/textbox.go @@ -83,7 +83,7 @@ func (this *textBox) SetFace (face font.Face) { func (this *textBox) SetWrap (wrap bool) { if this.wrap == wrap { return } - this.wrap = wrap + this.drawer.SetWrap(wrap) this.recalculateMinimumSize() this.invalidateLayout() } @@ -126,12 +126,12 @@ func (this *textBox) recalculateMinimumSize () { this.drawer.Em().Round(), this.drawer.LineHeight().Round()) - textSize := this.normalizedLayoutBoundsSpace() + textSize := this.drawer.MinimumSize() if !this.hOverflow && !this.wrap { - minimum.X = textSize.Dx() + minimum.X = textSize.X } if !this.vOverflow { - minimum.Y = textSize.Dy() + minimum.Y = textSize.Y } minimum.X += this.padding.Horizontal() @@ -147,16 +147,16 @@ func (this *textBox) doLayout () { previousContentBounds := this.contentBounds innerBounds := this.InnerBounds() - if this.hOverflow && !this.wrap { - this.drawer.SetMaxWidth(0) - } else { - this.drawer.SetMaxWidth(innerBounds.Dx()) - } - if this.vOverflow { - this.drawer.SetMaxHeight(0) - } else { - this.drawer.SetMaxHeight(innerBounds.Dy()) - } + this.drawer.SetMaxWidth(innerBounds.Dx()) + this.drawer.SetMaxHeight(innerBounds.Dy()) + // if this.hOverflow && !this.wrap { + // this.drawer.SetMaxWidth(0) + // } else { + // } + // if this.vOverflow { + // this.drawer.SetMaxHeight(0) + // } else { + // } this.contentBounds = this.normalizedLayoutBoundsSpace().Sub(this.scroll) diff --git a/window.go b/window.go index 82295c7..85da758 100644 --- a/window.go +++ b/window.go @@ -38,6 +38,7 @@ type window struct { modifiers input.Modifiers mousePosition image.Point + drags [10]anyBox onClose event.FuncBroadcaster @@ -112,10 +113,10 @@ func (backend *Backend) newWindow ( // Connect(backend.x, window.xWindow.Id) // xevent.KeyReleaseFun(window.handleKeyRelease). // Connect(backend.x, window.xWindow.Id) - // xevent.ButtonPressFun(window.handleButtonPress). - // Connect(backend.x, window.xWindow.Id) - // xevent.ButtonReleaseFun(window.handleButtonRelease). - // Connect(backend.x, window.xWindow.Id) + xevent.ButtonPressFun(window.handleButtonPress). + Connect(backend.x, window.xWindow.Id) + xevent.ButtonReleaseFun(window.handleButtonRelease). + Connect(backend.x, window.xWindow.Id) // xevent.MotionNotifyFun(window.handleMotionNotify). // Connect(backend.x, window.xWindow.Id) // xevent.SelectionNotifyFun(window.handleSelectionNotify).