aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTulir Asokan <tulir@maunium.net>2018-04-30 10:55:37 +0300
committerTulir Asokan <tulir@maunium.net>2018-04-30 10:55:37 +0300
commite48ff5bea4725d39818f24fa76b5ae74971f23a3 (patch)
tree549b8688ef59f7096a20544cd01ad75ec111c0e4
parent576bab9e2e9589942d4cac8742fa1b54e8b237f9 (diff)
Update dependencies
-rw-r--r--Gopkg.lock2
-rw-r--r--vendor/maunium.net/go/tview/README.md5
-rw-r--r--vendor/maunium.net/go/tview/ansii.go237
-rw-r--r--vendor/maunium.net/go/tview/application.go20
-rw-r--r--vendor/maunium.net/go/tview/box.go32
-rw-r--r--vendor/maunium.net/go/tview/checkbox.go40
-rw-r--r--vendor/maunium.net/go/tview/doc.go42
-rw-r--r--vendor/maunium.net/go/tview/dropdown.go40
-rw-r--r--vendor/maunium.net/go/tview/flex.go4
-rw-r--r--vendor/maunium.net/go/tview/form.go36
-rw-r--r--vendor/maunium.net/go/tview/grid.go2
-rw-r--r--vendor/maunium.net/go/tview/inputfield.go50
-rw-r--r--vendor/maunium.net/go/tview/list.go20
-rw-r--r--vendor/maunium.net/go/tview/primitive.go2
-rw-r--r--vendor/maunium.net/go/tview/table.go2
-rw-r--r--vendor/maunium.net/go/tview/textview.go93
-rw-r--r--vendor/maunium.net/go/tview/util.go222
17 files changed, 737 insertions, 112 deletions
diff --git a/Gopkg.lock b/Gopkg.lock
index ba6ad61..b78c6ef 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -139,7 +139,7 @@
branch = "master"
name = "maunium.net/go/tview"
packages = ["."]
- revision = "6146b7fe2331e23a78e217016705bc6801bfc55a"
+ revision = "7eabba90a261a481d36ace89daa79c56582238d7"
[solve-meta]
analyzer-name = "dep"
diff --git a/vendor/maunium.net/go/tview/README.md b/vendor/maunium.net/go/tview/README.md
index fb99073..3e5734e 100644
--- a/vendor/maunium.net/go/tview/README.md
+++ b/vendor/maunium.net/go/tview/README.md
@@ -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)
diff --git a/vendor/maunium.net/go/tview/ansii.go b/vendor/maunium.net/go/tview/ansii.go
new file mode 100644
index 0000000..0ce3d4a
--- /dev/null
+++ b/vendor/maunium.net/go/tview/ansii.go
@@ -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()
+}
diff --git a/vendor/maunium.net/go/tview/application.go b/vendor/maunium.net/go/tview/application.go
index f3d6328..387c4c4 100644
--- a/vendor/maunium.net/go/tview/application.go
+++ b/vendor/maunium.net/go/tview/application.go
@@ -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
diff --git a/vendor/maunium.net/go/tview/box.go b/vendor/maunium.net/go/tview/box.go
index 1bcbff0..ff3cc1e 100644
--- a/vendor/maunium.net/go/tview/box.go
+++ b/vendor/maunium.net/go/tview/box.go
@@ -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
diff --git a/vendor/maunium.net/go/tview/checkbox.go b/vendor/maunium.net/go/tview/checkbox.go
index 83404b8..ae58720 100644
--- a/vendor/maunium.net/go/tview/checkbox.go
+++ b/vendor/maunium.net/go/tview/checkbox.go
@@ -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)
+ }
}
})
}
diff --git a/vendor/maunium.net/go/tview/doc.go b/vendor/maunium.net/go/tview/doc.go
index 101dcd5..ccaaaf1 100644
--- a/vendor/maunium.net/go/tview/doc.go
+++ b/vendor/maunium.net/go/tview/doc.go
@@ -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
diff --git a/vendor/maunium.net/go/tview/dropdown.go b/vendor/maunium.net/go/tview/dropdown.go
index 981d1bd..515f978 100644
--- a/vendor/maunium.net/go/tview/dropdown.go
+++ b/vendor/maunium.net/go/tview/dropdown.go
@@ -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)
+ }
}
})
}
diff --git a/vendor/maunium.net/go/tview/flex.go b/vendor/maunium.net/go/tview/flex.go
index a698442..ad42d3a 100644
--- a/vendor/maunium.net/go/tview/flex.go
+++ b/vendor/maunium.net/go/tview/flex.go
@@ -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
diff --git a/vendor/maunium.net/go/tview/form.go b/vendor/maunium.net/go/tview/form.go
index b1e8f5e..fe0e980 100644
--- a/vendor/maunium.net/go/tview/form.go
+++ b/vendor/maunium.net/go/tview/form.go
@@ -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,
diff --git a/vendor/maunium.net/go/tview/grid.go b/vendor/maunium.net/go/tview/grid.go
index 719ac7a..77797ba 100644
--- a/vendor/maunium.net/go/tview/grid.go
+++ b/vendor/maunium.net/go/tview/grid.go
@@ -258,7 +258,7 @@ func (g *Grid) HasFocus() bool {
return true
}
}
- return false
+ return g.hasFocus
}
// InputHandler returns the handler for this primitive.
diff --git a/vendor/maunium.net/go/tview/inputfield.go b/vendor/maunium.net/go/tview/inputfield.go
index c168106..2c92a93 100644
--- a/vendor/maunium.net/go/tview/inputfield.go
+++ b/vendor/maunium.net/go/tview/inputfield.go
@@ -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)
+ }
}
})
}
diff --git a/vendor/maunium.net/go/tview/list.go b/vendor/maunium.net/go/tview/list.go
index 7395985..cc25262 100644
--- a/vendor/maunium.net/go/tview/list.go
+++ b/vendor/maunium.net/go/tview/list.go
@@ -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
diff --git a/vendor/maunium.net/go/tview/primitive.go b/vendor/maunium.net/go/tview/primitive.go
index a59033f..f5034c4 100644
--- a/vendor/maunium.net/go/tview/primitive.go
+++ b/vendor/maunium.net/go/tview/primitive.go
@@ -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))
diff --git a/vendor/maunium.net/go/tview/table.go b/vendor/maunium.net/go/tview/table.go
index 4f2336f..0446c6a 100644
--- a/vendor/maunium.net/go/tview/table.go
+++ b/vendor/maunium.net/go/tview/table.go
@@ -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
}
diff --git a/vendor/maunium.net/go/tview/textview.go b/vendor/maunium.net/go/tview/textview.go
index 8de0121..16b9dfb 100644
--- a/vendor/maunium.net/go/tview/textview.go
+++ b/vendor/maunium.net/go/tview/textview.go
@@ -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,12 +815,31 @@ 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.
diff --git a/vendor/maunium.net/go/tview/util.go b/vendor/maunium.net/go/tview/util.go
index f49c35f..2b3a544 100644
--- a/vendor/maunium.net/go/tview/util.go
+++ b/vendor/maunium.net/go/tview/util.go
@@ -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[]")
+}