clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

bug/architecture-test-trim-path
puzzletea / app / screen_support.go
screen_support.go 4.8K
1package app23import (4 "github.com/FelineStateMachine/puzzletea/theme"5 "github.com/FelineStateMachine/puzzletea/ui"67 "charm.land/bubbles/v2/key"8 "charm.land/bubbles/v2/list"9 "charm.land/bubbles/v2/spinner"10 tea "charm.land/bubbletea/v2"11)1213type helpSelectScreen struct {14 width int15 height int16 help helpState17}1819func (s helpSelectScreen) Resize(width, height int) screenModel {20 s.width = width21 s.height = height22 listWidth, listHeight := helpSelectListSize(width, height, s.help.selectList)23 s.help.selectList.SetSize(listWidth, listHeight)24 return s25}2627func (s helpSelectScreen) Update(msg tea.Msg) (screenModel, tea.Cmd, screenAction) {28 keyMsg, ok := msg.(tea.KeyPressMsg)29 if ok {30 switch {31 case key.Matches(keyMsg, rootKeys.Enter):32 entry, _ := selectedCategoryEntry(s.help.selectList.SelectedItem())33 return s, nil, helpSelectEnterAction{entry: entry}34 case key.Matches(keyMsg, rootKeys.Escape):35 return s, nil, backAction{target: optionsMenuView}36 }37 }3839 var cmd tea.Cmd40 s.help.selectList, cmd = s.help.selectList.Update(msg)41 return s, cmd, nil42}4344func (s helpSelectScreen) View(notice noticeState) string {45 return renderPanelView(s.width, s.height, notice,46 "How to Play",47 s.help.selectList.View(),48 "↑/↓ navigate • enter select • esc back",49 )50}5152type helpDetailScreen struct {53 width int54 height int55 help helpState56}5758func (s helpDetailScreen) Resize(width, height int) screenModel {59 m := model{60 width: width,61 height: height,62 help: s.help,63 }64 m = m.updateHelpDetailViewport()65 s.width = width66 s.height = height67 s.help = m.help68 return s69}7071func (s helpDetailScreen) Update(msg tea.Msg) (screenModel, tea.Cmd, screenAction) {72 keyMsg, ok := msg.(tea.KeyPressMsg)73 if ok && key.Matches(keyMsg, rootKeys.Escape) {74 return s, nil, backAction{target: helpSelectView}75 }7677 var cmd tea.Cmd78 s.help.viewport, cmd = s.help.viewport.Update(msg)79 return s, cmd, nil80}8182func (s helpDetailScreen) View(notice noticeState) string {83 return renderPanelView(s.width, s.height, notice,84 s.help.category.Definition.Name+" — Guide",85 s.help.viewport.View(),86 "↑/↓ scroll • esc back",87 )88}8990type statsScreen struct {91 width int92 height int93 stats statsState94}9596func (s statsScreen) Resize(width, height int) screenModel {97 m := model{98 width: width,99 height: height,100 stats: s.stats,101 }102 m = m.updateStatsViewport()103 s.width = width104 s.height = height105 s.stats = m.stats106 return s107}108109func (s statsScreen) Update(msg tea.Msg) (screenModel, tea.Cmd, screenAction) {110 keyMsg, ok := msg.(tea.KeyPressMsg)111 if ok && key.Matches(keyMsg, rootKeys.Escape) {112 return s, nil, backAction{target: mainMenuView}113 }114115 var cmd tea.Cmd116 s.stats.viewport, cmd = s.stats.viewport.Update(msg)117 return s, cmd, nil118}119120func (s statsScreen) View(notice noticeState) string {121 return renderStatsView(s.stats, s.width, s.height, notice)122}123124type themeSelectScreen struct {125 width int126 height int127 theme themeState128}129130func (s themeSelectScreen) Resize(width, height int) screenModel {131 const maxVisibleItems = 8132 menuW := min(width, 64)133 listW := min(menuW, theme.MaxNameLen+4)134 s.width = width135 s.height = height136 s.theme.list.SetSize(listW, min(height, maxVisibleItems*3))137 return s138}139140func (s themeSelectScreen) Update(msg tea.Msg) (screenModel, tea.Cmd, screenAction) {141 keyMsg, ok := msg.(tea.KeyPressMsg)142 if ok {143 switch {144 case s.theme.list.SettingFilter() && s.theme.list.FilterValue() == "" && key.Matches(keyMsg, rootKeys.Enter):145 s.theme.list.ResetFilter()146 return s, nil, nil147 case s.theme.list.SettingFilter() && key.Matches(keyMsg, rootKeys.Enter):148 case s.theme.list.FilterState() != list.Unfiltered && key.Matches(keyMsg, rootKeys.Escape):149 case key.Matches(keyMsg, rootKeys.Enter):150 return s, nil, confirmThemeAction{theme: s.theme}151 case key.Matches(keyMsg, rootKeys.Escape):152 return s, nil, backAction{target: optionsMenuView}153 }154 }155156 prev := s.theme.list.Index()157 var cmd tea.Cmd158 s.theme.list, cmd = s.theme.list.Update(msg)159 if s.theme.list.Index() != prev {160 if item, ok := s.theme.list.SelectedItem().(ui.MenuItem); ok {161 return s, cmd, previewThemeAction{name: item.ItemTitle}162 }163 }164 return s, cmd, nil165}166167func (s themeSelectScreen) View(notice noticeState) string {168 return themeSelectViewContent(s.width, s.height, s.theme, notice)169}170171type generatingScreen struct {172 width int173 height int174 spinner spinner.Model175}176177func (s generatingScreen) Resize(width, height int) screenModel {178 s.width = width179 s.height = height180 return s181}182183func (s generatingScreen) Update(msg tea.Msg) (screenModel, tea.Cmd, screenAction) {184 var cmd tea.Cmd185 s.spinner, cmd = s.spinner.Update(msg)186 return s, cmd, nil187}188189func (s generatingScreen) View(notice noticeState) string {190 content := s.spinner.View() + " Generating puzzle..."191 box := ui.GeneratingFrame().Render(appendNoticeContent(s.width, notice, content))192 return ui.CenterView(s.width, s.height, box)193}