feat(inputfield/autocompletion): Draw selected autocomplete suggestion in `inputfield` without changing it till confirmed with `ENTER`

This commit is contained in:
Andreas Bieber 2020-09-16 20:33:02 +02:00
parent bace1ac630
commit 75b6d31409
2 changed files with 56 additions and 9 deletions

View File

@ -76,6 +76,9 @@ type InputField struct {
// The background color of the selected ListItem.
autocompleteListSelectedBackgroundColor tcell.Color
// The text color of the suggestion.
autocompleteSuggestionTextColor tcell.Color
// The screen width of the label area. A value of 0 means use the width of
// the label text.
labelWidth int
@ -100,6 +103,9 @@ type InputField struct {
// nil, the list's main texts represent the current autocomplete entries.
autocompleteList *List
// The suggested completion of the current autocomplete ListItem.
autocompleteListSuggestion string
// An optional function which may reject the last character that was entered.
accept func(text string, ch rune) bool
@ -140,6 +146,7 @@ func NewInputField() *InputField {
autocompleteListBackgroundColor: Styles.MoreContrastBackgroundColor,
autocompleteListSelectedTextColor: Styles.PrimitiveBackgroundColor,
autocompleteListSelectedBackgroundColor: Styles.PrimaryTextColor,
autocompleteSuggestionTextColor: Styles.ContrastPrimaryTextColor,
}
}
@ -308,6 +315,12 @@ func (i *InputField) SetAutocompleteListSelectedBackgroundColor(color tcell.Colo
return i
}
// SetAutocompleteSuggestionColor sets the text color of the autocomplete suggestion in the input field.
func (i *InputField) SetAutocompleteSuggestionTextColor(color tcell.Color) *InputField {
i.autocompleteSuggestionTextColor = color
return i
}
// SetFormAttributes sets attributes shared by all form items.
func (i *InputField) SetFormAttributes(labelWidth int, bgColor, labelColor, labelColorFocused, fieldTextColor, fieldTextColorFocused, fieldBgColor, fieldBgColorFocused tcell.Color) FormItem {
i.Lock()
@ -405,6 +418,7 @@ func (i *InputField) Autocomplete() *InputField {
// No entries, no list.
i.Lock()
i.autocompleteList = nil
i.autocompleteListSuggestion = ""
i.Unlock()
return i
}
@ -415,6 +429,7 @@ func (i *InputField) Autocomplete() *InputField {
if i.autocompleteList == nil {
i.autocompleteList = NewList()
i.autocompleteList.
SetChangedFunc(i.autocompleteChanged).
ShowSecondaryText(false).
SetMainTextColor(i.autocompleteListTextColor).
SetSelectedTextColor(i.autocompleteListSelectedTextColor).
@ -443,6 +458,16 @@ func (i *InputField) Autocomplete() *InputField {
return i
}
// autocompleteChanged gets called when another item in the
// autocomplete list has been selected.
func (i *InputField) autocompleteChanged(_ int, item *ListItem) {
if referenceVal, isString := item.reference.(string); isString {
i.autocompleteListSuggestion = referenceVal[len(i.text):]
} else {
i.autocompleteListSuggestion = item.mainText[len(i.text):]
}
}
// SetAcceptanceFunc sets a handler which may reject the last character that was
// entered (by returning false).
//
@ -554,9 +579,11 @@ func (i *InputField) Draw(screen tcell.Screen) {
if i.maskCharacter > 0 {
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
}
drawnText := ""
if fieldWidth >= stringWidth(text) {
// We have enough space for the full text.
Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, fieldTextColor)
drawnText = Escape(text)
Print(screen, drawnText, x, y, fieldWidth, AlignLeft, fieldTextColor)
i.offset = 0
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
if textPos >= i.cursorPos {
@ -594,7 +621,12 @@ func (i *InputField) Draw(screen tcell.Screen) {
}
return false
})
Print(screen, Escape(text[i.offset:]), x, y, fieldWidth, AlignLeft, fieldTextColor)
drawnText = Escape(text[i.offset:])
Print(screen, drawnText, x, y, fieldWidth, AlignLeft, fieldTextColor)
}
// Draw suggestion
if i.maskCharacter == 0 && i.autocompleteListSuggestion != "" {
Print(screen, i.autocompleteListSuggestion, x+stringWidth(drawnText), y, fieldWidth-stringWidth(drawnText), AlignLeft, i.autocompleteSuggestionTextColor)
}
}
@ -767,14 +799,31 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
home()
case tcell.KeyEnd, tcell.KeyCtrlE:
end()
case tcell.KeyEnter, tcell.KeyEscape: // We might be done.
case tcell.KeyEnter: // We might be done.
if i.autocompleteList != nil {
currentItem := i.autocompleteList.GetCurrentItem()
i.Unlock()
if referenceVal, isString := currentItem.reference.(string); isString {
i.SetText(referenceVal)
} else {
i.SetText(currentItem.mainText)
}
i.Lock()
i.autocompleteList = nil
i.Unlock()
i.autocompleteListSuggestion = ""
} else {
i.Unlock()
finish(key)
}
i.Unlock()
return
case tcell.KeyEscape:
if i.autocompleteList != nil {
i.autocompleteList = nil
i.autocompleteListSuggestion = ""
} else {
finish(key)
}
i.Unlock()
return
case tcell.KeyDown, tcell.KeyTab: // Autocomplete selection.
if i.autocompleteList != nil {
@ -784,9 +833,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
newEntry = 0
}
i.autocompleteList.SetCurrentItem(newEntry)
currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice.
i.Unlock()
i.SetText(currentText)
} else {
i.Unlock()
finish(key)
@ -799,9 +846,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
newEntry = i.autocompleteList.GetItemCount() - 1
}
i.autocompleteList.SetCurrentItem(newEntry)
currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice.
i.Unlock()
i.SetText(currentText)
} else {
i.Unlock()
finish(key)

View File

@ -14,6 +14,7 @@ type Theme struct {
SecondaryTextColor tcell.Color // Secondary text (e.g. labels).
TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes).
InverseTextColor tcell.Color // Text on primary-colored backgrounds.
ContrastPrimaryTextColor tcell.Color // Primary text for contrasting elements.
ContrastSecondaryTextColor tcell.Color // Secondary text on ContrastBackgroundColor-colored backgrounds.
// Background
@ -46,6 +47,7 @@ var Styles = Theme{
SecondaryTextColor: tcell.ColorYellow.TrueColor(),
TertiaryTextColor: tcell.ColorGreen.TrueColor(),
InverseTextColor: tcell.ColorBlue.TrueColor(),
ContrastPrimaryTextColor: tcell.ColorBlack.TrueColor(),
ContrastSecondaryTextColor: tcell.ColorDarkCyan.TrueColor(),
PrimitiveBackgroundColor: tcell.ColorBlack.TrueColor(),