diff --git a/UI/Controls/HierarchyTree.axaml b/UI/Controls/HierarchyTree.axaml index a3b3724..355c76c 100644 --- a/UI/Controls/HierarchyTree.axaml +++ b/UI/Controls/HierarchyTree.axaml @@ -5,7 +5,7 @@ xmlns:assets="clr-namespace:TagHierarchyManager.UI.Assets" xmlns:vm="clr-namespace:TagHierarchyManager.UI.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="TagHierarchyManager.UI.Views.Controls.HierarchyTree" + x:Class="TagHierarchyManager.UI.Controls.HierarchyTree" x:DataType="vm:HierarchyTreeViewModel"> WatermarkProperty = AvaloniaProperty.Register( nameof(Watermark), string.Empty); - - private bool _inDataGridCell; + private bool _isTextBoxActive; @@ -44,7 +42,7 @@ public partial class MultiValueAutoCompleteBox : UserControl private bool _suppressPopup; - private SegmentData currentSegment = new(string.Empty, 0, 0); + private SegmentData _currentSegment = new(string.Empty, 0, 0); public MultiValueAutoCompleteBox() { @@ -66,7 +64,7 @@ public partial class MultiValueAutoCompleteBox : UserControl e.Handled = true; }; this.MultiValueAutoCompleteBoxTextBox.TextChanged += - (s, e) => this.RaiseEvent(new TextChangedEventArgs(TextChangedEvent)); + (_, _) => this.RaiseEvent(new TextChangedEventArgs(TextChangedEvent)); } public event EventHandler? TextChanged @@ -114,27 +112,12 @@ public partial class MultiValueAutoCompleteBox : UserControl this._lastTypedRawText = rawText; this.GetCurrentEditedSegment(); - Debug.WriteLine($"Current segment: {this.currentSegment.FullSegment}," + - $"Segment indexes: {this.currentSegment.IndexBack}-{this.currentSegment.IndexForward}," + + Debug.WriteLine($"Current segment: {this._currentSegment.FullSegment}," + + $"Segment indexes: {this._currentSegment.IndexBack}-{this._currentSegment.IndexForward}," + $"caret: {this.MultiValueAutoCompleteBoxTextBox.CaretIndex}"); this.Text = rawText; - this.RepopulateFilteredItems(this.currentSegment.FullSegment); - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - var current = this.GetVisualParent(); - while (current != null) - { - if (current is DataGridCell) - { - this._inDataGridCell = true; - break; - } - - current = current.GetVisualParent(); - } + this.RepopulateFilteredItems(this._currentSegment.FullSegment); } private void ApplyListBoxSelection(ListBox box, TagItemViewModel tag) @@ -142,15 +125,15 @@ public partial class MultiValueAutoCompleteBox : UserControl this._suppressPopup = true; box.SelectedItem = null; - var result = this._lastTypedRawText[..this.currentSegment.IndexBack] - + (this.currentSegment.SpaceAtBeginning ? ' ' : string.Empty) + var result = this._lastTypedRawText[..this._currentSegment.IndexBack] + + (this._currentSegment.SpaceAtBeginning ? ' ' : string.Empty) + tag.CurrentName - + this._lastTypedRawText[this.currentSegment.IndexForward..]; + + this._lastTypedRawText[this._currentSegment.IndexForward..]; this.Text = result; this.MultiValueAutoCompletePopup.IsOpen = false; this.MultiValueAutoCompleteBoxTextBox.Text = result; - var finalCaretIndex = this.currentSegment.IndexBack + tag.CurrentName.Length; - if (this.currentSegment.SpaceAtBeginning) finalCaretIndex++; + var finalCaretIndex = this._currentSegment.IndexBack + tag.CurrentName.Length; + if (this._currentSegment.SpaceAtBeginning) finalCaretIndex++; this.MultiValueAutoCompleteBoxTextBox.CaretIndex = finalCaretIndex; this.MultiValueAutoCompleteBoxTextBox.Focus(); this._lastTypedRawText = result; @@ -172,7 +155,7 @@ public partial class MultiValueAutoCompleteBox : UserControl var spaceAtBeginning = false; var activeSegment = this._lastTypedRawText[backIndex..forwardIndex]; if (activeSegment.Length > 0 && activeSegment[0] == ' ') spaceAtBeginning = true; - this.currentSegment = new SegmentData(activeSegment.Trim(), backIndex, forwardIndex, spaceAtBeginning); + this._currentSegment = new SegmentData(activeSegment.Trim(), backIndex, forwardIndex, spaceAtBeginning); } private void ListBox_OnKeyDown(object? sender, KeyEventArgs e) @@ -199,7 +182,7 @@ public partial class MultiValueAutoCompleteBox : UserControl var filtered = this.ItemsSource .Where(t => t.CurrentName.Contains(itemName, StringComparison.OrdinalIgnoreCase)) .OrderBy(t => t.CurrentName, StringComparer.OrdinalIgnoreCase).ToList(); - if (filtered.Count == 1 && filtered[0].CurrentName == this.currentSegment.FullSegment) return; + if (filtered.Count == 1 && filtered[0].CurrentName == this._currentSegment.FullSegment) return; foreach (var item in filtered) this.FilteredItems.Add(item); } diff --git a/UI/Controls/SearchPane.axaml b/UI/Controls/SearchPane.axaml index f27baaf..8d7bc33 100644 --- a/UI/Controls/SearchPane.axaml +++ b/UI/Controls/SearchPane.axaml @@ -5,7 +5,7 @@ xmlns:assets="clr-namespace:TagHierarchyManager.UI.Assets" xmlns:vm="clr-namespace:TagHierarchyManager.UI.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="TagHierarchyManager.UI.Views.Controls.SearchPane" + x:Class="TagHierarchyManager.UI.Controls.SearchPane" x:DataType="vm:SearchViewModel"> + SelectedItem="{Binding SelectedSearchResult}" + ContextRequested="SearchPane_ContextRequested"> @@ -47,6 +48,21 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/Controls/SearchPane.axaml.cs b/UI/Controls/SearchPane.axaml.cs index a26f994..428a041 100644 --- a/UI/Controls/SearchPane.axaml.cs +++ b/UI/Controls/SearchPane.axaml.cs @@ -1,14 +1,17 @@ using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; using TagHierarchyManager.UI.ViewModels; -namespace TagHierarchyManager.UI.Views.Controls; +namespace TagHierarchyManager.UI.Controls; public partial class SearchPane : UserControl { public SearchPane() { this.InitializeComponent(); + this.SearchResultsListBox.AddHandler(PointerPressedEvent, ListBox_OnPointerPressed, RoutingStrategies.Tunnel); } private SearchViewModel? ViewModel => this.DataContext as SearchViewModel; @@ -27,4 +30,22 @@ public partial class SearchPane : UserControl this.ViewModel?.SearchCommand.Execute(args); e.Handled = true; } + + private static void ListBox_OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + if (!e.GetCurrentPoint(null).Properties.IsRightButtonPressed) return; + if (e.Source is not Control source) return; + + var listItem = source.FindAncestorOfType(); + if (listItem is null) return; + e.Handled = true; + listItem.ContextMenu?.Open(listItem); + } + + private void SearchPane_ContextRequested(object? sender, ContextRequestedEventArgs e) + { + if (e.Source is Control { DataContext: TagItemViewModel tag } + && this.DataContext is SearchViewModel vm) + vm.ContextMenuTag = tag; + } } \ No newline at end of file diff --git a/UI/ViewModels/SearchViewModel.cs b/UI/ViewModels/SearchViewModel.cs index ec66884..85ced80 100644 --- a/UI/ViewModels/SearchViewModel.cs +++ b/UI/ViewModels/SearchViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using TagHierarchyManager.Common; @@ -23,6 +24,8 @@ public partial class SearchViewModel : ViewModelBase, IDisposable [ObservableProperty] private SearchModeOption _selectedSearchMode; [ObservableProperty] private TagItemViewModel? _selectedSearchResult; + + [ObservableProperty] private TagItemViewModel? _contextMenuTag; public SearchViewModel(MainWindowViewModel mainWindow, DialogService dialogService) { @@ -97,5 +100,12 @@ public partial class SearchViewModel : ViewModelBase, IDisposable } } + [RelayCommand] + private async Task StartContextMenuTagDeletion() + { + if (this.ContextMenuTag is null) return; + await this._mainWindow.StartTagDeletionAsync(this.ContextMenuTag.Id); + } + public sealed record SearchModeOption(TagDatabaseSearchMode Mode, string DisplayName); } \ No newline at end of file diff --git a/UI/Views/Windows/MainWindow.axaml b/UI/Views/Windows/MainWindow.axaml index 0bb497a..3e862c0 100644 --- a/UI/Views/Windows/MainWindow.axaml +++ b/UI/Views/Windows/MainWindow.axaml @@ -5,6 +5,7 @@ xmlns:assets="clr-namespace:TagHierarchyManager.UI.Assets" xmlns:vm="clr-namespace:TagHierarchyManager.UI.ViewModels" xmlns:controls="clr-namespace:TagHierarchyManager.UI.Views.Controls" + xmlns:controls1="clr-namespace:TagHierarchyManager.UI.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="TagHierarchyManager.UI.Views.MainWindow" x:DataType="vm:MainWindowViewModel" @@ -53,10 +54,10 @@ - + - +