v0.2.1 tui enhancement and better narrow layouts
This commit is contained in:
13 files changed
+577
-130
No files matched your search
+29
-8
@@ -36,36 +36,49 @@ type Field struct {
|
||||
|
||||
// Field draws a form row at y: the focus mark, the label in a column labelW
|
||||
// wide with the required mark, and below, the error, or the help while
|
||||
// focused. It returns the region for the value beside the label, where the
|
||||
// caller draws a TextInput, Choice or Toggle, and the rows it used.
|
||||
// focused, wrapped. The label column leaves the value half the row; labelW 0
|
||||
// stacks the label on its own row and the value under it, indented. It
|
||||
// returns the region for the value, where the caller draws a TextInput,
|
||||
// Radio or Toggle, and the rows it used.
|
||||
func (r Region) Field(y, labelW int, f Field, focused bool, th Theme) (Region, int) {
|
||||
label := th.Muted
|
||||
if focused {
|
||||
label = th.Text
|
||||
r.TextStyled(0, y, string(th.Glyphs.Focus), th.Accent)
|
||||
}
|
||||
x, rows := 4, 2 // stacked: the label's own row, the value under it
|
||||
if labelW > 0 {
|
||||
labelW = min(labelW, max(1, (r.W-3)/2))
|
||||
x, rows = 2+labelW+1, 1
|
||||
} else {
|
||||
labelW = r.W - 2
|
||||
}
|
||||
name := f.Label
|
||||
if f.Required {
|
||||
name = Truncate(name, labelW-2)
|
||||
r.TextStyled(2+RuneLen(name)+1, y, string(th.Glyphs.Required), th.Accent)
|
||||
}
|
||||
r.TextStyled(2, y, Truncate(name, labelW), label)
|
||||
value := r.Sub(x, y+rows-1, r.W-x, 1)
|
||||
|
||||
x := 2 + labelW + 1
|
||||
note, style := f.Error, th.Error
|
||||
if note == "" && focused {
|
||||
note, style = f.Help, th.Muted
|
||||
}
|
||||
if note == "" {
|
||||
return r.Sub(x, y, r.W-x, 1), 1
|
||||
return value, rows
|
||||
}
|
||||
r.TextStyled(x, y+1, Truncate(note, r.W-x), style)
|
||||
return r.Sub(x, y, r.W-x, 1), 2
|
||||
lines := WrapText(note, r.W-x)
|
||||
for i, l := range lines {
|
||||
r.TextStyled(x, y+rows+i, l, style)
|
||||
}
|
||||
return value, rows + len(lines)
|
||||
}
|
||||
|
||||
// TextInput draws state's text on r's first row, or, empty, the placeholder
|
||||
// muted: a default shows what an empty value means. Focused, it draws on the
|
||||
// Input background with the cursor, scrolling to keep the cursor in view.
|
||||
// Input background with the cursor, scrolling to keep the cursor in view; a
|
||||
// muted '…' marks text out of view.
|
||||
func (r Region) TextInput(state *TextFieldState, placeholder string, focused bool, th Theme) {
|
||||
if r.W < 1 || r.H < 1 {
|
||||
return
|
||||
@@ -75,7 +88,7 @@ func (r Region) TextInput(state *TextFieldState, placeholder string, focused boo
|
||||
bg = th.Input
|
||||
}
|
||||
state.AdjustScroll(r.W)
|
||||
text, style := []rune(placeholder), th.Muted.On(bg)
|
||||
text, style := []rune(Truncate(placeholder, r.W)), th.Muted.On(bg)
|
||||
if len(state.Text) > 0 {
|
||||
text, style = state.Text[state.Scroll:], th.Text.On(bg)
|
||||
}
|
||||
@@ -86,6 +99,14 @@ func (r Region) TextInput(state *TextFieldState, placeholder string, focused boo
|
||||
}
|
||||
r.Cell(x, 0, ch, style.Fg, style.Bg, style.Attr)
|
||||
}
|
||||
// Text scrolled out of view on either side is marked, never under the cursor
|
||||
hidden := th.Muted.On(bg)
|
||||
if state.Scroll > 0 && (state.Cursor != state.Scroll || !focused) {
|
||||
r.Cell(0, 0, '…', hidden.Fg, hidden.Bg, hidden.Attr)
|
||||
}
|
||||
if end := r.W - 1; len(text) > r.W && (state.Cursor-state.Scroll != end || !focused) {
|
||||
r.Cell(end, 0, '…', hidden.Fg, hidden.Bg, hidden.Attr)
|
||||
}
|
||||
if focused {
|
||||
x, ch := state.Cursor-state.Scroll, ' '
|
||||
if x < len(text) {
|
||||
|
||||
Reference in new issue
Block a user