anshdadhich commited on
Commit
e37dee7
·
verified ·
1 Parent(s): d34ad28

Upload FastSeekWpf/MainWindow.xaml.cs

Browse files
Files changed (1) hide show
  1. 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.Collapsed;
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; // Batch limit
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
- Hide();
315
- _isVisible = false;
 
 
 
 
 
 
 
 
316
  }
317
  else
318
  {
319
  CenterWindow();
 
 
 
320
  Show();
321
  Activate();
322
  SearchBox.Focus();
323
- SearchBox.SelectAll();
 
 
 
 
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.22;
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 (_selectedIndex + 1 < Results.Count)
348
  {
349
- UpdateSelection(_selectedIndex + 1);
 
350
  e.Handled = true;
351
  }
352
  break;
353
  case Key.Up:
354
- if (_selectedIndex > 0)
355
  {
356
- UpdateSelection(_selectedIndex - 1);
 
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
- UpdateVisibility();
 
 
 
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
- UpdateVisibility();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- UpdateSelection(idx);
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 (Results.Count == 0 || _selectedIndex < 0 || _selectedIndex >= Results.Count) return;
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; } = "";