Updated TUI field sizing
This commit is contained in:
+27
-2
@@ -63,10 +63,27 @@ type form struct {
|
|||||||
cursor int
|
cursor int
|
||||||
busy bool
|
busy bool
|
||||||
err string
|
err string
|
||||||
|
width int // current terminal width; inputs resize to fill it
|
||||||
|
|
||||||
submit func(values []string) tea.Cmd
|
submit func(values []string) tea.Cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultFieldWidth is the fallback input width used before the first
|
||||||
|
// WindowSizeMsg has arrived. Once we know the terminal size, inputs
|
||||||
|
// grow to fill the available horizontal space.
|
||||||
|
const defaultFieldWidth = 40
|
||||||
|
|
||||||
|
// fieldWidthFor derives the per-input visible width from the terminal
|
||||||
|
// width. It subtracts the modal's border+padding (6) and the form's
|
||||||
|
// label indent (2), then a couple of chars of safety margin.
|
||||||
|
func fieldWidthFor(termWidth int) int {
|
||||||
|
w := termWidth - 12
|
||||||
|
if w < defaultFieldWidth {
|
||||||
|
return defaultFieldWidth
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
func newForm(title string, fields []formField, submit func([]string) tea.Cmd) *form {
|
func newForm(title string, fields []formField, submit func([]string) tea.Cmd) *form {
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
fields[i].input.Prompt = ""
|
fields[i].input.Prompt = ""
|
||||||
@@ -89,7 +106,7 @@ func textField(label, hint string, required bool) formField {
|
|||||||
// contents and can tweak instead of retyping everything.
|
// contents and can tweak instead of retyping everything.
|
||||||
func textFieldWithValue(label, hint, value string, required bool) formField {
|
func textFieldWithValue(label, hint, value string, required bool) formField {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Width = 40
|
ti.Width = defaultFieldWidth
|
||||||
ti.Placeholder = hint
|
ti.Placeholder = hint
|
||||||
if value != "" {
|
if value != "" {
|
||||||
ti.SetValue(value)
|
ti.SetValue(value)
|
||||||
@@ -106,7 +123,7 @@ func passwordField(label, hint string) formField {
|
|||||||
// the actual value leaking on-screen.
|
// the actual value leaking on-screen.
|
||||||
func passwordFieldWithValue(label, hint, value string) formField {
|
func passwordFieldWithValue(label, hint, value string) formField {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Width = 40
|
ti.Width = defaultFieldWidth
|
||||||
ti.Placeholder = hint
|
ti.Placeholder = hint
|
||||||
ti.EchoMode = textinput.EchoPassword
|
ti.EchoMode = textinput.EchoPassword
|
||||||
ti.EchoCharacter = '•'
|
ti.EchoCharacter = '•'
|
||||||
@@ -148,6 +165,14 @@ func (f *form) View() string {
|
|||||||
|
|
||||||
func (f *form) Update(msg tea.Msg) (modal, tea.Cmd) {
|
func (f *form) Update(msg tea.Msg) (modal, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
f.width = msg.Width
|
||||||
|
w := fieldWidthFor(msg.Width)
|
||||||
|
for i := range f.fields {
|
||||||
|
f.fields[i].input.Width = w
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
|
||||||
case formSubmitErr:
|
case formSubmitErr:
|
||||||
f.busy = false
|
f.busy = false
|
||||||
f.err = string(msg)
|
f.err = string(msg)
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.width, m.height = msg.Width, msg.Height
|
m.width, m.height = msg.Width, msg.Height
|
||||||
m.resizeTabs()
|
m.resizeTabs()
|
||||||
|
if m.modal != nil {
|
||||||
|
m.modal, _ = m.modal.Update(msg)
|
||||||
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
case tickMsg:
|
case tickMsg:
|
||||||
@@ -175,8 +178,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
// Modal grabs all input while open.
|
// Modal grabs all input while open.
|
||||||
if m.modal != nil {
|
if m.modal != nil {
|
||||||
|
prev := m.modal
|
||||||
newModal, cmd := m.modal.Update(msg)
|
newModal, cmd := m.modal.Update(msg)
|
||||||
m.modal = newModal
|
m.modal = newModal
|
||||||
|
// If the modal handed off to a different modal (e.g. picker →
|
||||||
|
// form), seed the new one with the current terminal size so its
|
||||||
|
// text inputs can size themselves on first paint.
|
||||||
|
if newModal != nil && newModal != prev {
|
||||||
|
m.seedModalSize()
|
||||||
|
}
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +233,7 @@ func (m model) handleKey(km tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
return m, tea.Batch(loadStatusCmd(), loadConfigCmd())
|
return m, tea.Batch(loadStatusCmd(), loadConfigCmd())
|
||||||
case "a":
|
case "a":
|
||||||
m.modal = m.openAddPicker()
|
m.modal = m.openAddPicker()
|
||||||
|
m.seedModalSize()
|
||||||
return m, nil
|
return m, nil
|
||||||
case "d":
|
case "d":
|
||||||
return m.openRemoveConfirm()
|
return m.openRemoveConfirm()
|
||||||
@@ -501,9 +512,20 @@ func (m model) openRemoveConfirm() (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
m.modal = newConfirm(prompt, run)
|
m.modal = newConfirm(prompt, run)
|
||||||
|
m.seedModalSize()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// seedModalSize forwards the current terminal dimensions to the modal
|
||||||
|
// so its inputs can size themselves on first paint. Called whenever a
|
||||||
|
// new modal is installed.
|
||||||
|
func (m *model) seedModalSize() {
|
||||||
|
if m.modal == nil || m.width == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.modal, _ = m.modal.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
|
||||||
|
}
|
||||||
|
|
||||||
// openEditForm dispatches to the right pre-filled edit form based on the
|
// openEditForm dispatches to the right pre-filled edit form based on the
|
||||||
// active tab and the row under the cursor. Looks up the full record in
|
// active tab and the row under the cursor. Looks up the full record in
|
||||||
// m.peersFull / m.checksFull / m.alerts (populated by loadConfigCmd) so
|
// m.peersFull / m.checksFull / m.alerts (populated by loadConfigCmd) so
|
||||||
@@ -519,6 +541,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) {
|
|||||||
for i := range m.peersFull {
|
for i := range m.peersFull {
|
||||||
if m.peersFull[i].NodeID == id {
|
if m.peersFull[i].NodeID == id {
|
||||||
m.modal = newEditNodeForm(m.peersFull[i])
|
m.modal = newEditNodeForm(m.peersFull[i])
|
||||||
|
m.seedModalSize()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,6 +557,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) {
|
|||||||
for i := range m.checksFull {
|
for i := range m.checksFull {
|
||||||
if m.checksFull[i].ID == id {
|
if m.checksFull[i].ID == id {
|
||||||
m.modal = newEditCheckForm(m.checksFull[i])
|
m.modal = newEditCheckForm(m.checksFull[i])
|
||||||
|
m.seedModalSize()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,6 +583,7 @@ func (m model) openEditForm() (tea.Model, tea.Cmd) {
|
|||||||
m.setFlash("unsupported alert type", flashError)
|
m.setFlash("unsupported alert type", flashError)
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
m.seedModalSize()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
m.setFlash("alert not found in local cluster.yaml", flashError)
|
m.setFlash("alert not found in local cluster.yaml", flashError)
|
||||||
|
|||||||
Reference in New Issue
Block a user