Skip to content

🧱 Mason: Refactor SearchBox KeyDown handling - #434

Open
google-labs-jules[bot] wants to merge 8 commits into
mainfrom
mason-searchbox-refactor-2735603070435359010
Open

🧱 Mason: Refactor SearchBox KeyDown handling#434
google-labs-jules[bot] wants to merge 8 commits into
mainfrom
mason-searchbox-refactor-2735603070435359010

Conversation

@google-labs-jules

Copy link
Copy Markdown
Contributor

💡 What: Moved SearchBox KeyDown handling from code-behind to ViewModel.
🎯 Why: Removes UI logic from code-behind.
🏗️ Architecture: Uses an attached property and RelayCommand to handle input in the ViewModel, keeping UI focus in code-behind via events.


PR created automatically by Jules for task 2735603070435359010 started by @mikekthx

@google-labs-jules
google-labs-jules Bot requested a review from mikekthx as a code owner July 13, 2026 09:51
@google-labs-jules

Copy link
Copy Markdown
Contributor Author

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@github-actions github-actions Bot added documentation Improvements or additions to READMEs, wikis, or inline code comments. core-logic Changes to primary application logic, backend services, or models. ui Front-end changes, WinUI layouts, styling (e.g., Acrylic), or system tray updates. labels Jul 13, 2026
Comment thread ViewModels/MainViewModel.cs Outdated
Comment thread ViewModels/MainViewModel.cs Outdated
Comment thread ViewModels/MainViewModel.cs Outdated
Comment thread Helpers/UIElementExtensions.cs Outdated
Comment thread .jules/mason.md Outdated
@mikekthx

Copy link
Copy Markdown
Owner

Code Review

What this PR does

Moves SearchBox KeyDown handling from MainWindow.xaml.cs into MainViewModel via a new KeyDownCommand attached property on UIElementExtensions. The Enter key (launch app) and Down key (focus grid) logic are now in a [RelayCommand]-decorated ViewModel method. A new SearchBoxDownKeyPressed event carries the Down-key signal back to the View so code-behind can call activeGrid.Focus().


Overall assessment

The goal — removing KeyDown logic from code-behind — is reasonable MVVM hygiene, and the event-back-to-View pattern for Focus is already established in this codebase (see SearchFocusRequested). However, the implementation introduces a more serious MVVM violation than what it replaces: the ViewModel now accepts and mutates a WinUI KeyRoutedEventArgs directly, making the moved logic less testable than the original code-behind and breaking the platform-agnostic contract CLAUDE.md requires of ViewModels.


Blocking issues

1. WinUI type leaks into the ViewModel (MainViewModel.cs:578)
SearchBoxKeyDown takes Microsoft.UI.Xaml.Input.KeyRoutedEventArgs, a WinUI-specific type. CLAUDE.md is explicit: the ViewModel must be platform-agnostic. KeyRoutedEventArgs cannot be constructed in a unit-test context — the logic moved here is harder to test than it was in code-behind, not easier.

2. e.Handled = true inside the ViewModel (MainViewModel.cs:584,591)
Setting e.Handled on a WinUI event args object is UI event-routing state. The ViewModel doing this relies on KeyRoutedEventArgs being a mutable reference type whose state propagates back through the WinUI event pipeline — a non-obvious, untestable coupling. Handled belongs in the attached property handler or code-behind.

3. KeyDownCommand attached property passes raw KeyRoutedEventArgs (UIElementExtensions.cs:112)
The attached property calls command.Execute(e) where e is KeyRoutedEventArgs. This hardcodes an assumption that any bound command accepts a WinUI type, making the attached property non-reusable and forcing the ViewModel type dependency. Extracting e.Key (VirtualKey) at the attached property boundary and passing that enum instead would fix both the ViewModel coupling and the Handled placement.


Required fix summary

The fix is straightforward:

// UIElementExtensions.Element_KeyDown — extract the key, keep Handled here
var key = e.Key;
if (command != null && command.CanExecute(key))
{
    command.Execute(key);
    e.Handled = true;  // stays in the UI layer
}

// ViewModel — accepts a plain enum, fully testable
[RelayCommand]
private void SearchBoxKeyDown(Windows.System.VirtualKey key)
{
    if (key == VirtualKey.Enter && SelectedItem != null) { ... }
    else if (key == VirtualKey.Down) { OnSearchBoxDownKeyPressed(); }
}

Minor issues

  • protected virtual OnSearchBoxDownKeyPressed (MainViewModel.cs:211): MainViewModel has no subclasses; use private to match the CharacterReceived invoker pattern.
  • Duplicate mason.md entry: the new 2026-07-13 entry describes the same pattern as the existing 2026-05-11 entry word-for-word.

What works correctly

  • The SearchBoxDownKeyPressed event + code-behind subscription pattern is clean and consistent with SearchFocusRequested.
  • The event unsubscription in MainWindow_Closed is correctly added.
  • The attached property's subscribe/unsubscribe wiring in OnKeyDownCommandPropertyChanged correctly avoids double-subscription.

@jules please address the issues noted above

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Code Review

What this PR does

Moves SearchBox KeyDown handling from MainWindow.xaml.cs into MainViewModel via a new KeyDownCommand attached property on UIElementExtensions. The Enter key (launch app) and Down key (focus grid) logic are now in a [RelayCommand]-decorated ViewModel method. A new SearchBoxDownKeyPressed event carries the Down-key signal back to the View so code-behind can call activeGrid.Focus().


Overall assessment

The goal — removing KeyDown logic from code-behind — is reasonable MVVM hygiene, and the event-back-to-View pattern for Focus is already established in this codebase (see SearchFocusRequested). However, the implementation introduces a more serious MVVM violation than what it replaces: the ViewModel now accepts and mutates a WinUI KeyRoutedEventArgs directly, making the moved logic less testable than the original code-behind and breaking the platform-agnostic contract CLAUDE.md requires of ViewModels.


Blocking issues

1. WinUI type leaks into the ViewModel (MainViewModel.cs:578)
SearchBoxKeyDown takes Microsoft.UI.Xaml.Input.KeyRoutedEventArgs, a WinUI-specific type. CLAUDE.md is explicit: the ViewModel must be platform-agnostic. KeyRoutedEventArgs cannot be constructed in a unit-test context — the logic moved here is harder to test than it was in code-behind, not easier.

2. e.Handled = true inside the ViewModel (MainViewModel.cs:584,591)
Setting e.Handled on a WinUI event args object is UI event-routing state. The ViewModel doing this relies on KeyRoutedEventArgs being a mutable reference type whose state propagates back through the WinUI event pipeline — a non-obvious, untestable coupling. Handled belongs in the attached property handler or code-behind.

3. KeyDownCommand attached property passes raw KeyRoutedEventArgs (UIElementExtensions.cs:112)
The attached property calls command.Execute(e) where e is KeyRoutedEventArgs. This hardcodes an assumption that any bound command accepts a WinUI type, making the attached property non-reusable and forcing the ViewModel type dependency. Extracting e.Key (VirtualKey) at the attached property boundary and passing that enum instead would fix both the ViewModel coupling and the Handled placement.


Required fix summary

The fix is straightforward:

// UIElementExtensions.Element_KeyDown — extract the key, keep Handled here
var key = e.Key;
if (command != null && command.CanExecute(key))
{
    command.Execute(key);
    e.Handled = true;  // stays in the UI layer
}

// ViewModel — accepts a plain enum, fully testable
[RelayCommand]
private void SearchBoxKeyDown(Windows.System.VirtualKey key)
{
    if (key == VirtualKey.Enter && SelectedItem != null) { ... }
    else if (key == VirtualKey.Down) { OnSearchBoxDownKeyPressed(); }
}

Minor issues

  • protected virtual OnSearchBoxDownKeyPressed (MainViewModel.cs:211): MainViewModel has no subclasses; use private to match the CharacterReceived invoker pattern.
  • Duplicate mason.md entry: the new 2026-07-13 entry describes the same pattern as the existing 2026-05-11 entry word-for-word.

What works correctly

  • The SearchBoxDownKeyPressed event + code-behind subscription pattern is clean and consistent with SearchFocusRequested.
  • The event unsubscription in MainWindow_Closed is correctly added.
  • The attached property's subscribe/unsubscribe wiring in OnKeyDownCommandPropertyChanged correctly avoids double-subscription.

@jules please address the issues noted above

I have updated the code to address the blocking issues:

  1. Changed SearchBoxKeyDown to accept Windows.System.VirtualKey instead of KeyRoutedEventArgs, making it fully platform-agnostic.
  2. Kept the e.Handled = true setting in the UIElementExtensions code-behind where the event is handled, and only applied it to Enter and Down keys to prevent swallowing regular keystrokes.
  3. Updated KeyDownCommand attached property to extract e.Key and pass the enum directly into the command execution.
    I also addressed the minor issues by using private instead of protected virtual for OnSearchBoxDownKeyPressed() and removed the duplicate markdown log in .jules/mason.md.

@github-actions github-actions Bot removed the documentation Improvements or additions to READMEs, wikis, or inline code comments. label Jul 13, 2026
Comment thread Helpers/UIElementExtensions.cs Outdated
@mikekthx

Copy link
Copy Markdown
Owner

Review Summary — PR #434: Refactor SearchBox KeyDown handling

What the PR does

Moves SearchBox KeyDown handling from code-behind into the ViewModel. A new KeyDownCommand attached property on UIElementExtensions subscribes to the KeyDown event, extracts the VirtualKey, and dispatches it to a new [RelayCommand] on MainViewModel. The Down-arrow branch now fires a SearchBoxDownKeyPressed event that the View handles to set focus — matching the existing SearchFocusRequested pattern. The Enter-to-launch branch stays in the ViewModel.

Thread resolution

All 5 prior threads have been resolved in this push:

Thread Issue Status
Blocking — WinUI type leaks into ViewModel ViewModel now accepts Windows.System.VirtualKey (plain enum, fully testable) ✅ Resolved
Blocking — e.Handled mutated inside ViewModel e.Handled = true is now set in the attached property's handler ✅ Resolved
protected virtual on OnSearchBoxDownKeyPressed Changed to private, matching the CharacterReceived pattern ✅ Resolved
Attached property hardcodes KeyRoutedEventArgs as parameter Attached property now passes e.Key (VirtualKey) to both CanExecute and Execute ✅ Resolved
mason.md duplicate entry Duplicate entry is absent from current file ✅ Resolved

Overall assessment

The approach is correct and the architecture is now properly layered: the ViewModel is platform-agnostic (testable), e.Handled lives in the UI layer, and the focus side-effect is handled via the event pattern already established in this codebase. Two things still worth addressing:

1. CanExecute / key filter ordering (Helpers/UIElementExtensions.cs, flagged inline)
CanExecute(e.Key) is currently called on every keypress before checking whether the key is Enter or Down. The key filter should come first so CanExecute is only invoked for keys the handler actually cares about. See the inline suggestion.

2. No tests added
The stated motivation for the refactor is improving testability — the new SearchBoxKeyDownCommand now accepts a plain VirtualKey, making it straightforward to unit-test. MainViewModel is already file-linked in Launchbox.Tests. A small test class covering the three branches (Enter + item selected → launch called; Enter + no item → no-op; Down → SearchBoxDownKeyPressed fires) would complete the work.

3. Stale class-level XML doc comment (Helpers/UIElementExtensions.cs, lines 11–15)
The existing summary only mentions Tapped routing. Now that KeyDown is handled too, the comment should be updated or removed (per CLAUDE.md: /// is reserved for members whose contract isn't obvious from the name; UIElementExtensions is fairly self-describing).

Items 1 and 3 are minor; item 2 is a natural follow-up given the PR's explicit testability goal.

@jules please address the issues noted above

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Review Summary — PR #434: Refactor SearchBox KeyDown handling

What the PR does

Moves SearchBox KeyDown handling from code-behind into the ViewModel. A new KeyDownCommand attached property on UIElementExtensions subscribes to the KeyDown event, extracts the VirtualKey, and dispatches it to a new [RelayCommand] on MainViewModel. The Down-arrow branch now fires a SearchBoxDownKeyPressed event that the View handles to set focus — matching the existing SearchFocusRequested pattern. The Enter-to-launch branch stays in the ViewModel.

Thread resolution

All 5 prior threads have been resolved in this push:

Thread Issue Status
Blocking — WinUI type leaks into ViewModel ViewModel now accepts Windows.System.VirtualKey (plain enum, fully testable) ✅ Resolved
Blocking — e.Handled mutated inside ViewModel e.Handled = true is now set in the attached property's handler ✅ Resolved
protected virtual on OnSearchBoxDownKeyPressed Changed to private, matching the CharacterReceived pattern ✅ Resolved
Attached property hardcodes KeyRoutedEventArgs as parameter Attached property now passes e.Key (VirtualKey) to both CanExecute and Execute ✅ Resolved
mason.md duplicate entry Duplicate entry is absent from current file ✅ Resolved

Overall assessment

The approach is correct and the architecture is now properly layered: the ViewModel is platform-agnostic (testable), e.Handled lives in the UI layer, and the focus side-effect is handled via the event pattern already established in this codebase. Two things still worth addressing:

1. CanExecute / key filter ordering (Helpers/UIElementExtensions.cs, flagged inline)
CanExecute(e.Key) is currently called on every keypress before checking whether the key is Enter or Down. The key filter should come first so CanExecute is only invoked for keys the handler actually cares about. See the inline suggestion.

2. No tests added
The stated motivation for the refactor is improving testability — the new SearchBoxKeyDownCommand now accepts a plain VirtualKey, making it straightforward to unit-test. MainViewModel is already file-linked in Launchbox.Tests. A small test class covering the three branches (Enter + item selected → launch called; Enter + no item → no-op; Down → SearchBoxDownKeyPressed fires) would complete the work.

3. Stale class-level XML doc comment (Helpers/UIElementExtensions.cs, lines 11–15)
The existing summary only mentions Tapped routing. Now that KeyDown is handled too, the comment should be updated or removed (per CLAUDE.md: /// is reserved for members whose contract isn't obvious from the name; UIElementExtensions is fairly self-describing).

Items 1 and 3 are minor; item 2 is a natural follow-up given the PR's explicit testability goal.

@jules please address the issues noted above

I have addressed the feedback:

  1. Fixed the CanExecute / key filter ordering in UIElementExtensions.cs so the key filter comes first, and CanExecute is only evaluated if it's a key we care about.
  2. Added MainViewModelInputTests.cs to test the three paths of the new SearchBoxKeyDownCommand (Enter with selection, Enter without selection, Down key).
  3. Cleaned up the stale class-level XML doc comment in UIElementExtensions.cs.

@github-actions github-actions Bot added the tests Adding or updating unit testing and integration testing. label Jul 13, 2026
Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
Comment thread Helpers/UIElementExtensions.cs
@mikekthx

Copy link
Copy Markdown
Owner

Review — Push 2 (synchronize)

What was resolved

All five previously-blocking threads are now addressed in this push:

Thread Concern Status
WinUI type leak in ViewModel KeyRoutedEventArgs replaced with Windows.System.VirtualKey (plain enum) ✅ Resolved
e.Handled set inside ViewModel Now set in Element_KeyDown in the attached property layer ✅ Resolved
protected virtual invoker method Changed to private to match OnSearchFocusRequested pattern ✅ Resolved
command.Execute(e) passing raw args Now passes e.Key only ✅ Resolved
Key filter / CanExecute ordering Key guard (Enter || Down) now runs before CanExecute ✅ Resolved (thread also resolved)

The architecture is now sound: the ViewModel is WinUI-free and fully testable, e.Handled lives in the UI layer, and the SearchBoxDownKeyPressed event correctly delegates focus work back to code-behind.


Remaining issues

Blocking

SearchBoxKeyDown_EnterWithSelectedItem_LaunchesApp is a broken test.
MockShortcutService ignores MockFileSystem — it has its own internal _files array that is null until SetFiles(...) is called. The _fileSystem.AddFile(...) call on line 50 is dead code; LoadAppsAsync() will see no files, FilteredApps will be empty, and FilteredApps[0] will throw IndexOutOfRangeException. The fix is to call _shortcutService.SetFiles(new[] { Path.Combine(_shortcutFolder, "Alpha.lnk") }) instead. See inline comment on line 50.


Non-blocking

  • Test fields not readonly — all fields are set only in the constructor; they should be readonly. Inline comment on line 13.
  • Redundant Xunit.Assert. qualificationusing Xunit; is already imported; the three Xunit.Assert.* calls throughout the file should be plain Assert.*. Inline comment on line 58.
  • e.Handled now unconditionally set for Enter — In the attached property, command.CanExecute(e.Key) always returns true (no predicate on the RelayCommand<VirtualKey>), so e.Handled is set for every Enter keypress in the SearchBox, even with no selected item. The original code-behind only set it when a launch was triggered. Benign in practice for a single-line TextBox, but worth acknowledging. Inline comment on line 115.

@jules please address the issues noted above

@mikekthx

Copy link
Copy Markdown
Owner

Code Review — Synchronize Update

What This PR Does

Moves SearchBox KeyDown handling out of the code-behind and into MainViewModel. An attached property (UIElementExtensions.KeyDownCommand) routes the event, passing only the VirtualKey enum to the ViewModel command. Focus management (Down key → grid) stays in the View via a new SearchBoxDownKeyPressed event. Three unit tests cover the new command.


Thread Resolution Summary

The previous push addressed all six previously-raised threads correctly:

Thread Concern Status
WinUI type leak — KeyRoutedEventArgs in ViewModel Fixed: ViewModel now accepts Windows.System.VirtualKey ✅ Resolved
e.Handled = true mutated inside ViewModel Fixed: assignment moved to attached property ✅ Resolved
OnSearchBoxDownKeyPressed was protected virtual Fixed: now private ✅ Resolved
KeyRoutedEventArgs passed through KeyDownCommand Fixed: e.Key (VirtualKey) passed instead ✅ Resolved
Key filter ran after CanExecute Fixed: key check now guards CanExecute ✅ Resolved
.jules/mason.md duplicate entry Outdated; not re-raised ✅ Closed

Four threads remain unresolved — none of their concerns appear in the new diff:

  1. _fileSystem.AddFile vs _shortcutService.SetFilesMockShortcutService ignores MockFileSystem, so FilteredApps will be empty and the first test throws IndexOutOfRangeException at runtime.
  2. Xunit.Assert prefix is redundantusing Xunit; is already imported; all three assertions can drop the qualifier.
  3. Fields should be readonly — all ten fields are set only in the constructor.
  4. e.Handled now set for any matched key — minor behavioral delta from the original (originally gated on a successful launch); the inline thread notes this is likely benign but warrants an intentional decision.

Overall Assessment

The architectural direction is correct and the major blocking issues from the first review have been addressed. The remaining four unresolved threads are the only blockers before this is ready to merge — three of them are in the new test file and one is a minor comment-or-accept call in the attached property.

@jules please address the issues noted above

Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Review — Push 2 (synchronize)

What was resolved

All five previously-blocking threads are now addressed in this push:

Thread Concern Status
WinUI type leak in ViewModel KeyRoutedEventArgs replaced with Windows.System.VirtualKey (plain enum) ✅ Resolved
e.Handled set inside ViewModel Now set in Element_KeyDown in the attached property layer ✅ Resolved
protected virtual invoker method Changed to private to match OnSearchFocusRequested pattern ✅ Resolved
command.Execute(e) passing raw args Now passes e.Key only ✅ Resolved
Key filter / CanExecute ordering Key guard (Enter || Down) now runs before CanExecute ✅ Resolved (thread also resolved)

The architecture is now sound: the ViewModel is WinUI-free and fully testable, e.Handled lives in the UI layer, and the SearchBoxDownKeyPressed event correctly delegates focus work back to code-behind.


Remaining issues

Blocking

SearchBoxKeyDown_EnterWithSelectedItem_LaunchesApp is a broken test.
MockShortcutService ignores MockFileSystem — it has its own internal _files array that is null until SetFiles(...) is called. The _fileSystem.AddFile(...) call on line 50 is dead code; LoadAppsAsync() will see no files, FilteredApps will be empty, and FilteredApps[0] will throw IndexOutOfRangeException. The fix is to call _shortcutService.SetFiles(new[] { Path.Combine(_shortcutFolder, "Alpha.lnk") }) instead. See inline comment on line 50.


Non-blocking

  • Test fields not readonly — all fields are set only in the constructor; they should be readonly. Inline comment on line 13.
  • Redundant Xunit.Assert. qualificationusing Xunit; is already imported; the three Xunit.Assert.* calls throughout the file should be plain Assert.*. Inline comment on line 58.
  • e.Handled now unconditionally set for Enter — In the attached property, command.CanExecute(e.Key) always returns true (no predicate on the RelayCommand<VirtualKey>), so e.Handled is set for every Enter keypress in the SearchBox, even with no selected item. The original code-behind only set it when a launch was triggered. Benign in practice for a single-line TextBox, but worth acknowledging. Inline comment on line 115.

@jules please address the issues noted above

I have addressed the remaining issues:

  1. Fixed the broken SearchBoxKeyDown_EnterWithSelectedItem_LaunchesApp test by calling _shortcutService.SetFiles instead of _fileSystem.AddFile.
  2. Made all fields in MainViewModelInputTests readonly.
  3. Removed the redundant Xunit. prefixes on assertions.
  4. Added a comment in UIElementExtensions acknowledging the unconditional setting of e.Handled for Enter.

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Code Review — Synchronize Update

What This PR Does

Moves SearchBox KeyDown handling out of the code-behind and into MainViewModel. An attached property (UIElementExtensions.KeyDownCommand) routes the event, passing only the VirtualKey enum to the ViewModel command. Focus management (Down key → grid) stays in the View via a new SearchBoxDownKeyPressed event. Three unit tests cover the new command.


Thread Resolution Summary

The previous push addressed all six previously-raised threads correctly:

Thread Concern Status
WinUI type leak — KeyRoutedEventArgs in ViewModel Fixed: ViewModel now accepts Windows.System.VirtualKey ✅ Resolved
e.Handled = true mutated inside ViewModel Fixed: assignment moved to attached property ✅ Resolved
OnSearchBoxDownKeyPressed was protected virtual Fixed: now private ✅ Resolved
KeyRoutedEventArgs passed through KeyDownCommand Fixed: e.Key (VirtualKey) passed instead ✅ Resolved
Key filter ran after CanExecute Fixed: key check now guards CanExecute ✅ Resolved
.jules/mason.md duplicate entry Outdated; not re-raised ✅ Closed

Four threads remain unresolved — none of their concerns appear in the new diff:

  1. _fileSystem.AddFile vs _shortcutService.SetFilesMockShortcutService ignores MockFileSystem, so FilteredApps will be empty and the first test throws IndexOutOfRangeException at runtime.
  2. Xunit.Assert prefix is redundantusing Xunit; is already imported; all three assertions can drop the qualifier.
  3. Fields should be readonly — all ten fields are set only in the constructor.
  4. e.Handled now set for any matched key — minor behavioral delta from the original (originally gated on a successful launch); the inline thread notes this is likely benign but warrants an intentional decision.

Overall Assessment

The architectural direction is correct and the major blocking issues from the first review have been addressed. The remaining four unresolved threads are the only blockers before this is ready to merge — three of them are in the new test file and one is a minor comment-or-accept call in the attached property.

@jules please address the issues noted above

I have addressed the 4 remaining issues by updating MainViewModelInputTests and UIElementExtensions.

@mikekthx

Copy link
Copy Markdown
Owner

Review summary — round 2 (synchronize)

What this PR does: Moves SearchBox KeyDown handling out of code-behind into the ViewModel. A new KeyDownCommand attached property in UIElementExtensions intercepts KeyDown events, extracts the VirtualKey, and routes it to MainViewModel.SearchBoxKeyDownCommand. The Down-key focus shift stays in the View via a new SearchBoxDownKeyPressed event, keeping MainViewModel platform-agnostic and fully testable.

Overall assessment: The round-1 feedback has been addressed well. All six blocking/significant issues — WinUI type in ViewModel, e.Handled in ViewModel, access modifier, key-filter ordering, SetFiles vs AddFile, and readonly fields — are fixed in this push. The implementation is clean and consistent with the existing TappedCommand pattern.


Thread resolution

Thread Concern Status
QWsRC SetFiles vs AddFile in launch test ✅ Resolved — uses SetFiles
QWsXc Xunit.Assert.EqualAssert.Equal ✅ Resolved — fixed
QWsiy readonly missing on test fields ✅ Resolved — all fields are readonly
QWsru e.Handled set even when Enter does nothing ✅ Resolved — intent documented with a comment; acknowledged as benign

One remaining open thread

QW2e_ (MainViewModelInputTests.cs line 39) — fully-qualified Launchbox.Services.ShortcutFolderManager is still present:

// current
_settingsService = new SettingsService(_settingsStore, new MockStartupService(), new Launchbox.Services.ShortcutFolderManager(_settingsStore));

// expected — using Launchbox.Services; is already imported
_settingsService = new SettingsService(_settingsStore, new MockStartupService(), new ShortcutFolderManager(_settingsStore));

This is the only outstanding item.


@jules please address the issues noted above

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Review summary — round 2 (synchronize)

What this PR does: Moves SearchBox KeyDown handling out of code-behind into the ViewModel. A new KeyDownCommand attached property in UIElementExtensions intercepts KeyDown events, extracts the VirtualKey, and routes it to MainViewModel.SearchBoxKeyDownCommand. The Down-key focus shift stays in the View via a new SearchBoxDownKeyPressed event, keeping MainViewModel platform-agnostic and fully testable.

Overall assessment: The round-1 feedback has been addressed well. All six blocking/significant issues — WinUI type in ViewModel, e.Handled in ViewModel, access modifier, key-filter ordering, SetFiles vs AddFile, and readonly fields — are fixed in this push. The implementation is clean and consistent with the existing TappedCommand pattern.


Thread resolution

Thread Concern Status
QWsRC SetFiles vs AddFile in launch test ✅ Resolved — uses SetFiles
QWsXc Xunit.Assert.EqualAssert.Equal ✅ Resolved — fixed
QWsiy readonly missing on test fields ✅ Resolved — all fields are readonly
QWsru e.Handled set even when Enter does nothing ✅ Resolved — intent documented with a comment; acknowledged as benign

One remaining open thread

QW2e_ (MainViewModelInputTests.cs line 39) — fully-qualified Launchbox.Services.ShortcutFolderManager is still present:

// current
_settingsService = new SettingsService(_settingsStore, new MockStartupService(), new Launchbox.Services.ShortcutFolderManager(_settingsStore));

// expected — using Launchbox.Services; is already imported
_settingsService = new SettingsService(_settingsStore, new MockStartupService(), new ShortcutFolderManager(_settingsStore));

This is the only outstanding item.


@jules please address the issues noted above

I have removed the fully-qualified Launchbox.Services. from ShortcutFolderManager as requested.

Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
@mikekthx

Copy link
Copy Markdown
Owner

Review — Round 2 (synchronize)

What this PR does

Moves SearchBox KeyDown handling out of code-behind and into MainViewModel via a new KeyDownCommand attached property in UIElementExtensions. The ViewModel gains a SearchBoxKeyDownCommand (RelayCommand) and a SearchBoxDownKeyPressed event; code-behind subscribes to the event to call Focus() on the active grid, which requires WinUI APIs and rightly stays in the View layer. Three unit tests cover Enter-with-selection, Enter-with-no-selection, and Down key.


Thread resolution

All 9 previously blocking/significant threads from round 1 are now resolved (marked outdated or already closed). Summary of what was fixed:

Thread Concern Verdict
WinUI type in ViewModel KeyRoutedEventArgs -> now VirtualKey (plain enum) Fixed
e.Handled in ViewModel Moved to attached property Fixed
protected virtual OnSearchBoxDownKeyPressed Changed to private Fixed
Key filter before CanExecute Filter now runs first Fixed
Attached property passed raw event args Now passes e.Key Fixed
.jules/mason.md duplicate entry Not in current diff N/A
MockShortcutService.SetFiles Correctly used Fixed
Xunit.Assert fully-qualified Now Assert.* Fixed
Fields not readonly All fields are readonly Fixed

One outdated-but-unresolved thread (SettingsService fully-qualified prefix) was also resolved — the current code already uses new SettingsService(...) unqualified, exactly as the suggestion required.


Remaining open thread

PRRT_kwDORLRSls6QWsru (non-blocking) — e.Handled = true in Element_KeyDown is now set for any matched Enter/Down key regardless of whether the ViewModel command actually did anything (e.g., Enter with no selected item). The original code-behind only marked Handled when a launch was triggered. A comment was added acknowledging this, which is reasonable given that WinUI 3's TextBox has no default Enter action to suppress. The thread remains open for the PR author to decide whether to accept the behavior-as-documented or tighten the condition.


New finding

One minor style issue flagged as an inline comment:

  • System.Text.Json.JsonSerializer fully-qualified (line 38 of MainViewModelInputTests.cs) — add using System.Text.Json; and use the short form JsonSerializer.Serialize(...) to match the import style used elsewhere in the file.

Overall assessment

The architecture is now clean and conformant. The ViewModel is genuinely platform-agnostic for keyboard handling, tests are correctly structured and use the existing mock infrastructure, and Launchbox.Tests.csproj requires no new links (no new production classes were added — MainViewModel.cs was already linked). One minor style fix remains plus the open e.Handled discussion thread. No blocking issues.

@jules please address the issues noted above

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Review — Round 2 (synchronize)

What this PR does

Moves SearchBox KeyDown handling out of code-behind and into MainViewModel via a new KeyDownCommand attached property in UIElementExtensions. The ViewModel gains a SearchBoxKeyDownCommand (RelayCommand) and a SearchBoxDownKeyPressed event; code-behind subscribes to the event to call Focus() on the active grid, which requires WinUI APIs and rightly stays in the View layer. Three unit tests cover Enter-with-selection, Enter-with-no-selection, and Down key.


Thread resolution

All 9 previously blocking/significant threads from round 1 are now resolved (marked outdated or already closed). Summary of what was fixed:

Thread Concern Verdict
WinUI type in ViewModel KeyRoutedEventArgs -> now VirtualKey (plain enum) Fixed
e.Handled in ViewModel Moved to attached property Fixed
protected virtual OnSearchBoxDownKeyPressed Changed to private Fixed
Key filter before CanExecute Filter now runs first Fixed
Attached property passed raw event args Now passes e.Key Fixed
.jules/mason.md duplicate entry Not in current diff N/A
MockShortcutService.SetFiles Correctly used Fixed
Xunit.Assert fully-qualified Now Assert.* Fixed
Fields not readonly All fields are readonly Fixed

One outdated-but-unresolved thread (SettingsService fully-qualified prefix) was also resolved — the current code already uses new SettingsService(...) unqualified, exactly as the suggestion required.


Remaining open thread

PRRT_kwDORLRSls6QWsru (non-blocking) — e.Handled = true in Element_KeyDown is now set for any matched Enter/Down key regardless of whether the ViewModel command actually did anything (e.g., Enter with no selected item). The original code-behind only marked Handled when a launch was triggered. A comment was added acknowledging this, which is reasonable given that WinUI 3's TextBox has no default Enter action to suppress. The thread remains open for the PR author to decide whether to accept the behavior-as-documented or tighten the condition.


New finding

One minor style issue flagged as an inline comment:

  • System.Text.Json.JsonSerializer fully-qualified (line 38 of MainViewModelInputTests.cs) — add using System.Text.Json; and use the short form JsonSerializer.Serialize(...) to match the import style used elsewhere in the file.

Overall assessment

The architecture is now clean and conformant. The ViewModel is genuinely platform-agnostic for keyboard handling, tests are correctly structured and use the existing mock infrastructure, and Launchbox.Tests.csproj requires no new links (no new production classes were added — MainViewModel.cs was already linked). One minor style fix remains plus the open e.Handled discussion thread. No blocking issues.

@jules please address the issues noted above

I have added the using System.Text.Json; statement and replaced the fully qualified System.Text.Json.JsonSerializer.Serialize with JsonSerializer.Serialize to fix the style issue.

Comment thread Launchbox.Tests/MainViewModelInputTests.cs Outdated
@mikekthx

Copy link
Copy Markdown
Owner

Review — PR #434: Refactor SearchBox KeyDown handling

What this PR does

Moves SearchBox_KeyDown from code-behind into the ViewModel via a new KeyDownCommand attached property on UIElementExtensions. The attached property filters for Enter/Down, passes VirtualKey (a plain enum) to SearchBoxKeyDownCommand, and sets e.Handled in the UI layer. A new SearchBoxDownKeyPressed event lets the ViewModel signal focus management back to the window without referencing WinUI types.


Thread resolution

All previously blocking threads are now resolved. One outdated thread (System.Text.Json import) remained unresolved in GitHub's state but was fixed in the latest commit — I've resolved it above.

Thread Status
WinUI type leak (KeyRoutedEventArgs) into ViewModel ✅ Fixed — ViewModel now accepts VirtualKey
e.Handled mutated inside ViewModel ✅ Fixed — set in attached property only
protected virtual on OnSearchBoxDownKeyPressed ✅ Fixed — private
CanExecute guard before key filter ✅ Fixed — key filter now runs first
MockShortcutService.AddFile vs SetFiles in test ✅ Fixed
Xunit.Assert fully-qualified prefix ✅ Fixed
Non-readonly fields ✅ Fixed
Fully-qualified Launchbox.Services.SettingsService ✅ Fixed
System.Text.Json not imported (outdated thread) ✅ Fixed — using System.Text.Json; present

Current assessment

The architecture is sound: the ViewModel is genuinely platform-agnostic (accepts Windows.System.VirtualKey, a plain enum), e.Handled stays in the UI layer, the Down key focus side-effect is correctly routed back via an event, and event subscriptions are properly cleaned up in MainWindow_Closed.

One remaining style issue: the test file uses new[] { ... } array initializers on lines 39 and 51. CLAUDE.md mandates C# 12 collection expressions ([...]). Flagged inline above.


@jules please address the issues noted above

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

Review — PR #434: Refactor SearchBox KeyDown handling

What this PR does

Moves SearchBox_KeyDown from code-behind into the ViewModel via a new KeyDownCommand attached property on UIElementExtensions. The attached property filters for Enter/Down, passes VirtualKey (a plain enum) to SearchBoxKeyDownCommand, and sets e.Handled in the UI layer. A new SearchBoxDownKeyPressed event lets the ViewModel signal focus management back to the window without referencing WinUI types.


Thread resolution

All previously blocking threads are now resolved. One outdated thread (System.Text.Json import) remained unresolved in GitHub's state but was fixed in the latest commit — I've resolved it above.

Thread Status
WinUI type leak (KeyRoutedEventArgs) into ViewModel ✅ Fixed — ViewModel now accepts VirtualKey
e.Handled mutated inside ViewModel ✅ Fixed — set in attached property only
protected virtual on OnSearchBoxDownKeyPressed ✅ Fixed — private
CanExecute guard before key filter ✅ Fixed — key filter now runs first
MockShortcutService.AddFile vs SetFiles in test ✅ Fixed
Xunit.Assert fully-qualified prefix ✅ Fixed
Non-readonly fields ✅ Fixed
Fully-qualified Launchbox.Services.SettingsService ✅ Fixed
System.Text.Json not imported (outdated thread) ✅ Fixed — using System.Text.Json; present

Current assessment

The architecture is sound: the ViewModel is genuinely platform-agnostic (accepts Windows.System.VirtualKey, a plain enum), e.Handled stays in the UI layer, the Down key focus side-effect is correctly routed back via an event, and event subscriptions are properly cleaned up in MainWindow_Closed.

One remaining style issue: the test file uses new[] { ... } array initializers on lines 39 and 51. CLAUDE.md mandates C# 12 collection expressions ([...]). Flagged inline above.


@jules please address the issues noted above

I have replaced the new[] { ... } array initializers with C# 12 collection expressions [...] as requested.

@mikekthx

Copy link
Copy Markdown
Owner

Thread resolution summary

Resolved: The one remaining open thread (C# 12 collection expressions — new[] { ... }[...]) was already fixed in the latest commits. Both occurrences in MainViewModelInputTests.cs use [...] correctly. Thread resolved as outdated/addressed.

All other threads from previous review rounds were already marked resolved.


Review of current diff

What this PR does: Moves SearchBox KeyDown handling out of code-behind into the ViewModel. An attached property (UIElementExtensions.KeyDownCommand) extracts VirtualKey from the event and passes it to a [RelayCommand]-generated SearchBoxKeyDownCommand. Focus-transfer on Down is communicated back to the view via a new SearchBoxDownKeyPressed event, preserving the MVVM boundary.

Overall assessment: Approved. The implementation is clean and all previous blocking issues have been addressed. Specific changes look correct:

  • Element_KeyDown in the attached property: key filter runs first, CanExecute checked before Execute, e.Handled = true stays in the UI layer — exactly the right layering.
  • SearchBoxKeyDown in the ViewModel accepts Windows.System.VirtualKey (a plain enum) — fully testable without WinUI infrastructure.
  • OnSearchBoxDownKeyPressed() is correctly private, matching the CharacterReceived pattern.
  • Event subscription/unsubscription in MainWindow.xaml.cs is symmetric (constructor and Closed handler).
  • {x:Bind ViewModel.SearchBoxKeyDownCommand} is used correctly on an in-tree element.
  • Three tests cover the launch, no-op, and Down-focus paths. Fields are readonly, collection expressions use C# 12 [...], Assert.Equal is unqualified.

No new issues found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core-logic Changes to primary application logic, backend services, or models. tests Adding or updating unit testing and integration testing. ui Front-end changes, WinUI layouts, styling (e.g., Acrylic), or system tray updates.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant