| using System.Collections.ObjectModel; |
| using System.Diagnostics; |
| using FastSeek.Core; |
| using FastSeek.Core.Search; |
| using Microsoft.UI.Input; |
| using Microsoft.UI.Windowing; |
| using Microsoft.UI.Xaml; |
| using Microsoft.UI.Xaml.Controls; |
| using Microsoft.UI.Xaml.Input; |
| using Windows.System; |
|
|
| namespace FastSeek.WinUI; |
|
|
| public sealed partial class MainWindow : Window |
| { |
| private readonly FastSeekEngine _engine = new(); |
| private readonly ObservableCollection<ResultItem> _items = new(); |
|
|
| public MainWindow() |
| { |
| this.InitializeComponent(); |
| ResultsList.ItemsSource = _items; |
| ExtendsContentIntoTitleBar = true; |
| this.Closed += (_, _) => _engine.Dispose(); |
| SetSpotlightSizing(); |
| _ = InitializeEngineAsync(); |
| } |
|
|
| private async Task InitializeEngineAsync() |
| { |
| SearchBox.PlaceholderText = "Indexing NTFS drives..."; |
| try |
| { |
| await _engine.InitializeAsync(); |
| SearchBox.PlaceholderText = "Search files and folders..."; |
| } |
| catch (Exception ex) |
| { |
| SearchBox.PlaceholderText = "Initialization failed"; |
| _items.Clear(); |
| _items.Add(new ResultItem { Kind = "ERR", Name = "Could not initialize index", FullPath = ex.Message, Raw = null }); |
| } |
| } |
|
|
| private void SetSpotlightSizing() |
| { |
| var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); |
| var id = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd); |
| var appWindow = AppWindow.GetFromWindowId(id); |
| appWindow.Resize(new Windows.Graphics.SizeInt32(900, 560)); |
| if (appWindow.Presenter is OverlappedPresenter p) |
| { |
| p.IsMaximizable = false; |
| p.IsMinimizable = true; |
| p.IsResizable = true; |
| } |
| } |
|
|
| private async void SearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) |
| { |
| if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) return; |
| var query = sender.Text; |
| var results = await Task.Run(() => _engine.Search(query, 50)); |
| RenderResults(results); |
| } |
|
|
| private void RenderResults(List<SearchResult> results) |
| { |
| _items.Clear(); |
| foreach (var r in results) |
| { |
| _items.Add(new ResultItem |
| { |
| Kind = r.IsDir ? "DIR" : "FILE", |
| Name = r.Name, |
| FullPath = r.FullPath, |
| Raw = r, |
| }); |
| } |
| if (_items.Count > 0) ResultsList.SelectedIndex = 0; |
| } |
|
|
| private void SearchBox_KeyDown(object sender, KeyRoutedEventArgs e) |
| { |
| if (e.Key == VirtualKey.Down) |
| { |
| if (_items.Count == 0) return; |
| var i = Math.Min(ResultsList.SelectedIndex + 1, _items.Count - 1); |
| ResultsList.SelectedIndex = i; |
| ResultsList.ScrollIntoView(_items[i]); |
| e.Handled = true; |
| } |
| else if (e.Key == VirtualKey.Up) |
| { |
| if (_items.Count == 0) return; |
| var i = Math.Max(ResultsList.SelectedIndex - 1, 0); |
| ResultsList.SelectedIndex = i; |
| ResultsList.ScrollIntoView(_items[i]); |
| e.Handled = true; |
| } |
| else if (e.Key == VirtualKey.Enter) |
| { |
| OpenSelected(); |
| e.Handled = true; |
| } |
| else if (e.Key == VirtualKey.Escape) |
| { |
| this.Close(); |
| } |
| } |
|
|
| private void ResultsList_ItemClick(object sender, ItemClickEventArgs e) => OpenSelected(); |
|
|
| private void OpenSelected() |
| { |
| if (ResultsList.SelectedItem is not ResultItem item) return; |
| try |
| { |
| Process.Start(new ProcessStartInfo |
| { |
| FileName = item.FullPath, |
| UseShellExecute = true, |
| }); |
| } |
| catch |
| { |
| try |
| { |
| Process.Start(new ProcessStartInfo |
| { |
| FileName = "explorer.exe", |
| Arguments = $"/select,\"{item.FullPath}\"", |
| UseShellExecute = true, |
| }); |
| } |
| catch { } |
| } |
| } |
|
|
| private sealed class ResultItem |
| { |
| public required string Kind { get; init; } |
| public required string Name { get; init; } |
| public required string FullPath { get; init; } |
| public SearchResult? Raw { get; init; } |
| } |
| } |
|
|