using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.VisualTree; using ClaudeDo.Ui.Views; namespace ClaudeDo.Ui.Services; /// /// Clears focus from a TextBox when the user clicks outside of any text box, so input /// fields behave like the user expects. Registered once for every window in the app. /// Escape does the same, but only in the main window: modal windows bind Escape to closing /// themselves, and Mission Control's ConPTY tiles need Escape to reach the terminal, so this /// intentionally does not use a generic TopLevel handler. /// public static class FocusClearing { public static void Install() { InputElement.PointerPressedEvent.AddClassHandler( OnPointerPressed, RoutingStrategies.Tunnel, handledEventsToo: true); InputElement.KeyDownEvent.AddClassHandler( OnKeyDown, RoutingStrategies.Tunnel); } private static void OnPointerPressed(TopLevel topLevel, PointerPressedEventArgs e) { if (topLevel.FocusManager is not { } focusManager) return; if (focusManager.GetFocusedElement() is not TextBox) return; if (e.Source is Visual v && v.FindAncestorOfType(includeSelf: true) is not null) return; focusManager.Focus(null); } private static void OnKeyDown(MainWindow window, KeyEventArgs e) { if (e.Key != Key.Escape) return; if (window.FocusManager is not { } focusManager) return; if (focusManager.GetFocusedElement() is not TextBox) return; focusManager.Focus(null); e.Handled = true; } }