1package ui
2
3import (
4 "image"
5
6 "github.com/charmbracelet/bubbles/v2/key"
7 tea "github.com/charmbracelet/bubbletea/v2"
8 "github.com/charmbracelet/crush/internal/app"
9 "github.com/charmbracelet/lipgloss/v2"
10 uv "github.com/charmbracelet/ultraviolet"
11)
12
13type uiState uint8
14
15const (
16 uiStateMain uiState = iota
17)
18
19type UI struct {
20 app *app.App
21 com *Common
22
23 width, height int
24 state uiState
25
26 keyMap KeyMap
27 styles Styles
28
29 dialog *Overlay
30}
31
32func New(com *Common, app *app.App) *UI {
33 return &UI{
34 app: app,
35 com: com,
36 dialog: NewDialogOverlay(),
37 keyMap: DefaultKeyMap(),
38 }
39}
40
41func (m *UI) Init() tea.Cmd {
42 return nil
43}
44
45func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
46 var cmds []tea.Cmd
47 switch msg := msg.(type) {
48 case tea.WindowSizeMsg:
49 m.width = msg.Width
50 m.height = msg.Height
51 case tea.KeyPressMsg:
52 switch m.state {
53 case uiStateMain:
54 switch {
55 case key.Matches(msg, m.keyMap.Quit):
56 quitDialog := NewQuitDialog(m.com)
57 if !m.dialog.ContainsDialog(quitDialog.ID()) {
58 m.dialog.AddDialog(quitDialog)
59 return m, nil
60 }
61 }
62 }
63 }
64
65 updatedDialog, cmd := m.dialog.Update(msg)
66 m.dialog = updatedDialog
67 if cmd != nil {
68 cmds = append(cmds, cmd)
69 }
70
71 return m, tea.Batch(cmds...)
72}
73
74func (m *UI) View() tea.View {
75 var v tea.View
76
77 // The screen area we're working with
78 area := image.Rect(0, 0, m.width, m.height)
79 layers := []*lipgloss.Layer{}
80
81 if dialogView := m.dialog.View(); dialogView != "" {
82 dialogWidth, dialogHeight := lipgloss.Width(dialogView), lipgloss.Height(dialogView)
83 dialogArea := centerRect(area, dialogWidth, dialogHeight)
84 layers = append(layers,
85 lipgloss.NewLayer(dialogView).
86 X(dialogArea.Min.X).
87 Y(dialogArea.Min.Y),
88 )
89 }
90
91 v.Layer = lipgloss.NewCanvas(layers...)
92
93 return v
94}
95
96// centerRect returns a new [Rectangle] centered within the given area with the
97// specified width and height.
98func centerRect(area uv.Rectangle, width, height int) uv.Rectangle {
99 centerX := area.Min.X + area.Dx()/2
100 centerY := area.Min.Y + area.Dy()/2
101 minX := centerX - width/2
102 minY := centerY - height/2
103 maxX := minX + width
104 maxY := minY + height
105 return image.Rect(minX, minY, maxX, maxY)
106}