Upload FastSeekWpf/MainWindow.xaml.cs
Browse files- FastSeekWpf/MainWindow.xaml.cs +70 -79
FastSeekWpf/MainWindow.xaml.cs
CHANGED
|
@@ -14,6 +14,7 @@ using System.Windows.Data;
|
|
| 14 |
using System.Windows.Input;
|
| 15 |
using System.Windows.Interop;
|
| 16 |
using System.Windows.Media;
|
|
|
|
| 17 |
using System.Windows.Threading;
|
| 18 |
using FastSeekWpf.Core;
|
| 19 |
using FastSeekWpf.NativeInterop;
|
|
@@ -56,28 +57,21 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 56 |
DataContext = this;
|
| 57 |
_excludedDirs = CacheManager.LoadExclusions();
|
| 58 |
|
| 59 |
-
// Hook WndProc for custom messages
|
| 60 |
Loaded += (s, e) =>
|
| 61 |
{
|
| 62 |
var helper = new WindowInteropHelper(this);
|
| 63 |
var hwnd = helper.Handle;
|
| 64 |
HwndSource.FromHwnd(hwnd)?.AddHook(WndProc);
|
| 65 |
-
|
| 66 |
-
// Enable DWM rounded corners
|
| 67 |
EnableDwmRoundedCorners(hwnd);
|
| 68 |
};
|
| 69 |
}
|
| 70 |
|
| 71 |
-
/// <summary>
|
| 72 |
-
/// Enable native DWM rounded corners (Windows 11) and dark mode.
|
| 73 |
-
/// </summary>
|
| 74 |
private void EnableDwmRoundedCorners(IntPtr hwnd)
|
| 75 |
{
|
| 76 |
try
|
| 77 |
{
|
| 78 |
int cornerPref = Win32Api.DWMWCP_ROUND;
|
| 79 |
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_WINDOW_CORNER_PREFERENCE, ref cornerPref, Marshal.SizeOf(cornerPref));
|
| 80 |
-
|
| 81 |
int darkMode = 1;
|
| 82 |
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_USE_IMMERSIVE_DARK_MODE, ref darkMode, Marshal.SizeOf(darkMode));
|
| 83 |
Logger.Log("DWM rounded corners + dark mode enabled.");
|
|
@@ -93,11 +87,23 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 93 |
Logger.Log("Window_Loaded");
|
| 94 |
if (_initialized) return;
|
| 95 |
_initialized = true;
|
| 96 |
-
|
| 97 |
CenterWindow();
|
|
|
|
| 98 |
Task.Run(async () => await InitializeAsync());
|
| 99 |
}
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
private async Task InitializeAsync()
|
| 102 |
{
|
| 103 |
Logger.Log("InitializeAsync start");
|
|
@@ -117,7 +123,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 117 |
return;
|
| 118 |
}
|
| 119 |
|
| 120 |
-
// Try cache first
|
| 121 |
bool needFullScan = true;
|
| 122 |
var cache = CacheManager.LoadCache();
|
| 123 |
if (cache != null)
|
|
@@ -129,7 +134,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 129 |
}
|
| 130 |
Logger.Log($"Loaded cache: {_index.Count} files");
|
| 131 |
|
| 132 |
-
// Delta catch-up
|
| 133 |
var checkpoints = new List<JournalCheckpoint>(cache.Checkpoints);
|
| 134 |
bool deltaOk = true;
|
| 135 |
foreach (var drive in _drives)
|
|
@@ -161,7 +165,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 161 |
if (deltaOk)
|
| 162 |
{
|
| 163 |
needFullScan = false;
|
| 164 |
-
// Apply delta events
|
| 165 |
while (_eventQueue.TryDequeue(out var evt))
|
| 166 |
{
|
| 167 |
ApplyEvent(evt);
|
|
@@ -205,7 +208,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 205 |
Logger.Log("Cache saved.");
|
| 206 |
}
|
| 207 |
|
| 208 |
-
// Set up USN watchers for live updates
|
| 209 |
foreach (var drive in _drives)
|
| 210 |
{
|
| 211 |
try
|
|
@@ -222,14 +224,13 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 222 |
}
|
| 223 |
}
|
| 224 |
|
| 225 |
-
// Start live update processor
|
| 226 |
_cts = new CancellationTokenSource();
|
| 227 |
_ = Task.Run(() => LiveUpdateLoop(_cts.Token));
|
| 228 |
|
| 229 |
Dispatcher.Invoke(() =>
|
| 230 |
{
|
| 231 |
-
StatusText.Text = $"{_index.Count} files indexed";
|
| 232 |
-
StatusBar.Visibility = Visibility.
|
| 233 |
});
|
| 234 |
Logger.Log("InitializeAsync done.");
|
| 235 |
}
|
|
@@ -257,7 +258,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 257 |
{
|
| 258 |
ApplyEvent(evt);
|
| 259 |
processed++;
|
| 260 |
-
if (processed > 100) break;
|
| 261 |
}
|
| 262 |
if (processed > 50)
|
| 263 |
{
|
|
@@ -289,8 +290,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 289 |
}
|
| 290 |
}
|
| 291 |
|
| 292 |
-
// -- Window management --
|
| 293 |
-
|
| 294 |
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
| 295 |
{
|
| 296 |
if (msg == WM_TOGGLE_WINDOW)
|
|
@@ -311,16 +310,31 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 311 |
{
|
| 312 |
if (_isVisible)
|
| 313 |
{
|
| 314 |
-
|
| 315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
}
|
| 317 |
else
|
| 318 |
{
|
| 319 |
CenterWindow();
|
|
|
|
|
|
|
|
|
|
| 320 |
Show();
|
| 321 |
Activate();
|
| 322 |
SearchBox.Focus();
|
| 323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
_isVisible = true;
|
| 325 |
}
|
| 326 |
}
|
|
@@ -330,11 +344,9 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 330 |
var screenW = SystemParameters.PrimaryScreenWidth;
|
| 331 |
var screenH = SystemParameters.PrimaryScreenHeight;
|
| 332 |
Left = (screenW - Width) / 2;
|
| 333 |
-
Top = screenH * 0.
|
| 334 |
}
|
| 335 |
|
| 336 |
-
// -- Keyboard --
|
| 337 |
-
|
| 338 |
private void Window_KeyDown(object sender, KeyEventArgs e)
|
| 339 |
{
|
| 340 |
switch (e.Key)
|
|
@@ -344,16 +356,18 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 344 |
e.Handled = true;
|
| 345 |
break;
|
| 346 |
case Key.Down:
|
| 347 |
-
if (
|
| 348 |
{
|
| 349 |
-
|
|
|
|
| 350 |
e.Handled = true;
|
| 351 |
}
|
| 352 |
break;
|
| 353 |
case Key.Up:
|
| 354 |
-
if (
|
| 355 |
{
|
| 356 |
-
|
|
|
|
| 357 |
e.Handled = true;
|
| 358 |
}
|
| 359 |
break;
|
|
@@ -369,8 +383,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 369 |
SearchBox.Focus();
|
| 370 |
}
|
| 371 |
|
| 372 |
-
// -- Search (debounced, background thread) --
|
| 373 |
-
|
| 374 |
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
|
| 375 |
{
|
| 376 |
var query = SearchBox.Text.Trim();
|
|
@@ -381,7 +393,10 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 381 |
Dispatcher.Invoke(() =>
|
| 382 |
{
|
| 383 |
Results = new List<ResultItem>();
|
| 384 |
-
|
|
|
|
|
|
|
|
|
|
| 385 |
});
|
| 386 |
return;
|
| 387 |
}
|
|
@@ -394,7 +409,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 394 |
_searchDebounceCts = cts;
|
| 395 |
}
|
| 396 |
|
| 397 |
-
// Debounce: 25ms
|
| 398 |
Task.Run(async () =>
|
| 399 |
{
|
| 400 |
try
|
|
@@ -431,12 +445,33 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 431 |
if (cts.IsCancellationRequested) return;
|
| 432 |
_selectedIndex = 0;
|
| 433 |
Results = items;
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
StatusText.Text = $"{items.Count} results";
|
|
|
|
| 436 |
});
|
| 437 |
});
|
| 438 |
}
|
| 439 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
private void CancelPendingSearch()
|
| 441 |
{
|
| 442 |
lock (_searchLock)
|
|
@@ -447,45 +482,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 447 |
}
|
| 448 |
}
|
| 449 |
|
| 450 |
-
private void UpdateVisibility()
|
| 451 |
-
{
|
| 452 |
-
bool hasResults = Results.Count > 0;
|
| 453 |
-
SepLine.Visibility = hasResults ? Visibility.Visible : Visibility.Collapsed;
|
| 454 |
-
ResultsScroll.Visibility = hasResults ? Visibility.Visible : Visibility.Collapsed;
|
| 455 |
-
StatusBar.Visibility = Visibility.Visible;
|
| 456 |
-
}
|
| 457 |
-
|
| 458 |
-
private void UpdateSelection(int newIndex)
|
| 459 |
-
{
|
| 460 |
-
if (Results.Count == 0 || newIndex < 0 || newIndex >= Results.Count) return;
|
| 461 |
-
|
| 462 |
-
// Use new list to trigger PropertyChanged
|
| 463 |
-
var items = Results.Select((r, i) => r with
|
| 464 |
-
{
|
| 465 |
-
IsSelected = i == newIndex,
|
| 466 |
-
IsHovered = i == newIndex
|
| 467 |
-
}).ToList();
|
| 468 |
-
_selectedIndex = newIndex;
|
| 469 |
-
Results = items;
|
| 470 |
-
|
| 471 |
-
// Scroll selected item into view
|
| 472 |
-
if (ResultsList.ItemContainerGenerator.ContainerFromIndex(newIndex) is FrameworkElement container)
|
| 473 |
-
{
|
| 474 |
-
container.BringIntoView();
|
| 475 |
-
}
|
| 476 |
-
}
|
| 477 |
-
|
| 478 |
-
// -- Mouse --
|
| 479 |
-
|
| 480 |
-
private void ResultItem_MouseEnter(object sender, MouseEventArgs e)
|
| 481 |
-
{
|
| 482 |
-
if (sender is Border border && border.DataContext is ResultItem item)
|
| 483 |
-
{
|
| 484 |
-
int idx = Results.IndexOf(item);
|
| 485 |
-
if (idx >= 0) UpdateSelection(idx);
|
| 486 |
-
}
|
| 487 |
-
}
|
| 488 |
-
|
| 489 |
private void ResultItem_Click(object sender, MouseButtonEventArgs e)
|
| 490 |
{
|
| 491 |
if (sender is Border border && border.DataContext is ResultItem item)
|
|
@@ -493,7 +489,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 493 |
int idx = Results.IndexOf(item);
|
| 494 |
if (idx >= 0)
|
| 495 |
{
|
| 496 |
-
|
| 497 |
OpenResult(false);
|
| 498 |
}
|
| 499 |
}
|
|
@@ -501,8 +497,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 501 |
|
| 502 |
private void OpenResult(bool folderOnly)
|
| 503 |
{
|
| 504 |
-
if (
|
| 505 |
-
var item = Results[_selectedIndex];
|
| 506 |
string target = folderOnly
|
| 507 |
? (Directory.Exists(item.FullPath) ? item.FullPath : Path.GetDirectoryName(item.FullPath) ?? item.FullPath)
|
| 508 |
: item.FullPath;
|
|
@@ -511,8 +506,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 511 |
Hide(); _isVisible = false;
|
| 512 |
}
|
| 513 |
|
| 514 |
-
// -- Helpers --
|
| 515 |
-
|
| 516 |
private static string GetIconGlyph(ResultKind kind) => kind switch
|
| 517 |
{
|
| 518 |
ResultKind.App => "\uE7E8",
|
|
@@ -560,8 +553,6 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 560 |
}
|
| 561 |
}
|
| 562 |
|
| 563 |
-
// -- Result item model --
|
| 564 |
-
|
| 565 |
public record ResultItem
|
| 566 |
{
|
| 567 |
public string FullPath { get; init; } = "";
|
|
|
|
| 14 |
using System.Windows.Input;
|
| 15 |
using System.Windows.Interop;
|
| 16 |
using System.Windows.Media;
|
| 17 |
+
using System.Windows.Media.Animation;
|
| 18 |
using System.Windows.Threading;
|
| 19 |
using FastSeekWpf.Core;
|
| 20 |
using FastSeekWpf.NativeInterop;
|
|
|
|
| 57 |
DataContext = this;
|
| 58 |
_excludedDirs = CacheManager.LoadExclusions();
|
| 59 |
|
|
|
|
| 60 |
Loaded += (s, e) =>
|
| 61 |
{
|
| 62 |
var helper = new WindowInteropHelper(this);
|
| 63 |
var hwnd = helper.Handle;
|
| 64 |
HwndSource.FromHwnd(hwnd)?.AddHook(WndProc);
|
|
|
|
|
|
|
| 65 |
EnableDwmRoundedCorners(hwnd);
|
| 66 |
};
|
| 67 |
}
|
| 68 |
|
|
|
|
|
|
|
|
|
|
| 69 |
private void EnableDwmRoundedCorners(IntPtr hwnd)
|
| 70 |
{
|
| 71 |
try
|
| 72 |
{
|
| 73 |
int cornerPref = Win32Api.DWMWCP_ROUND;
|
| 74 |
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_WINDOW_CORNER_PREFERENCE, ref cornerPref, Marshal.SizeOf(cornerPref));
|
|
|
|
| 75 |
int darkMode = 1;
|
| 76 |
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_USE_IMMERSIVE_DARK_MODE, ref darkMode, Marshal.SizeOf(darkMode));
|
| 77 |
Logger.Log("DWM rounded corners + dark mode enabled.");
|
|
|
|
| 87 |
Logger.Log("Window_Loaded");
|
| 88 |
if (_initialized) return;
|
| 89 |
_initialized = true;
|
|
|
|
| 90 |
CenterWindow();
|
| 91 |
+
ShowEmptyState();
|
| 92 |
Task.Run(async () => await InitializeAsync());
|
| 93 |
}
|
| 94 |
|
| 95 |
+
private void ShowEmptyState()
|
| 96 |
+
{
|
| 97 |
+
Dispatcher.Invoke(() =>
|
| 98 |
+
{
|
| 99 |
+
ResultsScroll.Visibility = Visibility.Collapsed;
|
| 100 |
+
EmptyHint.Visibility = Visibility.Visible;
|
| 101 |
+
StatusBar.Visibility = Visibility.Visible;
|
| 102 |
+
StatusText.Text = "Indexing...";
|
| 103 |
+
SepLine.Visibility = Visibility.Collapsed;
|
| 104 |
+
});
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
private async Task InitializeAsync()
|
| 108 |
{
|
| 109 |
Logger.Log("InitializeAsync start");
|
|
|
|
| 123 |
return;
|
| 124 |
}
|
| 125 |
|
|
|
|
| 126 |
bool needFullScan = true;
|
| 127 |
var cache = CacheManager.LoadCache();
|
| 128 |
if (cache != null)
|
|
|
|
| 134 |
}
|
| 135 |
Logger.Log($"Loaded cache: {_index.Count} files");
|
| 136 |
|
|
|
|
| 137 |
var checkpoints = new List<JournalCheckpoint>(cache.Checkpoints);
|
| 138 |
bool deltaOk = true;
|
| 139 |
foreach (var drive in _drives)
|
|
|
|
| 165 |
if (deltaOk)
|
| 166 |
{
|
| 167 |
needFullScan = false;
|
|
|
|
| 168 |
while (_eventQueue.TryDequeue(out var evt))
|
| 169 |
{
|
| 170 |
ApplyEvent(evt);
|
|
|
|
| 208 |
Logger.Log("Cache saved.");
|
| 209 |
}
|
| 210 |
|
|
|
|
| 211 |
foreach (var drive in _drives)
|
| 212 |
{
|
| 213 |
try
|
|
|
|
| 224 |
}
|
| 225 |
}
|
| 226 |
|
|
|
|
| 227 |
_cts = new CancellationTokenSource();
|
| 228 |
_ = Task.Run(() => LiveUpdateLoop(_cts.Token));
|
| 229 |
|
| 230 |
Dispatcher.Invoke(() =>
|
| 231 |
{
|
| 232 |
+
StatusText.Text = $"{_index.Count:N0} files indexed";
|
| 233 |
+
StatusBar.Visibility = Visibility.Visible;
|
| 234 |
});
|
| 235 |
Logger.Log("InitializeAsync done.");
|
| 236 |
}
|
|
|
|
| 258 |
{
|
| 259 |
ApplyEvent(evt);
|
| 260 |
processed++;
|
| 261 |
+
if (processed > 100) break;
|
| 262 |
}
|
| 263 |
if (processed > 50)
|
| 264 |
{
|
|
|
|
| 290 |
}
|
| 291 |
}
|
| 292 |
|
|
|
|
|
|
|
| 293 |
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
| 294 |
{
|
| 295 |
if (msg == WM_TOGGLE_WINDOW)
|
|
|
|
| 310 |
{
|
| 311 |
if (_isVisible)
|
| 312 |
{
|
| 313 |
+
if (Resources["FadeAnim"] is Storyboard fade)
|
| 314 |
+
{
|
| 315 |
+
fade.Completed += (s, e) => { Hide(); _isVisible = false; };
|
| 316 |
+
BeginStoryboard(fade);
|
| 317 |
+
}
|
| 318 |
+
else
|
| 319 |
+
{
|
| 320 |
+
Hide();
|
| 321 |
+
_isVisible = false;
|
| 322 |
+
}
|
| 323 |
}
|
| 324 |
else
|
| 325 |
{
|
| 326 |
CenterWindow();
|
| 327 |
+
SearchBox.Clear();
|
| 328 |
+
Results = new List<ResultItem>();
|
| 329 |
+
ShowEmptyState();
|
| 330 |
Show();
|
| 331 |
Activate();
|
| 332 |
SearchBox.Focus();
|
| 333 |
+
|
| 334 |
+
if (Resources["AppearAnim"] is Storyboard appear)
|
| 335 |
+
{
|
| 336 |
+
BeginStoryboard(appear);
|
| 337 |
+
}
|
| 338 |
_isVisible = true;
|
| 339 |
}
|
| 340 |
}
|
|
|
|
| 344 |
var screenW = SystemParameters.PrimaryScreenWidth;
|
| 345 |
var screenH = SystemParameters.PrimaryScreenHeight;
|
| 346 |
Left = (screenW - Width) / 2;
|
| 347 |
+
Top = screenH * 0.18;
|
| 348 |
}
|
| 349 |
|
|
|
|
|
|
|
| 350 |
private void Window_KeyDown(object sender, KeyEventArgs e)
|
| 351 |
{
|
| 352 |
switch (e.Key)
|
|
|
|
| 356 |
e.Handled = true;
|
| 357 |
break;
|
| 358 |
case Key.Down:
|
| 359 |
+
if (ResultsList.Items.Count > 0 && ResultsList.SelectedIndex + 1 < ResultsList.Items.Count)
|
| 360 |
{
|
| 361 |
+
ResultsList.SelectedIndex++;
|
| 362 |
+
ResultsList.ScrollIntoView(ResultsList.SelectedItem);
|
| 363 |
e.Handled = true;
|
| 364 |
}
|
| 365 |
break;
|
| 366 |
case Key.Up:
|
| 367 |
+
if (ResultsList.SelectedIndex > 0)
|
| 368 |
{
|
| 369 |
+
ResultsList.SelectedIndex--;
|
| 370 |
+
ResultsList.ScrollIntoView(ResultsList.SelectedItem);
|
| 371 |
e.Handled = true;
|
| 372 |
}
|
| 373 |
break;
|
|
|
|
| 383 |
SearchBox.Focus();
|
| 384 |
}
|
| 385 |
|
|
|
|
|
|
|
| 386 |
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
|
| 387 |
{
|
| 388 |
var query = SearchBox.Text.Trim();
|
|
|
|
| 393 |
Dispatcher.Invoke(() =>
|
| 394 |
{
|
| 395 |
Results = new List<ResultItem>();
|
| 396 |
+
ResultsScroll.Visibility = Visibility.Collapsed;
|
| 397 |
+
EmptyHint.Visibility = Visibility.Visible;
|
| 398 |
+
SepLine.Visibility = Visibility.Collapsed;
|
| 399 |
+
StatusText.Text = $"{_index.Count:N0} files indexed";
|
| 400 |
});
|
| 401 |
return;
|
| 402 |
}
|
|
|
|
| 409 |
_searchDebounceCts = cts;
|
| 410 |
}
|
| 411 |
|
|
|
|
| 412 |
Task.Run(async () =>
|
| 413 |
{
|
| 414 |
try
|
|
|
|
| 445 |
if (cts.IsCancellationRequested) return;
|
| 446 |
_selectedIndex = 0;
|
| 447 |
Results = items;
|
| 448 |
+
|
| 449 |
+
if (items.Count > 0)
|
| 450 |
+
{
|
| 451 |
+
ResultsScroll.Visibility = Visibility.Visible;
|
| 452 |
+
EmptyHint.Visibility = Visibility.Collapsed;
|
| 453 |
+
SepLine.Visibility = Visibility.Visible;
|
| 454 |
+
ResultsList.SelectedIndex = 0;
|
| 455 |
+
}
|
| 456 |
+
else
|
| 457 |
+
{
|
| 458 |
+
ResultsScroll.Visibility = Visibility.Collapsed;
|
| 459 |
+
EmptyHint.Visibility = Visibility.Visible;
|
| 460 |
+
EmptyHint.Text = $"No results for \"{query}\"";
|
| 461 |
+
SepLine.Visibility = Visibility.Collapsed;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
StatusText.Text = $"{items.Count} results";
|
| 465 |
+
StatusBar.Visibility = Visibility.Visible;
|
| 466 |
});
|
| 467 |
});
|
| 468 |
}
|
| 469 |
|
| 470 |
+
private void ResultsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
| 471 |
+
{
|
| 472 |
+
_selectedIndex = ResultsList.SelectedIndex;
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
private void CancelPendingSearch()
|
| 476 |
{
|
| 477 |
lock (_searchLock)
|
|
|
|
| 482 |
}
|
| 483 |
}
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
private void ResultItem_Click(object sender, MouseButtonEventArgs e)
|
| 486 |
{
|
| 487 |
if (sender is Border border && border.DataContext is ResultItem item)
|
|
|
|
| 489 |
int idx = Results.IndexOf(item);
|
| 490 |
if (idx >= 0)
|
| 491 |
{
|
| 492 |
+
ResultsList.SelectedIndex = idx;
|
| 493 |
OpenResult(false);
|
| 494 |
}
|
| 495 |
}
|
|
|
|
| 497 |
|
| 498 |
private void OpenResult(bool folderOnly)
|
| 499 |
{
|
| 500 |
+
if (ResultsList.SelectedItem is not ResultItem item) return;
|
|
|
|
| 501 |
string target = folderOnly
|
| 502 |
? (Directory.Exists(item.FullPath) ? item.FullPath : Path.GetDirectoryName(item.FullPath) ?? item.FullPath)
|
| 503 |
: item.FullPath;
|
|
|
|
| 506 |
Hide(); _isVisible = false;
|
| 507 |
}
|
| 508 |
|
|
|
|
|
|
|
| 509 |
private static string GetIconGlyph(ResultKind kind) => kind switch
|
| 510 |
{
|
| 511 |
ResultKind.App => "\uE7E8",
|
|
|
|
| 553 |
}
|
| 554 |
}
|
| 555 |
|
|
|
|
|
|
|
| 556 |
public record ResultItem
|
| 557 |
{
|
| 558 |
public string FullPath { get; init; } = "";
|