package ui import "9front/gui/draw" // A Field is one line of editable text. // // Deliberately small: a cursor at the end, backspace, and ^U to clear. // There is no selection, no mouse cursor placement and no history, // because a form that asks for a summary and a time does not need them // and every one of them is a place to get subtly wrong. A program that // needs a real editor should hand the job to one. type Field struct { Label string Value string R draw.Rectangle // set by Draw, used for hit testing } // Draw paints the field and records where it landed in f.R, so the // caller can make it clickable. It does not register a hit region // itself: the first matching region wins a click, and a do-nothing one // here would silently swallow the caller's. func (u *UI) Draw(f *Field, r draw.Rectangle, focused bool) { f.R = r lw := u.F.Width("attendees ") + 8 u.Text(draw.Point{X: r.Min.X, Y: r.Min.Y + 2}, "ink", f.Label) box := draw.Rect(r.Min.X+lw, r.Min.Y, r.Max.X, r.Max.Y) u.Fill(box, "bg") c := "rule" if focused { c = "border" } u.Border(box, c) s := f.Value if focused { s += "|" } // keep the end of the line in view: that is where you are typing for len(s) > 0 && u.F.Width(s) > box.Dx()-8 { s = s[1:] } u.Text(draw.Point{X: box.Min.X + 4, Y: box.Min.Y + 2}, "ink", s) } // oneLine keeps a paste to the first line: these are one-line fields, // and a pasted paragraph would silently lose everything after the first // newline anyway. func oneLine(s string) string { for i, r := range s { if r == '\n' || r == '\r' { return s[:i] } } return s } // Key applies one typed rune and says whether it changed anything. func (f *Field) Key(r rune) bool { switch r { case '\b', 0x7F: // backspace, del if f.Value == "" { return false } v := []rune(f.Value) f.Value = string(v[:len(v)-1]) return true case 0x15: // ^U if f.Value == "" { return false } f.Value = "" return true case 0x16, 0x19: // ^V, ^Y: paste if v, err := draw.Snarfed("/dev"); err == nil { f.Value += oneLine(v) return true } return false case '\n', '\r', '\t', 0x1B: return false // the form deals with these } if r < ' ' { return false } f.Value += string(r) return true }