org/internal/ui/stateform.go
2026-07-22 10:57:42 -04:00

116 lines
2.7 KiB
Go

package ui
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/textinput"
)
// stateForm backs the add/edit state form (modeSettingsStateForm).
// field: 0 = name, 1 = color, 2 = done.
type stateForm struct {
editing bool
index int // index into config.States.States when editing
name textinput.Model
color textinput.Model
done bool
field int
}
// startStateForm initializes the form and switches into the form mode.
// editing=false adds a new state; editing=true edits config.States.States[index].
func (m *uiModel) startStateForm(editing bool, index int) {
name := textinput.New()
name.Placeholder = "STATE NAME"
name.CharLimit = 32
name.Width = 30
color := textinput.New()
color.Placeholder = "0-255"
color.CharLimit = 3
color.Width = 12
sf := stateForm{editing: editing, index: index, name: name, color: color}
if editing {
st := m.config.States.States[index]
sf.name.SetValue(st.Name)
sf.color.SetValue(st.Color)
sf.done = st.Done
sf.field = 1 // name is read-only when editing
} else {
sf.color.SetValue("99")
sf.field = 0
}
m.stateForm = sf
m.focusStateFormField()
m.mode = modeSettingsStateForm
}
// stateFormMinField is the lowest navigable field: name (0) when adding,
// color (1) when editing (name is read-only).
func (m *uiModel) stateFormMinField() int {
if m.stateForm.editing {
return 1
}
return 0
}
// moveStateFormField shifts the focused field by delta, clamped to [min, 2].
func (m *uiModel) moveStateFormField(delta int) {
f := m.stateForm.field + delta
if min := m.stateFormMinField(); f < min {
f = min
}
if f > 2 {
f = 2
}
m.stateForm.field = f
m.focusStateFormField()
}
// focusStateFormField focuses the active text field (none for the Done field).
func (m *uiModel) focusStateFormField() {
m.stateForm.name.Blur()
m.stateForm.color.Blur()
switch m.stateForm.field {
case 0:
m.stateForm.name.Focus()
case 1:
m.stateForm.color.Focus()
}
}
// applyStateForm commits the form to config. It mutates config only (no Save).
// Returns false (without mutating) if a new state's name is empty.
func (m *uiModel) applyStateForm() bool {
sf := &m.stateForm
color := strings.TrimSpace(sf.color.Value())
if sf.editing {
st := &m.config.States.States[sf.index]
if color != "" {
st.Color = color
}
st.Done = sf.done
m.setStatus(fmt.Sprintf("Updated state '%s' (saved)", st.Name))
return true
}
name := strings.ToUpper(strings.TrimSpace(sf.name.Value()))
if name == "" {
m.setStatus("State name required")
return false
}
if color == "" {
color = "99"
}
if sf.done {
m.config.AddDoneState(name, color)
} else {
m.config.AddState(name, color)
}
m.setStatus(fmt.Sprintf("Added state '%s' (saved)", name))
return true
}