Public Access
Merge branch 'claude/sleepy-chebyshev-cda68d'
This commit is contained in:
+41
-6
@@ -53,7 +53,8 @@ shell that sliced the setup
|
||||
and unlock cards at the terminal column's left edge, put every one of their buttons inside the WebView's
|
||||
rectangle at the window's default width — so the flow could only be completed by keyboard — and handed
|
||||
Win32 focus to WebView2 on any click in that region, which makes a text box stop accepting keystrokes with
|
||||
no visible cause.
|
||||
no visible cause. That last symptom is the focus asymmetry documented further down, not a separate fault:
|
||||
focus crosses into the WebView readily and does not come back on its own.
|
||||
|
||||
The fix is to collapse the control, not to cover it: `IsVisible="{Binding IsUnlocked}"` on the
|
||||
`NativeWebView`. That is safe, and this is the part worth recording, because the opposite was asserted here
|
||||
@@ -108,11 +109,45 @@ Related and not yet addressed: the conflict log above the terminal is an `ItemsC
|
||||
`ScrollViewer` and no `MaxHeight` on an `Auto` row, so enough conflicts squeeze the terminal row toward
|
||||
nothing.
|
||||
|
||||
**Nothing hands the terminal keyboard focus after connecting.** The page calls `term.focus()`, which focuses
|
||||
the textarea inside the document, but Avalonia's focus is still on the Connect button — so the first
|
||||
keystrokes after a successful connect go to the shell's UI, not to the remote shell. Click inside the
|
||||
terminal first. This is a focus-plumbing gap between Avalonia and the native child window, not a terminal
|
||||
bug.
|
||||
**Keyboard focus crosses into the WebView by itself and does not come back.** This is the asymmetry to
|
||||
know; the connect-focus bug that led here was only its first symptom. Measured on Windows with a harness
|
||||
that reports `GetFocus()`, the class name of the window holding it, and the page's own
|
||||
`document.hasFocus()` at each step.
|
||||
|
||||
- **Into the page: nothing custom is needed.** `NativeWebView` overrides `Focusable` to true and its
|
||||
`OnGotFocus` calls the adapter's `Focus()`, which on Windows is
|
||||
`ICoreWebView2Controller::MoveFocus(PROGRAMMATIC)`. A plain Avalonia `Terminal.Focus()` therefore moves
|
||||
real Win32 focus to the `Chrome_WidgetWin_1` child and the page reports `hasFocus: true`. No `SetFocus`
|
||||
P/Invoke and no COM work — the package version of this entry that assumed otherwise was wrong. The
|
||||
control also replays a `Focus()` that arrived before its adapter existed, and re-asserts itself: while
|
||||
it holds Win32 focus its `GotFocus` handler pulls Avalonia's *logical* focus back onto the control. Worth
|
||||
stating positively, because the reasonable guess before measuring — that crossing into a child HWND must
|
||||
need `SetFocus` — is the wrong way round: it is the return trip that needs it.
|
||||
- **Out of the page: the package does nothing at all.** `OnLostFocus` calls the adapter's `ResignFocus()`,
|
||||
and on Windows that method is **empty**. So `someTextBox.Focus()` moves Avalonia's focused element while
|
||||
Win32 focus stays on WebView2: a text box with a caret that silently receives nothing. `Window.Activate()`
|
||||
and `Window.Focus()` were both measured and neither recovers it. The hand-back has to be
|
||||
`SetFocus(topLevelHwnd)` — see `Views/NativeKeyboardFocus.cs`. A real mouse click *does* recover it,
|
||||
because Avalonia's window sets focus on pointer input, which is exactly why this is invisible to anyone
|
||||
who clicks before typing.
|
||||
- **Collapsing the control does not release the keyboard.** With `IsVisible=false` the holder window is
|
||||
hidden but Win32 focus stays on it — measured as focus held by a window reporting `visible=False`, with
|
||||
Avalonia's focused element becoming `(none)`. So locking the vault after touching the terminal left the
|
||||
unlock passphrase box eating keystrokes. The lock path now hands the keyboard back and focuses that box.
|
||||
- **`Focus()` on a collapsed control is a no-op and is not replayed on reveal.** Order matters: reveal,
|
||||
then focus. Focus does survive a lock/unlock cycle when done that way.
|
||||
- **There is no Tab-out.** The package subscribes `ICoreWebView2Controller::add_MoveFocusRequested` and its
|
||||
handler body is empty, so WebView2's request to move focus off itself is discarded; xterm eats Tab
|
||||
anyway. The way out is `Ctrl+Shift+F6`, intercepted in `terminal.js` and sent to the host as a web
|
||||
message — measured arriving verbatim in `WebMessageReceivedEventArgs.Body`. It has to be handled in the
|
||||
page, because once the child window owns Win32 focus Avalonia sees no key events and no `KeyBinding`
|
||||
could fire. Not Escape, and not a bare F6: both are keys a TUI legitimately binds, and Ctrl+Shift is the
|
||||
range terminal emulators conventionally keep for themselves.
|
||||
|
||||
None of this is covered by a test, and cannot be here: headless Avalonia has no native window, so a
|
||||
headless test renders and focuses correctly and would confirm the wrong belief. What the suite covers is
|
||||
the plumbing that drives it — that connecting asks for focus once per session, that a failed connect does
|
||||
not, and that locking stops the forwarding.
|
||||
|
||||
**The Windows app manifest must declare a `supportedOS` list.** Without it the process reports a
|
||||
downlevel Windows version and Avalonia's native control host fails outright — *"Unable to create child
|
||||
|
||||
Reference in New Issue
Block a user