Overhauled mouse events

Everything gets an image.Point instead of an x y pair, and most
things now get modifiers.
This commit is contained in:
Sasha Koshka 2023-04-20 14:44:54 -04:00
parent eaee284aaf
commit 53f78cb0e7
12 changed files with 183 additions and 69 deletions

View File

@ -189,6 +189,7 @@ func (window *window) handleButtonPress (
point := image.Pt(int(buttonEvent.EventX), int(buttonEvent.EventY)) point := image.Pt(int(buttonEvent.EventX), int(buttonEvent.EventY))
insideWindow := point.In(window.canvas.Bounds()) insideWindow := point.In(window.canvas.Bounds())
scrolling := buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 scrolling := buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7
modifiers := window.modifiersFromState(buttonEvent.State)
if !insideWindow && window.shy && !scrolling { if !insideWindow && window.shy && !scrolling {
window.Close() window.Close()
@ -200,8 +201,8 @@ func (window *window) handleButtonPress (
sum.add(buttonEvent.Detail, window, buttonEvent.State) sum.add(buttonEvent.Detail, window, buttonEvent.State)
window.compressScrollSum(buttonEvent, &sum) window.compressScrollSum(buttonEvent, &sum)
child.HandleScroll ( child.HandleScroll (
point.X, point.Y, point, float64(sum.x), float64(sum.y),
float64(sum.x), float64(sum.y)) modifiers)
} }
} }
} else { } else {
@ -209,14 +210,13 @@ func (window *window) handleButtonPress (
window.system.drags[buttonEvent.Detail] = underneath window.system.drags[buttonEvent.Detail] = underneath
if child, ok := underneath.element.(tomo.MouseTarget); ok { if child, ok := underneath.element.(tomo.MouseTarget); ok {
child.HandleMouseDown ( child.HandleMouseDown (
point.X, point.Y, point, input.Button(buttonEvent.Detail),
input.Button(buttonEvent.Detail)) modifiers)
} }
callback := func (container tomo.MouseTargetContainer, child tomo.Element) { callback := func (container tomo.MouseTargetContainer, child tomo.Element) {
container.HandleChildMouseDown ( container.HandleChildMouseDown (
point.X, point.Y, point, input.Button(buttonEvent.Detail),
input.Button(buttonEvent.Detail), modifiers, child)
child)
} }
underneath.forMouseTargetContainers(callback) underneath.forMouseTargetContainers(callback)
} }
@ -230,20 +230,25 @@ func (window *window) handleButtonRelease (
) { ) {
buttonEvent := *event.ButtonReleaseEvent buttonEvent := *event.ButtonReleaseEvent
if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { return } if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { return }
modifiers := window.modifiersFromState(buttonEvent.State)
dragging := window.system.drags[buttonEvent.Detail] dragging := window.system.drags[buttonEvent.Detail]
if dragging != nil { if dragging != nil {
if child, ok := dragging.element.(tomo.MouseTarget); ok { if child, ok := dragging.element.(tomo.MouseTarget); ok {
child.HandleMouseUp ( child.HandleMouseUp (
image.Pt (
int(buttonEvent.EventX), int(buttonEvent.EventX),
int(buttonEvent.EventY), int(buttonEvent.EventY)),
input.Button(buttonEvent.Detail)) input.Button(buttonEvent.Detail),
modifiers)
} }
callback := func (container tomo.MouseTargetContainer, child tomo.Element) { callback := func (container tomo.MouseTargetContainer, child tomo.Element) {
container.HandleChildMouseUp ( container.HandleChildMouseUp (
image.Pt (
int(buttonEvent.EventX), int(buttonEvent.EventX),
int(buttonEvent.EventY), int(buttonEvent.EventY)),
input.Button(buttonEvent.Detail), input.Button(buttonEvent.Detail),
child) modifiers, child)
} }
dragging.forMouseTargetContainers(callback) dragging.forMouseTargetContainers(callback)
} }
@ -263,7 +268,7 @@ func (window *window) handleMotionNotify (
for _, child := range window.system.drags { for _, child := range window.system.drags {
if child == nil { continue } if child == nil { continue }
if child, ok := child.element.(tomo.MotionTarget); ok { if child, ok := child.element.(tomo.MotionTarget); ok {
child.HandleMotion(x, y) child.HandleMotion(image.Pt(x, y))
handled = true handled = true
} }
} }
@ -271,7 +276,7 @@ func (window *window) handleMotionNotify (
if !handled { if !handled {
child := window.system.childAt(image.Pt(x, y)) child := window.system.childAt(image.Pt(x, y))
if child, ok := child.element.(tomo.MotionTarget); ok { if child, ok := child.element.(tomo.MotionTarget); ok {
child.HandleMotion(x, y) child.HandleMotion(image.Pt(x, y))
} }
} }

View File

@ -99,11 +99,17 @@ type MouseTarget interface {
// HandleMouseDown is called when a mouse button is pressed down on this // HandleMouseDown is called when a mouse button is pressed down on this
// element. // element.
HandleMouseDown (x, y int, button input.Button) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers)
// HandleMouseUp is called when a mouse button is released that was // HandleMouseUp is called when a mouse button is released that was
// originally pressed down on this element. // originally pressed down on this element.
HandleMouseUp (x, y int, button input.Button) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers)
} }
// MouseTargetContainer represents an element that wants to know when one // MouseTargetContainer represents an element that wants to know when one
@ -115,11 +121,19 @@ type MouseTargetContainer interface {
// HandleMouseDown is called when a mouse button is pressed down on a // HandleMouseDown is called when a mouse button is pressed down on a
// child element. // child element.
HandleChildMouseDown (x, y int, button input.Button, child Element) HandleChildMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child Element)
// HandleMouseUp is called when a mouse button is released that was // HandleMouseUp is called when a mouse button is released that was
// originally pressed down on a child element. // originally pressed down on a child element.
HandleChildMouseUp (x, y int, button input.Button, child Element) HandleChildMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child Element)
} }
// MotionTarget represents an element that can receive mouse motion events. // MotionTarget represents an element that can receive mouse motion events.
@ -129,7 +143,7 @@ type MotionTarget interface {
// HandleMotion is called when the mouse is moved over this element, // HandleMotion is called when the mouse is moved over this element,
// or the mouse is moving while being held down and originally pressed // or the mouse is moving while being held down and originally pressed
// down on this element. // down on this element.
HandleMotion (x, y int) HandleMotion (position image.Point)
} }
// ScrollTarget represents an element that can receive mouse scroll events. // ScrollTarget represents an element that can receive mouse scroll events.
@ -138,7 +152,10 @@ type ScrollTarget interface {
// HandleScroll is called when the mouse is scrolled. The X and Y // HandleScroll is called when the mouse is scrolled. The X and Y
// direction of the scroll event are passed as deltaX and deltaY. // direction of the scroll event are passed as deltaX and deltaY.
HandleScroll (x, y int, deltaX, deltaY float64) HandleScroll (
position image.Point,
deltaX, deltaY float64,
modifiers input.Modifiers)
} }
// Flexible represents an element who's preferred minimum height can change in // Flexible represents an element who's preferred minimum height can change in
@ -158,7 +175,8 @@ type Flexible interface {
// minimum size that the element may be resized to. // minimum size that the element may be resized to.
// //
// It is important to note that if a parent container checks for // It is important to note that if a parent container checks for
// flexible chilren, it itself will likely need to be flexible. // flexible chilren, it itself will likely need to be either scrollable
// or flexible.
FlexibleHeightFor (width int) int FlexibleHeightFor (width int) int
} }

View File

@ -177,7 +177,11 @@ func (element *Button) HandleFocusChange () {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Button) HandleMouseDown (x, y int, button input.Button) { func (element *Button) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
@ -185,10 +189,14 @@ func (element *Button) HandleMouseDown (x, y int, button input.Button) {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Button) HandleMouseUp (x, y int, button input.Button) { func (element *Button) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
element.pressed = false element.pressed = false
within := image.Point { x, y }.In(element.entity.Bounds()) within := position.In(element.entity.Bounds())
if element.Enabled() && within && element.onClick != nil { if element.Enabled() && within && element.onClick != nil {
element.onClick() element.onClick()
} }

View File

@ -130,18 +130,26 @@ func (element *Checkbox) HandleFocusChange () {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) { func (element *Checkbox) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
element.pressed = true element.pressed = true
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Checkbox) HandleMouseUp (x, y int, button input.Button) { func (element *Checkbox) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button != input.ButtonLeft || !element.pressed { return } if button != input.ButtonLeft || !element.pressed { return }
element.pressed = false element.pressed = false
within := image.Point { x, y }.In(element.entity.Bounds()) within := position.In(element.entity.Bounds())
if within { if within {
element.checked = !element.checked element.checked = !element.checked
} }

View File

@ -124,13 +124,26 @@ func (element *Directory) Layout () {
} }
} }
func (element *Directory) HandleMouseDown (x, y int, button input.Button) { func (element *Directory) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
element.selectNone() element.selectNone()
} }
func (element *Directory) HandleMouseUp (x, y int, button input.Button) { } func (element *Directory) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) { }
func (element *Directory) HandleChildMouseDown (x, y int, button input.Button, child tomo.Element) { func (element *Directory) HandleChildMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child tomo.Element,
) {
element.selectNone() element.selectNone()
if child, ok := child.(tomo.Selectable); ok { if child, ok := child.(tomo.Selectable); ok {
index := element.entity.IndexOf(child) index := element.entity.IndexOf(child)
@ -138,7 +151,12 @@ func (element *Directory) HandleChildMouseDown (x, y int, button input.Button, c
} }
} }
func (element *Directory) HandleChildMouseUp (int, int, input.Button, tomo.Element) { } func (element *Directory) HandleChildMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child tomo.Element,
) { }
func (element *Directory) HandleChildFlexibleHeightChange (child tomo.Flexible) { func (element *Directory) HandleChildFlexibleHeightChange (child tomo.Flexible) {
element.updateMinimumSize() element.updateMinimumSize()

View File

@ -165,7 +165,11 @@ func (element *File) SetEnabled (enabled bool) {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *File) HandleMouseDown (x, y int, button input.Button) { func (element *File) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
if !element.entity.Focused() { element.Focus() } if !element.entity.Focused() { element.Focus() }
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
@ -173,10 +177,14 @@ func (element *File) HandleMouseDown (x, y int, button input.Button) {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *File) HandleMouseUp (x, y int, button input.Button) { func (element *File) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
element.pressed = false element.pressed = false
within := image.Point { x, y }.In(element.entity.Bounds()) within := position.In(element.entity.Bounds())
if time.Since(element.lastClick) < element.config.DoubleClickDelay() { if time.Since(element.lastClick) < element.config.DoubleClickDelay() {
if element.Enabled() && within && element.onChoose != nil { if element.Enabled() && within && element.onChoose != nil {
element.onChoose() element.onChoose()

View File

@ -87,7 +87,12 @@ func (element *List) Layout () {
} }
} }
func (element *List) HandleChildMouseDown (x, y int, button input.Button, child tomo.Element) { func (element *List) HandleChildMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child tomo.Element,
) {
if child, ok := child.(tomo.Selectable); ok { if child, ok := child.(tomo.Selectable); ok {
index := element.entity.IndexOf(child) index := element.entity.IndexOf(child)
if element.selected == index { return } if element.selected == index { return }
@ -99,7 +104,12 @@ func (element *List) HandleChildMouseDown (x, y int, button input.Button, child
} }
} }
func (element *List) HandleChildMouseUp (int, int, input.Button, tomo.Element) { } func (element *List) HandleChildMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
child tomo.Element,
) { }
func (element *List) HandleChildFlexibleHeightChange (child tomo.Flexible) { func (element *List) HandleChildFlexibleHeightChange (child tomo.Flexible) {
element.updateMinimumSize() element.updateMinimumSize()

View File

@ -2,7 +2,7 @@ package elements
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
// import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" import "git.tebibyte.media/sashakoshka/tomo/default/config"
@ -179,8 +179,9 @@ func (element *Scroll) HandleChildScrollBoundsChange (tomo.Scrollable) {
} }
func (element *Scroll) HandleScroll ( func (element *Scroll) HandleScroll (
x, y int, position image.Point,
deltaX, deltaY float64, deltaX, deltaY float64,
modifiers input.Modifiers,
) { ) {
horizontal, vertical := element.child.ScrollAxes() horizontal, vertical := element.child.ScrollAxes()
if !horizontal { deltaX = 0 } if !horizontal { deltaX = 0 }

View File

@ -82,18 +82,21 @@ func (element *ScrollBar) Draw (destination canvas.Canvas) {
element.bar) element.bar)
} }
func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) { func (element *ScrollBar) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
velocity := element.config.ScrollVelocity() velocity := element.config.ScrollVelocity()
point := image.Pt(x, y)
if point.In(element.bar) { if position.In(element.bar) {
// the mouse is pressed down within the bar's handle // the mouse is pressed down within the bar's handle
element.dragging = true element.dragging = true
element.entity.Invalidate() element.entity.Invalidate()
element.dragOffset = element.dragOffset =
point.Sub(element.bar.Min). position.Sub(element.bar.Min).
Add(element.entity.Bounds().Min) Add(element.entity.Bounds().Min)
element.dragTo(point) element.dragTo(position)
} else { } else {
// the mouse is pressed down within the bar's gutter // the mouse is pressed down within the bar's gutter
switch button { switch button {
@ -102,7 +105,7 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
// the middle of the handle // the middle of the handle
element.dragging = true element.dragging = true
element.dragOffset = element.fallbackDragOffset() element.dragOffset = element.fallbackDragOffset()
element.dragTo(point) element.dragTo(position)
case input.ButtonMiddle: case input.ButtonMiddle:
// page up/down on middle click // page up/down on middle click
@ -112,7 +115,7 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
} else { } else {
viewport = element.viewportBounds.Dx() viewport = element.viewportBounds.Dx()
} }
if element.isAfterHandle(point) { if element.isAfterHandle(position) {
element.scrollBy(viewport) element.scrollBy(viewport)
} else { } else {
element.scrollBy(-viewport) element.scrollBy(-viewport)
@ -120,7 +123,7 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
case input.ButtonRight: case input.ButtonRight:
// inch up/down on right click // inch up/down on right click
if element.isAfterHandle(point) { if element.isAfterHandle(position) {
element.scrollBy(velocity) element.scrollBy(velocity)
} else { } else {
element.scrollBy(-velocity) element.scrollBy(-velocity)
@ -129,20 +132,28 @@ func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
} }
} }
func (element *ScrollBar) HandleMouseUp (x, y int, button input.Button) { func (element *ScrollBar) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if element.dragging { if element.dragging {
element.dragging = false element.dragging = false
element.entity.Invalidate() element.entity.Invalidate()
} }
} }
func (element *ScrollBar) HandleMotion (x, y int) { func (element *ScrollBar) HandleMotion (position image.Point) {
if element.dragging { if element.dragging {
element.dragTo(image.Pt(x, y)) element.dragTo(position)
} }
} }
func (element *ScrollBar) HandleScroll (x, y int, deltaX, deltaY float64) { func (element *ScrollBar) HandleScroll (
position image.Point,
deltaX, deltaY float64,
modifiers input.Modifiers,
) {
if element.vertical { if element.vertical {
element.scrollBy(int(deltaY)) element.scrollBy(int(deltaY))
} else { } else {

View File

@ -111,12 +111,16 @@ func (element *slider) HandleFocusChange () {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *slider) HandleMouseDown (x, y int, button input.Button) { func (element *slider) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
if button == input.ButtonLeft { if button == input.ButtonLeft {
element.dragging = true element.dragging = true
element.value = element.valueFor(x, y) element.value = element.valueFor(position.X, position.Y)
if element.onSlide != nil { if element.onSlide != nil {
element.onSlide() element.onSlide()
} }
@ -124,7 +128,11 @@ func (element *slider) HandleMouseDown (x, y int, button input.Button) {
} }
} }
func (element *slider) HandleMouseUp (x, y int, button input.Button) { func (element *slider) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button != input.ButtonLeft || !element.dragging { return } if button != input.ButtonLeft || !element.dragging { return }
element.dragging = false element.dragging = false
if element.onRelease != nil { if element.onRelease != nil {
@ -133,10 +141,10 @@ func (element *slider) HandleMouseUp (x, y int, button input.Button) {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *slider) HandleMotion (x, y int) { func (element *slider) HandleMotion (position image.Point) {
if element.dragging { if element.dragging {
element.dragging = true element.dragging = true
element.value = element.valueFor(x, y) element.value = element.valueFor(position.X, position.Y)
if element.onSlide != nil { if element.onSlide != nil {
element.onSlide() element.onSlide()
} }
@ -144,7 +152,11 @@ func (element *slider) HandleMotion (x, y int) {
} }
} }
func (element *slider) HandleScroll (x, y int, deltaX, deltaY float64) { } func (element *slider) HandleScroll (
position image.Point,
deltaX, deltaY float64,
modifiers input.Modifiers,
) { }
func (element *slider) HandleKeyDown (key input.Key, modifiers input.Modifiers) { func (element *slider) HandleKeyDown (key input.Key, modifiers input.Modifiers) {
switch key { switch key {

View File

@ -101,19 +101,26 @@ func (element *Switch) HandleFocusChange () {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Switch) HandleMouseDown (x, y int, button input.Button) { func (element *Switch) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
element.pressed = true element.pressed = true
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *Switch) HandleMouseUp (x, y int, button input.Button) { func (element *Switch) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button != input.ButtonLeft || !element.pressed { return } if button != input.ButtonLeft || !element.pressed { return }
element.pressed = false element.pressed = false
within := image.Point { x, y }. within := position.In(element.entity.Bounds())
In(element.entity.Bounds())
if within { if within {
element.checked = !element.checked element.checked = !element.checked
} }

View File

@ -143,12 +143,16 @@ func (element *TextBox) HandleFocusChange () {
element.entity.Invalidate() element.entity.Invalidate()
} }
func (element *TextBox) HandleMouseDown (x, y int, button input.Button) { func (element *TextBox) HandleMouseDown (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if !element.Enabled() { return } if !element.Enabled() { return }
element.Focus() element.Focus()
if button == input.ButtonLeft { if button == input.ButtonLeft {
runeIndex := element.atPosition(image.Pt(x, y)) runeIndex := element.atPosition(position)
if runeIndex == -1 { return } if runeIndex == -1 { return }
if time.Since(element.lastClick) < element.config.DoubleClickDelay() { if time.Since(element.lastClick) < element.config.DoubleClickDelay() {
@ -164,19 +168,19 @@ func (element *TextBox) HandleMouseDown (x, y int, button input.Button) {
} }
} }
func (element *TextBox) HandleMotion (x, y int) { func (element *TextBox) HandleMotion (position image.Point) {
if !element.Enabled() { return } if !element.Enabled() { return }
switch element.dragging { switch element.dragging {
case 1: case 1:
runeIndex := element.atPosition(image.Pt(x, y)) runeIndex := element.atPosition(position)
if runeIndex > -1 { if runeIndex > -1 {
element.dot.End = runeIndex element.dot.End = runeIndex
element.entity.Invalidate() element.entity.Invalidate()
} }
case 2: case 2:
runeIndex := element.atPosition(image.Pt(x, y)) runeIndex := element.atPosition(position)
if runeIndex > -1 { if runeIndex > -1 {
if runeIndex < element.dot.Start { if runeIndex < element.dot.Start {
element.dot.End = element.dot.End =
@ -213,7 +217,11 @@ func (element *TextBox) atPosition (position image.Point) int {
fixedutil.Pt(position.Sub(offset).Add(textBoundsMin))) fixedutil.Pt(position.Sub(offset).Add(textBoundsMin)))
} }
func (element *TextBox) HandleMouseUp (x, y int, button input.Button) { func (element *TextBox) HandleMouseUp (
position image.Point,
button input.Button,
modifiers input.Modifiers,
) {
if button == input.ButtonLeft { if button == input.ButtonLeft {
element.dragging = 0 element.dragging = 0
} }