nt

A sensible note-taking program
git clone git://git.laack.co/nt.git
Log | Files | Refs | README

inputfield.go (23649B)


      1 package tview
      2 
      3 import (
      4 	"math"
      5 	"strconv"
      6 	"strings"
      7 	"sync"
      8 
      9 	"github.com/gdamore/tcell/v2"
     10 	"github.com/rivo/uniseg"
     11 )
     12 
     13 const (
     14 	AutocompletedNavigate = iota // The user navigated the autocomplete list (using the errow keys).
     15 	AutocompletedTab             // The user selected an autocomplete entry using the tab key.
     16 	AutocompletedEnter           // The user selected an autocomplete entry using the enter key.
     17 	AutocompletedClick           // The user selected an autocomplete entry by clicking the mouse button on it.
     18 )
     19 
     20 // Predefined InputField acceptance functions.
     21 var (
     22 	// InputFieldInteger accepts integers.
     23 	InputFieldInteger = func(text string, ch rune) bool {
     24 		if text == "-" {
     25 			return true
     26 		}
     27 		_, err := strconv.Atoi(text)
     28 		return err == nil
     29 	}
     30 
     31 	// InputFieldFloat accepts floating-point numbers.
     32 	InputFieldFloat = func(text string, ch rune) bool {
     33 		if text == "-" || text == "." || text == "-." {
     34 			return true
     35 		}
     36 		_, err := strconv.ParseFloat(text, 64)
     37 		return err == nil
     38 	}
     39 
     40 	// InputFieldMaxLength returns an input field accept handler which accepts
     41 	// input strings up to a given length. Use it like this:
     42 	//
     43 	//   inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters.
     44 	InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool {
     45 		return func(text string, ch rune) bool {
     46 			return len([]rune(text)) <= maxLength
     47 		}
     48 	}
     49 )
     50 
     51 // InputField is a one-line box into which the user can enter text. Use
     52 // [InputField.SetAcceptanceFunc] to accept or reject input,
     53 // [InputField.SetChangedFunc] to listen for changes, and
     54 // [InputField.SetMaskCharacter] to hide input from onlookers (e.g. for password
     55 // input).
     56 //
     57 // The input field also has an optional autocomplete feature. It is initialized
     58 // by the [InputField.SetAutocompleteFunc] function. For more control over the
     59 // autocomplete drop-down's behavior, you can also set the
     60 // [InputField.SetAutocompletedFunc].
     61 //
     62 // Navigation and editing is the same as for a [TextArea], with the following
     63 // exceptions:
     64 //
     65 //   - Tab, BackTab, Enter, Escape: Finish editing.
     66 //
     67 // Note that while pressing Tab or Enter is intercepted by the input field, it
     68 // is possible to paste such characters into the input field, possibly resulting
     69 // in multi-line input. You can use [InputField.SetAcceptanceFunc] to prevent
     70 // this.
     71 //
     72 // If autocomplete functionality is configured:
     73 //
     74 //   - Down arrow: Open the autocomplete drop-down.
     75 //   - Tab, Enter: Select the current autocomplete entry.
     76 //
     77 // See https://github.com/rivo/tview/wiki/InputField for an example.
     78 type InputField struct {
     79 	*Box
     80 
     81 	// The text area providing the core functionality of the input field.
     82 	textArea *TextArea
     83 
     84 	// The screen width of the input area. A value of 0 means extend as much as
     85 	// possible.
     86 	fieldWidth int
     87 
     88 	// An optional autocomplete function which receives the current text of the
     89 	// input field and returns a slice of strings to be displayed in a drop-down
     90 	// selection.
     91 	autocomplete func(text string) []string
     92 
     93 	// The List object which shows the selectable autocomplete entries. If not
     94 	// nil, the list's main texts represent the current autocomplete entries.
     95 	autocompleteList      *List
     96 	autocompleteListMutex sync.Mutex
     97 
     98 	// The styles of the autocomplete entries.
     99 	autocompleteStyles struct {
    100 		main       tcell.Style
    101 		selected   tcell.Style
    102 		background tcell.Color
    103 		useTags    bool
    104 	}
    105 
    106 	// An optional function which is called when the user selects an
    107 	// autocomplete entry. The text and index of the selected entry (within the
    108 	// list) is provided, as well as the user action causing the selection (one
    109 	// of the "Autocompleted" values). The function should return true if the
    110 	// autocomplete list should be closed. If nil, the input field will be
    111 	// updated automatically when the user navigates the autocomplete list.
    112 	autocompleted func(text string, index int, source int) bool
    113 
    114 	// An optional function which may reject the last character that was entered.
    115 	accept func(text string, ch rune) bool
    116 
    117 	// An optional function which is called when the input has changed.
    118 	changed func(text string)
    119 
    120 	// An optional function which is called when the user indicated that they
    121 	// are done entering text. The key which was pressed is provided (tab,
    122 	// shift-tab, enter, or escape).
    123 	done func(tcell.Key)
    124 
    125 	// A callback function set by the Form class and called when the user leaves
    126 	// this form item.
    127 	finished func(tcell.Key)
    128 }
    129 
    130 // NewInputField returns a new input field.
    131 func NewInputField() *InputField {
    132 	i := &InputField{
    133 		Box:      NewBox(),
    134 		textArea: NewTextArea().SetWrap(false),
    135 	}
    136 	i.textArea.SetChangedFunc(func() {
    137 		if i.changed != nil {
    138 			i.changed(i.textArea.GetText())
    139 		}
    140 	}).SetFocusFunc(func() {
    141 		// Forward focus event to the input field.
    142 		if i.Box.focus != nil {
    143 			i.Box.focus()
    144 		}
    145 	})
    146 	i.textArea.textStyle = tcell.StyleDefault.Background(Styles.ContrastBackgroundColor).Foreground(Styles.PrimaryTextColor)
    147 	i.textArea.placeholderStyle = tcell.StyleDefault.Background(Styles.ContrastBackgroundColor).Foreground(Styles.ContrastSecondaryTextColor)
    148 	i.autocompleteStyles.main = tcell.StyleDefault.Background(Styles.MoreContrastBackgroundColor).Foreground(Styles.PrimitiveBackgroundColor)
    149 	i.autocompleteStyles.selected = tcell.StyleDefault.Background(Styles.PrimaryTextColor).Foreground(Styles.PrimitiveBackgroundColor)
    150 	i.autocompleteStyles.background = Styles.MoreContrastBackgroundColor
    151 	i.autocompleteStyles.useTags = true
    152 	return i
    153 }
    154 
    155 // SetText sets the current text of the input field. This can be undone by the
    156 // user. Calling this function will also trigger a "changed" event.
    157 func (i *InputField) SetText(text string) *InputField {
    158 	i.textArea.Replace(0, i.textArea.GetTextLength(), text)
    159 	return i
    160 }
    161 
    162 // GetText returns the current text of the input field.
    163 func (i *InputField) GetText() string {
    164 	return i.textArea.GetText()
    165 }
    166 
    167 // SetLabel sets the text to be displayed before the input area.
    168 func (i *InputField) SetLabel(label string) *InputField {
    169 	i.textArea.SetLabel(label)
    170 	return i
    171 }
    172 
    173 // GetLabel returns the text to be displayed before the input area.
    174 func (i *InputField) GetLabel() string {
    175 	return i.textArea.GetLabel()
    176 }
    177 
    178 // SetLabelWidth sets the screen width of the label. A value of 0 will cause the
    179 // primitive to use the width of the label string.
    180 func (i *InputField) SetLabelWidth(width int) *InputField {
    181 	i.textArea.SetLabelWidth(width)
    182 	return i
    183 }
    184 
    185 // SetPlaceholder sets the text to be displayed when the input text is empty.
    186 func (i *InputField) SetPlaceholder(text string) *InputField {
    187 	i.textArea.SetPlaceholder(text)
    188 	return i
    189 }
    190 
    191 // SetLabelColor sets the text color of the label.
    192 func (i *InputField) SetLabelColor(color tcell.Color) *InputField {
    193 	i.textArea.SetLabelStyle(i.textArea.GetLabelStyle().Foreground(color))
    194 	return i
    195 }
    196 
    197 // SetLabelStyle sets the style of the label.
    198 func (i *InputField) SetLabelStyle(style tcell.Style) *InputField {
    199 	i.textArea.SetLabelStyle(style)
    200 	return i
    201 }
    202 
    203 // GetLabelStyle returns the style of the label.
    204 func (i *InputField) GetLabelStyle() tcell.Style {
    205 	return i.textArea.GetLabelStyle()
    206 }
    207 
    208 // SetFieldBackgroundColor sets the background color of the input area.
    209 func (i *InputField) SetFieldBackgroundColor(color tcell.Color) *InputField {
    210 	i.textArea.SetTextStyle(i.textArea.GetTextStyle().Background(color))
    211 	return i
    212 }
    213 
    214 // SetFieldTextColor sets the text color of the input area.
    215 func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField {
    216 	i.textArea.SetTextStyle(i.textArea.GetTextStyle().Foreground(color))
    217 	return i
    218 }
    219 
    220 // SetFieldStyle sets the style of the input area (when no placeholder is
    221 // shown).
    222 func (i *InputField) SetFieldStyle(style tcell.Style) *InputField {
    223 	i.textArea.SetTextStyle(style)
    224 	return i
    225 }
    226 
    227 // GetFieldStyle returns the style of the input area (when no placeholder is
    228 // shown).
    229 func (i *InputField) GetFieldStyle() tcell.Style {
    230 	return i.textArea.GetTextStyle()
    231 }
    232 
    233 // SetPlaceholderTextColor sets the text color of placeholder text.
    234 func (i *InputField) SetPlaceholderTextColor(color tcell.Color) *InputField {
    235 	i.textArea.SetPlaceholderStyle(i.textArea.GetPlaceholderStyle().Foreground(color))
    236 	return i
    237 }
    238 
    239 // SetPlaceholderStyle sets the style of the input area (when a placeholder is
    240 // shown).
    241 func (i *InputField) SetPlaceholderStyle(style tcell.Style) *InputField {
    242 	i.textArea.SetPlaceholderStyle(style)
    243 	return i
    244 }
    245 
    246 // GetPlaceholderStyle returns the style of the input area (when a placeholder
    247 // is shown).
    248 func (i *InputField) GetPlaceholderStyle() tcell.Style {
    249 	return i.textArea.GetPlaceholderStyle()
    250 }
    251 
    252 // SetAutocompleteStyles sets the colors and style of the autocomplete entries.
    253 // For details, see [List.SetMainTextStyle], [List.SetSelectedStyle], and
    254 // [Box.SetBackgroundColor].
    255 func (i *InputField) SetAutocompleteStyles(background tcell.Color, main, selected tcell.Style) *InputField {
    256 	i.autocompleteStyles.background = background
    257 	i.autocompleteStyles.main = main
    258 	i.autocompleteStyles.selected = selected
    259 	return i
    260 }
    261 
    262 // SetAutocompleteUseTags sets whether or not the autocomplete entries may
    263 // contain style tags affecting their appearance. The default is true.
    264 func (i *InputField) SetAutocompleteUseTags(useTags bool) *InputField {
    265 	i.autocompleteStyles.useTags = useTags
    266 	return i
    267 }
    268 
    269 // SetFormAttributes sets attributes shared by all form items.
    270 func (i *InputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
    271 	i.textArea.SetFormAttributes(labelWidth, labelColor, bgColor, fieldTextColor, fieldBgColor)
    272 	return i
    273 }
    274 
    275 // SetFieldWidth sets the screen width of the input area. A value of 0 means
    276 // extend as much as possible.
    277 func (i *InputField) SetFieldWidth(width int) *InputField {
    278 	i.fieldWidth = width
    279 	return i
    280 }
    281 
    282 // GetFieldWidth returns this primitive's field width.
    283 func (i *InputField) GetFieldWidth() int {
    284 	return i.fieldWidth
    285 }
    286 
    287 // GetFieldHeight returns this primitive's field height.
    288 func (i *InputField) GetFieldHeight() int {
    289 	return 1
    290 }
    291 
    292 // SetDisabled sets whether or not the item is disabled / read-only.
    293 func (i *InputField) SetDisabled(disabled bool) FormItem {
    294 	i.textArea.SetDisabled(disabled)
    295 	if i.finished != nil {
    296 		i.finished(-1)
    297 	}
    298 	return i
    299 }
    300 
    301 // SetMaskCharacter sets a character that masks user input on a screen. A value
    302 // of 0 disables masking.
    303 func (i *InputField) SetMaskCharacter(mask rune) *InputField {
    304 	if mask == 0 {
    305 		i.textArea.setTransform(nil)
    306 		return i
    307 	}
    308 	maskStr := string(mask)
    309 	maskWidth := uniseg.StringWidth(maskStr)
    310 	i.textArea.setTransform(func(cluster, rest string, boundaries int) (newCluster string, newBoundaries int) {
    311 		return maskStr, maskWidth << uniseg.ShiftWidth
    312 	})
    313 	return i
    314 }
    315 
    316 // SetAutocompleteFunc sets an autocomplete callback function which may return
    317 // strings to be selected from a drop-down based on the current text of the
    318 // input field. The drop-down appears only if len(entries) > 0. The callback is
    319 // invoked in this function and whenever the current text changes or when
    320 // [InputField.Autocomplete] is called. Entries are cleared when the user
    321 // selects an entry or presses Escape.
    322 func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entries []string)) *InputField {
    323 	i.autocomplete = callback
    324 	i.Autocomplete()
    325 	return i
    326 }
    327 
    328 // SetAutocompletedFunc sets a callback function which is invoked when the user
    329 // selects an entry from the autocomplete drop-down list. The function is passed
    330 // the text of the selected entry (stripped of any style tags), the index of the
    331 // entry, and the user action that caused the selection, for example
    332 // [AutocompletedNavigate]. It returns true if the autocomplete drop-down should
    333 // be closed after the callback returns or false if it should remain open, in
    334 // which case [InputField.Autocomplete] is called to update the drop-down's
    335 // contents.
    336 //
    337 // If no such callback is set (or nil is provided), the input field will be
    338 // updated with the selection any time the user navigates the autocomplete
    339 // drop-down list. So this function essentially gives you more control over the
    340 // autocomplete functionality.
    341 func (i *InputField) SetAutocompletedFunc(autocompleted func(text string, index int, source int) bool) *InputField {
    342 	i.autocompleted = autocompleted
    343 	return i
    344 }
    345 
    346 // Autocomplete invokes the autocomplete callback (if there is one, see
    347 // [InputField.SetAutocompleteFunc]). If the length of the returned autocomplete
    348 // entries slice is greater than 0, the input field will present the user with a
    349 // corresponding drop-down list the next time the input field is drawn.
    350 //
    351 // It is safe to call this function from any goroutine. Note that the input
    352 // field is not redrawn automatically unless called from the main goroutine
    353 // (e.g. in response to events).
    354 func (i *InputField) Autocomplete() *InputField {
    355 	i.autocompleteListMutex.Lock()
    356 	defer i.autocompleteListMutex.Unlock()
    357 	if i.autocomplete == nil {
    358 		return i
    359 	}
    360 
    361 	// Do we have any autocomplete entries?
    362 	text := i.textArea.GetText()
    363 	entries := i.autocomplete(text)
    364 	if len(entries) == 0 {
    365 		// No entries, no list.
    366 		i.autocompleteList = nil
    367 		return i
    368 	}
    369 
    370 	// Make a list if we have none.
    371 	if i.autocompleteList == nil {
    372 		i.autocompleteList = NewList()
    373 		i.autocompleteList.ShowSecondaryText(false).
    374 			SetMainTextStyle(i.autocompleteStyles.main).
    375 			SetSelectedStyle(i.autocompleteStyles.selected).
    376 			SetUseStyleTags(i.autocompleteStyles.useTags, i.autocompleteStyles.useTags).
    377 			SetHighlightFullLine(true).
    378 			SetBackgroundColor(i.autocompleteStyles.background)
    379 	}
    380 
    381 	// Fill it with the entries.
    382 	currentIndex := i.autocompleteList.GetCurrentItem()
    383 	var currentSelection string
    384 	if currentIndex >= 0 && currentIndex < i.autocompleteList.GetItemCount() {
    385 		currentSelection, _ = i.autocompleteList.GetItemText(currentIndex)
    386 	}
    387 	currentEntry := -1
    388 	suffixLength := math.MaxInt
    389 	i.autocompleteList.Clear()
    390 	for index, entry := range entries {
    391 		i.autocompleteList.AddItem(entry, "", 0, nil)
    392 		if currentSelection != "" && entry == currentSelection {
    393 			currentEntry = index
    394 		}
    395 		if currentSelection == "" && strings.HasPrefix(entry, text) && len(entry)-len(text) < suffixLength {
    396 			currentEntry = index
    397 			suffixLength = len(text) - len(entry)
    398 		}
    399 	}
    400 
    401 	// Set the selection if we have one.
    402 	if currentEntry >= 0 {
    403 		i.autocompleteList.SetCurrentItem(currentEntry)
    404 	}
    405 
    406 	return i
    407 }
    408 
    409 // SetAcceptanceFunc sets a handler which may reject the last character that was
    410 // entered, by returning false. The handler receives the text as it would be
    411 // after the change and the last character entered. If the handler is nil, all
    412 // input is accepted. The function is only called when a single rune is inserted
    413 // at the current cursor position.
    414 //
    415 // This package defines a number of variables prefixed with InputField which may
    416 // be used for common input (e.g. numbers, maximum text length). See for example
    417 // [InputFieldInteger].
    418 //
    419 // When text is pasted, lastChar is 0.
    420 func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField {
    421 	i.accept = handler
    422 	return i
    423 }
    424 
    425 // SetChangedFunc sets a handler which is called whenever the text of the input
    426 // field has changed. It receives the current text (after the change).
    427 func (i *InputField) SetChangedFunc(handler func(text string)) *InputField {
    428 	i.changed = handler
    429 	return i
    430 }
    431 
    432 // SetDoneFunc sets a handler which is called when the user is done entering
    433 // text. The callback function is provided with the key that was pressed, which
    434 // is one of the following:
    435 //
    436 //   - KeyEnter: Done entering text.
    437 //   - KeyEscape: Abort text input.
    438 //   - KeyTab: Move to the next field.
    439 //   - KeyBacktab: Move to the previous field.
    440 func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
    441 	i.done = handler
    442 	return i
    443 }
    444 
    445 // SetFinishedFunc sets a callback invoked when the user leaves this form item.
    446 func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
    447 	i.finished = handler
    448 	return i
    449 }
    450 
    451 // Focus is called when this primitive receives focus.
    452 func (i *InputField) Focus(delegate func(p Primitive)) {
    453 	// If we're part of a form and this item is disabled, there's nothing the
    454 	// user can do here so we're finished.
    455 	if i.finished != nil && i.textArea.GetDisabled() {
    456 		i.finished(-1)
    457 		return
    458 	}
    459 
    460 	i.Box.Focus(delegate)
    461 }
    462 
    463 // HasFocus returns whether or not this primitive has focus.
    464 func (i *InputField) HasFocus() bool {
    465 	return i.textArea.HasFocus() || i.Box.HasFocus()
    466 }
    467 
    468 // Blur is called when this primitive loses focus.
    469 func (i *InputField) Blur() {
    470 	i.textArea.Blur()
    471 	i.Box.Blur()
    472 	i.autocompleteList = nil // Hide the autocomplete drop-down.
    473 }
    474 
    475 // Draw draws this primitive onto the screen.
    476 func (i *InputField) Draw(screen tcell.Screen) {
    477 	i.Box.DrawForSubclass(screen, i)
    478 
    479 	// Prepare
    480 	x, y, width, height := i.GetInnerRect()
    481 	if height < 1 || width < 1 {
    482 		return
    483 	}
    484 
    485 	// Resize text area.
    486 	labelWidth := i.textArea.GetLabelWidth()
    487 	if labelWidth == 0 {
    488 		labelWidth = TaggedStringWidth(i.textArea.GetLabel())
    489 	}
    490 	fieldWidth := i.fieldWidth
    491 	if fieldWidth == 0 {
    492 		fieldWidth = width - labelWidth
    493 	}
    494 	i.textArea.SetRect(x, y, labelWidth+fieldWidth, 1)
    495 	i.textArea.setMinCursorPadding(fieldWidth-1, 1)
    496 
    497 	// Draw text area.
    498 	i.textArea.hasFocus = i.HasFocus() // Force cursor positioning.
    499 	i.textArea.Draw(screen)
    500 
    501 	// Draw autocomplete list.
    502 	i.autocompleteListMutex.Lock()
    503 	defer i.autocompleteListMutex.Unlock()
    504 	if i.autocompleteList != nil && i.HasFocus() {
    505 		// How much space do we need?
    506 		lheight := i.autocompleteList.GetItemCount()
    507 		lwidth := 0
    508 		for index := 0; index < lheight; index++ {
    509 			entry, _ := i.autocompleteList.GetItemText(index)
    510 			width := TaggedStringWidth(entry)
    511 			if width > lwidth {
    512 				lwidth = width
    513 			}
    514 		}
    515 
    516 		// We prefer to drop down but if there is no space, maybe drop up?
    517 		lx := x + labelWidth
    518 		ly := y + 1
    519 		_, sheight := screen.Size()
    520 		if ly+lheight >= sheight && ly-2 > lheight-ly {
    521 			ly = y - lheight
    522 			if ly < 0 {
    523 				ly = 0
    524 			}
    525 		}
    526 		if ly+lheight >= sheight {
    527 			lheight = sheight - ly
    528 		}
    529 		i.autocompleteList.SetRect(lx, ly, lwidth, lheight)
    530 		i.autocompleteList.Draw(screen)
    531 	}
    532 }
    533 
    534 // InputHandler returns the handler for this primitive.
    535 func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
    536 	return i.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
    537 		if i.textArea.GetDisabled() {
    538 			return
    539 		}
    540 
    541 		// Trigger changed events.
    542 		var skipAutocomplete bool
    543 		currentText := i.textArea.GetText()
    544 		defer func() {
    545 			if skipAutocomplete {
    546 				return
    547 			}
    548 			if i.textArea.GetText() != currentText {
    549 				i.Autocomplete()
    550 			}
    551 		}()
    552 
    553 		// If we have an autocomplete list, there are certain keys we will
    554 		// forward to it.
    555 		i.autocompleteListMutex.Lock()
    556 		defer i.autocompleteListMutex.Unlock()
    557 		if i.autocompleteList != nil {
    558 			i.autocompleteList.SetChangedFunc(nil)
    559 			i.autocompleteList.SetSelectedFunc(nil)
    560 			switch key := event.Key(); key {
    561 			case tcell.KeyEscape: // Close the list.
    562 				i.autocompleteList = nil
    563 				return
    564 			case tcell.KeyEnter, tcell.KeyTab: // Intentional selection.
    565 				index := i.autocompleteList.GetCurrentItem()
    566 				text, _ := i.autocompleteList.GetItemText(index)
    567 				if i.autocompleted != nil {
    568 					source := AutocompletedEnter
    569 					if key == tcell.KeyTab {
    570 						source = AutocompletedTab
    571 					}
    572 					if i.autocompleted(stripTags(text), index, source) {
    573 						i.autocompleteList = nil
    574 						currentText = i.GetText()
    575 					}
    576 				} else {
    577 					i.SetText(text)
    578 					skipAutocomplete = true
    579 					i.autocompleteList = nil
    580 				}
    581 				return
    582 			case tcell.KeyDown, tcell.KeyUp, tcell.KeyPgDn, tcell.KeyPgUp:
    583 				i.autocompleteList.SetChangedFunc(func(index int, text, secondaryText string, shortcut rune) {
    584 					text = stripTags(text)
    585 					if i.autocompleted != nil {
    586 						if i.autocompleted(text, index, AutocompletedNavigate) {
    587 							i.autocompleteList = nil
    588 							currentText = i.GetText()
    589 						}
    590 					} else {
    591 						i.SetText(text)
    592 						currentText = stripTags(text) // We want to keep the autocomplete list open and unchanged.
    593 					}
    594 				})
    595 				i.autocompleteList.InputHandler()(event, setFocus)
    596 				return
    597 			}
    598 		}
    599 
    600 		// Finish up.
    601 		finish := func(key tcell.Key) {
    602 			if i.done != nil {
    603 				i.done(key)
    604 			}
    605 			if i.finished != nil {
    606 				i.finished(key)
    607 			}
    608 		}
    609 
    610 		// Process special key events for the input field.
    611 		switch key := event.Key(); key {
    612 		case tcell.KeyDown:
    613 			i.autocompleteListMutex.Unlock() // We're still holding a lock.
    614 			i.Autocomplete()
    615 			i.autocompleteListMutex.Lock()
    616 		case tcell.KeyEnter, tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab:
    617 			finish(key)
    618 		case tcell.KeyCtrlV:
    619 			if i.accept != nil && !i.accept(i.textArea.getTextBeforeCursor()+i.textArea.GetClipboardText()+i.textArea.getTextAfterCursor(), 0) {
    620 				return
    621 			}
    622 			i.textArea.InputHandler()(event, setFocus)
    623 		case tcell.KeyRune:
    624 			if event.Modifiers()&tcell.ModAlt == 0 && i.accept != nil {
    625 				// Check if this rune is accepted.
    626 				r := event.Rune()
    627 				if !i.accept(i.textArea.getTextBeforeCursor()+string(r)+i.textArea.getTextAfterCursor(), r) {
    628 					return
    629 				}
    630 			}
    631 			fallthrough
    632 		default:
    633 			// Forward other key events to the text area.
    634 			i.textArea.InputHandler()(event, setFocus)
    635 		}
    636 	})
    637 }
    638 
    639 // MouseHandler returns the mouse handler for this primitive.
    640 func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
    641 	return i.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
    642 		if i.textArea.GetDisabled() {
    643 			return false, nil
    644 		}
    645 
    646 		var skipAutocomplete bool
    647 		currentText := i.GetText()
    648 		defer func() {
    649 			if skipAutocomplete {
    650 				return
    651 			}
    652 			if i.textArea.GetText() != currentText {
    653 				i.Autocomplete()
    654 			}
    655 		}()
    656 
    657 		// If we have an autocomplete list, forward the mouse event to it.
    658 		i.autocompleteListMutex.Lock()
    659 		defer i.autocompleteListMutex.Unlock()
    660 		if i.autocompleteList != nil {
    661 			i.autocompleteList.SetChangedFunc(nil)
    662 			i.autocompleteList.SetSelectedFunc(func(index int, text, secondaryText string, shortcut rune) {
    663 				text = stripTags(text)
    664 				if i.autocompleted != nil {
    665 					if i.autocompleted(text, index, AutocompletedClick) {
    666 						i.autocompleteList = nil
    667 						currentText = i.GetText()
    668 					}
    669 					return
    670 				}
    671 				i.SetText(text)
    672 				skipAutocomplete = true
    673 				i.autocompleteList = nil
    674 			})
    675 			if consumed, _ = i.autocompleteList.MouseHandler()(action, event, setFocus); consumed {
    676 				setFocus(i)
    677 				return
    678 			}
    679 		}
    680 
    681 		// Is mouse event within the input field?
    682 		x, y := event.Position()
    683 		if !i.InRect(x, y) {
    684 			return false, nil
    685 		}
    686 
    687 		// Forward mouse event to the text area.
    688 		consumed, capture = i.textArea.MouseHandler()(action, event, setFocus)
    689 
    690 		return
    691 	})
    692 }
    693 
    694 // PasteHandler returns the handler for this primitive.
    695 func (i *InputField) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
    696 	return i.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
    697 		// Input field may be disabled.
    698 		if i.textArea.GetDisabled() {
    699 			return
    700 		}
    701 
    702 		// The autocomplete drop down may be open.
    703 		i.autocompleteListMutex.Lock()
    704 		defer i.autocompleteListMutex.Unlock()
    705 		if i.autocompleteList != nil {
    706 			return
    707 		}
    708 
    709 		// We may not accept this text.
    710 		if i.accept != nil && !i.accept(i.textArea.getTextBeforeCursor()+pastedText+i.textArea.getTextAfterCursor(), 0) {
    711 			return
    712 		}
    713 
    714 		// Forward the pasted text to the text area.
    715 		i.textArea.PasteHandler()(pastedText, setFocus)
    716 	})
    717 }