diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
index 108d523..8314411 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
index 7fd7a3f..fcc5878 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
@@ -1,14 +1,10 @@
-using Avalonia.Input;
using FileTime.App.Core.ViewModels;
+using FileTime.App.FuzzyPanel;
namespace FileTime.App.FrequencyNavigation.ViewModels;
-public interface IFrequencyNavigationViewModel : IModalViewModel
+public interface IFrequencyNavigationViewModel : IFuzzyPanelViewModel, IModalViewModel
{
IObservable ShowWindow { get; }
- List FilteredMatches { get; }
- string SearchText { get; set; }
- string SelectedItem { get; set; }
void Close();
- void HandleKeyDown(KeyEventArgs keyEventArgs);
}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation/FileTime.App.FrequencyNavigation.csproj b/src/AppCommon/FileTime.App.FrequencyNavigation/FileTime.App.FrequencyNavigation.csproj
index e6fddd6..ad9f25c 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation/FileTime.App.FrequencyNavigation.csproj
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation/FileTime.App.FrequencyNavigation.csproj
@@ -10,6 +10,7 @@
+
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
index 14fd07d..b40986b 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
@@ -3,82 +3,53 @@ using FileTime.App.Core.Services;
using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels;
using FileTime.App.FrequencyNavigation.Services;
+using FileTime.App.FuzzyPanel;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
-using MvvmGen;
namespace FileTime.App.FrequencyNavigation.ViewModels;
-[ViewModel]
-[Inject(typeof(IFrequencyNavigationService), "_frequencyNavigationService")]
-[Inject(typeof(IUserCommandHandlerService), "_userCommandHandlerService")]
-[Inject(typeof(ITimelessContentProvider), "_timelessContentProvider")]
-public partial class FrequencyNavigationViewModel : IFrequencyNavigationViewModel
+public class FrequencyNavigationViewModel : FuzzyPanelViewModel, IFrequencyNavigationViewModel
{
- private string _searchText;
+ private readonly IFrequencyNavigationService _frequencyNavigationService;
+ private readonly IUserCommandHandlerService _userCommandHandlerService;
+ private readonly ITimelessContentProvider _timelessContentProvider;
- [Property] private IObservable _showWindow;
- [Property] private List _filteredMatches;
- [Property] private string? _selectedItem;
-
- public string SearchText
+ public FrequencyNavigationViewModel(
+ IFrequencyNavigationService frequencyNavigationService,
+ IUserCommandHandlerService userCommandHandlerService,
+ ITimelessContentProvider timelessContentProvider)
{
- get => _searchText;
- set
- {
- if (_searchText == value) return;
+ _frequencyNavigationService = frequencyNavigationService;
+ _userCommandHandlerService = userCommandHandlerService;
+ _timelessContentProvider = timelessContentProvider;
- _searchText = value;
- OnPropertyChanged();
-
- UpdateFilteredMatches();
- }
+ ShowWindow = _frequencyNavigationService.ShowWindow;
}
public void Close()
=> _frequencyNavigationService.CloseNavigationWindow();
- public async void HandleKeyDown(KeyEventArgs keyEventArgs)
+ public override async Task HandleKeyDown(KeyEventArgs keyEventArgs)
{
- if (keyEventArgs.Key == Key.Down)
- {
- var nextItem = FilteredMatches.SkipWhile(i => i != SelectedItem).Skip(1).FirstOrDefault();
+ var handled = await base.HandleKeyDown(keyEventArgs);
- if (nextItem is not null)
- {
- SelectedItem = nextItem;
- }
- }
- else if (keyEventArgs.Key == Key.Up)
- {
- var previousItem = FilteredMatches.TakeWhile(i => i != SelectedItem).LastOrDefault();
+ if (handled) return true;
- if (previousItem is not null)
- {
- SelectedItem = previousItem;
- }
- }
- else if (keyEventArgs.Key == Key.Enter)
+ if (keyEventArgs.Key == Key.Enter)
{
var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present);
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, targetContainer));
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
Close();
+ return true;
}
+
+ return false;
}
- partial void OnInitialize()
- => ShowWindow = _frequencyNavigationService.ShowWindow;
-
- private void UpdateFilteredMatches()
- {
- FilteredMatches = new List(_frequencyNavigationService.GetMatchingContainers(_searchText));
- if (SelectedItem != null && FilteredMatches.Contains(SelectedItem)) return;
-
- SelectedItem = FilteredMatches.Count > 0
- ? FilteredMatches[0]
- : null;
- }
+ public override void UpdateFilteredMatches() =>
+ FilteredMatches = new List(_frequencyNavigationService.GetMatchingContainers(SearchText));
string IModalViewModel.Name => "FrequencyNavigation";
}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/FileTime.App.FuzzyPanel.Abstraction.csproj b/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/FileTime.App.FuzzyPanel.Abstraction.csproj
new file mode 100644
index 0000000..f2f54a5
--- /dev/null
+++ b/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/FileTime.App.FuzzyPanel.Abstraction.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net7.0
+ enable
+ enable
+ FileTime.App.FuzzyPanel
+
+
+
+
+
+
+
diff --git a/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/IFuzzyPanelViewModel.cs b/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/IFuzzyPanelViewModel.cs
new file mode 100644
index 0000000..dc968fd
--- /dev/null
+++ b/src/AppCommon/FileTime.App.FuzzyPanel.Abstraction/IFuzzyPanelViewModel.cs
@@ -0,0 +1,12 @@
+using Avalonia.Input;
+
+namespace FileTime.App.FuzzyPanel;
+
+public interface IFuzzyPanelViewModel where TItem : class
+{
+ List FilteredMatches { get; }
+ TItem? SelectedItem { get; }
+ string SearchText { get; set; }
+ void UpdateFilteredMatches();
+ Task HandleKeyDown(KeyEventArgs keyEventArgs);
+}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.FuzzyPanel/FileTime.App.FuzzyPanel.csproj b/src/AppCommon/FileTime.App.FuzzyPanel/FileTime.App.FuzzyPanel.csproj
new file mode 100644
index 0000000..04448d7
--- /dev/null
+++ b/src/AppCommon/FileTime.App.FuzzyPanel/FileTime.App.FuzzyPanel.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs b/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs
new file mode 100644
index 0000000..74ca308
--- /dev/null
+++ b/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs
@@ -0,0 +1,68 @@
+using System.ComponentModel;
+using Avalonia.Input;
+using PropertyChanged.SourceGenerator;
+
+namespace FileTime.App.FuzzyPanel;
+
+public abstract partial class FuzzyPanelViewModel : IFuzzyPanelViewModel where TItem : class
+{
+ private string _searchText = String.Empty;
+
+ [Notify(set: Setter.Protected)] private IObservable _showWindow;
+ [Notify(set: Setter.Protected)] private List _filteredMatches;
+ [Notify(set: Setter.Protected)] private TItem? _selectedItem;
+
+ public string SearchText
+ {
+ get => _searchText;
+ set
+ {
+ if (_searchText == value) return;
+
+ _searchText = value;
+ OnPropertyChanged(new PropertyChangedEventArgs(nameof(SearchText)));
+
+ UpdateFilteredMatchesInternal();
+ }
+ }
+
+ private void UpdateFilteredMatchesInternal()
+ {
+ UpdateFilteredMatches();
+ if (SelectedItem != null && FilteredMatches.Contains(SelectedItem)) return;
+
+ SelectedItem = FilteredMatches.Count > 0
+ ? FilteredMatches[0]
+ : null;
+ }
+
+ public abstract void UpdateFilteredMatches();
+
+ public virtual Task HandleKeyDown(KeyEventArgs keyEventArgs)
+ {
+ if (keyEventArgs.Key == Key.Down)
+ {
+ var nextItem = FilteredMatches.SkipWhile(i => i != SelectedItem).Skip(1).FirstOrDefault();
+
+ if (nextItem is not null)
+ {
+ SelectedItem = nextItem;
+ }
+
+ return Task.FromResult(true);
+ }
+ else if (keyEventArgs.Key == Key.Up)
+ {
+ var previousItem = FilteredMatches.TakeWhile(i => i != SelectedItem).LastOrDefault();
+
+ if (previousItem is not null)
+ {
+ SelectedItem = previousItem;
+ }
+
+ return Task.FromResult(true);
+ }
+
+ return Task.FromResult(false);
+ }
+}
\ No newline at end of file
diff --git a/src/FileTime.sln b/src/FileTime.sln
index 6d28bdd..4bc0bb2 100644
--- a/src/FileTime.sln
+++ b/src/FileTime.sln
@@ -75,6 +75,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.CommandPalette
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.CommandPalette.Abstractions", "AppCommon\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj", "{5B3D2008-371F-485C-92C0-127F6CD64F64}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.FuzzyPanel", "AppCommon\FileTime.App.FuzzyPanel\FileTime.App.FuzzyPanel.csproj", "{7FDCE43D-D084-4539-B797-8A72D4DD610D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.FuzzyPanel.Abstraction", "AppCommon\FileTime.App.FuzzyPanel.Abstraction\FileTime.App.FuzzyPanel.Abstraction.csproj", "{7690E4EA-DA2A-45B4-AD83-80EE07A05169}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -193,6 +197,14 @@ Global
{5B3D2008-371F-485C-92C0-127F6CD64F64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B3D2008-371F-485C-92C0-127F6CD64F64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B3D2008-371F-485C-92C0-127F6CD64F64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FDCE43D-D084-4539-B797-8A72D4DD610D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FDCE43D-D084-4539-B797-8A72D4DD610D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FDCE43D-D084-4539-B797-8A72D4DD610D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FDCE43D-D084-4539-B797-8A72D4DD610D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7690E4EA-DA2A-45B4-AD83-80EE07A05169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7690E4EA-DA2A-45B4-AD83-80EE07A05169}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7690E4EA-DA2A-45B4-AD83-80EE07A05169}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7690E4EA-DA2A-45B4-AD83-80EE07A05169}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -227,6 +239,8 @@ Global
{D8D4A5C3-14B5-49E7-B029-D6E5D9574388} = {A5291117-3001-498B-AC8B-E14F71F72570}
{D0CC03DA-4705-48BD-9C4F-B11545D8BC83} = {A5291117-3001-498B-AC8B-E14F71F72570}
{5B3D2008-371F-485C-92C0-127F6CD64F64} = {A5291117-3001-498B-AC8B-E14F71F72570}
+ {7FDCE43D-D084-4539-B797-8A72D4DD610D} = {A5291117-3001-498B-AC8B-E14F71F72570}
+ {7690E4EA-DA2A-45B4-AD83-80EE07A05169} = {A5291117-3001-498B-AC8B-E14F71F72570}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}