etk/select.go

214 lines
5.5 KiB
Go

package etk
import (
"image"
"image/color"
"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(AlignCenter)
s.label.SetForeground(Style.ButtonTextColor)
s.SetBackground(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(AlignCenter)
t.SetForeground(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()
if !visible {
s.background = Style.ButtonBgColor
} else {
s.background = color.RGBA{uint8(float64(Style.ButtonBgColor.R) * 0.95), uint8(float64(Style.ButtonBgColor.G) * 0.95), uint8(float64(Style.ButtonBgColor.B) * 0.95), 255}
}
}
// Cursor returns the cursor shape shown when a mouse cursor hovers over the
// widget, or -1 to let widgets beneath determine the cursor shape.
func (s *Select) Cursor() ebiten.CursorShapeType {
return ebiten.CursorShapePointer
}
// 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.
r := s.rect
borderSize := Scale(Style.ButtonBorderSize)
borderLeft, borderTop := Style.ButtonBorderLeft, Style.ButtonBorderTop
borderRight, borderBottom := Style.ButtonBorderRight, Style.ButtonBorderBottom
if !s.open {
screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
} else {
borderLeft, borderTop, borderRight, borderBottom = borderRight, borderBottom, borderLeft, borderTop
screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
}
return nil
}