Skip to content

Commit 9d13513

Browse files
feat: add menu
1 parent 29428fa commit 9d13513

File tree

4 files changed

+238
-2
lines changed

4 files changed

+238
-2
lines changed

internal/menu/keys.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package menu
2+
3+
import "github.com/charmbracelet/bubbles/key"
4+
5+
type KeyMap struct {
6+
Quit key.Binding
7+
Help key.Binding
8+
Left key.Binding
9+
Right key.Binding
10+
Up key.Binding
11+
Down key.Binding
12+
Start key.Binding
13+
}
14+
15+
func DefaultKeyMap() *KeyMap {
16+
return &KeyMap{
17+
Quit: key.NewBinding(key.WithKeys("esc", "ctrl+c"), key.WithHelp("esc", "quit")),
18+
Help: key.NewBinding(key.WithKeys("?"), key.WithHelp("?", "help")),
19+
Left: key.NewBinding(key.WithKeys("j", "a", "left"), key.WithHelp("a, j, left", "move left")),
20+
Right: key.NewBinding(key.WithKeys("l", "d", "right"), key.WithHelp("d, l, right", "move right")),
21+
Up: key.NewBinding(key.WithKeys("i", "w", "up"), key.WithHelp("w, i, right", "move up")),
22+
Down: key.NewBinding(key.WithKeys("k", "s", "down"), key.WithHelp("s, k, down", "move down")),
23+
Start: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "start game")),
24+
}
25+
}
26+
27+
func (k *KeyMap) ShortHelp() []key.Binding {
28+
return []key.Binding{
29+
k.Quit,
30+
k.Help,
31+
}
32+
}
33+
34+
func (k *KeyMap) FullHelp() [][]key.Binding {
35+
return [][]key.Binding{
36+
{
37+
k.Quit,
38+
k.Help,
39+
k.Left,
40+
k.Right,
41+
},
42+
{
43+
k.Up,
44+
k.Down,
45+
k.Start,
46+
},
47+
}
48+
}

internal/menu/model.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package menu
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Broderick-Westrope/tetrigo/internal/marathon"
7+
"github.com/charmbracelet/bubbles/help"
8+
"github.com/charmbracelet/bubbles/key"
9+
tea "github.com/charmbracelet/bubbletea"
10+
"github.com/charmbracelet/lipgloss"
11+
)
12+
13+
const (
14+
modeMenu = iota
15+
modeGame
16+
)
17+
18+
type option interface{}
19+
20+
type setting struct {
21+
name string
22+
options []option
23+
index int
24+
}
25+
26+
type Model struct {
27+
settings []setting
28+
settingIndex int
29+
game tea.Model
30+
mode int
31+
32+
keys *KeyMap
33+
styles *Styles
34+
help help.Model
35+
}
36+
37+
func InitialModel() *Model {
38+
m := Model{
39+
settings: []setting{
40+
{
41+
name: "Level",
42+
options: []option{1, 5, 10, 15},
43+
index: 0,
44+
},
45+
{
46+
name: "Players",
47+
options: []option{uint(1)},
48+
index: 0,
49+
},
50+
{
51+
name: "Mode",
52+
options: []option{"Marathon"},
53+
index: 0,
54+
},
55+
},
56+
settingIndex: 0,
57+
keys: DefaultKeyMap(),
58+
styles: DefaultStyles(),
59+
mode: modeMenu,
60+
help: help.New(),
61+
}
62+
return &m
63+
}
64+
65+
func (m Model) Init() tea.Cmd {
66+
return nil
67+
}
68+
69+
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
70+
if m.mode == modeGame {
71+
return m.game.Update(msg)
72+
}
73+
74+
switch msg := msg.(type) {
75+
case tea.KeyMsg:
76+
switch {
77+
case key.Matches(msg, m.keys.Quit):
78+
return m, tea.Quit
79+
case key.Matches(msg, m.keys.Left):
80+
m.settingIndex--
81+
if m.settingIndex < 0 {
82+
m.settingIndex = len(m.settings) - 1
83+
}
84+
case key.Matches(msg, m.keys.Right):
85+
m.settingIndex++
86+
if m.settingIndex >= len(m.settings) {
87+
m.settingIndex = 0
88+
}
89+
case key.Matches(msg, m.keys.Up):
90+
m.settings[m.settingIndex].index--
91+
if m.settings[m.settingIndex].index < 0 {
92+
m.settings[m.settingIndex].index = len(m.settings[m.settingIndex].options) - 1
93+
}
94+
case key.Matches(msg, m.keys.Down):
95+
m.settings[m.settingIndex].index++
96+
if m.settings[m.settingIndex].index >= len(m.settings[m.settingIndex].options) {
97+
m.settings[m.settingIndex].index = 0
98+
}
99+
case key.Matches(msg, m.keys.Start):
100+
return m, m.startGame()
101+
case key.Matches(msg, m.keys.Help):
102+
m.help.ShowAll = !m.help.ShowAll
103+
}
104+
}
105+
106+
return m, nil
107+
}
108+
109+
func (m Model) View() string {
110+
settings := make([]string, len(m.settings))
111+
for i := range m.settings {
112+
settings[i] = m.renderSetting(i, i == m.settingIndex)
113+
}
114+
return lipgloss.JoinVertical(lipgloss.Center,
115+
renderTitle(),
116+
lipgloss.JoinHorizontal(lipgloss.Top, settings...),
117+
) + "\n" + m.help.View(m.keys)
118+
}
119+
120+
func renderTitle() string {
121+
output := ` ______________________ ______________
122+
/_ __/ ____/_ __/ __ \/ _/ ____/ __ \
123+
/ / / __/ / / / /_/ // // / __/ / / /
124+
/ / / /___ / / / _, _// // /_/ / /_/ /
125+
/_/ /_____/ /_/ /_/ |_/___/\____/\____/
126+
`
127+
return output
128+
}
129+
130+
func (m *Model) renderSetting(index int, isSelected bool) string {
131+
output := fmt.Sprintf("%v:\n", m.settings[index].name)
132+
for i, option := range m.settings[index].options {
133+
if i == m.settings[index].index {
134+
output += " > "
135+
} else {
136+
output += " "
137+
}
138+
output += fmt.Sprintf("%v", option)
139+
if i < len(m.settings[index].options)-1 {
140+
output += "\n"
141+
}
142+
}
143+
if isSelected {
144+
return m.styles.settingSelected.Render(output)
145+
}
146+
return m.styles.settingUnselected.Render(output)
147+
}
148+
149+
func (m *Model) startGame() tea.Cmd {
150+
var level uint
151+
var mode string
152+
// var players uint
153+
for _, setting := range m.settings {
154+
switch setting.name {
155+
case "Level":
156+
intLevel := setting.options[setting.index].(int)
157+
level = uint(intLevel)
158+
// case "Players":
159+
// players = setting.options[setting.index].(uint)
160+
case "Mode":
161+
mode = setting.options[setting.index].(string)
162+
}
163+
}
164+
165+
switch mode {
166+
case "Marathon":
167+
m.mode = modeGame
168+
m.game = marathon.InitialModel(level)
169+
return m.game.Init()
170+
}
171+
return nil
172+
}

internal/menu/style.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package menu
2+
3+
import "github.com/charmbracelet/lipgloss"
4+
5+
type Styles struct {
6+
settingSelected lipgloss.Style
7+
settingUnselected lipgloss.Style
8+
}
9+
10+
func DefaultStyles() *Styles {
11+
s := Styles{
12+
settingSelected: lipgloss.NewStyle().Padding(1, 2),
13+
}
14+
s.settingUnselected = s.settingSelected.Copy().Foreground(lipgloss.Color("241"))
15+
return &s
16+
}

main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import (
44
"fmt"
55
"os"
66

7-
"github.com/Broderick-Westrope/tetrigo/internal/marathon"
7+
"github.com/Broderick-Westrope/tetrigo/internal/menu"
88
tea "github.com/charmbracelet/bubbletea"
99
)
1010

1111
func main() {
12-
p := tea.NewProgram(marathon.InitialModel(1))
12+
p := tea.NewProgram(menu.InitialModel())
1313
if _, err := p.Run(); err != nil {
1414
fmt.Printf("Alas, there's been an error: %v", err)
1515
os.Exit(1)

0 commit comments

Comments
 (0)