diff --git a/demos/inputfield/autocomplete/main.go b/demos/inputfield/autocomplete/main.go
index e3203e51..3577e67a 100644
--- a/demos/inputfield/autocomplete/main.go
+++ b/demos/inputfield/autocomplete/main.go
@@ -33,6 +33,12 @@ func main() {
 		}
 		return
 	})
+	inputField.SetAutocompletedFunc(func(text string, index, source int) bool {
+		if source != tview.AutocompletedNavigate {
+			inputField.SetText(text)
+		}
+		return source == tview.AutocompletedEnter || source == tview.AutocompletedClick
+	})
 	if err := app.EnableMouse(true).SetRoot(inputField, true).Run(); err != nil {
 		panic(err)
 	}
diff --git a/inputfield.go b/inputfield.go
index a8782f8d..a8a05da6 100644
--- a/inputfield.go
+++ b/inputfield.go
@@ -11,15 +11,30 @@ import (
 	"github.com/rivo/uniseg"
 )
 
+const (
+	AutocompletedNavigate = iota // The user navigated the autocomplete list (using the errow keys).
+	AutocompletedTab             // The user selected an autocomplete entry using the tab key.
+	AutocompletedEnter           // The user selected an autocomplete entry using the enter key.
+	AutocompletedClick           // The user selected an autocomplete entry by clicking the mouse button on it.
+)
+
 // InputField is a one-line box (three lines if there is a title) where the
-// user can enter text. Use SetAcceptanceFunc() to accept or reject input,
-// SetChangedFunc() to listen for changes, and SetMaskCharacter() to hide input
-// from onlookers (e.g. for password input).
+// user can enter text. Use [InputField.SetAcceptanceFunc] to accept or reject
+// input, [InputField.SetChangedFunc] to listen for changes, and
+// [InputField.SetMaskCharacter] to hide input from onlookers (e.g. for password
+// input).
+//
+// The input field also has an optional autocomplete feature. It is initialized
+// by the [InputField.SetAutocompleteFunc] function. For more control over the
+// autocomplete drop-down's behavior, you can also set the
+// [InputField.SetAutocompletedFunc].
 //
 // The following keys can be used for navigation and editing:
 //
 //   - Left arrow: Move left by one character.
 //   - Right arrow: Move right by one character.
+//   - Down arrow: Open the autocomplete drop-down.
+//   - Tab, Enter: Select the current autocomplete entry.
 //   - Home, Ctrl-A, Alt-a: Move to the beginning of the line.
 //   - End, Ctrl-E, Alt-e: Move to the end of the line.
 //   - Alt-left, Alt-b: Move left by one word.
@@ -84,6 +99,14 @@ type InputField struct {
 		background tcell.Color
 	}
 
+	// An optional function which is called when the user selects an
+	// autocomplete entry. The text and index of the selected entry (within the
+	// list) is provided, as well as the user action causing the selection (one
+	// of the "Autocompleted" values). The function should return true if the
+	// autocomplete list should be closed. If nil, the input field will be
+	// updated automatically when the user navigates the autocomplete list.
+	autocompleted func(text string, index int, source int) bool
+
 	// An optional function which may reject the last character that was entered.
 	accept func(text string, ch rune) bool
 
@@ -273,6 +296,24 @@ func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entr
 	return i
 }
 
+// SetAutocompletedFunc sets a callback function which is invoked when the user
+// selects an entry from the autocomplete drop-down list. The function is passed
+// the text of the selected entry (stripped of any color tags), the index of the
+// entry, and the user action that caused the selection, e.g.
+// [AutocompletedNavigate]. It returns true if the autocomplete drop-down should
+// be closed after the callback returns or false if it should remain open, in
+// which case [InputField.Autocomplete] is called to update the drop-down's
+// contents.
+//
+// If no such callback is set (or nil is provided), the input field will be
+// updated with the selection any time the user navigates the autocomplete
+// drop-down list. So this function essentially gives you more control over the
+// autocomplete functionality.
+func (i *InputField) SetAutocompletedFunc(autocompleted func(text string, index int, source int) bool) *InputField {
+	i.autocompleted = autocompleted
+	return i
+}
+
 // Autocomplete invokes the autocomplete callback (if there is one). If the
 // length of the returned autocomplete entries slice is greater than 0, the
 // input field will present the user with a corresponding drop-down list the
@@ -550,21 +591,6 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
 			return true
 		}
 
-		// Change the autocomplete selection.
-		autocompleteSelect := func(offset int) {
-			count := i.autocompleteList.GetItemCount()
-			newEntry := i.autocompleteList.GetCurrentItem() + offset
-			if newEntry >= count {
-				newEntry = 0
-			} else if newEntry < 0 {
-				newEntry = count - 1
-			}
-			i.autocompleteList.SetCurrentItem(newEntry)
-			currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice.
-			currentText = stripTags(currentText)
-			i.SetText(currentText)
-		}
-
 		// Finish up.
 		finish := func(key tcell.Key) {
 			if i.done != nil {
@@ -575,9 +601,51 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
 			}
 		}
 
-		// Process key event.
+		// If we have an autocomplete list, there are certain keys we will
+		// forward to it.
 		i.autocompleteListMutex.Lock()
 		defer i.autocompleteListMutex.Unlock()
+		if i.autocompleteList != nil {
+			i.autocompleteList.SetChangedFunc(nil)
+			switch key := event.Key(); key {
+			case tcell.KeyEscape: // Close the list.
+				i.autocompleteList = nil
+				return
+			case tcell.KeyEnter, tcell.KeyTab: // Intentional selection.
+				if i.autocompleted != nil {
+					index := i.autocompleteList.GetCurrentItem()
+					text, _ := i.autocompleteList.GetItemText(index)
+					source := AutocompletedEnter
+					if key == tcell.KeyTab {
+						source = AutocompletedTab
+					}
+					if i.autocompleted(stripTags(text), index, source) {
+						i.autocompleteList = nil
+						currentText = i.GetText()
+					}
+				} else {
+					i.autocompleteList = nil
+				}
+				return
+			case tcell.KeyDown, tcell.KeyUp, tcell.KeyPgDn, tcell.KeyPgUp:
+				i.autocompleteList.SetChangedFunc(func(index int, text, secondaryText string, shortcut rune) {
+					text = stripTags(text)
+					if i.autocompleted != nil {
+						if i.autocompleted(text, index, AutocompletedNavigate) {
+							i.autocompleteList = nil
+							currentText = i.GetText()
+						}
+					} else {
+						i.SetText(text)
+						currentText = stripTags(text) // We want to keep the autocomplete list open and unchanged.
+					}
+				})
+				i.autocompleteList.InputHandler()(event, setFocus)
+				return
+			}
+		}
+
+		// Process key event for the input field.
 		switch key := event.Key(); key {
 		case tcell.KeyRune: // Regular character.
 			if event.Modifiers()&tcell.ModAlt > 0 {
@@ -646,37 +714,12 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
 			home()
 		case tcell.KeyEnd, tcell.KeyCtrlE:
 			end()
-		case tcell.KeyEnter:
-			if i.autocompleteList != nil {
-				autocompleteSelect(0)
-				i.autocompleteList = nil
-			} else {
-				finish(key)
-			}
-		case tcell.KeyEscape:
-			if i.autocompleteList != nil {
-				i.autocompleteList = nil
-			} else {
-				finish(key)
-			}
-		case tcell.KeyTab:
-			if i.autocompleteList != nil {
-				autocompleteSelect(0)
-			} else {
-				finish(key)
-			}
 		case tcell.KeyDown:
-			if i.autocompleteList != nil {
-				autocompleteSelect(1)
-			} else {
-				finish(key)
-			}
-		case tcell.KeyUp, tcell.KeyBacktab: // Autocomplete selection.
-			if i.autocompleteList != nil {
-				autocompleteSelect(-1)
-			} else {
-				finish(key)
-			}
+			i.autocompleteListMutex.Unlock() // We're still holding a lock.
+			i.Autocomplete()
+			i.autocompleteListMutex.Lock()
+		case tcell.KeyEnter, tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab:
+			finish(key)
 		}
 	})
 }
@@ -684,6 +727,39 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
 // MouseHandler returns the mouse handler for this primitive.
 func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
 	return i.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
+		currentText := i.GetText()
+		defer func() {
+			if i.GetText() != currentText {
+				i.Autocomplete()
+				if i.changed != nil {
+					i.changed(i.text)
+				}
+			}
+		}()
+
+		// If we have an autocomplete list, forward the mouse event to it.
+		i.autocompleteListMutex.Lock()
+		defer i.autocompleteListMutex.Unlock()
+		if i.autocompleteList != nil {
+			i.autocompleteList.SetChangedFunc(func(index int, text, secondaryText string, shortcut rune) {
+				text = stripTags(text)
+				if i.autocompleted != nil {
+					if i.autocompleted(text, index, AutocompletedClick) {
+						i.autocompleteList = nil
+						currentText = i.GetText()
+					}
+					return
+				}
+				i.SetText(text)
+				i.autocompleteList = nil
+			})
+			if consumed, _ = i.autocompleteList.MouseHandler()(action, event, setFocus); consumed {
+				setFocus(i)
+				return
+			}
+		}
+
+		// Is mouse event within the input field?
 		x, y := event.Position()
 		_, rectY, _, _ := i.GetInnerRect()
 		if !i.InRect(x, y) {
diff --git a/list.go b/list.go
index 8fffc7b2..5ce1a4eb 100644
--- a/list.go
+++ b/list.go
@@ -113,6 +113,8 @@ func (l *List) SetCurrentItem(index int) *List {
 
 	l.currentItem = index
 
+	l.adjustOffset()
+
 	return l
 }
 
@@ -471,18 +473,6 @@ func (l *List) Draw(screen tcell.Screen) {
 		}
 	}
 
-	// Adjust offset to keep the current selection in view.
-	if l.currentItem < l.itemOffset {
-		l.itemOffset = l.currentItem
-	} else if l.showSecondaryText {
-		if 2*(l.currentItem-l.itemOffset) >= height-1 {
-			l.itemOffset = (2*l.currentItem + 3 - height) / 2
-		}
-	} else {
-		if l.currentItem-l.itemOffset >= height {
-			l.itemOffset = l.currentItem + 1 - height
-		}
-	}
 	if l.horizontalOffset < 0 {
 		l.horizontalOffset = 0
 	}
@@ -565,6 +555,23 @@ func (l *List) Draw(screen tcell.Screen) {
 	l.overflowing = overflowing
 }
 
+// adjustOffset adjusts the vertical offset to keep the current selection in
+// view.
+func (l *List) adjustOffset() {
+	_, _, _, height := l.GetInnerRect()
+	if l.currentItem < l.itemOffset {
+		l.itemOffset = l.currentItem
+	} else if l.showSecondaryText {
+		if 2*(l.currentItem-l.itemOffset) >= height-1 {
+			l.itemOffset = (2*l.currentItem + 3 - height) / 2
+		}
+	} else {
+		if l.currentItem-l.itemOffset >= height {
+			l.itemOffset = l.currentItem + 1 - height
+		}
+	}
+}
+
 // InputHandler returns the handler for this primitive.
 func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
 	return l.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
@@ -662,9 +669,12 @@ func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
 			}
 		}
 
-		if l.currentItem != previousItem && l.currentItem < len(l.items) && l.changed != nil {
-			item := l.items[l.currentItem]
-			l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
+		if l.currentItem != previousItem && l.currentItem < len(l.items) {
+			if l.changed != nil {
+				item := l.items[l.currentItem]
+				l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
+			}
+			l.adjustOffset()
 		}
 	})
 }
@@ -699,6 +709,7 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
 		// Process mouse event.
 		switch action {
 		case MouseLeftClick:
+			setFocus(l)
 			index := l.indexAtPoint(event.Position())
 			if index != -1 {
 				item := l.items[index]
@@ -708,12 +719,14 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
 				if l.selected != nil {
 					l.selected(index, item.MainText, item.SecondaryText, item.Shortcut)
 				}
-				if index != l.currentItem && l.changed != nil {
-					l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
+				if index != l.currentItem {
+					if l.changed != nil {
+						l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
+					}
+					l.adjustOffset()
 				}
 				l.currentItem = index
 			}
-			setFocus(l)
 			consumed = true
 		case MouseScrollUp:
 			if l.itemOffset > 0 {