aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2025-07-04 08:45:38 -0400
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-20 15:57:22 +0200
commit9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a (patch)
treef8e8f8e3e989f205c9a43370e03c43bc24dd27bb
parentbb3f387c9cbb92efea0bcfec3f4f5c56e7da2ea3 (diff)
downloadmuhqs-game-9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a.tar.gz
muhqs-game-9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a.zip
ui: include touch inputs in the input queue
-rw-r--r--go/ui/collection.go72
-rw-r--r--go/ui/touchManager.go58
-rw-r--r--go/ui/update.go61
3 files changed, 94 insertions, 97 deletions
diff --git a/go/ui/collection.go b/go/ui/collection.go
index 9ccaa176..dfd5fbb5 100644
--- a/go/ui/collection.go
+++ b/go/ui/collection.go
@@ -133,49 +133,8 @@ func (c *Collection) switchFocus(fcsbl Widget) {
}
}
-func (c *Collection) handleTouchInputs() error {
- for i := len(TouchManager.Taps) - 1; i >= 0; i-- {
- tap := TouchManager.Taps[i]
- for i := len(c.widgets) - 1; i >= 0; i-- {
- w := c.widgets[i]
- if !w.Contains(tap.X, tap.Y) {
- continue
- }
- // Skip collections their widgets will receive left over input
- // during the following updateWidgets call,
- if _, ok := w.(CollectionInterface); ok {
- continue
- }
- if w.IsClickable() {
- w.Click(tap.X, tap.Y)
- }
- if w.IsFocusable() {
- c.switchFocus(w)
- } else if c.focused != nil {
- c.switchFocus(nil)
- }
- log.Printf("consume Tap for %T %v\n", w, w)
- TouchManager.consumeTap(i)
- break
- }
- }
-
- // TODO: handle pinching
- if TouchManager.Pan != nil {
- pan := TouchManager.Pan
- iX, iY := pan.Incremental()
-
- w := c.FindWidgetAt(int(pan.originX), (pan.originY))
- if w != nil && w.IsScrollable() {
- w.Scroll(int(iX)/10, int(iY)/10)
- }
- }
-
- return nil
-}
-
func passInput(ev InputEvent, textInput *TextInput) {
- textInput.AddInput(ev.ctx.(TextCtx).in)
+ textInput.AddInput(ev.Ctx.(TextCtx).in)
textInput.HandleKey()
}
@@ -198,38 +157,35 @@ func (c *Collection) updateWidgets() error {
func (c *Collection) Update() error {
defer c.updateWidgets()
- if err := c.handleTouchInputs(); err != nil {
- return err
- }
-
for i := len(Input) - 1; i >= 0; i-- {
ev := Input[i]
- w := c.FindWidgetAt(ev.x, ev.y)
+ w := c.FindWidgetAt(ev.X, ev.Y)
if w != nil {
- switch ev.kind {
- case Click:
- if w.IsClickable() && ev.ctx.(ClickCtx).btn == ebiten.MouseButtonLeft {
- w.Click(ev.x, ev.y)
- consumeInput(i)
+ switch ev.Kind {
+ case Click, Tap:
+ // Only consider taps or left clicks
+ if w.IsClickable() && (ev.Kind != Click || ev.Ctx.(ClickCtx).Btn == ebiten.MouseButtonLeft) {
+ w.Click(ev.X, ev.Y)
+ ConsumeInput(i)
}
// TODO: switch focus
- case Scroll:
+ case Scroll, Pan:
if w.IsScrollable() {
- s := ev.ctx.(ScrollCtx)
+ s := ev.Ctx.(DistanceCtx)
w.Scroll(s.scrollX, s.scrollY)
- consumeInput(i)
+ ConsumeInput(i)
}
case Text:
if txt, ok := w.(*TextInput); ok {
passInput(ev, txt)
- consumeInput(i)
+ ConsumeInput(i)
}
}
// handle focused text input
- } else if ev.kind == Text {
+ } else if ev.Kind == Text {
if txt, ok := c.focused.(*TextInput); ok {
passInput(ev, txt)
- consumeInput(i)
+ ConsumeInput(i)
}
}
}
diff --git a/go/ui/touchManager.go b/go/ui/touchManager.go
index ef4448d5..462b9934 100644
--- a/go/ui/touchManager.go
+++ b/go/ui/touchManager.go
@@ -30,13 +30,15 @@ const (
_pinch
)
-var TouchTypes = struct {
- Tap TouchType
- Pan TouchType
- Pinch TouchType
-}{Tap: _tap,
- Pan: _pan,
- Pinch: _pinch}
+var touchTypes = struct {
+ tap TouchType
+ pan TouchType
+ pinch TouchType
+}{
+ tap: _tap,
+ pan: _pan,
+ pinch: _pinch,
+}
// TouchInput is a "manager" for touch input and provides logical encapsulations of
// touch interactions like panning, pinching, and tapping.
@@ -44,16 +46,16 @@ type TouchInput struct {
touchIDs []ebiten.TouchID
touches map[ebiten.TouchID]*touch
- Pinch *pinch
- Pan *pan
- Taps []Tap
+ pinch *pinch
+ pan *pan
+ taps []tap
}
-func (t *TouchInput) consumeTap(i int) {
- if len(t.Taps) == 1 {
- t.Taps = []Tap{}
+func (t *TouchInput) consumetap(i int) {
+ if len(t.taps) == 1 {
+ t.taps = []tap{}
} else {
- t.Taps[i] = t.Taps[len(t.Taps)-1]
+ t.taps[i] = t.taps[len(t.taps)-1]
}
}
@@ -83,26 +85,26 @@ func hypotenuse(xa, ya, xb, yb int) float64 {
// have one touch, held longer than (N)ms, then we have a pan.
// If we have quickly released presses, then they are represented in taps.
func (in *TouchInput) Update() error {
- in.Taps = []Tap{}
+ in.taps = []tap{}
// What's gone?
for id, t := range in.touches {
if inpututil.IsTouchJustReleased(id) {
- if in.Pinch != nil && (id == in.Pinch.id1 || id == in.Pinch.id2) {
+ if in.pinch != nil && (id == in.pinch.id1 || id == in.pinch.id2) {
// FIXME: what about this frame's movement?
- in.Pinch = nil
+ in.pinch = nil
}
- if in.Pan != nil && id == in.Pan.id {
+ if in.pan != nil && id == in.pan.id {
// FIXME: what about this frame's movement?
- in.Pan = nil
+ in.pan = nil
}
// If this one has not been touched long, or moved far, then it's a tap.
diff := hypotenuse(t.originX, t.originY, t.currX, t.currY)
if !t.wasPinch && !t.isPan && (t.duration <= 75 || diff < 5) {
- in.Taps = append(in.Taps, Tap{
- X: t.currX,
- Y: t.currY,
+ in.taps = append(in.taps, tap{
+ x: t.currX,
+ y: t.currY,
})
}
@@ -137,10 +139,10 @@ func (in *TouchInput) Update() error {
t1, t2 := in.touches[id1], in.touches[id2]
originDiff := hypotenuse(t1.originX, t1.originY, t2.originX, t2.originY)
currDiff := hypotenuse(t1.currX, t1.currY, t2.currX, t2.currY)
- if in.Pinch == nil && in.Pan == nil && math.Abs(originDiff-currDiff) > 3 {
+ if in.pinch == nil && in.pan == nil && math.Abs(originDiff-currDiff) > 3 {
t1.wasPinch = true
t2.wasPinch = true
- in.Pinch = &pinch{
+ in.pinch = &pinch{
id1: id1,
id2: id2,
originH: originDiff,
@@ -151,11 +153,11 @@ func (in *TouchInput) Update() error {
// Potential pan.
id := in.touchIDs[0]
t := in.touches[id]
- if !t.wasPinch && in.Pan == nil && in.Pinch == nil {
+ if !t.wasPinch && in.pan == nil && in.pinch == nil {
diff := math.Abs(hypotenuse(t.originX, t.originY, t.currX, t.currY))
if diff > 0.25 {
t.isPan = true
- in.Pan = &pan{
+ in.pan = &pan{
id: id,
originX: t.originX,
originY: t.originY,
@@ -223,6 +225,6 @@ func (p *pan) Incremental() (float64, float64) {
return float64(deltaX), float64(deltaY)
}
-type Tap struct {
- X, Y int
+type tap struct {
+ x, y int
}
diff --git a/go/ui/update.go b/go/ui/update.go
index fdb0f19e..81d060ff 100644
--- a/go/ui/update.go
+++ b/go/ui/update.go
@@ -7,36 +7,55 @@ import (
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
+// InputType distinguishes the different supported kinds of input.
type InputType int
const (
+ // Click represents a mouse click.
Click InputType = iota + 1
+ // Scroll represents a mouse wheel event.
Scroll
+ // Text represents any keyboard input.
Text
+ // Tap represents a short touch input.
+ Tap
+ // Pan represents a longer moving touch input.
+ Pan
+ // Pinch represents a moving multi touch input.
+ Pinch
)
+// InputEvent contains all information associated with an occurred user input.
type InputEvent struct {
- kind InputType
- x, y int
- ctx any
+ Kind InputType
+ // X and Y coordinates the event occured.
+ X, Y int
+ // Ctx contains additional information for the event.
+ Ctx any
}
+// ClickCtx contains the pressed mouse button.
type ClickCtx struct {
- btn ebiten.MouseButton
+ Btn ebiten.MouseButton
}
-type ScrollCtx struct {
+// DistanceCtx contains the input's distance.
+// Scroll and Pan events contain a DistanceCtx.
+type DistanceCtx struct {
scrollX, scrollY int
}
+// TextCtx contains the inputted runes.
type TextCtx struct {
in []rune
}
-// Input queue
+// Input contains all occurred input events since the last call to ui.Update.
+// The input events should be handled in reverse order to prevent modify during iteration errors.
var Input []InputEvent
-func consumeInput(i int) {
+// ConsumeInput removes the i-ths input from the quehe.
+func ConsumeInput(i int) {
log.Println("consume input", i)
if len(Input) == 1 {
Input = []InputEvent{}
@@ -45,29 +64,49 @@ func consumeInput(i int) {
}
}
-// Update all ui singletons and collect all occurred input
+// AppendInput appends an input event to the input queue.
+func AppendInput(ev InputEvent) {
+ Input = append(Input, ev)
+}
+
+// Update all ui singletons and collect all occurred input.
func Update() error {
Input = []InputEvent{}
if err := TouchManager.Update(); err != nil {
return err
}
+ for _, tap := range TouchManager.taps {
+ AppendInput(InputEvent{Tap, tap.x, tap.y, nil})
+ }
+
+ if TouchManager.pan != nil {
+ pan := TouchManager.pan
+ iX, iY := pan.Incremental()
+ AppendInput(InputEvent{Pan, pan.originX, pan.originY, DistanceCtx{int(iX), int(iY)}})
+
+ }
+
+ // TODO: handle pinching
+ if TouchManager.pinch != nil {
+ }
+
// TODO: make hover detector a singleton
x, y := ebiten.CursorPosition()
scrollX, scrollY := ebiten.Wheel()
if scrollX != 0 || scrollY != 0 {
- Input = append(Input, InputEvent{Scroll, x, y, ScrollCtx{int(scrollX), int(scrollY)}})
+ AppendInput(InputEvent{Scroll, x, y, DistanceCtx{int(scrollX), int(scrollY)}})
}
var in []rune
in = ebiten.AppendInputChars(in)
if len(in) > 0 {
- Input = append(Input, InputEvent{Text, x, y, TextCtx{in}})
+ AppendInput(InputEvent{Text, x, y, TextCtx{in}})
}
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
- Input = append(Input, InputEvent{Click, x, y, ClickCtx{ebiten.MouseButtonLeft}})
+ AppendInput(InputEvent{Click, x, y, ClickCtx{ebiten.MouseButtonLeft}})
}
return nil