1package modes
2
3import (
4 "github.com/patriceckhart/zot/packages/tui"
5)
6
7type settingsDialog struct {
8 active bool
9 items []settingsItem
10 cursor int
11 selecting bool
12 optionCursor int
13}
14
15type settingsItem struct {
16 key string
17 label string
18 desc string
19 value bool
20 options []settingsOption
21 choice int
22 disabled bool
23 hint string
24}
25
26type settingsOption struct {
27 value string
28 label string
29 desc string
30}
31
32type settingsAction struct {
33 Toggle bool
34 Key string
35 Value bool
36 StringValue string
37 Close bool
38}
39
40func newSettingsDialog() *settingsDialog { return &settingsDialog{} }
41
42func (d *settingsDialog) Open(items []settingsItem) bool {
43 if len(items) == 0 {
44 return false
45 }
46 d.items = items
47 d.cursor = 0
48 d.selecting = false
49 d.optionCursor = 0
50 d.active = true
51 return true
52}
53
54func (d *settingsDialog) Close() {
55 d.active = false
56 d.selecting = false
57}
58func (d *settingsDialog) Active() bool { return d != nil && d.active }
59
60func (d *settingsDialog) HandleKey(k tui.Key) settingsAction {
61 if d.selecting {
62 return d.handleOptionKey(k)
63 }
64 switch k.Kind {
65 case tui.KeyUp:
66 if d.cursor > 0 {
67 d.cursor--
68 }
69 case tui.KeyDown:
70 if d.cursor < len(d.items)-1 {
71 d.cursor++
72 }
73 case tui.KeyEsc:
74 d.Close()
75 return settingsAction{Close: true}
76 case tui.KeyEnter:
77 return d.toggleCurrent()
78 case tui.KeyRune:
79 if k.Rune == ' ' {
80 return d.toggleCurrent()
81 }
82 }
83 return settingsAction{}
84}
85
86func (d *settingsDialog) handleOptionKey(k tui.Key) settingsAction {
87 it := d.items[d.cursor]
88 switch k.Kind {
89 case tui.KeyUp:
90 if d.optionCursor > 0 {
91 d.optionCursor--
92 }
93 case tui.KeyDown:
94 if d.optionCursor < len(it.options)-1 {
95 d.optionCursor++
96 }
97 case tui.KeyEsc:
98 d.selecting = false
99 case tui.KeyEnter:
100 return d.selectCurrentOption()
101 case tui.KeyRune:
102 if k.Rune == ' ' {
103 return d.selectCurrentOption()
104 }
105 }
106 return settingsAction{}
107}
108
109func (d *settingsDialog) toggleCurrent() settingsAction {
110 if len(d.items) == 0 {
111 d.Close()
112 return settingsAction{Close: true}
113 }
114 it := d.items[d.cursor]
115 if it.disabled {
116 return settingsAction{}
117 }
118 if len(it.options) > 0 {
119 d.optionCursor = it.choice
120 if d.optionCursor < 0 || d.optionCursor >= len(it.options) {
121 d.optionCursor = 0
122 }
123 d.selecting = true
124 return settingsAction{}
125 }
126 it.value = !it.value
127 d.items[d.cursor] = it
128 return settingsAction{Toggle: true, Key: it.key, Value: it.value}
129}
130
131func (d *settingsDialog) selectCurrentOption() settingsAction {
132 if len(d.items) == 0 {
133 d.Close()
134 return settingsAction{Close: true}
135 }
136 it := d.items[d.cursor]
137 if len(it.options) == 0 {
138 d.selecting = false
139 return settingsAction{}
140 }
141 if d.optionCursor < 0 || d.optionCursor >= len(it.options) {
142 d.optionCursor = 0
143 }
144 it.choice = d.optionCursor
145 d.items[d.cursor] = it
146 d.selecting = false
147 return settingsAction{Toggle: true, Key: it.key, StringValue: it.options[it.choice].value}
148}
149
150func (d *settingsDialog) Render(th tui.Theme, width int) []string {
151 if !d.Active() {
152 return nil
153 }
154 if d.selecting {
155 return d.renderOptions(th, width)
156 }
157 var lines []string
158 lines = append(lines, frameHeader(th, "settings", width))
159 lines = append(lines, th.FG256(th.Muted, "change with enter/space, esc to close:"))
160 for i, it := range d.items {
161 box := "[ ]"
162 if it.value {
163 box = "[x]"
164 }
165 plain := " " + box + " " + it.label
166 if len(it.options) > 0 {
167 box = "[>]"
168 if it.choice < 0 || it.choice >= len(it.options) {
169 it.choice = 0
170 }
171 plain = " " + box + " " + it.label + ": " + it.options[it.choice].label
172 }
173 if it.hint != "" {
174 plain += " " + th.FG256(th.Muted, "("+it.hint+")")
175 }
176 if it.disabled {
177 lines = append(lines, th.FG256(th.Muted, plain))
178 } else if i == d.cursor {
179 lines = append(lines, th.PadHighlight(plain, width))
180 } else {
181 lines = append(lines, plain)
182 }
183 if it.desc != "" {
184 lines = append(lines, th.FG256(th.Muted, " "+it.desc))
185 }
186 }
187 lines = append(lines, frameRule(th, width))
188 return lines
189}
190
191func (d *settingsDialog) renderOptions(th tui.Theme, width int) []string {
192 if len(d.items) == 0 || d.cursor < 0 || d.cursor >= len(d.items) {
193 d.selecting = false
194 return d.Render(th, width)
195 }
196 it := d.items[d.cursor]
197 lines := []string{frameHeader(th, "settings: "+it.label, width)}
198 if it.desc != "" {
199 lines = append(lines, th.FG256(th.Muted, it.desc))
200 }
201 lines = append(lines, th.FG256(th.Muted, "select with enter/space, esc to go back:"))
202 for idx, opt := range it.options {
203 marker := " "
204 if idx == it.choice {
205 marker = "✓ "
206 }
207 plain := " " + marker + opt.label
208 if idx == d.optionCursor {
209 lines = append(lines, th.PadHighlight(plain, width))
210 } else {
211 lines = append(lines, plain)
212 }
213 if opt.desc != "" {
214 lines = append(lines, th.FG256(th.Muted, " "+opt.desc))
215 }
216 }
217 lines = append(lines, frameRule(th, width))
218 return lines
219}