Add List
This commit is contained in:
parent
84765fcd5a
commit
801d4c27a0
7 changed files with 303 additions and 21 deletions
|
@ -22,6 +22,7 @@
|
|||
- Frame: Widget container. All child widgets are displayed at once. Child widgets are not repositioned by default.
|
||||
- Grid: Highly customizable cell-based layout. Each widget added to the Grid may span multiple cells.
|
||||
- Input: Text input widget. The Input widget is simply a Text widget that also accepts user input.
|
||||
- List: A list of widgets as selectable items.
|
||||
- Text: Text display widget.
|
||||
- Window: Widget paging mechanism. Only one widget added to a window is displayed at a time.
|
||||
|
||||
|
|
17
doc.go
17
doc.go
|
@ -8,13 +8,14 @@ based on official widgets.
|
|||
|
||||
The following official widgets are available:
|
||||
|
||||
Box - Building block for creating other widgets.
|
||||
Button - Clickable button.
|
||||
Flex - Flexible stack-based layout. Each Flex widget may be oriented horizontally or vertically.
|
||||
Frame - Widget container. All child widgets are displayed at once. Child widgets are not repositioned by default.
|
||||
Grid - Highly customizable cell-based layout. Widgets added to the Grid may span multiple cells.
|
||||
Input - Text input widget. The Input widget is simply a Text widget that also accepts user input.
|
||||
Text - Text display widget.
|
||||
Window - Widget paging mechanism. Only one widget added to a window is displayed at a time.
|
||||
Box - Building block for creating other widgets.
|
||||
Button - Clickable button.
|
||||
Flex - Flexible stack-based layout. Each Flex widget may be oriented horizontally or vertically.
|
||||
Frame - Widget container. All child widgets are displayed at once. Child widgets are not repositioned by default.
|
||||
Grid - Highly customizable cell-based layout. Widgets added to the Grid may span multiple cells.
|
||||
Input - Text input widget. The Input widget is simply a Text widget that also accepts user input.
|
||||
List - A list of widgets as selectable items.
|
||||
Text - Text display widget.
|
||||
Window - Widget paging mechanism. Only one widget added to a window is displayed at a time.
|
||||
*/
|
||||
package etk
|
||||
|
|
4
go.mod
4
go.mod
|
@ -3,7 +3,7 @@ module code.rocket9labs.com/tslocum/etk
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231128010227-689683b75174
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231218071755-e4087431ad9f
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3
|
||||
github.com/llgcode/draw2d v0.0.0-20231212091825-f55e0c776b44
|
||||
golang.org/x/image v0.14.0
|
||||
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/ebitengine/purego v0.5.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/jezek/xgb v1.1.1 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20231214170342-aacd6d4b4611 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848 // indirect
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,5 +1,5 @@
|
|||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231128010227-689683b75174 h1:jxE3JcaE4ovMWaZHLA6MvYgkmdkdbEEUv943Bp+MoxU=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231128010227-689683b75174/go.mod h1:yLsZtW6XgrQPpqBpQfJJkueurBUUYNnkPr68cS1MtJc=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231218071755-e4087431ad9f h1:/6Mpu+9TfQyee7uJ1epZEEfGGCT67bvZKpVhWy8T7cs=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231218071755-e4087431ad9f/go.mod h1:hA/frrbchjCX75HUG/GH97X3eH8xw++AKC2F+z+uXyA=
|
||||
github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY=
|
||||
github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
|
@ -12,8 +12,8 @@ github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFl
|
|||
github.com/llgcode/draw2d v0.0.0-20231212091825-f55e0c776b44 h1:1ad70i0s40IpMtRm2ST+Nvr03X7mlHWtdALYkFNrlxk=
|
||||
github.com/llgcode/draw2d v0.0.0-20231212091825-f55e0c776b44/go.mod h1:muweRyJCZ1mZSMiCgYbAicfnwZFoeHpNr6A6QBu+rBg=
|
||||
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e h1:ZAvbj5hI/G/EbAYAcj4yCXUNiFKefEhH0qfImDDD0/8=
|
||||
golang.org/x/exp/shiny v0.0.0-20231214170342-aacd6d4b4611 h1:4KULGL0aJvCqUs5c7fA/eWELbK2rVfV9wpZTWW1Qs/I=
|
||||
golang.org/x/exp/shiny v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
|
||||
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848 h1:LnDWUUS+bxOesHc+QBvFFmS4v0ZH+Vtg0EncMANwN9Q=
|
||||
golang.org/x/exp/shiny v0.0.0-20231219180239-dc181d75b848/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg=
|
||||
|
|
7
grid.go
7
grid.go
|
@ -142,13 +142,6 @@ func (g *Grid) Draw(screen *ebiten.Image) error {
|
|||
g.updated = false
|
||||
}
|
||||
|
||||
for _, child := range g.children {
|
||||
err := child.Draw(screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
276
list.go
Normal file
276
list.go
Normal file
|
@ -0,0 +1,276 @@
|
|||
package etk
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// SelectionMode represents a mode of selection.
|
||||
type SelectionMode int
|
||||
|
||||
// Selection modes.
|
||||
const (
|
||||
// SelectNone disables selection.
|
||||
SelectNone SelectionMode = iota
|
||||
|
||||
// SelectRow enables selection by row.
|
||||
SelectRow
|
||||
|
||||
// SelectColumn enables selection by column.
|
||||
SelectColumn
|
||||
)
|
||||
|
||||
// List is a list of widgets. Rows or cells may optionally be selectable.
|
||||
type List struct {
|
||||
grid *Grid
|
||||
itemHeight int
|
||||
highlightColor color.RGBA
|
||||
maxY int
|
||||
selectionMode SelectionMode
|
||||
selectedX, selectedY int
|
||||
selectedFunc func(index int) (accept bool)
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewList returns a new List widget.
|
||||
func NewList(itemHeight int, onSelected func(index int) (accept bool)) *List {
|
||||
return &List{
|
||||
grid: NewGrid(),
|
||||
itemHeight: itemHeight,
|
||||
highlightColor: color.RGBA{255, 255, 255, 255},
|
||||
maxY: -1,
|
||||
selectedY: -1,
|
||||
selectedFunc: onSelected,
|
||||
}
|
||||
}
|
||||
|
||||
// Rect returns the position and size of the widget.
|
||||
func (l *List) Rect() image.Rectangle {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.Rect()
|
||||
}
|
||||
|
||||
// SetRect sets the position and size of the widget.
|
||||
func (l *List) SetRect(r image.Rectangle) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.SetRect(r)
|
||||
}
|
||||
|
||||
// Background returns the background color of the widget.
|
||||
func (l *List) Background() color.RGBA {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.Background()
|
||||
}
|
||||
|
||||
// SetBackground sets the background color of the widget.
|
||||
func (l *List) SetBackground(background color.RGBA) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.SetBackground(background)
|
||||
}
|
||||
|
||||
// Focus returns the focus state of the widget.
|
||||
func (l *List) Focus() bool {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.Focus()
|
||||
}
|
||||
|
||||
// SetFocus sets the focus state of the widget.
|
||||
func (l *List) SetFocus(focus bool) (accept bool) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.SetFocus(focus)
|
||||
}
|
||||
|
||||
// Visible returns the visibility of the widget.
|
||||
func (l *List) Visible() bool {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.Visible()
|
||||
}
|
||||
|
||||
// SetVisible sets the visibility of the widget.
|
||||
func (l *List) SetVisible(visible bool) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.SetVisible(visible)
|
||||
}
|
||||
|
||||
// SetColumnSizes sets the size of each column. A size of -1 represents an equal
|
||||
// proportion of the available space.
|
||||
func (l *List) SetColumnSizes(size ...int) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.SetColumnSizes(size...)
|
||||
}
|
||||
|
||||
// SetItemHeight sets the height of the list items.
|
||||
func (l *List) SetItemHeight(itemHeight int) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if l.itemHeight == itemHeight {
|
||||
return
|
||||
}
|
||||
l.itemHeight = itemHeight
|
||||
|
||||
if l.maxY == -1 {
|
||||
return
|
||||
}
|
||||
rowSizes := make([]int, l.maxY+1)
|
||||
for i := range rowSizes {
|
||||
rowSizes[i] = l.itemHeight
|
||||
}
|
||||
l.grid.SetRowSizes(rowSizes...)
|
||||
}
|
||||
|
||||
// SetSelectionMode sets the selection mode of the list.
|
||||
func (l *List) SetSelectionMode(selectionMode SelectionMode) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if l.selectionMode == selectionMode {
|
||||
return
|
||||
}
|
||||
l.selectionMode = selectionMode
|
||||
}
|
||||
|
||||
// SetHighlightColor sets the color used to highlight the currently selected item.
|
||||
func (l *List) SetHighlightColor(c color.RGBA) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.highlightColor = c
|
||||
}
|
||||
|
||||
// SelectedItem returns the selected list item.
|
||||
func (l *List) SelectedItem() (x int, y int) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.selectedX, l.selectedY
|
||||
}
|
||||
|
||||
// SetSelectedItem sets the selected list item.
|
||||
func (l *List) SetSelectedItem(x int, y int) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.selectedX, l.selectedY = x, y
|
||||
}
|
||||
|
||||
// SetSelectedFunc sets a handler which is called when a list item is selected.
|
||||
// Providing a nil function value will remove the existing handler (if set).
|
||||
// The handler may return false to return the selection to its original state.
|
||||
func (l *List) SetSelectedFunc(f func(index int) (accept bool)) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.selectedFunc = 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.
|
||||
func (l *List) Children() []Widget {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.Children()
|
||||
}
|
||||
|
||||
// AddChildAt adds a widget to the list at the specified position.
|
||||
func (l *List) AddChildAt(w Widget, x int, y int) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.AddChildAt(&ignoreMouse{w}, x, y, 1, 1)
|
||||
if y > l.maxY {
|
||||
l.maxY = y
|
||||
rowSizes := make([]int, l.maxY+1)
|
||||
for i := range rowSizes {
|
||||
rowSizes[i] = l.itemHeight
|
||||
}
|
||||
l.grid.SetRowSizes(rowSizes...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleKeyboard is called when a keyboard event occurs.
|
||||
func (l *List) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
return l.grid.HandleKeyboard(key, r)
|
||||
}
|
||||
|
||||
// HandleMouse is called when a mouse event occurs. Only mouse events that
|
||||
// are on top of the widget are passed to the widget.
|
||||
func (l *List) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if !clicked || (cursor.X == 0 && cursor.Y == 0) {
|
||||
return true, nil
|
||||
}
|
||||
selected := (cursor.Y - l.grid.rect.Min.Y) / l.itemHeight
|
||||
if selected >= 0 && selected <= l.maxY {
|
||||
lastSelected := l.selectedY
|
||||
l.selectedY = selected
|
||||
if l.selectedFunc != nil {
|
||||
accept := l.selectedFunc(l.selectedY)
|
||||
if !accept {
|
||||
l.selectedY = lastSelected
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Draw draws the widget on the screen.
|
||||
func (l *List) Draw(screen *ebiten.Image) error {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
// Draw grid.
|
||||
err := l.grid.Draw(screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Highlight selection.
|
||||
if l.selectionMode == SelectNone || l.selectedY < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
x, y := l.grid.rect.Min.X, l.grid.rect.Min.Y+l.selectedY*l.itemHeight
|
||||
w, h := l.grid.rect.Dx(), l.itemHeight
|
||||
r := image.Rect(x, y, x+w, y+h)
|
||||
screen.SubImage(r).(*ebiten.Image).Fill(l.highlightColor)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear clears all items from the list.
|
||||
func (l *List) Clear() {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.grid.Clear()
|
||||
l.maxY = -1
|
||||
l.selectedX, l.selectedY = 0, -1
|
||||
}
|
11
widget.go
11
widget.go
|
@ -49,3 +49,14 @@ type Widget interface {
|
|||
// in reverse order.
|
||||
Children() []Widget
|
||||
}
|
||||
|
||||
// ignoreMouse wraps a widget to ignore mouse events.
|
||||
type ignoreMouse struct {
|
||||
Widget
|
||||
}
|
||||
|
||||
// HandleMouse is called when a mouse event occurs. Only mouse events that
|
||||
// are on top of the widget are passed to the widget.
|
||||
func (i *ignoreMouse) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue