From ad9e2501de06f2897c851b8763ec466783d3a39c Mon Sep 17 00:00:00 2001 From: Oliver <480930+rivo@users.noreply.github.com> Date: Sun, 3 Nov 2024 12:33:52 +0100 Subject: [PATCH] Tags can be turned off for List and Autocomplete; also added Unescape function. Fixes #1016 --- inputfield.go | 13 ++++++++++++- list.go | 39 +++++++++++++++++++++++++++++++++------ strings.go | 7 ++++++- util.go | 7 +++++-- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/inputfield.go b/inputfield.go index 0541130..3511278 100644 --- a/inputfield.go +++ b/inputfield.go @@ -1,6 +1,7 @@ package tview import ( + "math" "strconv" "strings" "sync" @@ -99,6 +100,7 @@ type InputField struct { main tcell.Style selected tcell.Style background tcell.Color + useTags bool } // An optional function which is called when the user selects an @@ -146,6 +148,7 @@ func NewInputField() *InputField { i.autocompleteStyles.main = tcell.StyleDefault.Background(Styles.MoreContrastBackgroundColor).Foreground(Styles.PrimitiveBackgroundColor) i.autocompleteStyles.selected = tcell.StyleDefault.Background(Styles.PrimaryTextColor).Foreground(Styles.PrimitiveBackgroundColor) i.autocompleteStyles.background = Styles.MoreContrastBackgroundColor + i.autocompleteStyles.useTags = true return i } @@ -256,6 +259,13 @@ func (i *InputField) SetAutocompleteStyles(background tcell.Color, main, selecte return i } +// SetAutocompleteUseTags sets whether or not the autocomplete entries may +// contain style tags affecting their appearance. The default is true. +func (i *InputField) SetAutocompleteUseTags(useTags bool) *InputField { + i.autocompleteStyles.useTags = useTags + return i +} + // SetFormAttributes sets attributes shared by all form items. func (i *InputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { i.textArea.SetFormAttributes(labelWidth, labelColor, bgColor, fieldTextColor, fieldBgColor) @@ -363,13 +373,14 @@ func (i *InputField) Autocomplete() *InputField { i.autocompleteList.ShowSecondaryText(false). SetMainTextStyle(i.autocompleteStyles.main). SetSelectedStyle(i.autocompleteStyles.selected). + SetUseStyleTags(i.autocompleteStyles.useTags, i.autocompleteStyles.useTags). SetHighlightFullLine(true). SetBackgroundColor(i.autocompleteStyles.background) } // Fill it with the entries. currentEntry := -1 - suffixLength := 9999 // I'm just waiting for the day somebody opens an issue with this number being too small. + suffixLength := math.MaxInt i.autocompleteList.Clear() for index, entry := range entries { i.autocompleteList.AddItem(entry, "", 0, nil) diff --git a/list.go b/list.go index 8da6566..0b96a3a 100644 --- a/list.go +++ b/list.go @@ -30,6 +30,9 @@ type listItem struct { // - Right / left: Scroll horizontally. Only if the list is wider than the // available space. // +// By default, list item texts can contain style tags. Use +// [List.SetUseStyleTags] to disable this feature. +// // See [List.SetChangedFunc] for a way to be notified when the user navigates // to a list item. See [List.SetSelectedFunc] for a way to be notified when a // list item was selected. @@ -65,6 +68,12 @@ type List struct { // If true, the entire row is highlighted when selected. highlightFullLine bool + // Whether or not style tags can be used in the main text. + mainStyleTags bool + + // Whether or not style tags can be used in the secondary text. + secondaryStyleTags bool + // Whether or not navigating the list will wrap around. wrapAround bool @@ -98,6 +107,8 @@ func NewList() *List { secondaryTextStyle: tcell.StyleDefault.Foreground(Styles.TertiaryTextColor).Background(Styles.PrimitiveBackgroundColor), shortcutStyle: tcell.StyleDefault.Foreground(Styles.SecondaryTextColor).Background(Styles.PrimitiveBackgroundColor), selectedStyle: tcell.StyleDefault.Foreground(Styles.PrimitiveBackgroundColor).Background(Styles.PrimaryTextColor), + mainStyleTags: true, + secondaryStyleTags: true, } } @@ -268,6 +279,14 @@ func (l *List) SetSelectedStyle(style tcell.Style) *List { return l } +// SetUseStyleTags sets a flag which determines whether style tags are used in +// the main and secondary texts. The default is true. +func (l *List) SetUseStyleTags(mainStyleTags, secondaryStyleTags bool) *List { + l.mainStyleTags = mainStyleTags + l.secondaryStyleTags = secondaryStyleTags + return l +} + // SetSelectedFocusOnly sets a flag which determines when the currently selected // list item is highlighted. If set to true, selected items are only highlighted // when the list has focus. If set to false, they are always highlighted. @@ -328,7 +347,7 @@ func (l *List) SetDoneFunc(handler func()) *List { return l } -// AddItem calls InsertItem() with an index of -1. +// AddItem calls [List.InsertItem] with an index of -1. func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List { l.InsertItem(-1, mainText, secondaryText, shortcut, selected) return l @@ -336,8 +355,8 @@ func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected f // InsertItem adds a new item to the list at the specified index. An index of 0 // will insert the item at the beginning, an index of 1 before the second item, -// and so on. An index of GetItemCount() or higher will insert the item at the -// end of the list. Negative indices are also allowed: An index of -1 will +// and so on. An index of [List.GetItemCount] or higher will insert the item at +// the end of the list. Negative indices are also allowed: An index of -1 will // insert the item at the end of the list, an index of -2 before the last item, // and so on. An index of -GetItemCount()-1 or lower will insert the item at the // beginning. @@ -351,7 +370,7 @@ func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected f // // The "selected" callback will be invoked when the user selects the item. You // may provide nil if no such callback is needed or if all events are handled -// through the selected callback set with SetSelectedFunc(). +// through the selected callback set with [List.SetSelectedFunc]. // // The currently selected item will shift its position accordingly. If the list // was previously empty, a "changed" event is fired because the new item becomes @@ -511,7 +530,11 @@ func (l *List) Draw(screen tcell.Screen) { if selected { style = l.selectedStyle } - _, _, printedWidth := printWithStyle(screen, item.MainText, x, y, l.horizontalOffset, width, AlignLeft, style, false) + mainText := item.MainText + if !l.mainStyleTags { + mainText = Escape(mainText) + } + _, _, printedWidth := printWithStyle(screen, mainText, x, y, l.horizontalOffset, width, AlignLeft, style, false) if printedWidth > maxWidth { maxWidth = printedWidth } @@ -530,7 +553,11 @@ func (l *List) Draw(screen tcell.Screen) { // Secondary text. if l.showSecondaryText { - _, _, printedWidth := printWithStyle(screen, item.SecondaryText, x, y, l.horizontalOffset, width, AlignLeft, l.secondaryTextStyle, false) + secondaryText := item.SecondaryText + if !l.secondaryStyleTags { + secondaryText = Escape(secondaryText) + } + _, _, printedWidth := printWithStyle(screen, secondaryText, x, y, l.horizontalOffset, width, AlignLeft, l.secondaryTextStyle, false) if printedWidth > maxWidth { maxWidth = printedWidth } diff --git a/strings.go b/strings.go index 70bce60..81c6a71 100644 --- a/strings.go +++ b/strings.go @@ -593,7 +593,12 @@ func WordWrap(text string, width int) (lines []string) { // box.SetTitle(tview.Escape("[squarebrackets]")) // fmt.Fprint(textView, tview.Escape(`["quoted"]`)) func Escape(text string) string { - return nonEscapePattern.ReplaceAllString(text, "$1[]") + return escapePattern.ReplaceAllString(text, "$1[]") +} + +// Unescape unescapes text previously escaped with [Escape]. +func Unescape(text string) string { + return unescapePattern.ReplaceAllString(text, "$1]") } // stripTags strips style tags from the given string. (Region tags are not diff --git a/util.go b/util.go index b275c8c..3ea302a 100644 --- a/util.go +++ b/util.go @@ -19,7 +19,10 @@ const ( var ( // Regular expression used to escape style/region tags. - nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`) + escapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`) + + // Regular expression used to unescape escaped style/region tags. + unescapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\[\]`) // The number of colors available in the terminal. availableColors = 256 @@ -48,7 +51,7 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc return end - start, width } -// printWithStyle works like Print() but it takes a style instead of just a +// printWithStyle works like [Print] but it takes a style instead of just a // foreground color. The skipWidth parameter specifies the number of cells // skipped at the beginning of the text. It returns the start index, end index // (exclusively), and screen width of the text actually printed. If