userinterface.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // localizing all of the functions required to construct the user interface
  2. package itashi
  3. import (
  4. "fmt"
  5. tea "github.com/charmbracelet/bubbletea"
  6. )
  7. type model struct {
  8. choices []string
  9. cursor int
  10. selected map[int]struct{}
  11. }
  12. func initialModel() model {
  13. return model{
  14. // Our to-do list is a grocery list
  15. choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"},
  16. // A map which indicates which choices are selected. We're using
  17. // the map like a mathematical set. The keys refer to the indexes
  18. // of the `choices` slice, above.
  19. selected: make(map[int]struct{}),
  20. }
  21. }
  22. func (m model) Init() tea.Cmd {
  23. // Just return `nil`, which means "no I/O right now, please."
  24. return nil
  25. }
  26. func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  27. switch msg := msg.(type) {
  28. // Is it a key press?
  29. case tea.KeyMsg:
  30. // Cool, what was the actual key pressed?
  31. switch msg.String() {
  32. // These keys should exit the program.
  33. case "ctrl+c", "q":
  34. return m, tea.Quit
  35. // The "up" and "k" keys move the cursor up
  36. case "up", "k":
  37. if m.cursor > 0 {
  38. m.cursor--
  39. }
  40. // The "down" and "j" keys move the cursor down
  41. case "down", "j":
  42. if m.cursor < len(m.choices)-1 {
  43. m.cursor++
  44. }
  45. // The "enter" key and the spacebar (a literal space) toggle
  46. // the selected state for the item that the cursor is pointing at.
  47. case "enter", " ":
  48. _, ok := m.selected[m.cursor]
  49. if ok {
  50. delete(m.selected, m.cursor)
  51. } else {
  52. m.selected[m.cursor] = struct{}{}
  53. }
  54. }
  55. }
  56. // Return the updated model to the Bubble Tea runtime for processing.
  57. // Note that we're not returning a command.
  58. return m, nil
  59. }
  60. func (m model) View() string {
  61. // The header
  62. s := "What should we buy at the market?\n\n"
  63. // Iterate over our choices
  64. for i, choice := range m.choices {
  65. // Is the cursor pointing at this choice?
  66. cursor := " " // no cursor
  67. if m.cursor == i {
  68. cursor = ">" // cursor!
  69. }
  70. // Is this choice selected?
  71. checked := " " // not selected
  72. if _, ok := m.selected[i]; ok {
  73. checked = "x" // selected!
  74. }
  75. // Render the row
  76. s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
  77. }
  78. // The footer
  79. s += "\nPress q to quit.\n"
  80. // Send the UI for rendering
  81. return s
  82. }