Add Select
This commit is contained in:
parent
bde57bc0ac
commit
fb51eb32ad
6 changed files with 232 additions and 26 deletions
2
box.go
2
box.go
|
@ -34,7 +34,7 @@ func (b *Box) Rect() image.Rectangle {
|
|||
return b.rect
|
||||
}
|
||||
|
||||
// SetRect returns the position and size of the widget.
|
||||
// SetRect sets the position and size of the widget.
|
||||
func (b *Box) SetRect(r image.Rectangle) {
|
||||
b.Lock()
|
||||
b.rect = r
|
||||
|
|
|
@ -73,7 +73,6 @@ func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (ha
|
|||
|
||||
// Draw draws the button on the screen.
|
||||
func (b *Button) Draw(screen *ebiten.Image) error {
|
||||
// TODO background color
|
||||
// Draw background.
|
||||
screen.SubImage(b.rect).(*ebiten.Image).Fill(Style.ButtonBgColor)
|
||||
|
||||
|
|
31
game.go
31
game.go
|
@ -114,8 +114,13 @@ func SetDebug(debug bool) {
|
|||
drawDebug = debug
|
||||
}
|
||||
|
||||
// ScreenSize returns the current screen size.
|
||||
func ScreenSize() (width int, height int) {
|
||||
return lastWidth, lastHeight
|
||||
}
|
||||
|
||||
// Layout sets the current screen size and resizes the root widget.
|
||||
func Layout(outsideWidth, outsideHeight int) {
|
||||
func Layout(outsideWidth int, outsideHeight int) {
|
||||
if outsideWidth != lastWidth || outsideHeight != lastHeight {
|
||||
lastWidth, lastHeight = outsideWidth, outsideHeight
|
||||
}
|
||||
|
@ -242,28 +247,22 @@ func Update() error {
|
|||
}
|
||||
|
||||
func at(w Widget, p image.Point) Widget {
|
||||
if w == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !p.In(w.Rect()) {
|
||||
if w == nil || !w.Visible() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, child := range w.Children() {
|
||||
if !child.Visible() {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.In(child.Rect()) {
|
||||
result := at(child, p)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
result := at(child, p)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return w
|
||||
if p.In(w.Rect()) {
|
||||
return w
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// At returns the widget at the provided screen location.
|
||||
|
|
|
@ -15,7 +15,7 @@ type Keyboard struct {
|
|||
incoming []*kibodo.Input
|
||||
}
|
||||
|
||||
// NewBox returns a new Keyboard widget.
|
||||
// NewKeyboard returns a new Keyboard widget.
|
||||
func NewKeyboard() *Keyboard {
|
||||
k := kibodo.NewKeyboard()
|
||||
k.Show()
|
||||
|
|
28
list.go
28
list.go
|
@ -41,6 +41,7 @@ type List struct {
|
|||
scrollAreaColor color.RGBA
|
||||
scrollHandleColor color.RGBA
|
||||
scrollDrag bool
|
||||
drawBorder bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -282,6 +283,11 @@ func (l *List) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error)
|
|||
return l.grid.HandleKeyboard(key, r)
|
||||
}
|
||||
|
||||
// SetDrawBorder enables or disables borders being drawn around the list.
|
||||
func (l *List) SetDrawBorder(drawBorder bool) {
|
||||
l.drawBorder = drawBorder
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -380,15 +386,23 @@ func (l *List) Draw(screen *ebiten.Image) error {
|
|||
}
|
||||
|
||||
// Highlight selection.
|
||||
if l.selectionMode == SelectNone || l.selectedY < 0 {
|
||||
return nil
|
||||
drawHighlight := l.selectionMode != SelectNone && l.selectedY >= 0
|
||||
if drawHighlight {
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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)
|
||||
// Draw border.
|
||||
if l.drawBorder {
|
||||
const borderSize = 4
|
||||
screen.SubImage(image.Rect(l.grid.rect.Min.X, l.grid.rect.Min.Y, l.grid.rect.Max.X, l.grid.rect.Min.Y+borderSize)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(l.grid.rect.Min.X, l.grid.rect.Max.Y-borderSize, l.grid.rect.Max.X, l.grid.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(l.grid.rect.Min.X, l.grid.rect.Min.Y, l.grid.rect.Min.X+borderSize, l.grid.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(l.grid.rect.Max.X-borderSize, l.grid.rect.Min.Y, l.grid.rect.Max.X, l.grid.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
}
|
||||
|
||||
// Draw scroll bar.
|
||||
|
|
194
select.go
Normal file
194
select.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
package etk
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"code.rocketnine.space/tslocum/messeji"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// Select is a dropdown selection widget.
|
||||
type Select struct {
|
||||
*Box
|
||||
|
||||
label *Text
|
||||
list *List
|
||||
onSelect func(index int) (accept bool)
|
||||
items []string
|
||||
open bool
|
||||
}
|
||||
|
||||
// NewSelect returns a new Select widget.
|
||||
func NewSelect(itemHeight int, onSelect func(index int) (accept bool)) *Select {
|
||||
s := &Select{
|
||||
Box: NewBox(),
|
||||
label: NewText(""),
|
||||
onSelect: onSelect,
|
||||
}
|
||||
s.label.SetVertical(messeji.AlignCenter)
|
||||
s.label.SetForegroundColor(Style.ButtonTextColor)
|
||||
s.label.SetBackgroundColor(Style.ButtonBgColor)
|
||||
s.list = NewList(itemHeight, s.selectList)
|
||||
s.list.SetBackground(Style.ButtonBgColor)
|
||||
s.list.SetDrawBorder(true)
|
||||
s.list.SetVisible(false)
|
||||
s.list.SetSelectionMode(SelectRow)
|
||||
s.AddChild(s.list)
|
||||
s.updateLabel()
|
||||
return s
|
||||
}
|
||||
|
||||
// SetRect sets the position and size of the widget.
|
||||
func (s *Select) SetRect(r image.Rectangle) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.rect = r
|
||||
s.label.SetRect(r)
|
||||
listRect := r.Add(image.Point{X: 0, Y: r.Dy()})
|
||||
itemCount := len(s.items)
|
||||
listRect.Max.Y = listRect.Min.Y + itemCount*s.list.itemHeight
|
||||
_, height := ScreenSize()
|
||||
if listRect.Max.Y > height {
|
||||
listRect.Max.Y = height
|
||||
}
|
||||
s.list.SetRect(listRect)
|
||||
}
|
||||
|
||||
// SetHighlightColor sets the color used to highlight the currently selected item.
|
||||
func (s *Select) SetHighlightColor(c color.RGBA) {
|
||||
s.list.SetHighlightColor(c)
|
||||
}
|
||||
|
||||
// SetSelectedItem sets the currently selected item.
|
||||
func (s *Select) SetSelectedItem(index int) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if index < 0 || index >= len(s.items) {
|
||||
return
|
||||
}
|
||||
s.list.SetSelectedItem(0, index)
|
||||
s.updateLabel()
|
||||
}
|
||||
|
||||
// Children returns the children of the widget.
|
||||
func (s *Select) Children() []Widget {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
return s.children
|
||||
}
|
||||
|
||||
// AddChild adds a child to the widget. Selection options are added via AddOption.
|
||||
func (s *Select) AddChild(w ...Widget) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.children = append(s.children, w...)
|
||||
}
|
||||
|
||||
// Clear removes all children from the widget.
|
||||
func (s *Select) Clear() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.items = nil
|
||||
s.list.Clear()
|
||||
s.updateLabel()
|
||||
}
|
||||
|
||||
// AddOption adds an option to the widget.
|
||||
func (s *Select) AddOption(label string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.items = append(s.items, label)
|
||||
if len(s.items) == 1 {
|
||||
s.list.selectedY = 0
|
||||
s.updateLabel()
|
||||
}
|
||||
|
||||
t := NewText(label)
|
||||
t.SetVertical(messeji.AlignCenter)
|
||||
t.SetForegroundColor(Style.ButtonTextColor)
|
||||
s.list.AddChildAt(t, 0, len(s.items)-1)
|
||||
}
|
||||
|
||||
func (s *Select) updateLabel() {
|
||||
var text string
|
||||
if len(s.items) > 0 && s.list.selectedY >= 0 && s.list.selectedY < len(s.items) {
|
||||
text = s.items[s.list.selectedY]
|
||||
}
|
||||
if s.open {
|
||||
text = "▼ " + text
|
||||
} else {
|
||||
text = "▶ " + text
|
||||
}
|
||||
s.label.SetText(text)
|
||||
}
|
||||
|
||||
func (s *Select) selectList(index int) (accept bool) {
|
||||
s.Lock()
|
||||
s.list.grid.visible = false
|
||||
s.open = false
|
||||
onSelect := s.onSelect
|
||||
s.Unlock()
|
||||
|
||||
if onSelect != nil {
|
||||
if !onSelect(index) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
s.list.selectedY = index
|
||||
s.updateLabel()
|
||||
return true
|
||||
}
|
||||
|
||||
// SetMenuVisible sets the visibility of the dropdown menu.
|
||||
func (s *Select) SetMenuVisible(visible bool) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s._setMenuVisible(visible)
|
||||
}
|
||||
|
||||
func (s *Select) _setMenuVisible(visible bool) {
|
||||
s.open = visible
|
||||
s.list.SetVisible(visible)
|
||||
s.updateLabel()
|
||||
}
|
||||
|
||||
// HandleKeyboard is called when a keyboard event occurs.
|
||||
func (s *Select) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// HandleMouse is called when a mouse event occurs.
|
||||
func (s *Select) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if clicked {
|
||||
s._setMenuVisible(!s.open)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Draw draws the widget on the screen.
|
||||
func (s *Select) Draw(screen *ebiten.Image) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// Draw label.
|
||||
s.label.Draw(screen)
|
||||
|
||||
// Draw border.
|
||||
const borderSize = 4
|
||||
screen.SubImage(image.Rect(s.rect.Min.X, s.rect.Min.Y, s.rect.Max.X, s.rect.Min.Y+borderSize)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(s.rect.Min.X, s.rect.Max.Y-borderSize, s.rect.Max.X, s.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(s.rect.Min.X, s.rect.Min.Y, s.rect.Min.X+borderSize, s.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
screen.SubImage(image.Rect(s.rect.Max.X-borderSize, s.rect.Min.Y, s.rect.Max.X, s.rect.Max.Y)).(*ebiten.Image).Fill(Style.BorderColor)
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue