diff --git a/box.go b/box.go index a588405..2eb77cc 100644 --- a/box.go +++ b/box.go @@ -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{} diff --git a/button.go b/button.go index b18072d..86d968c 100644 --- a/button.go +++ b/button.go @@ -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. diff --git a/doc.go b/doc.go index 65905dc..1d610a1 100644 --- a/doc.go +++ b/doc.go @@ -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. diff --git a/flex.go b/flex.go index bfe2e01..6a5e53c 100644 --- a/flex.go +++ b/flex.go @@ -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() diff --git a/frame.go b/frame.go index cbbf5cf..0436d17 100644 --- a/frame.go +++ b/frame.go @@ -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() diff --git a/game.go b/game.go index fe22df5..be99dc1 100644 --- a/game.go +++ b/game.go @@ -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 diff --git a/grid.go b/grid.go index 68eb0bb..4c4aa1b 100644 --- a/grid.go +++ b/grid.go @@ -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() diff --git a/input.go b/input.go index a432707..dbaf05e 100644 --- a/input.go +++ b/input.go @@ -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 } diff --git a/keybind.go b/keybind.go index ea69a52..15f1379 100644 --- a/keybind.go +++ b/keybind.go @@ -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}, diff --git a/style.go b/style.go index e8d92f3..b4ab00e 100644 --- a/style.go +++ b/style.go @@ -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(), diff --git a/text.go b/text.go index c877ded..aa0a641 100644 --- a/text.go +++ b/text.go @@ -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{} diff --git a/widget.go b/widget.go index feb39f7..715f9c3 100644 --- a/widget.go +++ b/widget.go @@ -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 } diff --git a/window.go b/window.go index 540402f..428ce17 100644 --- a/window.go +++ b/window.go @@ -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 -}