Update dependencies

This commit is contained in:
Tulir Asokan 2018-04-30 10:55:37 +03:00
parent 576bab9e2e
commit e48ff5bea4
17 changed files with 737 additions and 112 deletions

2
Gopkg.lock generated
View File

@ -139,7 +139,7 @@
branch = "master"
name = "maunium.net/go/tview"
packages = ["."]
revision = "6146b7fe2331e23a78e217016705bc6801bfc55a"
revision = "7eabba90a261a481d36ace89daa79c56582238d7"
[solve-meta]
analyzer-name = "dep"

View File

@ -64,6 +64,11 @@ Add your issue here on GitHub. Feel free to get in touch if you have any questio
(There are no corresponding tags in the project. I only keep such a history in this README.)
- v0.14 (2018-04-13)
- Added an `Escape()` function which keep strings like color or region tags from being recognized as such.
- Added `ANSIIWriter()` and `TranslateANSII()` which convert ANSII escape sequences to `tview` color tags.
- v0.13 (2018-04-01)
- Added background colors and text attributes to color tags.
- v0.12 (2018-03-13)
- Added "suspended mode" to `Application`.
- v0.11 (2018-03-02)

237
vendor/maunium.net/go/tview/ansii.go generated vendored Normal file
View File

@ -0,0 +1,237 @@
package tview
import (
"bytes"
"fmt"
"io"
"strconv"
"strings"
)
// The states of the ANSII escape code parser.
const (
ansiiText = iota
ansiiEscape
ansiiSubstring
ansiiControlSequence
)
// ansii is a io.Writer which translates ANSII escape codes into tview color
// tags.
type ansii struct {
io.Writer
// Reusable buffers.
buffer *bytes.Buffer // The entire output text of one Write().
csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings.
// The current state of the parser. One of the ansii constants.
state int
}
// ANSIIWriter returns an io.Writer which translates any ANSII escape codes
// written to it into tview color tags. Other escape codes don't have an effect
// and are simply removed. The translated text is written to the provided
// writer.
func ANSIIWriter(writer io.Writer) io.Writer {
return &ansii{
Writer: writer,
buffer: new(bytes.Buffer),
csiParameter: new(bytes.Buffer),
csiIntermediate: new(bytes.Buffer),
state: ansiiText,
}
}
// Write parses the given text as a string of runes, translates ANSII escape
// codes to color tags and writes them to the output writer.
func (a *ansii) Write(text []byte) (int, error) {
defer func() {
a.buffer.Reset()
}()
for _, r := range string(text) {
switch a.state {
// We just entered an escape sequence.
case ansiiEscape:
switch r {
case '[': // Control Sequence Introducer.
a.csiParameter.Reset()
a.csiIntermediate.Reset()
a.state = ansiiControlSequence
case 'c': // Reset.
fmt.Fprint(a.buffer, "[-:-:-]")
a.state = ansiiText
case 'P', ']', 'X', '^', '_': // Substrings and commands.
a.state = ansiiSubstring
default: // Ignore.
a.state = ansiiText
}
// CSI Sequences.
case ansiiControlSequence:
switch {
case r >= 0x30 && r <= 0x3f: // Parameter bytes.
if _, err := a.csiParameter.WriteRune(r); err != nil {
return 0, err
}
case r >= 0x20 && r <= 0x2f: // Intermediate bytes.
if _, err := a.csiIntermediate.WriteRune(r); err != nil {
return 0, err
}
case r >= 0x40 && r <= 0x7e: // Final byte.
switch r {
case 'E': // Next line.
count, _ := strconv.Atoi(a.csiParameter.String())
if count == 0 {
count = 1
}
fmt.Fprint(a.buffer, strings.Repeat("\n", count))
case 'm': // Select Graphic Rendition.
var (
background, foreground, attributes string
clearAttributes bool
)
fields := strings.Split(a.csiParameter.String(), ";")
if len(fields) == 0 || len(fields) == 1 && fields[0] == "0" {
// Reset.
if _, err := a.buffer.WriteString("[-:-:-]"); err != nil {
return 0, err
}
break
}
lookupColor := func(colorNumber int, bright bool) string {
if colorNumber < 0 || colorNumber > 7 {
return "black"
}
if bright {
colorNumber += 8
}
return [...]string{
"black",
"red",
"green",
"yellow",
"blue",
"darkmagenta",
"darkcyan",
"white",
"#7f7f7f",
"#ff0000",
"#00ff00",
"#ffff00",
"#5c5cff",
"#ff00ff",
"#00ffff",
"#ffffff",
}[colorNumber]
}
for index, field := range fields {
switch field {
case "1", "01":
attributes += "b"
case "2", "02":
attributes += "d"
case "4", "04":
attributes += "u"
case "5", "05":
attributes += "l"
case "7", "07":
attributes += "7"
case "22", "24", "25", "27":
clearAttributes = true
case "30", "31", "32", "33", "34", "35", "36", "37":
colorNumber, _ := strconv.Atoi(field)
foreground = lookupColor(colorNumber-30, false)
case "40", "41", "42", "43", "44", "45", "46", "47":
colorNumber, _ := strconv.Atoi(field)
background = lookupColor(colorNumber-40, false)
case "90", "91", "92", "93", "94", "95", "96", "97":
colorNumber, _ := strconv.Atoi(field)
foreground = lookupColor(colorNumber-90, true)
case "100", "101", "102", "103", "104", "105", "106", "107":
colorNumber, _ := strconv.Atoi(field)
background = lookupColor(colorNumber-100, true)
case "38", "48":
var color string
if len(fields) > index+1 {
if fields[index+1] == "5" && len(fields) > index+2 { // 8-bit colors.
colorNumber, _ := strconv.Atoi(fields[index+2])
if colorNumber <= 7 {
color = lookupColor(colorNumber, false)
} else if colorNumber <= 15 {
color = lookupColor(colorNumber, true)
} else if colorNumber <= 231 {
red := (colorNumber - 16) / 36
green := ((colorNumber - 16) / 6) % 6
blue := (colorNumber - 16) % 6
color = fmt.Sprintf("%02x%02x%02x", 255*red/5, 255*green/5, 255*blue/5)
} else if colorNumber <= 255 {
grey := 255 * (colorNumber - 232) / 23
color = fmt.Sprintf("%02x%02x%02x", grey, grey, grey)
}
} else if fields[index+1] == "2" && len(fields) > index+4 { // 24-bit colors.
red, _ := strconv.Atoi(fields[index+2])
green, _ := strconv.Atoi(fields[index+3])
blue, _ := strconv.Atoi(fields[index+4])
color = fmt.Sprintf("%02x%02x%02x", red, green, blue)
}
}
if len(color) > 0 {
if field == "38" {
foreground = color
} else {
background = color
}
}
}
}
if len(attributes) > 0 || clearAttributes {
attributes = ":" + attributes
}
if len(foreground) > 0 || len(background) > 0 || len(attributes) > 0 {
fmt.Fprintf(a.buffer, "[%s:%s%s]", foreground, background, attributes)
}
}
a.state = ansiiText
default: // Undefined byte.
a.state = ansiiText // Abort CSI.
}
// We just entered a substring/command sequence.
case ansiiSubstring:
if r == 27 { // Most likely the end of the substring.
a.state = ansiiEscape
} // Ignore all other characters.
// "ansiiText" and all others.
default:
if r == 27 {
// This is the start of an escape sequence.
a.state = ansiiEscape
} else {
// Just a regular rune. Send to buffer.
if _, err := a.buffer.WriteRune(r); err != nil {
return 0, err
}
}
}
}
// Write buffer to target writer.
n, err := a.buffer.WriteTo(a.Writer)
if err != nil {
return int(n), err
}
return len(text), nil
}
// TranslateANSII replaces ANSII escape sequences found in the provided string
// with tview's color tags and returns the resulting string.
func TranslateANSII(text string) string {
var buffer bytes.Buffer
writer := ANSIIWriter(&buffer)
writer.Write([]byte(text))
return buffer.String()
}

View File

@ -38,6 +38,8 @@ type Application struct {
// be forwarded).
mouseCapture func(event *tcell.EventMouse) *tcell.EventMouse
pasteCapture func(event *tcell.EventPaste) *tcell.EventPaste
// An optional callback function which is invoked just before the root
// primitive is drawn.
beforeDraw func(screen tcell.Screen) bool
@ -190,6 +192,24 @@ func (a *Application) Run() error {
//a.Draw()
}
}
case *tcell.EventPaste:
a.RLock()
p := a.focus
a.RUnlock()
if a.pasteCapture != nil {
event = a.pasteCapture(event)
if event == nil {
break
}
}
if p != nil {
if handler := p.PasteHandler(); handler != nil {
handler(event)
a.Draw()
}
}
case *tcell.EventResize:
a.Lock()
screen := a.screen

32
vendor/maunium.net/go/tview/box.go generated vendored
View File

@ -62,6 +62,8 @@ type Box struct {
// nothing should be forwarded).
mouseCapture func(event *tcell.EventMouse) *tcell.EventMouse
pasteCapture func(event *tcell.EventPaste) *tcell.EventPaste
// An optional function which is called before the box is drawn.
draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)
}
@ -218,6 +220,36 @@ func (b *Box) GetMouseCapture() func(event *tcell.EventMouse) *tcell.EventMouse
return b.mouseCapture
}
func (b *Box) WrapPasteHandler(pasteHandler func(*tcell.EventPaste)) func(*tcell.EventPaste) {
return func(event *tcell.EventPaste) {
if b.pasteCapture != nil {
event = b.pasteCapture(event)
}
if event != nil && pasteHandler != nil {
pasteHandler(event)
}
}
}
func (b *Box) PasteHandler() func(event *tcell.EventPaste) {
return b.WrapPasteHandler(func(event *tcell.EventPaste) {
// Default paste handler just calls input handler with each character.
inputHandler := b.InputHandler()
for _, char := range event.Text() {
inputHandler(tcell.NewEventKey(tcell.KeyRune, char, tcell.ModNone), nil)
}
})
}
func (b *Box) SetPasteCapture(capture func(event *tcell.EventPaste) *tcell.EventPaste) *Box {
b.pasteCapture = capture
return b
}
func (b *Box) GetPasteCapture() func(event *tcell.EventPaste) *tcell.EventPaste {
return b.pasteCapture
}
// SetBackgroundColor sets the box's background color.
func (b *Box) SetBackgroundColor(color tcell.Color) *Box {
b.backgroundColor = color

View File

@ -17,6 +17,10 @@ type Checkbox struct {
// The text to be displayed before the input area.
label string
// The screen width of the label area. A value of 0 means use the width of
// the label text.
labelWidth int
// The label color.
labelColor tcell.Color
@ -34,6 +38,10 @@ type Checkbox struct {
// are done entering text. The key which was pressed is provided (tab,
// shift-tab, or escape).
done func(tcell.Key)
// A callback function set by the Form class and called when the user leaves
// this form item.
finished func(tcell.Key)
}
// NewCheckbox returns a new input field.
@ -68,6 +76,13 @@ func (c *Checkbox) GetLabel() string {
return c.label
}
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (c *Checkbox) SetLabelWidth(width int) *Checkbox {
c.labelWidth = width
return c
}
// SetLabelColor sets the color of the label.
func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox {
c.labelColor = color
@ -87,8 +102,8 @@ func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox {
}
// SetFormAttributes sets attributes shared by all form items.
func (c *Checkbox) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
c.label = label
func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
c.labelWidth = labelWidth
c.labelColor = labelColor
c.backgroundColor = bgColor
c.fieldTextColor = fieldTextColor
@ -121,9 +136,10 @@ func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox {
return c
}
// SetFinishedFunc calls SetDoneFunc().
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
return c.SetDoneFunc(handler)
c.finished = handler
return c
}
// Draw draws this primitive onto the screen.
@ -138,8 +154,17 @@ func (c *Checkbox) Draw(screen tcell.Screen) {
}
// Draw label.
_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor)
x += drawnWidth
if c.labelWidth > 0 {
labelWidth := c.labelWidth
if labelWidth > rightLimit-x {
labelWidth = rightLimit - x
}
Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor)
x += labelWidth
} else {
_, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor)
x += drawnWidth
}
// Draw checkbox.
fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor)
@ -170,6 +195,9 @@ func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
if c.done != nil {
c.done(key)
}
if c.finished != nil {
c.finished(key)
}
}
})
}

42
vendor/maunium.net/go/tview/doc.go generated vendored
View File

@ -77,13 +77,53 @@ applies to almost everything from box titles, list text, form item labels, to
table cells. In a TextView, this functionality has to be switched on explicitly.
See the TextView documentation for more information.
Color tags may contain not just the foreground (text) color but also the
background color and additional flags. In fact, the full definition of a color
tag is as follows:
[<foreground>:<background>:<flags>]
Each of the three fields can be left blank and trailing fields can be ommitted.
(Empty square brackets "[]", however, are not considered color tags.) Colors
that are not specified will be left unchanged. A field with just a dash ("-")
means "reset to default".
You can specify the following flags (some flags may not be supported by your
terminal):
l: blink
b: bold
d: dim
r: reverse (switch foreground and background color)
u: underline
Examples:
[yellow]Yellow text
[yellow:red]Yellow text on red background
[:red]Red background, text color unchanged
[yellow::u]Yellow text underlined
[::bl]Bold, blinking text
[::-]Colors unchanged, flags reset
[-]Reset foreground color
[-:-:-]Reset everything
[:]No effect
[]Not a valid color tag, will print square brackets as they are
In the rare event that you want to display a string such as "[red]" or
"[#00ff1a]" without applying its effect, you need to put an opening square
bracket before the closing square bracket. Examples:
bracket before the closing square bracket. Note that the text inside the
brackets will be matched less strictly than region or colors tags. I.e. any
character that may be used in color or region tags will be recognized. Examples:
[red[] will be output as [red]
["123"[] will be output as ["123"]
[#6aff00[[] will be output as [#6aff00[]
[a#"[[[] will be output as [a#"[[]
[] will be output as [] (see color tags above)
[[] will be output as [[] (not an escaped tag)
You can use the Escape() function to insert brackets automatically where needed.
Styles

View File

@ -51,6 +51,10 @@ type DropDown struct {
// The color for prefixes.
prefixTextColor tcell.Color
// The screen width of the label area. A value of 0 means use the width of
// the label text.
labelWidth int
// The screen width of the input area. A value of 0 means extend as much as
// possible.
fieldWidth int
@ -59,6 +63,10 @@ type DropDown struct {
// are done selecting options. The key which was pressed is provided (tab,
// shift-tab, or escape).
done func(tcell.Key)
// A callback function set by the Form class and called when the user leaves
// this form item.
finished func(tcell.Key)
}
// NewDropDown returns a new drop-down.
@ -113,6 +121,13 @@ func (d *DropDown) GetLabel() string {
return d.label
}
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (d *DropDown) SetLabelWidth(width int) *DropDown {
d.labelWidth = width
return d
}
// SetLabelColor sets the color of the label.
func (d *DropDown) SetLabelColor(color tcell.Color) *DropDown {
d.labelColor = color
@ -140,8 +155,8 @@ func (d *DropDown) SetPrefixTextColor(color tcell.Color) *DropDown {
}
// SetFormAttributes sets attributes shared by all form items.
func (d *DropDown) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
d.label = label
func (d *DropDown) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
d.labelWidth = labelWidth
d.labelColor = labelColor
d.backgroundColor = bgColor
d.fieldTextColor = fieldTextColor
@ -210,9 +225,10 @@ func (d *DropDown) SetDoneFunc(handler func(key tcell.Key)) *DropDown {
return d
}
// SetFinishedFunc calls SetDoneFunc().
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (d *DropDown) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
return d.SetDoneFunc(handler)
d.finished = handler
return d
}
// Draw draws this primitive onto the screen.
@ -227,8 +243,17 @@ func (d *DropDown) Draw(screen tcell.Screen) {
}
// Draw label.
_, drawnWidth := Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor)
x += drawnWidth
if d.labelWidth > 0 {
labelWidth := d.labelWidth
if labelWidth > rightLimit-x {
labelWidth = rightLimit - x
}
Print(screen, d.label, x, y, labelWidth, AlignLeft, d.labelColor)
x += labelWidth
} else {
_, drawnWidth := Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor)
x += drawnWidth
}
// What's the longest option text?
maxWidth := 0
@ -359,6 +384,9 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
if d.done != nil {
d.done(key)
}
if d.finished != nil {
d.finished(key)
}
}
})
}

View File

@ -69,8 +69,8 @@ func (f *Flex) SetFullScreen(fullScreen bool) *Flex {
// that its size is flexible and may be changed. The "proportion" argument
// defines the relative size of the item compared to other flexible-size items.
// For example, items with a proportion of 2 will be twice as large as items
// with a proportion of 1. Must be at least 1 if fixedSize > 0 (ignored
// otherwise)
// with a proportion of 1. The proportion must be at least 1 if fixedSize == 0
// (ignored otherwise).
//
// If "focus" is set to true, the item will receive focus when the Flex
// primitive receives focus. If multiple items have the "focus" flag set to

36
vendor/maunium.net/go/tview/form.go generated vendored
View File

@ -1,8 +1,6 @@
package tview
import (
"strings"
"maunium.net/go/tcell"
)
@ -20,7 +18,7 @@ type FormItem interface {
GetLabel() string
// SetFormAttributes sets a number of item attributes at once.
SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem
SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem
// GetFieldWidth returns the width of the form item's field (the area which
// is manipulated by the user) in number of screen cells. A value of 0
@ -233,7 +231,14 @@ func (f *Form) Clear(includeButtons bool) *Form {
// AddFormItem adds a new item to the form. This can be used to add your own
// objects to the form. Note, however, that the Form class will override some
// of its attributes to make it work in the form context.
// of its attributes to make it work in the form context. Specifically, these
// are:
//
// - The label width
// - The label color
// - The background color
// - The field text color
// - The field background color
func (f *Form) AddFormItem(item FormItem) *Form {
f.items = append(f.items, item)
return f
@ -246,6 +251,18 @@ func (f *Form) GetFormItem(index int) FormItem {
return f.items[index]
}
// GetFormItemByLabel returns the first form element with the given label. If
// no such element is found, nil is returned. Buttons are not searched and will
// therefore not be returned.
func (f *Form) GetFormItemByLabel(label string) FormItem {
for _, item := range f.items {
if item.GetLabel() == label {
return item
}
}
return nil
}
// SetCancelFunc sets a handler which is called when the user hits the Escape
// key.
func (f *Form) SetCancelFunc(callback func()) *Form {
@ -267,8 +284,7 @@ func (f *Form) Draw(screen tcell.Screen) {
// Find the longest label.
var maxLabelWidth int
for _, item := range f.items {
label := strings.TrimSpace(item.GetLabel())
labelWidth := StringWidth(label)
labelWidth := StringWidth(item.GetLabel())
if labelWidth > maxLabelWidth {
maxLabelWidth = labelWidth
}
@ -280,20 +296,18 @@ func (f *Form) Draw(screen tcell.Screen) {
var focusedPosition struct{ x, y, width, height int }
for index, item := range f.items {
// Calculate the space needed.
label := strings.TrimSpace(item.GetLabel())
labelWidth := StringWidth(label)
labelWidth := StringWidth(item.GetLabel())
var itemWidth int
if f.horizontal {
fieldWidth := item.GetFieldWidth()
if fieldWidth == 0 {
fieldWidth = DefaultFormFieldWidth
}
label += " "
labelWidth++
itemWidth = labelWidth + fieldWidth
} else {
// We want all fields to align vertically.
label += strings.Repeat(" ", maxLabelWidth-labelWidth)
labelWidth = maxLabelWidth
itemWidth = width
}
@ -308,7 +322,7 @@ func (f *Form) Draw(screen tcell.Screen) {
itemWidth = rightLimit - x
}
item.SetFormAttributes(
label,
labelWidth,
f.labelColor,
f.backgroundColor,
f.fieldTextColor,

View File

@ -258,7 +258,7 @@ func (g *Grid) HasFocus() bool {
return true
}
}
return false
return g.hasFocus
}
// InputHandler returns the handler for this primitive.

View File

@ -41,6 +41,10 @@ type InputField struct {
// The text color of the placeholder.
placeholderTextColor tcell.Color
// The screen width of the label area. A value of 0 means use the width of
// the label text.
labelWidth int
// The screen width of the input area. A value of 0 means extend as much as
// possible.
fieldWidth int
@ -59,6 +63,10 @@ type InputField struct {
// are done entering text. The key which was pressed is provided (tab,
// shift-tab, enter, or escape).
done func(tcell.Key)
// A callback function set by the Form class and called when the user leaves
// this form item.
finished func(tcell.Key)
}
// NewInputField returns a new input field.
@ -97,6 +105,13 @@ func (i *InputField) GetLabel() string {
return i.label
}
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
// primitive to use the width of the label string.
func (i *InputField) SetLabelWidth(width int) *InputField {
i.labelWidth = width
return i
}
// SetPlaceholder sets the text to be displayed when the input text is empty.
func (i *InputField) SetPlaceholder(text string) *InputField {
i.placeholder = text
@ -121,15 +136,15 @@ func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField {
return i
}
// SetPlaceholderExtColor sets the text color of placeholder text.
func (i *InputField) SetPlaceholderExtColor(color tcell.Color) *InputField {
// SetPlaceholderTextColor sets the text color of placeholder text.
func (i *InputField) SetPlaceholderTextColor(color tcell.Color) *InputField {
i.placeholderTextColor = color
return i
}
// SetFormAttributes sets attributes shared by all form items.
func (i *InputField) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
i.label = label
func (i *InputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
i.labelWidth = labelWidth
i.labelColor = labelColor
i.backgroundColor = bgColor
i.fieldTextColor = fieldTextColor
@ -186,9 +201,10 @@ func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
return i
}
// SetFinishedFunc calls SetDoneFunc().
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
return i.SetDoneFunc(handler)
i.finished = handler
return i
}
// Draw draws this primitive onto the screen.
@ -203,8 +219,17 @@ func (i *InputField) Draw(screen tcell.Screen) {
}
// Draw label.
_, drawnWidth := Print(screen, i.label, x, y, rightLimit-x, AlignLeft, i.labelColor)
x += drawnWidth
if i.labelWidth > 0 {
labelWidth := i.labelWidth
if labelWidth > rightLimit-x {
labelWidth = rightLimit - x
}
Print(screen, i.label, x, y, labelWidth, AlignLeft, i.labelColor)
x += labelWidth
} else {
_, drawnWidth := Print(screen, i.label, x, y, rightLimit-x, AlignLeft, i.labelColor)
x += drawnWidth
}
// Draw input area.
fieldWidth := i.fieldWidth
@ -280,7 +305,11 @@ func (i *InputField) setCursor(screen tcell.Screen) {
if i.fieldWidth > 0 && fieldWidth > i.fieldWidth-1 {
fieldWidth = i.fieldWidth - 1
}
x += StringWidth(i.label) + fieldWidth
if i.labelWidth > 0 {
x += i.labelWidth + fieldWidth
} else {
x += StringWidth(i.label) + fieldWidth
}
if x >= rightLimit {
x = rightLimit - 1
}
@ -323,6 +352,9 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
if i.done != nil {
i.done(key)
}
if i.finished != nil {
i.finished(key)
}
}
})
}

20
vendor/maunium.net/go/tview/list.go generated vendored
View File

@ -173,6 +173,26 @@ func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected f
return l
}
// GetItemCount returns the number of items in the list.
func (l *List) GetItemCount() int {
return len(l.items)
}
// GetItemText returns an item's texts (main and secondary). Panics if the index
// is out of range.
func (l *List) GetItemText(index int) (main, secondary string) {
return l.items[index].MainText, l.items[index].SecondaryText
}
// SetItemText sets an item's main and secondary text. Panics if the index is
// out of range.
func (l *List) SetItemText(index int, main, secondary string) *List {
item := l.items[index]
item.MainText = main
item.SecondaryText = secondary
return l
}
// Clear removes all items from the list.
func (l *List) Clear() *List {
l.items = nil

View File

@ -36,6 +36,8 @@ type Primitive interface {
MouseHandler() func(event *tcell.EventMouse, setFocus func(p Primitive))
PasteHandler() func(event *tcell.EventPaste)
// Focus is called by the application when the primitive receives focus.
// Implementers may call delegate() to pass the focus on to another primitive.
Focus(delegate func(p Primitive))

View File

@ -590,7 +590,7 @@ ColumnLoop:
expansion := 0
for _, row := range rows {
if cell := getCell(row, column); cell != nil {
cellWidth := StringWidth(cell.Text)
_, _, _, _, cellWidth := decomposeString(cell.Text)
if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth {
cellWidth = cell.MaxWidth
}

View File

@ -8,6 +8,7 @@ import (
"unicode/utf8"
"maunium.net/go/tcell"
"github.com/lucasb-eyer/go-colorful"
runewidth "github.com/mattn/go-runewidth"
)
@ -17,12 +18,14 @@ var TabSize = 4
// textViewIndex contains information about each line displayed in the text
// view.
type textViewIndex struct {
Line int // The index into the "buffer" variable.
Pos int // The index into the "buffer" string (byte position).
NextPos int // The (byte) index of the next character in this buffer line.
Width int // The screen width of this line.
Color tcell.Color // The starting color.
Region string // The starting region ID.
Line int // The index into the "buffer" variable.
Pos int // The index into the "buffer" string (byte position).
NextPos int // The (byte) index of the next character in this buffer line.
Width int // The screen width of this line.
ForegroundColor string // The starting foreground color ("" = don't change, "-" = reset).
BackgroundColor string // The starting background color ("" = don't change, "-" = reset).
Attributes string // The starting attributes ("" = don't change, "-" = reset).
Region string // The starting region ID.
}
// TextView is a box which displays text. It implements the io.Writer interface
@ -499,7 +502,6 @@ func (t *TextView) reindexBuffer(width int) {
// Initial states.
regionID := ""
var highlighted bool
color := t.textColor
// Go through each line in the buffer.
for bufferIndex, str := range t.buffer {
@ -507,11 +509,10 @@ func (t *TextView) reindexBuffer(width int) {
var (
colorTagIndices [][]int
colorTags [][]string
escapeIndices [][]int
)
if t.dynamicColors {
colorTagIndices = colorPattern.FindAllStringIndex(str, -1)
colorTags = colorPattern.FindAllStringSubmatch(str, -1)
str = colorPattern.ReplaceAllString(str, "")
colorTagIndices, colorTags, escapeIndices, str, _ = decomposeString(str)
}
// Find all regions in this line. Then remove them.
@ -523,13 +524,11 @@ func (t *TextView) reindexBuffer(width int) {
regionIndices = regionPattern.FindAllStringIndex(str, -1)
regions = regionPattern.FindAllStringSubmatch(str, -1)
str = regionPattern.ReplaceAllString(str, "")
}
// Find all replace tags in this line. Then replace them.
var escapeIndices [][]int
if t.dynamicColors || t.regions {
escapeIndices = escapePattern.FindAllStringIndex(str, -1)
str = escapePattern.ReplaceAllString(str, "[$1$2]")
if !t.dynamicColors {
// We haven't detected escape tags yet. Do it now.
escapeIndices = escapePattern.FindAllStringIndex(str, -1)
str = escapePattern.ReplaceAllString(str, "[$1$2]")
}
}
// Split the line if required.
@ -559,13 +558,18 @@ func (t *TextView) reindexBuffer(width int) {
}
// Create index from split lines.
var originalPos, colorPos, regionPos, escapePos int
var (
originalPos, colorPos, regionPos, escapePos int
foregroundColor, backgroundColor, attributes string
)
for _, splitLine := range splitLines {
line := &textViewIndex{
Line: bufferIndex,
Pos: originalPos,
Color: color,
Region: regionID,
Line: bufferIndex,
Pos: originalPos,
ForegroundColor: foregroundColor,
BackgroundColor: backgroundColor,
Attributes: attributes,
Region: regionID,
}
// Shift original position with tags.
@ -574,7 +578,7 @@ func (t *TextView) reindexBuffer(width int) {
if colorPos < len(colorTagIndices) && colorTagIndices[colorPos][0] <= originalPos+lineLength {
// Process color tags.
originalPos += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0]
color = tcell.GetColor(colorTags[colorPos][1])
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos])
colorPos++
} else if regionPos < len(regionIndices) && regionIndices[regionPos][0] <= originalPos+lineLength {
// Process region tags.
@ -712,6 +716,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
}
// Draw the buffer.
defaultStyle := tcell.StyleDefault.Foreground(t.textColor)
for line := t.lineOffset; line < len(t.index); line++ {
// Are we done?
if line-t.lineOffset >= height {
@ -721,17 +726,19 @@ func (t *TextView) Draw(screen tcell.Screen) {
// Get the text for this line.
index := t.index[line]
text := t.buffer[index.Line][index.Pos:index.NextPos]
color := index.Color
foregroundColor := index.ForegroundColor
backgroundColor := index.BackgroundColor
attributes := index.Attributes
regionID := index.Region
// Get color tags.
var (
colorTagIndices [][]int
colorTags [][]string
escapeIndices [][]int
)
if t.dynamicColors {
colorTagIndices = colorPattern.FindAllStringIndex(text, -1)
colorTags = colorPattern.FindAllStringSubmatch(text, -1)
colorTagIndices, colorTags, escapeIndices, _, _ = decomposeString(text)
}
// Get regions.
@ -742,12 +749,9 @@ func (t *TextView) Draw(screen tcell.Screen) {
if t.regions {
regionIndices = regionPattern.FindAllStringIndex(text, -1)
regions = regionPattern.FindAllStringSubmatch(text, -1)
}
// Get escape tags.
var escapeIndices [][]int
if t.dynamicColors || t.regions {
escapeIndices = escapePattern.FindAllStringIndex(text, -1)
if !t.dynamicColors {
escapeIndices = escapePattern.FindAllStringIndex(text, -1)
}
}
// Calculate the position of the line.
@ -770,7 +774,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
// Get the color.
if currentTag < len(colorTags) && pos >= colorTagIndices[currentTag][0] && pos < colorTagIndices[currentTag][1] {
if pos == colorTagIndices[currentTag][1]-1 {
color = tcell.GetColor(colorTags[currentTag][1])
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[currentTag])
currentTag++
}
continue
@ -811,13 +815,32 @@ func (t *TextView) Draw(screen tcell.Screen) {
break
}
// Mix the existing style with the new style.
_, _, existingStyle, _ := screen.GetContent(x+posX, y+line-t.lineOffset)
_, background, _ := existingStyle.Decompose()
style := overlayStyle(background, defaultStyle, foregroundColor, backgroundColor, attributes)
// Do we highlight this character?
style := tcell.StyleDefault.Background(t.backgroundColor).Foreground(color)
var highlighted bool
if len(regionID) > 0 {
if _, ok := t.highlights[regionID]; ok {
style = tcell.StyleDefault.Background(color).Foreground(t.backgroundColor)
highlighted = true
}
}
if highlighted {
fg, bg, _ := style.Decompose()
if bg == tcell.ColorDefault {
r, g, b := fg.RGB()
c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255}
_, _, li := c.Hcl()
if li < .5 {
bg = tcell.ColorWhite
} else {
bg = tcell.ColorBlack
}
}
style = style.Background(fg).Foreground(bg)
}
// Draw the character.
for offset := 0; offset < chWidth; offset++ {

222
vendor/maunium.net/go/tview/util.go generated vendored
View File

@ -1,6 +1,7 @@
package tview
import (
"fmt"
"math"
"regexp"
"strconv"
@ -104,11 +105,19 @@ var joints = map[string]rune{
// Common regular expressions.
var (
colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6})\]`)
regionPattern = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`)
escapePattern = regexp.MustCompile(`\[("[a-zA-Z0-9_,;: \-\.]*"|[a-zA-Z]+|#[0-9a-zA-Z]{6})\[(\[*)\]`)
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
spacePattern = regexp.MustCompile(`\s+`)
colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([lbdru]+|\-)?)?)?\]`)
regionPattern = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`)
escapePattern = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`)
nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`)
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
spacePattern = regexp.MustCompile(`\s+`)
)
// Positions of substrings in regular expressions.
const (
colorForegroundPos = 1
colorBackgroundPos = 3
colorFlagPos = 5
)
// Predefined InputField acceptance functions.
@ -150,40 +159,168 @@ func init() {
}
}
// styleFromTag takes the given style, defined by a foreground color (fgColor),
// a background color (bgColor), and style attributes, and modifies it based on
// the substrings (tagSubstrings) extracted by the regular expression for color
// tags. The new colors and attributes are returned where empty strings mean
// "don't modify" and a dash ("-") means "reset to default".
func styleFromTag(fgColor, bgColor, attributes string, tagSubstrings []string) (newFgColor, newBgColor, newAttributes string) {
if tagSubstrings[colorForegroundPos] != "" {
color := tagSubstrings[colorForegroundPos]
if color == "-" {
fgColor = "-"
} else if color != "" {
fgColor = color
}
}
if tagSubstrings[colorBackgroundPos-1] != "" {
color := tagSubstrings[colorBackgroundPos]
if color == "-" {
bgColor = "-"
} else if color != "" {
bgColor = color
}
}
if tagSubstrings[colorFlagPos-1] != "" {
flags := tagSubstrings[colorFlagPos]
if flags == "-" {
attributes = "-"
} else if flags != "" {
attributes = flags
}
}
return fgColor, bgColor, attributes
}
// overlayStyle mixes a background color with a foreground color (fgColor),
// a (possibly new) background color (bgColor), and style attributes, and
// returns the resulting style. For a definition of the colors and attributes,
// see styleFromTag(). Reset instructions cause the corresponding part of the
// default style to be used.
func overlayStyle(background tcell.Color, defaultStyle tcell.Style, fgColor, bgColor, attributes string) tcell.Style {
defFg, defBg, defAttr := defaultStyle.Decompose()
style := defaultStyle.Background(background)
if fgColor == "-" {
style = style.Foreground(defFg)
} else if fgColor != "" {
style = style.Foreground(tcell.GetColor(fgColor))
}
if bgColor == "-" {
style = style.Background(defBg)
} else if bgColor != "" {
style = style.Background(tcell.GetColor(bgColor))
}
if attributes == "-" {
style = style.Bold(defAttr&tcell.AttrBold > 0)
style = style.Blink(defAttr&tcell.AttrBlink > 0)
style = style.Reverse(defAttr&tcell.AttrReverse > 0)
style = style.Underline(defAttr&tcell.AttrUnderline > 0)
style = style.Dim(defAttr&tcell.AttrDim > 0)
} else if attributes != "" {
style = style.Normal()
for _, flag := range attributes {
switch flag {
case 'l':
style = style.Blink(true)
case 'b':
style = style.Bold(true)
case 'd':
style = style.Dim(true)
case 'r':
style = style.Reverse(true)
case 'u':
style = style.Underline(true)
}
}
}
return style
}
// decomposeString returns information about a string which may contain color
// tags. It returns the indices of the color tags (as returned by
// re.FindAllStringIndex()), the color tags themselves (as returned by
// re.FindAllStringSubmatch()), the indices of an escaped tags, the string
// stripped by any color tags and escaped, and the screen width of the stripped
// string.
func decomposeString(text string) (colorIndices [][]int, colors [][]string, escapeIndices [][]int, stripped string, width int) {
// Get positions of color and escape tags.
colorIndices = colorPattern.FindAllStringIndex(text, -1)
colors = colorPattern.FindAllStringSubmatch(text, -1)
escapeIndices = escapePattern.FindAllStringIndex(text, -1)
// Because the color pattern detects empty tags, we need to filter them out.
for i := len(colorIndices) - 1; i >= 0; i-- {
if colorIndices[i][1]-colorIndices[i][0] == 2 {
colorIndices = append(colorIndices[:i], colorIndices[i+1:]...)
colors = append(colors[:i], colors[i+1:]...)
}
}
// Remove the color tags from the original string.
var from int
buf := make([]byte, 0, len(text))
for _, indices := range colorIndices {
buf = append(buf, []byte(text[from:indices[0]])...)
from = indices[1]
}
buf = append(buf, text[from:]...)
// Escape string.
stripped = string(escapePattern.ReplaceAll(buf, []byte("[$1$2]")))
// Get the width of the stripped string.
width = runewidth.StringWidth(stripped)
return
}
// Print prints text onto the screen into the given box at (x,y,maxWidth,1),
// not exceeding that box. "align" is one of AlignLeft, AlignCenter, or
// AlignRight. The screen's background color will not be changed.
//
// You can change the text color mid-text by inserting a color tag. See the
// package description for details.
// You can change the colors and text styles mid-text by inserting a color tag.
// See the package description for details.
//
// Returns the number of actual runes printed (not including color tags) and the
// actual width used for the printed runes.
func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) {
return printWithStyle(screen, text, x, y, maxWidth, align, tcell.StyleDefault.Foreground(color))
}
// printWithStyle works like Print() but it takes a style instead of just a
// foreground color.
func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int, style tcell.Style) (int, int) {
if maxWidth < 0 {
return 0, 0
}
// Get positions of color and escape tags. Remove them from original string.
colorIndices := colorPattern.FindAllStringIndex(text, -1)
colors := colorPattern.FindAllStringSubmatch(text, -1)
escapeIndices := escapePattern.FindAllStringIndex(text, -1)
strippedText := escapePattern.ReplaceAllString(colorPattern.ReplaceAllString(text, ""), "[$1$2]")
// Decompose the text.
colorIndices, colors, escapeIndices, strippedText, _ := decomposeString(text)
// We deal with runes, not with bytes.
runes := []rune(strippedText)
// This helper function takes positions for a substring of "runes" and a start
// color and returns the substring with the original tags and the new start
// color.
substring := func(from, to int, color tcell.Color) (string, tcell.Color) {
var colorPos, escapePos, runePos, startPos int
// This helper function takes positions for a substring of "runes" and returns
// a new string corresponding to this substring, making sure printing that
// substring will observe color tags.
substring := func(from, to int) string {
var (
colorPos, escapePos, runePos, startPos int
foregroundColor, backgroundColor, attributes string
)
for pos := range text {
// Handle color tags.
if colorPos < len(colorIndices) && pos >= colorIndices[colorPos][0] && pos < colorIndices[colorPos][1] {
if pos == colorIndices[colorPos][1]-1 {
if runePos <= from {
color = tcell.GetColor(colors[colorPos][1])
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
}
colorPos++
}
@ -203,13 +340,13 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc
if runePos == from {
startPos = pos
} else if runePos >= to {
return text[startPos:pos], color
return fmt.Sprintf(`[%s:%s:%s]%s`, foregroundColor, backgroundColor, attributes, text[startPos:pos])
}
runePos++
}
return text[startPos:], color
return fmt.Sprintf(`[%s:%s:%s]%s`, foregroundColor, backgroundColor, attributes, text[startPos:])
}
// We want to reduce everything to AlignLeft.
@ -224,17 +361,16 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc
width += w
start = index
}
text, color = substring(start, len(runes), color)
return Print(screen, text, x+maxWidth-width, y, width, AlignLeft, color)
return printWithStyle(screen, substring(start, len(runes)), x+maxWidth-width, y, width, AlignLeft, style)
} else if align == AlignCenter {
width := runewidth.StringWidth(strippedText)
if width == maxWidth {
// Use the exact space.
return Print(screen, text, x, y, maxWidth, AlignLeft, color)
return printWithStyle(screen, text, x, y, maxWidth, AlignLeft, style)
} else if width < maxWidth {
// We have more space than we need.
half := (maxWidth - width) / 2
return Print(screen, text, x+half, y, maxWidth-half, AlignLeft, color)
return printWithStyle(screen, text, x+half, y, maxWidth-half, AlignLeft, style)
} else {
// Chop off runes until we have a perfect fit.
var choppedLeft, choppedRight, leftIndex, rightIndex int
@ -250,20 +386,22 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc
rightIndex--
}
}
text, color = substring(leftIndex, rightIndex, color)
return Print(screen, text, x, y, maxWidth, AlignLeft, color)
return printWithStyle(screen, substring(leftIndex, rightIndex), x, y, maxWidth, AlignLeft, style)
}
}
// Draw text.
drawn := 0
drawnWidth := 0
var colorPos, escapePos int
var (
colorPos, escapePos int
foregroundColor, backgroundColor, attributes string
)
for pos, ch := range text {
// Handle color tags.
if colorPos < len(colorIndices) && pos >= colorIndices[colorPos][0] && pos < colorIndices[colorPos][1] {
if pos == colorIndices[colorPos][1]-1 {
color = tcell.GetColor(colors[colorPos][1])
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
colorPos++
}
continue
@ -286,11 +424,12 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc
finalX := x + drawnWidth
// Print the rune.
_, _, style, _ := screen.GetContent(finalX, y)
style = style.Foreground(color)
_, _, finalStyle, _ := screen.GetContent(finalX, y)
_, background, _ := finalStyle.Decompose()
finalStyle = overlayStyle(background, style, foregroundColor, backgroundColor, attributes)
for offset := 0; offset < chWidth; offset++ {
// To avoid undesired effects, we place the same character in all cells.
screen.SetContent(finalX+offset, y, ch, nil, style)
screen.SetContent(finalX+offset, y, ch, nil, finalStyle)
}
drawn++
@ -308,7 +447,8 @@ func PrintSimple(screen tcell.Screen, text string, x, y int) {
// StringWidth returns the width of the given string needed to print it on
// screen. The text may contain color tags which are not counted.
func StringWidth(text string) int {
return runewidth.StringWidth(escapePattern.ReplaceAllString(colorPattern.ReplaceAllString(text, ""), "[$1$2]"))
_, _, _, _, width := decomposeString(text)
return width
}
// WordWrap splits a text such that each resulting line does not exceed the
@ -319,13 +459,7 @@ func StringWidth(text string) int {
//
// Text is always split at newline characters ('\n').
func WordWrap(text string, width int) (lines []string) {
// Strip color tags.
strippedText := escapePattern.ReplaceAllString(colorPattern.ReplaceAllString(text, ""), "[$1$2]")
// Keep track of color tags and escape patterns so we can restore the original
// indices.
colorTagIndices := colorPattern.FindAllStringIndex(text, -1)
escapeIndices := escapePattern.FindAllStringIndex(text, -1)
colorTagIndices, _, escapeIndices, strippedText, _ := decomposeString(text)
// Find candidate breakpoints.
breakPoints := boundaryPattern.FindAllStringIndex(strippedText, -1)
@ -454,3 +588,13 @@ func PrintJoinedBorder(screen tcell.Screen, x, y int, ch rune, color tcell.Color
// We only print something if we have something.
screen.SetContent(x, y, result, nil, style)
}
// Escape escapes the given text such that color and/or region tags are not
// recognized and substituted by the print functions of this package. For
// example, to include a tag-like string in a box title or in a TextView:
//
// box.SetTitle(tview.Escape("[squarebrackets]"))
// fmt.Fprint(textView, tview.Escape(`["quoted"]`))
func Escape(text string) string {
return nonEscapePattern.ReplaceAllString(text, "$1[]")
}