Let a tap on the phone's host list mean connect

Choosing a machine raised the connect bar over the bottom of the list: a
password box, CONNECT, EDIT, MOVE and DELETE. Five controls in the way of the
one thing a tap on a machine's name obviously means.

So the gestures split. A tap connects. A long press raises the bar, with all
five. The pencil in the phone's header — its only persistent chrome — edits
whichever host is chosen, which is the one of the five common enough to be worth
a control that is always in the same place.

The flag doing it is the desktop's own IsHostPaneOpen rather than a second one.
That head made exactly this move when a selection stopped opening its drawer, and
the question both are asking is "has somebody asked about this host" — answering
it twice is how two heads come to disagree about what a selection means.

One tap cannot finish: a host that authenticates with a typed password has
nowhere on a list to be given one. That tap raises the bar with the box in it and
says so, and a second tap with the box filled in connects. The branch is in the
view model rather than in the head, because "can this machine be reached without
asking for anything" is the same question the bar's own password box answers, and
a copy of it in a view would be a second reading of a binding chain that has one.

Two mechanics worth knowing. Avalonia raises Tapped on release whatever the press
lasted, so a long press would open the bar and then connect — one touch firing
both gestures — which is why HostsScreen tracks the hold and swallows the tap it
precedes. And Holding only fires once IsHoldingEnabled is set, so that and the
handler are attached together rather than one in markup and one in code.

ConnectToRecent now opens the pane rather than selecting the row. On the phone it
has to: a selection alone raises nothing now, so going back to a recent machine
would land on a screen with nothing to press.
This commit is contained in:
2026-08-05 08:43:33 +02:00
parent 69bc9e270b
commit 746711da9d
8 changed files with 412 additions and 26 deletions
@@ -31,6 +31,23 @@
Both editors are cards in the list's own row, swapped for the list rather than stacked over it, which is
what SnippetsScreen does and for the reason written there: a form on top of the list hides what it is
about. There is no dialog and no editor screen anywhere on this head.
── ◆ v4: A TAP CONNECTS, AND THE BAR IS WHAT A LONG PRESS IS FOR. ───────────────────────────────────────
Choosing a row used to raise the connect bar over the bottom of the list — a password box, CONNECT, EDIT,
MOVE and DELETE, five controls in the way of the one thing a tap on a machine's name means. So the
gestures split, and each one now does what it looks like it does:
· a tap connects, and the bar never appears;
· a long press asks about the host: the bar comes up, with all five;
· the pencil in the phone's header edits whatever is chosen, which is the one action common enough to
be worth a control that is always in the same place.
The one case a tap cannot finish is a host that authenticates with a typed password, since there is
nowhere to type it. That tap raises the bar with the box in it and says so — see
VaultViewModel.ConnectToRowAsync, which is where the branch lives and why it is not in this head.
It is the same move the desktop grid made when a selection stopped opening its drawer, and it reuses that
head's flag rather than inventing a second one; see VaultViewModel.IsHostPaneOpen.
-->
<!--
@@ -180,9 +197,20 @@
</StackPanel>
<!-- ============ the list ============ -->
<ListBox Grid.Row="2" Margin="0,6,0,0" IsVisible="{Binding !AnEditorIsOpen}"
<!--
◆ Named, because the long press is attached from code-behind: the gesture only fires at all once
IsHoldingEnabled is on, and setting the attached property and the handler together is what keeps those
two from drifting apart. Tapped stays here, beside the bindings it is about.
Both are on the list rather than on the row, and the row stays a plain Grid rather than becoming a
Button, for the reason FilesScreen writes out: a button as the item template swallows the press before
the list sees it, so nothing is ever selected and every control that reads the selection stops working.
Both handlers fire after the list has moved its selection, which is what lets them read it.
-->
<ListBox x:Name="Rows" Grid.Row="2" Margin="0,6,0,0" IsVisible="{Binding !AnEditorIsOpen}"
ItemsSource="{Binding SidebarRows}"
SelectedItem="{Binding SelectedSidebarRow}"
Tapped="OnRowTapped"
Background="Transparent" BorderThickness="0">
<ListBox.Styles>
@@ -548,8 +576,10 @@
<!-- ============ connect ============ -->
<!--
Raised over the list when a host is chosen, because the desktop's right-hand column has nowhere to go
at this width. It names the host: the selection that is obvious on a wide window is not obvious here.
Raised by a long press on a row, not by choosing one. A tap connects — see the v4 note at the top of
this file — so what is left in here is everything a tap cannot be: the password for a host that wants
one, and EDIT, MOVE and DELETE. It names the host, because the selection that is obvious on a wide
window is not obvious under a thumb.
Gone entirely while an editor is up, rather than merely greyed. The editor replaces the list above it,
so a bar left in place would be a set of buttons about a host that is no longer on screen — and the