dialog.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package core
  2. import (
  3. "github.com/charmbracelet/bubbles/key"
  4. tea "github.com/charmbracelet/bubbletea"
  5. "github.com/charmbracelet/lipgloss"
  6. "github.com/kujtimiihoxha/termai/internal/tui/layout"
  7. "github.com/kujtimiihoxha/termai/internal/tui/util"
  8. )
  9. type SizeableModel interface {
  10. tea.Model
  11. layout.Sizeable
  12. }
  13. type DialogMsg struct {
  14. Content SizeableModel
  15. WidthRatio float64
  16. HeightRatio float64
  17. MinWidth int
  18. MinHeight int
  19. }
  20. type DialogCloseMsg struct{}
  21. type KeyBindings struct {
  22. Return key.Binding
  23. }
  24. var keys = KeyBindings{
  25. Return: key.NewBinding(
  26. key.WithKeys("esc"),
  27. key.WithHelp("esc", "close"),
  28. ),
  29. }
  30. type DialogCmp interface {
  31. tea.Model
  32. layout.Bindings
  33. }
  34. type dialogCmp struct {
  35. content SizeableModel
  36. screenWidth int
  37. screenHeight int
  38. widthRatio float64
  39. heightRatio float64
  40. minWidth int
  41. minHeight int
  42. width int
  43. height int
  44. }
  45. func (d *dialogCmp) Init() tea.Cmd {
  46. return nil
  47. }
  48. func (d *dialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  49. switch msg := msg.(type) {
  50. case tea.WindowSizeMsg:
  51. d.screenWidth = msg.Width
  52. d.screenHeight = msg.Height
  53. d.width = max(int(float64(d.screenWidth)*d.widthRatio), d.minWidth)
  54. d.height = max(int(float64(d.screenHeight)*d.heightRatio), d.minHeight)
  55. if d.content != nil {
  56. d.content.SetSize(d.width, d.height)
  57. }
  58. return d, nil
  59. case DialogMsg:
  60. d.content = msg.Content
  61. d.widthRatio = msg.WidthRatio
  62. d.heightRatio = msg.HeightRatio
  63. d.minWidth = msg.MinWidth
  64. d.minHeight = msg.MinHeight
  65. d.width = max(int(float64(d.screenWidth)*d.widthRatio), d.minWidth)
  66. d.height = max(int(float64(d.screenHeight)*d.heightRatio), d.minHeight)
  67. if d.content != nil {
  68. d.content.SetSize(d.width, d.height)
  69. }
  70. case DialogCloseMsg:
  71. d.content = nil
  72. return d, nil
  73. case tea.KeyMsg:
  74. if key.Matches(msg, keys.Return) {
  75. return d, util.CmdHandler(DialogCloseMsg{})
  76. }
  77. }
  78. if d.content != nil {
  79. u, cmd := d.content.Update(msg)
  80. d.content = u.(SizeableModel)
  81. return d, cmd
  82. }
  83. return d, nil
  84. }
  85. func (d *dialogCmp) BindingKeys() []key.Binding {
  86. bindings := []key.Binding{keys.Return}
  87. if d.content == nil {
  88. return bindings
  89. }
  90. if c, ok := d.content.(layout.Bindings); ok {
  91. return append(bindings, c.BindingKeys()...)
  92. }
  93. return bindings
  94. }
  95. func (d *dialogCmp) View() string {
  96. return lipgloss.NewStyle().Width(d.width).Height(d.height).Render(d.content.View())
  97. }
  98. func NewDialogCmp() DialogCmp {
  99. return &dialogCmp{}
  100. }