Add List.SetConfirmedFunc

This commit is contained in:
Trevor Slocum 2024-10-09 13:21:50 -07:00
parent af0a6c2c30
commit e52e0e8692
4 changed files with 138 additions and 29 deletions

12
go.mod
View file

@ -5,18 +5,18 @@ go 1.22.0
toolchain go1.23.0
require (
github.com/hajimehoshi/ebiten/v2 v2.7.10
github.com/hajimehoshi/ebiten/v2 v2.8.1
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195
golang.org/x/image v0.20.0
golang.org/x/image v0.21.0
)
require (
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect
github.com/ebitengine/gomobile v0.0.0-20241001034212-22433622d8a5 // indirect
github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/purego v0.7.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/jezek/xgb v1.1.1 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
)

30
go.sum
View file

@ -1,26 +1,28 @@
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM=
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY=
github.com/ebitengine/gomobile v0.0.0-20241001034212-22433622d8a5 h1:vAmkJvgX1ukQ772M7yxWCmSfDwr8vLAZTWlJqM1ox4A=
github.com/ebitengine/gomobile v0.0.0-20241001034212-22433622d8a5/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY=
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4=
github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA=
github.com/hajimehoshi/ebiten/v2 v2.7.10 h1:fsVukQdPDUlalSSpFkuszTy0cK2DL0fxFoSnTVdlmAM=
github.com/hajimehoshi/ebiten/v2 v2.7.10/go.mod h1:Ulbq5xDmdx47P24EJ+Mb31Zps7vQq+guieG9mghQUaA=
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
github.com/hajimehoshi/ebiten/v2 v2.8.1 h1:6n6ZXnbeSCZccdqrH7s9Ut+dll9TEostUqbc72Tis/g=
github.com/hajimehoshi/ebiten/v2 v2.8.1/go.mod h1:SXx/whkvpfsavGo6lvZykprerakl+8Uo1X8d2U5aAnA=
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 h1:Vdz2cBh5Fw2MYHWi3ED2PraDQaWEUhNCr1XFHrP4N5A=
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195/go.mod h1:1Vk0LDW6jG5cGc2D9RQUxHaE0vYhTvIwSo9mOL6K4/U=
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e h1:ZAvbj5hI/G/EbAYAcj4yCXUNiFKefEhH0qfImDDD0/8=
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=

View file

@ -1,12 +1,28 @@
package etk
import "github.com/hajimehoshi/ebiten/v2"
import (
"time"
// Shortcuts represents a keyboard shortcut configuration.
"github.com/hajimehoshi/ebiten/v2"
)
// Shortcuts represents the keyboard, mouse and gamepad input configurations.
type Shortcuts struct {
DoubleClickThreshold time.Duration
MoveLeftKeyboard []ebiten.Key
MoveRightKeyboard []ebiten.Key
MoveDownKeyboard []ebiten.Key
MoveUpKeyboard []ebiten.Key
MoveLeftGamepad []ebiten.StandardGamepadButton
MoveRightGamepad []ebiten.StandardGamepadButton
MoveDownGamepad []ebiten.StandardGamepadButton
MoveUpGamepad []ebiten.StandardGamepadButton
ConfirmKeyboard []ebiten.Key
ConfirmMouse []ebiten.MouseButton
ConfirmGamepad []ebiten.GamepadButton
ConfirmGamepad []ebiten.StandardGamepadButton
// A sentinel rune value may be set for the confirm and back actions.
// This allows working around on-screen keyboard issues on Android.
@ -14,9 +30,21 @@ type Shortcuts struct {
BackRune rune
}
// Bindings is the current keyboard shortcut configuration.
// Bindings is the current keyboard, mouse and gamepad input configurations.
var Bindings = &Shortcuts{
DoubleClickThreshold: 200 * time.Millisecond,
MoveLeftKeyboard: []ebiten.Key{ebiten.KeyLeft},
MoveRightKeyboard: []ebiten.Key{ebiten.KeyRight},
MoveDownKeyboard: []ebiten.Key{ebiten.KeyDown},
MoveUpKeyboard: []ebiten.Key{ebiten.KeyUp},
MoveLeftGamepad: []ebiten.StandardGamepadButton{ebiten.StandardGamepadButtonLeftLeft},
MoveRightGamepad: []ebiten.StandardGamepadButton{ebiten.StandardGamepadButtonLeftRight},
MoveDownGamepad: []ebiten.StandardGamepadButton{ebiten.StandardGamepadButtonLeftBottom},
MoveUpGamepad: []ebiten.StandardGamepadButton{ebiten.StandardGamepadButtonLeftTop},
ConfirmKeyboard: []ebiten.Key{ebiten.KeyEnter, ebiten.KeyKPEnter},
ConfirmMouse: []ebiten.MouseButton{ebiten.MouseButtonLeft, ebiten.MouseButtonRight},
ConfirmGamepad: []ebiten.GamepadButton{ebiten.GamepadButton0},
ConfirmGamepad: []ebiten.StandardGamepadButton{ebiten.StandardGamepadButtonRightBottom},
}

87
list.go
View file

@ -5,6 +5,7 @@ import (
"image/color"
"math"
"sync"
"time"
"github.com/hajimehoshi/ebiten/v2"
)
@ -28,12 +29,15 @@ const (
type List struct {
rect image.Rectangle
grid *Grid
focused bool
itemHeight int
highlightColor color.RGBA
maxY int
selectionMode SelectionMode
selectedX, selectedY int
selectedTime time.Time
selectedFunc func(index int) (accept bool)
confirmedFunc func(index int)
items [][]Widget
offset int
recreateGrid bool
@ -126,7 +130,7 @@ func (l *List) Focus() bool {
l.Lock()
defer l.Unlock()
return l.grid.Focus()
return l.focused
}
// SetFocus sets the focus state of the widget.
@ -134,7 +138,8 @@ func (l *List) SetFocus(focus bool) (accept bool) {
l.Lock()
defer l.Unlock()
return l.grid.SetFocus(focus)
l.focused = focus
return true
}
// Visible returns the visibility of the widget.
@ -267,6 +272,15 @@ func (l *List) SetSelectedFunc(f func(index int) (accept bool)) {
l.selectedFunc = f
}
// SetConfirmedFunc sets a handler which is called when the list selection is confirmed.
// Providing a nil function value will remove the existing handler (if set).
func (l *List) SetConfirmedFunc(f func(index int)) {
l.Lock()
defer l.Unlock()
l.confirmedFunc = f
}
// Children returns the children of the widget. Children are drawn in the
// order they are returned. Keyboard and mouse events are passed to children
// in reverse order.
@ -334,6 +348,53 @@ func (l *List) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error)
l.Lock()
defer l.Unlock()
if r == 0 {
// Handle confirmation.
for _, confirmKey := range Bindings.ConfirmKeyboard {
if key == confirmKey {
confirmedFunc := l.confirmedFunc
if confirmedFunc != nil {
l.Unlock()
confirmedFunc(l.selectedY)
l.Lock()
}
return true, nil
}
}
// Handle movement.
move := func(x int, y int) {
y = l.selectedY + y
if y >= 0 && y <= l.maxY {
l.selectedY = y
}
}
for _, leftKey := range Bindings.MoveLeftKeyboard {
if key == leftKey {
move(-1, 0)
return true, nil
}
}
for _, rightKey := range Bindings.MoveRightKeyboard {
if key == rightKey {
move(1, 0)
return true, nil
}
}
for _, downKey := range Bindings.MoveDownKeyboard {
if key == downKey {
move(0, 1)
return true, nil
}
}
for _, upKey := range Bindings.MoveUpKeyboard {
if key == upKey {
move(0, -1)
return true, nil
}
}
}
return l.grid.HandleKeyboard(key, r)
}
@ -393,12 +454,30 @@ func (l *List) HandleMouse(cursor image.Point, pressed bool, clicked bool) (hand
if selected >= 0 && selected <= l.maxY {
lastSelected := l.selectedY
l.selectedY = selected
if l.selectedFunc != nil {
accept := l.selectedFunc(l.selectedY)
selectedFunc := l.selectedFunc
if selectedFunc != nil {
l.Unlock()
accept := selectedFunc(l.selectedY)
l.Lock()
if !accept {
l.selectedY = lastSelected
return true, nil
}
}
if selected == lastSelected && time.Since(l.selectedTime) <= Bindings.DoubleClickThreshold {
confirmedFunc := l.confirmedFunc
if confirmedFunc != nil {
l.Unlock()
confirmedFunc(l.selectedY)
l.Lock()
}
l.selectedTime = time.Time{}
return true, nil
}
l.selectedTime = time.Now()
}
return true, nil
}