Upload FastSeekWpf/MainWindow.xaml.cs
Browse files- FastSeekWpf/MainWindow.xaml.cs +82 -21
FastSeekWpf/MainWindow.xaml.cs
CHANGED
|
@@ -36,6 +36,10 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 36 |
private bool _initialized;
|
| 37 |
private bool _isVisible;
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
public event PropertyChangedEventHandler? PropertyChanged;
|
| 40 |
|
| 41 |
private List<ResultItem> _results = new();
|
|
@@ -55,10 +59,30 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 55 |
Loaded += (s, e) =>
|
| 56 |
{
|
| 57 |
var helper = new WindowInteropHelper(this);
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
};
|
| 60 |
}
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
private void Window_Loaded(object sender, RoutedEventArgs e)
|
| 63 |
{
|
| 64 |
if (_initialized) return;
|
|
@@ -305,13 +329,15 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 305 |
SearchBox.Focus();
|
| 306 |
}
|
| 307 |
|
| 308 |
-
// -- Search --
|
| 309 |
|
| 310 |
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
|
| 311 |
{
|
| 312 |
var query = SearchBox.Text.Trim();
|
|
|
|
| 313 |
if (string.IsNullOrEmpty(query))
|
| 314 |
{
|
|
|
|
| 315 |
Dispatcher.Invoke(() =>
|
| 316 |
{
|
| 317 |
Results = new List<ResultItem>();
|
|
@@ -320,33 +346,67 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 320 |
return;
|
| 321 |
}
|
| 322 |
|
| 323 |
-
|
| 324 |
-
|
|
|
|
|
|
|
| 325 |
{
|
| 326 |
-
|
| 327 |
}
|
| 328 |
|
| 329 |
-
|
|
|
|
| 330 |
{
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
}).ToList();
|
| 340 |
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
});
|
| 348 |
}
|
| 349 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
private void UpdateVisibility()
|
| 351 |
{
|
| 352 |
bool hasResults = Results.Count > 0;
|
|
@@ -452,6 +512,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged
|
|
| 452 |
protected override void OnClosing(CancelEventArgs e)
|
| 453 |
{
|
| 454 |
_cts?.Cancel();
|
|
|
|
| 455 |
foreach (var w in _watchers) w.Dispose();
|
| 456 |
_watchers.Clear();
|
| 457 |
CacheManager.SaveCache(_index);
|
|
|
|
| 36 |
private bool _initialized;
|
| 37 |
private bool _isVisible;
|
| 38 |
|
| 39 |
+
// Debounced search
|
| 40 |
+
private CancellationTokenSource? _searchDebounceCts;
|
| 41 |
+
private readonly object _searchLock = new();
|
| 42 |
+
|
| 43 |
public event PropertyChangedEventHandler? PropertyChanged;
|
| 44 |
|
| 45 |
private List<ResultItem> _results = new();
|
|
|
|
| 59 |
Loaded += (s, e) =>
|
| 60 |
{
|
| 61 |
var helper = new WindowInteropHelper(this);
|
| 62 |
+
var hwnd = helper.Handle;
|
| 63 |
+
HwndSource.FromHwnd(hwnd)?.AddHook(WndProc);
|
| 64 |
+
|
| 65 |
+
// Enable DWM rounded corners
|
| 66 |
+
EnableDwmRoundedCorners(hwnd);
|
| 67 |
};
|
| 68 |
}
|
| 69 |
|
| 70 |
+
/// <summary>
|
| 71 |
+
/// Enable native DWM rounded corners (Windows 11) and dark mode.
|
| 72 |
+
/// </summary>
|
| 73 |
+
private void EnableDwmRoundedCorners(IntPtr hwnd)
|
| 74 |
+
{
|
| 75 |
+
try
|
| 76 |
+
{
|
| 77 |
+
int cornerPref = Win32Api.DWMWCP_ROUND;
|
| 78 |
+
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_WINDOW_CORNER_PREFERENCE, ref cornerPref, Marshal.SizeOf(cornerPref));
|
| 79 |
+
|
| 80 |
+
int darkMode = 1;
|
| 81 |
+
Win32Api.DwmSetWindowAttribute(hwnd, Win32Api.DWMWA_USE_IMMERSIVE_DARK_MODE, ref darkMode, Marshal.SizeOf(darkMode));
|
| 82 |
+
}
|
| 83 |
+
catch { /* DWM not available on older Windows */ }
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
private void Window_Loaded(object sender, RoutedEventArgs e)
|
| 87 |
{
|
| 88 |
if (_initialized) return;
|
|
|
|
| 329 |
SearchBox.Focus();
|
| 330 |
}
|
| 331 |
|
| 332 |
+
// -- Search (debounced, background thread) --
|
| 333 |
|
| 334 |
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
|
| 335 |
{
|
| 336 |
var query = SearchBox.Text.Trim();
|
| 337 |
+
|
| 338 |
if (string.IsNullOrEmpty(query))
|
| 339 |
{
|
| 340 |
+
CancelPendingSearch();
|
| 341 |
Dispatcher.Invoke(() =>
|
| 342 |
{
|
| 343 |
Results = new List<ResultItem>();
|
|
|
|
| 346 |
return;
|
| 347 |
}
|
| 348 |
|
| 349 |
+
CancelPendingSearch();
|
| 350 |
+
|
| 351 |
+
var cts = new CancellationTokenSource();
|
| 352 |
+
lock (_searchLock)
|
| 353 |
{
|
| 354 |
+
_searchDebounceCts = cts;
|
| 355 |
}
|
| 356 |
|
| 357 |
+
// Debounce: 25ms
|
| 358 |
+
Task.Run(async () =>
|
| 359 |
{
|
| 360 |
+
try
|
| 361 |
+
{
|
| 362 |
+
await Task.Delay(25, cts.Token);
|
| 363 |
+
}
|
| 364 |
+
catch (TaskCanceledException)
|
| 365 |
+
{
|
| 366 |
+
return;
|
| 367 |
+
}
|
|
|
|
| 368 |
|
| 369 |
+
if (cts.IsCancellationRequested) return;
|
| 370 |
+
|
| 371 |
+
List<SearchResult> results;
|
| 372 |
+
lock (_indexLock)
|
| 373 |
+
{
|
| 374 |
+
results = SearchEngine.Search(_index, query, 50, _caseSensitive, _excludedDirs);
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
var items = results.Select((r, i) => new ResultItem
|
| 378 |
+
{
|
| 379 |
+
FullPath = r.FullPath,
|
| 380 |
+
DisplayName = Path.GetFileNameWithoutExtension(r.FullPath),
|
| 381 |
+
IsSelected = i == 0,
|
| 382 |
+
IsHovered = false,
|
| 383 |
+
Kind = r.Kind,
|
| 384 |
+
IconGlyph = GetIconGlyph(r.Kind),
|
| 385 |
+
BadgeLabel = GetBadgeLabel(r.Kind),
|
| 386 |
+
BadgeColor = GetBadgeColor(r.Kind)
|
| 387 |
+
}).ToList();
|
| 388 |
+
|
| 389 |
+
Dispatcher.Invoke(() =>
|
| 390 |
+
{
|
| 391 |
+
if (cts.IsCancellationRequested) return;
|
| 392 |
+
_selectedIndex = 0;
|
| 393 |
+
Results = items;
|
| 394 |
+
UpdateVisibility();
|
| 395 |
+
StatusText.Text = $"{items.Count} results";
|
| 396 |
+
});
|
| 397 |
});
|
| 398 |
}
|
| 399 |
|
| 400 |
+
private void CancelPendingSearch()
|
| 401 |
+
{
|
| 402 |
+
lock (_searchLock)
|
| 403 |
+
{
|
| 404 |
+
_searchDebounceCts?.Cancel();
|
| 405 |
+
_searchDebounceCts?.Dispose();
|
| 406 |
+
_searchDebounceCts = null;
|
| 407 |
+
}
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
private void UpdateVisibility()
|
| 411 |
{
|
| 412 |
bool hasResults = Results.Count > 0;
|
|
|
|
| 512 |
protected override void OnClosing(CancelEventArgs e)
|
| 513 |
{
|
| 514 |
_cts?.Cancel();
|
| 515 |
+
CancelPendingSearch();
|
| 516 |
foreach (var w in _watchers) w.Dispose();
|
| 517 |
_watchers.Clear();
|
| 518 |
CacheManager.SaveCache(_index);
|