From bcd6025aa371904d8d56069ce936d72142d10082 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 19 Oct 2025 11:36:12 +0200 Subject: [PATCH] refactor: move ui code from game/state/handlers/menu.go to its own package in game/ui --- game/state/handlers/menu.go | 63 +++++++++++-------------------------- game/ui/button.go | 33 +++++++++++++++++++ game/ui/menu.go | 59 ++++++++++++++++++++++++++++++++++ game/ui/widget.go | 6 ++++ tetris.go | 2 +- 5 files changed, 118 insertions(+), 45 deletions(-) create mode 100644 game/ui/button.go create mode 100644 game/ui/menu.go create mode 100644 game/ui/widget.go diff --git a/game/state/handlers/menu.go b/game/state/handlers/menu.go index 53e38a9..e544d76 100644 --- a/game/state/handlers/menu.go +++ b/game/state/handlers/menu.go @@ -2,59 +2,39 @@ package handlers import ( "tetris/assets" - "tetris/engine/audio" "tetris/engine/render" "tetris/game" "tetris/game/state" + "tetris/game/ui" rl "github.com/gen2brain/raylib-go/raylib" ) -type entry struct { - label string - state string +type MainMenu struct { + menu ui.Menu } -type Menu struct { - selected int - entries []entry -} - -func NewMenu() *Menu { - return &Menu{ - selected: 0, - entries: []entry{ - {"Start", "gameplay"}, - {"Quit", "quit"}, - }, +func NewMainMenu(fsm state.Transitioner) *MainMenu { + return &MainMenu{ + menu: ui.NewMenu([]ui.Widget{ + ui.NewButton("Start", func() { fsm.Switch("gameplay") }), + ui.NewButton("Quit", func() { fsm.Switch("quit") }), + }), } } -func (menu *Menu) Enter() { - menu.selected = 0 +func (main *MainMenu) Enter() { + main.menu.Select(0) } -func (Menu) Exit() { +func (MainMenu) Exit() { } -func (menu *Menu) Update(fsm state.Transitioner, delta float32) { - if rl.IsKeyPressed(rl.KeyEnter) { - fsm.Switch(menu.entries[menu.selected].state) - audio.Play(assets.SFX_MENU_ENTER) - } else if rl.IsKeyPressed(rl.KeyDown) { - if menu.selected+1 < len(menu.entries) { - menu.selected = menu.selected + 1 - audio.Play(assets.SFX_MENU_SELECT) - } - } else if rl.IsKeyPressed(rl.KeyUp) { - if menu.selected-1 >= 0 { - menu.selected = menu.selected - 1 - audio.Play(assets.SFX_MENU_SELECT) - } - } +func (menu *MainMenu) Update(fsm state.Transitioner, delta float32) { + menu.menu.HandleInput() } -func (Menu) renderLogo(offset_x, offset_y int32) { +func (MainMenu) renderLogo(offset_x, offset_y int32) { for y := range assets.LOGO_HEIGHT { for x := range assets.LOGO_STRIDE { index := assets.Logo[x+(y*assets.LOGO_STRIDE)] @@ -76,20 +56,15 @@ func (Menu) renderLogo(offset_x, offset_y int32) { } } -func (menu Menu) renderEntries(offset_x, offset_y int32) { +func (menu MainMenu) renderEntries(offset_x, offset_y int32) { y := offset_y - for i, entry := range menu.entries { - - col := rl.White - if i == menu.selected { - col = rl.Red - } - render.DrawTextCenter(offset_x, y, 32, entry.label, col) + for i, entry := range menu.menu.Entries() { + entry.Draw(offset_x, y, menu.menu.IsSelected(i)) y += 40 } } -func (menu Menu) Render() { +func (menu MainMenu) Render() { render.Begin(rl.Black) menu.renderLogo(20, 150) diff --git a/game/ui/button.go b/game/ui/button.go new file mode 100644 index 0000000..5677142 --- /dev/null +++ b/game/ui/button.go @@ -0,0 +1,33 @@ +package ui + +import ( + "tetris/engine/render" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type Button struct { + Text string + Action func() +} + +func NewButton(text string, action func()) Button { + return Button{ + Text: text, + Action: action, + } +} + +func (b Button) HandleInput() { + if rl.IsKeyPressed(rl.KeyEnter) { + b.Action() + } +} + +func (b Button) Draw(x, y int32, selected bool) { + col := rl.White + if selected { + col = rl.Red + } + render.DrawTextCenter(x, y, 32, b.Text, col) +} diff --git a/game/ui/menu.go b/game/ui/menu.go new file mode 100644 index 0000000..a6c61e2 --- /dev/null +++ b/game/ui/menu.go @@ -0,0 +1,59 @@ +package ui + +import ( + "tetris/assets" + "tetris/engine/audio" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type Menu struct { + selected int + entries []Widget +} + +func NewMenu(entries []Widget) Menu { + return Menu{ + entries: entries, + } +} + +func (menu Menu) Entries() []Widget { + return menu.entries +} + +func (menu *Menu) Select(index int) { + menu.selected = min(index, len(menu.entries)-1) +} + +func (menu Menu) Selected() Widget { + return menu.entries[menu.selected] +} + +func (menu Menu) IsSelected(index int) bool { + return menu.selected == index +} + +func (menu *Menu) Next() { + if menu.selected+1 < len(menu.entries) { + menu.selected = menu.selected + 1 + audio.Play(assets.SFX_MENU_SELECT) + } +} + +func (menu *Menu) Previous() { + if menu.selected-1 >= 0 { + menu.selected = menu.selected - 1 + audio.Play(assets.SFX_MENU_SELECT) + } +} + +func (menu *Menu) HandleInput() { + if rl.IsKeyPressed(rl.KeyDown) { + menu.Next() + } else if rl.IsKeyPressed(rl.KeyUp) { + menu.Previous() + } else { + menu.Selected().HandleInput() + } +} diff --git a/game/ui/widget.go b/game/ui/widget.go new file mode 100644 index 0000000..3f03541 --- /dev/null +++ b/game/ui/widget.go @@ -0,0 +1,6 @@ +package ui + +type Widget interface { + HandleInput() + Draw(x, y int32, selected bool) +} diff --git a/tetris.go b/tetris.go index 43830ac..61a9ce6 100644 --- a/tetris.go +++ b/tetris.go @@ -41,7 +41,7 @@ func main() { // Setup state machine. fsm := machine.New() - fsm.Register("menu", handlers.NewMenu()) + fsm.Register("menu", handlers.NewMainMenu(fsm)) fsm.Register("gameover", &handlers.GameOver{}) fsm.Register("gameplay", handlers.NewGamePlay()) fsm.Start("menu")