|
|
@ -47,7 +47,7 @@ public class CompletionList : TemplatedControl |
|
|
|
|
|
|
|
|
|
|
|
public CompletionList() |
|
|
|
public CompletionList() |
|
|
|
{ |
|
|
|
{ |
|
|
|
DoubleTapped += OnDoubleTapped; |
|
|
|
AddHandler(DoubleTappedEvent, OnDoubleTapped); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
@ -75,8 +75,10 @@ public class CompletionList : TemplatedControl |
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// Dependency property for <see cref="FooterText" />. |
|
|
|
/// Dependency property for <see cref="FooterText" />. |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|
public static readonly StyledProperty<string?> FooterTextProperty = AvaloniaProperty.Register<CompletionList, string?>( |
|
|
|
public static readonly StyledProperty<string?> FooterTextProperty = AvaloniaProperty.Register< |
|
|
|
"FooterText", "Press Enter to insert, Tab to replace"); |
|
|
|
CompletionList, |
|
|
|
|
|
|
|
string? |
|
|
|
|
|
|
|
>("FooterText", "Press Enter to insert, Tab to replace"); |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// Gets/Sets the text displayed in the footer of the completion list. |
|
|
|
/// Gets/Sets the text displayed in the footer of the completion list. |
|
|
@ -101,14 +103,21 @@ public class CompletionList : TemplatedControl |
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// Raises the InsertionRequested event. |
|
|
|
/// Raises the InsertionRequested event. |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|
public void RequestInsertion(ICompletionData item, RoutedEventArgs triggeringEvent, string? appendText = null) |
|
|
|
public void RequestInsertion( |
|
|
|
|
|
|
|
ICompletionData item, |
|
|
|
|
|
|
|
RoutedEventArgs triggeringEvent, |
|
|
|
|
|
|
|
string? appendText = null |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
InsertionRequested?.Invoke(this, new InsertionRequestEventArgs |
|
|
|
InsertionRequested?.Invoke( |
|
|
|
|
|
|
|
this, |
|
|
|
|
|
|
|
new InsertionRequestEventArgs |
|
|
|
{ |
|
|
|
{ |
|
|
|
Item = item, |
|
|
|
Item = item, |
|
|
|
TriggeringEvent = triggeringEvent, |
|
|
|
TriggeringEvent = triggeringEvent, |
|
|
|
AppendText = appendText |
|
|
|
AppendText = appendText |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
@ -149,12 +158,8 @@ public class CompletionList : TemplatedControl |
|
|
|
/// mapped to strings that will be appended to the completion when selected. |
|
|
|
/// mapped to strings that will be appended to the completion when selected. |
|
|
|
/// The string may be empty. |
|
|
|
/// The string may be empty. |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|
public Dictionary<Key, string> CompletionAcceptKeys { get; init; } = new() |
|
|
|
public Dictionary<Key, string> CompletionAcceptKeys { get; init; } = |
|
|
|
{ |
|
|
|
new() { [Key.Enter] = "", [Key.Tab] = "" }; |
|
|
|
[Key.Enter] = "", |
|
|
|
|
|
|
|
[Key.Tab] = "", |
|
|
|
|
|
|
|
[Key.OemComma] = ",", |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// Gets the scroll viewer used in this list box. |
|
|
|
/// Gets the scroll viewer used in this list box. |
|
|
@ -221,8 +226,10 @@ public class CompletionList : TemplatedControl |
|
|
|
break; |
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
// Check insertion keys |
|
|
|
// Check insertion keys |
|
|
|
if (CompletionAcceptKeys.TryGetValue(e.Key, out var appendText) |
|
|
|
if ( |
|
|
|
&& CurrentList?.Count > 0) |
|
|
|
CompletionAcceptKeys.TryGetValue(e.Key, out var appendText) |
|
|
|
|
|
|
|
&& CurrentList?.Count > 0 |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
e.Handled = true; |
|
|
|
e.Handled = true; |
|
|
|
|
|
|
|
|
|
|
@ -244,7 +251,7 @@ public class CompletionList : TemplatedControl |
|
|
|
{ |
|
|
|
{ |
|
|
|
//TODO TEST |
|
|
|
//TODO TEST |
|
|
|
if ( |
|
|
|
if ( |
|
|
|
((AvaloniaObject?) e.Source) |
|
|
|
((AvaloniaObject?)e.Source) |
|
|
|
.VisualAncestorsAndSelf() |
|
|
|
.VisualAncestorsAndSelf() |
|
|
|
.TakeWhile(obj => obj != this) |
|
|
|
.TakeWhile(obj => obj != this) |
|
|
|
.Any(obj => obj is ListBoxItem) |
|
|
|
.Any(obj => obj is ListBoxItem) |
|
|
@ -302,7 +309,7 @@ public class CompletionList : TemplatedControl |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SelectItem gets called twice for every typed character (once from FormatLine), this helps execute SelectItem only once |
|
|
|
// SelectItem gets called twice for every typed character (once from FormatLine), this helps execute SelectItem only once |
|
|
|
private string _currentText; |
|
|
|
private string? _currentText; |
|
|
|
|
|
|
|
|
|
|
|
private ObservableCollection<ICompletionData>? _currentList; |
|
|
|
private ObservableCollection<ICompletionData>? _currentList; |
|
|
|
|
|
|
|
|
|
|
@ -337,7 +344,10 @@ public class CompletionList : TemplatedControl |
|
|
|
_currentText = text; |
|
|
|
_currentText = text; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private IReadOnlyList<ICompletionData> FilterItems(IEnumerable<ICompletionData> items, string query) |
|
|
|
private IReadOnlyList<ICompletionData> FilterItems( |
|
|
|
|
|
|
|
IEnumerable<ICompletionData> items, |
|
|
|
|
|
|
|
string query |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
using var _ = new CodeTimer(); |
|
|
|
using var _ = new CodeTimer(); |
|
|
|
|
|
|
|
|
|
|
@ -361,11 +371,13 @@ public class CompletionList : TemplatedControl |
|
|
|
var listToFilter = _completionData; |
|
|
|
var listToFilter = _completionData; |
|
|
|
|
|
|
|
|
|
|
|
// if the user just typed one more character, don't filter all data but just filter what we are already displaying |
|
|
|
// if the user just typed one more character, don't filter all data but just filter what we are already displaying |
|
|
|
if (!fullUpdate |
|
|
|
if ( |
|
|
|
|
|
|
|
!fullUpdate |
|
|
|
&& FilteredCompletionData.Count > 0 |
|
|
|
&& FilteredCompletionData.Count > 0 |
|
|
|
&& !string.IsNullOrEmpty(_currentText) |
|
|
|
&& !string.IsNullOrEmpty(_currentText) |
|
|
|
&& !string.IsNullOrEmpty(query) |
|
|
|
&& !string.IsNullOrEmpty(query) |
|
|
|
&& query.StartsWith(_currentText, StringComparison.Ordinal)) |
|
|
|
&& query.StartsWith(_currentText, StringComparison.Ordinal) |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
listToFilter = FilteredCompletionData; |
|
|
|
listToFilter = FilteredCompletionData; |
|
|
|
} |
|
|
|
} |
|
|
@ -380,9 +392,11 @@ public class CompletionList : TemplatedControl |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Fast path if both only 1 item, and item is the same |
|
|
|
// Fast path if both only 1 item, and item is the same |
|
|
|
if (FilteredCompletionData.Count == 1 |
|
|
|
if ( |
|
|
|
|
|
|
|
FilteredCompletionData.Count == 1 |
|
|
|
&& matchingItems.Count == 1 |
|
|
|
&& matchingItems.Count == 1 |
|
|
|
&& FilteredCompletionData[0] == matchingItems[0]) |
|
|
|
&& FilteredCompletionData[0] == matchingItems[0] |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Just update the character highlighting |
|
|
|
// Just update the character highlighting |
|
|
|
matchingItems[0].UpdateCharHighlighting(query); |
|
|
|
matchingItems[0].UpdateCharHighlighting(query); |
|
|
@ -411,16 +425,19 @@ public class CompletionList : TemplatedControl |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|
private void SelectItemFiltering(string query, bool fullUpdate = false) |
|
|
|
private void SelectItemFiltering(string query, bool fullUpdate = false) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (_listBox is null) throw new NullReferenceException("ListBox not set"); |
|
|
|
if (_listBox is null) |
|
|
|
|
|
|
|
throw new NullReferenceException("ListBox not set"); |
|
|
|
|
|
|
|
|
|
|
|
var listToFilter = _completionData; |
|
|
|
var listToFilter = _completionData; |
|
|
|
|
|
|
|
|
|
|
|
// if the user just typed one more character, don't filter all data but just filter what we are already displaying |
|
|
|
// if the user just typed one more character, don't filter all data but just filter what we are already displaying |
|
|
|
if (!fullUpdate |
|
|
|
if ( |
|
|
|
|
|
|
|
!fullUpdate |
|
|
|
&& _currentList != null |
|
|
|
&& _currentList != null |
|
|
|
&& !string.IsNullOrEmpty(_currentText) |
|
|
|
&& !string.IsNullOrEmpty(_currentText) |
|
|
|
&& !string.IsNullOrEmpty(query) |
|
|
|
&& !string.IsNullOrEmpty(query) |
|
|
|
&& query.StartsWith(_currentText, StringComparison.Ordinal)) |
|
|
|
&& query.StartsWith(_currentText, StringComparison.Ordinal) |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
listToFilter = _currentList; |
|
|
|
listToFilter = _currentList; |
|
|
|
} |
|
|
|
} |
|
|
@ -562,7 +579,8 @@ public class CompletionList : TemplatedControl |
|
|
|
throw new NullReferenceException("ListBox not set"); |
|
|
|
throw new NullReferenceException("ListBox not set"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (index == _listBox.SelectedIndex) return; |
|
|
|
if (index == _listBox.SelectedIndex) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (index < 0) |
|
|
|
if (index < 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|