diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2025-07-04 08:45:38 -0400 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-08-20 15:57:22 +0200 |
| commit | 9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a (patch) | |
| tree | f8e8f8e3e989f205c9a43370e03c43bc24dd27bb | |
| parent | bb3f387c9cbb92efea0bcfec3f4f5c56e7da2ea3 (diff) | |
| download | muhqs-game-9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a.tar.gz muhqs-game-9e3e8952f77fbd6b35c4cb0c5c1bf9cc26f9e35a.zip | |
ui: include touch inputs in the input queue
| -rw-r--r-- | go/ui/collection.go | 72 | ||||
| -rw-r--r-- | go/ui/touchManager.go | 58 | ||||
| -rw-r--r-- | go/ui/update.go | 61 |
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 |
