Document everything

This commit is contained in:
Trevor Slocum 2023-10-28 23:04:32 -07:00
parent 6e8eb92a2c
commit 36965d0b79
13 changed files with 228 additions and 105 deletions

35
box.go
View file

@ -8,6 +8,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Box is a building block for other widgets. It may also be used as a spacer
// in layout widgets.
type Box struct {
rect image.Rectangle
children []Widget
@ -17,12 +19,14 @@ type Box struct {
sync.Mutex
}
// NewBox returns a new Box widget.
func NewBox() *Box {
return &Box{
visible: true,
}
}
// Rect returns the position and size of the widget.
func (b *Box) Rect() image.Rectangle {
b.Lock()
defer b.Unlock()
@ -30,6 +34,7 @@ func (b *Box) Rect() image.Rectangle {
return b.rect
}
// SetRect returns the position and size of the widget.
func (b *Box) SetRect(r image.Rectangle) {
b.Lock()
defer b.Unlock()
@ -37,6 +42,7 @@ func (b *Box) SetRect(r image.Rectangle) {
b.rect = r
}
// Background returns the background color of the widget.
func (b *Box) Background() color.RGBA {
b.Lock()
defer b.Unlock()
@ -44,6 +50,7 @@ func (b *Box) Background() color.RGBA {
return b.background
}
// SetBackground sets the background color of the widget.
func (b *Box) SetBackground(background color.RGBA) {
b.Lock()
defer b.Unlock()
@ -51,30 +58,45 @@ func (b *Box) SetBackground(background color.RGBA) {
b.background = background
}
// Focus returns the focus state of the widget.
func (b *Box) Focus() bool {
return false
}
// SetFocus sets the focus state of the widget.
func (b *Box) SetFocus(focus bool) bool {
return false
}
// Visible returns the visibility of the widget.
func (b *Box) Visible() bool {
return b.visible
}
// SetVisible sets the visibility of the widget.
func (b *Box) SetVisible(visible bool) {
b.visible = visible
}
func (b *Box) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return false, nil
}
// HandleKeyboard is called when a keyboard event occurs.
func (b *Box) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
return false, nil
}
// HandleMouse is called when a mouse event occurs. Only mouse events that
// are on top of the widget are passed to the widget.
func (b *Box) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return false, nil
}
// Draw draws the widget on the screen.
func (b *Box) Draw(screen *ebiten.Image) error {
return nil
}
// 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 (b *Box) Children() []Widget {
b.Lock()
defer b.Unlock()
@ -82,6 +104,7 @@ func (b *Box) Children() []Widget {
return b.children
}
// AddChild adds a child to the widget.
func (b *Box) AddChild(w ...Widget) {
b.Lock()
defer b.Unlock()
@ -89,8 +112,4 @@ func (b *Box) AddChild(w ...Widget) {
b.children = append(b.children, w...)
}
func (b *Box) Draw(screen *ebiten.Image) error {
return nil
}
var _ Widget = &Box{}

View file

@ -7,6 +7,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Button is a clickable button.
type Button struct {
*Box
@ -15,6 +16,7 @@ type Button struct {
onSelected func() error
}
// NewButton returns a new Button widget.
func NewButton(label string, onSelected func() error) *Button {
textColor := Style.ButtonTextColor
if textColor.A == 0 {
@ -36,12 +38,19 @@ func NewButton(label string, onSelected func() error) *Button {
}
}
// SetRect sets the position and size of the Button.
func (b *Button) SetRect(r image.Rectangle) {
b.Box.rect = r
b.Label.SetRect(r)
}
// HandleKeyboard is called when a keyboard event occurs.
func (b *Button) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return false, nil
}
// HandleMouse is called when a mouse event occurs.
func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
if !clicked {
return true, nil
@ -58,10 +67,7 @@ func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (ha
return true, onSelected()
}
func (b *Button) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return false, nil
}
// Draw draws the button on the screen.
func (b *Button) Draw(screen *ebiten.Image) error {
// TODO background color
// Draw background.

4
doc.go
View file

@ -8,11 +8,11 @@ based on official widgets.
The following official widgets are available:
Box - Building block for creating custom widgets.
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. Each widget added to the Grid may span multiple cells.
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.

16
flex.go
View file

@ -6,18 +6,22 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Flex is a flexible stack-based layout. Each Flex widget may be oriented
// horizontally or vertically.
type Flex struct {
*Box
vertical bool
}
// NewFlex returns a new Flex widget.
func NewFlex() *Flex {
return &Flex{
Box: NewBox(),
}
}
// SetRect sets the position and size of the widget.
func (f *Flex) SetRect(r image.Rectangle) {
f.Lock()
defer f.Unlock()
@ -26,6 +30,7 @@ func (f *Flex) SetRect(r image.Rectangle) {
f.reposition()
}
// SetVertical sets the orientation of the child widget stacking.
func (f *Flex) SetVertical(v bool) {
f.Lock()
defer f.Unlock()
@ -38,14 +43,17 @@ func (f *Flex) SetVertical(v bool) {
f.reposition()
}
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return false, nil
}
// HandleKeyboard is called when a keyboard event occurs.
func (f *Flex) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return false, nil
}
// HandleMouse is called when a mouse event occurs.
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return false, nil
}
// Draw draws the widget on the screen.
func (f *Flex) Draw(screen *ebiten.Image) error {
f.Lock()
defer f.Unlock()

View file

@ -9,25 +9,14 @@ type Frame struct {
positionChildren bool
}
// NewFrame returns a new Frame widget.
func NewFrame() *Frame {
return &Frame{
Box: NewBox(),
}
}
func (f *Frame) SetPositionChildren(position bool) {
f.Lock()
defer f.Unlock()
f.positionChildren = position
if f.positionChildren {
for _, w := range f.children {
w.SetRect(f.rect)
}
}
}
// SetRect sets the position and size of the widget.
func (f *Frame) SetRect(r image.Rectangle) {
f.Lock()
defer f.Unlock()
@ -41,6 +30,22 @@ func (f *Frame) SetRect(r image.Rectangle) {
}
}
// SetPositionChildren sets a flag that determines whether child widgets are
// repositioned when the Frame is repositioned.
func (f *Frame) SetPositionChildren(position bool) {
f.Lock()
defer f.Unlock()
f.positionChildren = position
if f.positionChildren {
for _, w := range f.children {
w.SetRect(f.rect)
}
}
}
// AddChild adds a child to the widget.
func (f *Frame) AddChild(w ...Widget) {
f.Lock()
defer f.Unlock()

View file

@ -32,6 +32,9 @@ const (
backspaceRepeatTime = 75 * time.Millisecond
)
// SetRoot sets the root widget. The root widget and all of its children will
// be drawn on the screen and receive user input. The root widget will also be
// focused. To temporarily disable etk, set a nil root widget.
func SetRoot(w Widget) {
root = w
if root != nil && (lastWidth != 0 || lastHeight != 0) {
@ -40,6 +43,7 @@ func SetRoot(w Widget) {
SetFocus(root)
}
// SetFocus focuses a widget.
func SetFocus(w Widget) {
lastFocused := focusedWidget
if w != nil && !w.SetFocus(true) {
@ -51,10 +55,12 @@ func SetFocus(w Widget) {
focusedWidget = w
}
// Focused returns the currently focused widget. If no widget is focused, nil is returned.
func Focused() Widget {
return focusedWidget
}
// Layout sets the current screen size and resizes the root widget.
func Layout(outsideWidth, outsideHeight int) {
if outsideWidth != lastWidth || outsideHeight != lastHeight {
lastWidth, lastHeight = outsideWidth, outsideHeight
@ -66,6 +72,7 @@ func Layout(outsideWidth, outsideHeight int) {
root.SetRect(image.Rect(0, 0, outsideWidth, outsideHeight))
}
// Update handles user input and passes it to the focused or clicked widget.
func Update() error {
if root == nil {
return nil
@ -203,6 +210,7 @@ func update(w Widget, cursor image.Point, pressed bool, clicked bool, mouseHandl
return mouseHandled, nil
}
// Draw draws the root widget and its children to the screen.
func Draw(screen *ebiten.Image) error {
if root == nil {
return nil

35
grid.go
View file

@ -6,6 +6,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Grid is a highly customizable cell-based layout. Widgets added to the Grid
// may span multiple cells.
type Grid struct {
*Box
@ -21,12 +23,14 @@ type Grid struct {
updated bool
}
// NewGrid returns a new Grid widget.
func NewGrid() *Grid {
return &Grid{
Box: NewBox(),
}
}
// SetRect sets the position and size of the widget.
func (g *Grid) SetRect(r image.Rectangle) {
g.Lock()
defer g.Unlock()
@ -35,6 +39,8 @@ func (g *Grid) SetRect(r image.Rectangle) {
g.updated = true
}
// SetColumnSizes sets the size of each column. A size of -1 represents an equal
// proportion of the available space.
func (g *Grid) SetColumnSizes(size ...int) {
g.Lock()
defer g.Unlock()
@ -43,6 +49,7 @@ func (g *Grid) SetColumnSizes(size ...int) {
g.updated = true
}
// SetColumnPadding sets the amount of padding between each column.
func (g *Grid) SetColumnPadding(padding int) {
g.Lock()
defer g.Unlock()
@ -51,6 +58,8 @@ func (g *Grid) SetColumnPadding(padding int) {
g.updated = true
}
// SetRowSizes sets the size of each row. A size of -1 represents an equal
// proportion of the available space.
func (g *Grid) SetRowSizes(size ...int) {
g.Lock()
defer g.Unlock()
@ -59,6 +68,7 @@ func (g *Grid) SetRowSizes(size ...int) {
g.updated = true
}
// SetRowPadding sets the amount of padding between each row.
func (g *Grid) SetRowPadding(padding int) {
g.Lock()
defer g.Unlock()
@ -67,6 +77,8 @@ func (g *Grid) SetRowPadding(padding int) {
g.updated = true
}
// AddChild adds a widget to the Grid at 0,0. To add widgets to a Grid, you
// should use AddChildAt instead.
func (g *Grid) AddChild(wgt ...Widget) {
g.Box.AddChild(wgt...)
@ -78,6 +90,8 @@ func (g *Grid) AddChild(wgt ...Widget) {
g.updated = true
}
// AddChildAt adds a widget to the Grid at the specified position. Each widget
// added to the grid may span multiple cells.
func (g *Grid) AddChildAt(wgt Widget, x int, y int, columns int, rows int) {
g.Box.AddChild(wgt)
@ -87,15 +101,7 @@ func (g *Grid) AddChildAt(wgt Widget, x int, y int, columns int, rows int) {
g.updated = true
}
func (g *Grid) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
if g.updated {
g.reposition()
g.updated = false
}
return false, nil
}
// HandleKeyboard is called when a keyboard event occurs.
func (g *Grid) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
if g.updated {
g.reposition()
@ -105,6 +111,17 @@ func (g *Grid) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return false, nil
}
// HandleMouse is called when a mouse event occurs.
func (g *Grid) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
if g.updated {
g.reposition()
g.updated = false
}
return false, nil
}
// Draw draws the widget on the screen.
func (g *Grid) Draw(screen *ebiten.Image) error {
g.Lock()
defer g.Unlock()

View file

@ -7,6 +7,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Input is a text input widget. The Input widget is simply a Text widget that
// also accepts user input.
type Input struct {
*Box
Field *messeji.InputField
@ -14,6 +16,7 @@ type Input struct {
focus bool
}
// NewInput returns a new Input widget.
func NewInput(prefix string, text string, onSelected func(text string) (handled bool)) *Input {
textColor := Style.TextColorDark
/*if TextColor == nil {
@ -38,30 +41,19 @@ func NewInput(prefix string, text string, onSelected func(text string) (handled
}
}
// Clear clears the field's buffer.
func (i *Input) Clear() {
i.Field.SetText("")
}
// Write writes to the field's buffer.
func (i *Input) Write(p []byte) (n int, err error) {
return i.Field.Write(p)
}
func (i *Input) Text() string {
return i.Field.Text()
}
// SetRect sets the position and size of the widget.
func (i *Input) SetRect(r image.Rectangle) {
i.Box.rect = r
i.Field.SetRect(r)
}
// Focus returns the focus state of the widget.
func (i *Input) Focus() bool {
return i.focus
}
// SetFocus sets the focus state of the widget.
func (i *Input) SetFocus(focus bool) bool {
i.focus = focus
@ -73,6 +65,22 @@ func (i *Input) SetFocus(focus bool) bool {
return true
}
// Clear clears the textbuffer.
func (i *Input) Clear() {
i.Field.SetText("")
}
// Write writes to the text buffer.
func (i *Input) Write(p []byte) (n int, err error) {
return i.Field.Write(p)
}
// Text returns the content of the text buffer.
func (i *Input) Text() string {
return i.Field.Text()
}
// HandleKeyboard is called when a keyboard event occurs.
func (i *Input) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
if !i.focus {
return false, nil
@ -81,12 +89,13 @@ func (i *Input) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error)
return i.Field.HandleKeyboardEvent(key, r)
}
// HandleMouse is called when a mouse event occurs.
func (i *Input) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return i.Field.HandleMouseEvent(cursor, pressed, clicked)
}
// Draw draws the widget on the screen.
func (i *Input) Draw(screen *ebiten.Image) error {
// Draw label.
i.Field.Draw(screen)
return nil
}

View file

@ -2,12 +2,14 @@ package etk
import "github.com/hajimehoshi/ebiten/v2"
// Shortcuts represents a keyboard shortcut configuration.
type Shortcuts struct {
ConfirmKeyboard []ebiten.Key
ConfirmMouse []ebiten.MouseButton
ConfirmGamepad []ebiten.GamepadButton
}
// Bindings is the current keyboard shortcut configuration.
var Bindings = &Shortcuts{
ConfirmKeyboard: []ebiten.Key{ebiten.KeyEnter, ebiten.KeyKPEnter},
ConfirmMouse: []ebiten.MouseButton{ebiten.MouseButtonLeft},

View file

@ -28,6 +28,7 @@ func defaultFont() font.Face {
return defaultFont
}
// Attributes represents a default attribute configuration.
type Attributes struct {
TextFont font.Face
@ -48,6 +49,7 @@ type Attributes struct {
ButtonBgColorDisabled color.RGBA
}
// Style is the current default attribute configuration.
var Style = &Attributes{
TextFont: defaultFont(),

67
text.go
View file

@ -8,6 +8,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Text is a text display widget.
type Text struct {
*messeji.TextField
@ -15,6 +16,7 @@ type Text struct {
children []Widget
}
// NewText returns a new Text widget.
func NewText(text string) *Text {
textColor := Style.TextColorLight
@ -30,6 +32,7 @@ func NewText(text string) *Text {
}
}
// Background returns the background color of the widget.
func (t *Text) Background() color.RGBA {
t.Lock()
defer t.Unlock()
@ -37,6 +40,7 @@ func (t *Text) Background() color.RGBA {
return t.background
}
// SetBackground sets the background color of the widget.
func (t *Text) SetBackground(background color.RGBA) {
t.Lock()
defer t.Unlock()
@ -44,14 +48,48 @@ func (t *Text) SetBackground(background color.RGBA) {
t.background = background
}
// Focus returns the focus state of the widget.
func (t *Text) Focus() bool {
return false
}
// SetFocus sets the focus state of the widget.
func (t *Text) SetFocus(focus bool) bool {
return false
}
// Clear clears the text buffer.
func (t *Text) Clear() {
t.TextField.SetText("")
}
// Write writes to the text buffer.
func (t *Text) Write(p []byte) (n int, err error) {
return t.TextField.Write(p)
}
// Text returns the content of the text buffer.
func (t *Text) Text() string {
return t.TextField.Text()
}
// HandleKeyboard is called when a keyboard event occurs.
func (t *Text) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
return t.TextField.HandleKeyboardEvent(key, r)
}
// HandleMouse is called when a mouse event occurs.
func (t *Text) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return t.TextField.HandleMouseEvent(cursor, pressed, clicked)
}
// Draw draws the widget on the screen.
func (t *Text) Draw(screen *ebiten.Image) error {
t.TextField.Draw(screen)
return nil
}
// Children returns the children of the widget.
func (t *Text) Children() []Widget {
t.Lock()
defer t.Unlock()
@ -59,6 +97,7 @@ func (t *Text) Children() []Widget {
return t.children
}
// AddChild adds a child to the widget.
func (t *Text) AddChild(w ...Widget) {
t.Lock()
defer t.Unlock()
@ -66,32 +105,4 @@ func (t *Text) AddChild(w ...Widget) {
t.children = append(t.children, w...)
}
// Clear clears the field's buffer.
func (t *Text) Clear() {
t.TextField.SetText("")
}
// Write writes to the field's buffer.
func (t *Text) Write(p []byte) (n int, err error) {
return t.TextField.Write(p)
}
func (t *Text) Text() string {
return t.TextField.Text()
}
func (t *Text) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
return t.TextField.HandleKeyboardEvent(key, r)
}
func (t *Text) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return t.TextField.HandleMouseEvent(cursor, pressed, clicked)
}
func (t *Text) Draw(screen *ebiten.Image) error {
// Draw label.
t.TextField.Draw(screen)
return nil
}
var _ Widget = &Text{}

View file

@ -7,17 +7,45 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Widget represents an interface element. Most widgets will embed Box and build
// on top of it.
type Widget interface {
// Rect returns the position and size of the widget.
Rect() image.Rectangle
// SetRect sets the position and size of the widget.
SetRect(r image.Rectangle)
// Background returns the background color of the widget.
Background() color.RGBA
// SetBackground sets the background color of the widget.
SetBackground(background color.RGBA)
// Focus returns the focus state of the widget.
Focus() bool
// SetFocus sets the focus state of the widget.
SetFocus(focus bool) (accept bool)
// Visible returns the visibility of the widget.
Visible() bool
// SetVisible sets the visibility of the widget.
SetVisible(visible bool)
// HandleKeyboard is called when a keyboard event occurs.
HandleKeyboard(ebiten.Key, rune) (handled bool, err error)
// HandleMouse is called when a mouse event occurs. Only mouse events that
// are on top of the widget are passed to the widget.
HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error)
// Draw draws the widget on the screen.
Draw(screen *ebiten.Image) error
// 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.
Children() []Widget
}

View file

@ -6,7 +6,8 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
// Window displays and passes input to only one child widget at a time.
// Window is a widget paging mechanism. Only one widget added to a window is
// displayed at a time.
type Window struct {
*Box
@ -17,20 +18,14 @@ type Window struct {
hasLabel bool
}
// NewWindow returns a new Window widget.
func NewWindow() *Window {
return &Window{
Box: NewBox(),
}
}
func (w *Window) childrenUpdated() {
if len(w.allChildren) == 0 {
w.children = nil
return
}
w.children = []Widget{w.allChildren[w.active]}
}
// SetRect sets the position and size of the widget.
func (w *Window) SetRect(r image.Rectangle) {
w.Lock()
defer w.Unlock()
@ -42,6 +37,31 @@ func (w *Window) SetRect(r image.Rectangle) {
}
}
// HandleKeyboard is called when a keyboard event occurs.
func (w *Window) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return true, nil
}
// HandleMouse is called when a mouse event occurs.
func (w *Window) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return true, nil
}
// Draw draws the widget on the screen.
func (w *Window) Draw(screen *ebiten.Image) error {
// TODO draw labels
return nil
}
func (w *Window) childrenUpdated() {
if len(w.allChildren) == 0 {
w.children = nil
return
}
w.children = []Widget{w.allChildren[w.active]}
}
// AddChild adds a child to the window.
func (w *Window) AddChild(wgt ...Widget) {
w.allChildren = append(w.allChildren, wgt...)
@ -55,6 +75,7 @@ func (w *Window) AddChild(wgt ...Widget) {
w.childrenUpdated()
}
// AddChildWithLabel adds a child to the window, as well as an associated label.
func (w *Window) AddChildWithLabel(wgt Widget, label string) {
w.Lock()
defer w.Unlock()
@ -70,16 +91,3 @@ func (w *Window) AddChildWithLabel(wgt Widget, label string) {
w.childrenUpdated()
}
func (w *Window) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return true, nil
}
func (w *Window) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return true, nil
}
func (w *Window) Draw(screen *ebiten.Image) error {
// TODO draw labels
return nil
}