diff --git a/docs/manual-checks.md b/docs/manual-checks.md index 2c4467d..cc3a96a 100644 --- a/docs/manual-checks.md +++ b/docs/manual-checks.md @@ -269,33 +269,32 @@ single-process test can reach. Open the hosts screen without creating any group. -**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no headings -between the cards, no UNGROUPED, nothing saying the hosts are unfiled. +**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no chip on any +card, nothing saying the hosts are unfiled. -**Failure means:** the "invisible until used" property is gone, and every existing user gets a heading they -did not ask for. `RebuildSidebarRows` returns early when `Groups` is empty; that early return is the feature. +**Failure means:** the "invisible until used" property is gone, and every existing user gets a section they +did not ask for. `HasGroups` is what hides the row of group cards; `HostRowViewModel.HasGroup` hides the chip. -### 3.2 Filing hosts, and folding a heading +### 3.2 Filing hosts, and reading which group a card is in -Make two groups, file some hosts into each through the host editor, then click a heading. +Make two groups and file some hosts into each through the host editor. -**Pass:** the heading's chevron flips and its hosts disappear; the count on the heading does not change, -because it counts what is in the group rather than what is on screen. Clicking again brings them back. - -**Also check:** clicking a heading does not change which host is selected — the drawer stays open on the -same machine and its EDIT and DELETE go on acting on it. This is asserted in a test, but the test drives the -view model directly; what it cannot see is whether the `ListBox` writes something else back through the -binding first. +**Pass:** every filed card carries the group's name as an accent chip under its address; the cards stay one +flat wrap, in one order, with nothing between them. **There is no heading and no fold on the desktop** — the +headings, their chevrons and UNGROUPED are the phone's, whose list has no room for a row of group cards. **Then press a group card.** The grid narrows to that group's hosts, the card is marked as chosen, and SHOW ALL appears beside GROUPS. Pressing it brings the rest back and unmarks the card. +**Failure means:** a full-width bar with a chevron between the cards is the old grouping coming back through +`SidebarRows` — the desktop grid binds `VisibleHosts`. See `HostsScreen.axaml`. + ### 3.3 Deleting a group with hosts in it Select a group with hosts and press DELETE. **Pass:** the question names how many hosts are filed under it and says they stay. Agreeing removes the -group; the hosts reappear under UNGROUPED with everything else about them unchanged. +group; the hosts lose their chip and are otherwise unchanged. **Failure means:** if the hosts vanish, the delete is rewriting host payloads, which it must not — see `HostGroupRepository`. @@ -305,8 +304,8 @@ group; the hosts reappear under UNGROUPED with everything else about them unchan Make a group on machine A, file a host into it, sync. On machine B, sync, then delete the group and sync again. Back on A, sync. -**Pass:** A shows the host under UNGROUPED. Open that host's editor: the group picker shows "(a group that is -no longer here)" and *keeps it selected*. Change the port and save. +**Pass:** A shows the host with no chip on its card, as though it were in no group. Open that host's editor: +the group picker shows "(a group that is no longer here)" and *keeps it selected*. Change the port and save. **Failure means:** if the picker opened on "No group", saving has just unfiled the host — quietly, as a side effect of an unrelated edit. That is the case `BuildGroupChoices` adds the placeholder for. @@ -642,33 +641,45 @@ Start 7.1's slow connection and press GIVE UP (or the tab's cross) while it is s opened — it is a real shell, and one running with nothing naming it would be worse than one that comes back. -### 7.6 Dragging a host into a group · **least covered, like all drag and drop** +### 7.6 Dragging a host onto a group card · **least covered, like all drag and drop** -Make two groups and file a host into one. Drag a host card onto another group's heading; onto a host card -inside another group; and onto UNGROUPED. +Make two groups and file a host into one. Drag a host card up onto the other group's card. -**Pass:** whatever is under the pointer washes accent while the pointer is over it, the cursor shows a move -rather than a refusal, and the drop files the host — it moves under that heading and the counts on both -headings change. Dropping onto its own group's heading is refused while still in the air. +**Pass:** the group card under the pointer takes a two-pixel accent border while the pointer is over it, the +cursor shows a move rather than a refusal, and the drop files the host — the chip on its card changes to the +new group's name, the host counts under both group cards change, and the status line says where it went. -**Failure means:** headless Avalonia cannot synthesise a platform drag, so nothing about this gesture is -automated. The write it performs is: `MovingAHostToAGroup_FilesItAndLeavesItSelected`. +**Also check three refusals**, each of which must show the "no" cursor and mark nothing: over the card of the +group the host is *already* in; over another **host** card, which is deliberately not a target now that there +are no headings to say which group it would mean; and over the empty space around the cards. + +**And getting a host back out** is the host's own editor — pick "No group" in its picker. There is no +UNGROUPED target on the desktop any more, because there is no UNGROUPED heading for it to be. + +**And a drag held near the top or bottom edge of the grid scrolls it**, which is what makes this usable at +all with forty machines: the group cards are the first thing in the scroller, and a drag cannot use the +wheel. The pointer has to keep moving inside the band — a stationary pointer gets no drag events. + +**Failure means:** headless Avalonia cannot synthesise a platform drag, so the picking up, the cursor and +the drop are covered by nothing. What *is* automated is the decision each drag event takes — +`HostGridTests.TheGroupCardsAreWhatAcceptsADroppedHost` raises a real `DragOver` over both kinds of card — +and the write at the end, `ShellFlowTests.MovingAHostToAGroup_FilesItAndLeavesItSelected`. ### 7.7 A click still selects, and a double click still connects -Click host cards; drag one a few pixels without releasing; double-click one. Then double-click a group -heading. +Click host cards; drag one a few pixels without releasing; double-click one. Then double-click the space +around the cards. -**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking a -heading folds it and unfolds it again and connects to nothing. +**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking the +space around the cards connects to nothing. **Failure means:** the 5-pixel threshold in `HostsScreen.axaml.cs` is not doing its job — the same failure -as 2.16 on the other screen, and here it would make the grid unusable. A heading that connects means the +as 2.16 on the other screen, and here it would make the grid unusable. Empty space that connects means the double-tap handler has lost its check that the pointer was over a card. ### 7.8 The highlight clears after a drag that goes nowhere -Drag a host over a heading and release outside the grid, or press Escape mid-drag. +Drag a host over a group card and release outside the grid, or press Escape mid-drag. **Pass:** the wash goes away. @@ -676,7 +687,7 @@ Drag a host over a heading and release outside the grid, or press Escape mid-dra With host A selected, right-click host B and choose Delete. -**Pass:** the question names **B**. Then right-click a group heading. +**Pass:** the question names **B**. Then right-click the space around the cards. **Pass:** no menu opens at all, and the host selection has not moved. diff --git a/src/DodoSSH.Client.App/App.axaml b/src/DodoSSH.Client.App/App.axaml index 8ad5b88..80ad3eb 100644 --- a/src/DodoSSH.Client.App/App.axaml +++ b/src/DodoSSH.Client.App/App.axaml @@ -276,9 +276,12 @@ - + - - + + @@ -198,10 +207,18 @@ SelectedItem is GroupFilter and not SelectedGroup. The two are nearly the same thing here — GroupFilter assigns SelectedGroup — but only one of them may narrow the grid; see the property for why the phone's own use of SelectedGroup must not. + + ◆ THESE CARDS ARE THE DROP TARGET. A host card dragged onto one is filed under that group, and + that is the whole of what a drag does on this screen. It used to be a heading inside the host + grid, and the headings are gone — see the note on the host grid below. A group is a thing that + is drawn once, at the top, where the eye already goes to narrow the grid; a target that is also + the label of what you are aiming at needs no explaining. See the code-behind for the gesture. --> + SelectedItem="{Binding GroupFilter}" + ToolTip.Tip="Press a group to show only its hosts. Drag a host card onto one to file it there."> @@ -257,15 +274,31 @@ focus off the terminal's child window and then calls Focus() on something that refuses it, leaving the window with nothing focused and the keystrokes going nowhere. - It binds SidebarRows rather than VisibleHosts, so the group headings are still in it. Two - reasons, and neither is decoration: a grid showing every group at once has to say which cards - belong to which, and a heading is the drop target that takes a host out of one group and into - another. See VaultViewModel.RebuildSidebarRows for why a heading is a row rather than a - container, and the code-behind for what a drag does with one. + ◆ CARDS ONLY, AND NO HEADINGS. It binds VisibleHosts rather than SidebarRows, which is the + collection with the group headings folded into it. Those headings were a full-width fold-away + bar between the cards — a chevron, a name and a count — and in a wrap of cards they read as a + collapsed dropdown that had somehow been dealt the hosts: the moment a first group existed the + grid stopped being a grid. Two controls saying "group" on one screen was one too many, and the + cards above are the better of the two. SidebarRows and the fold are still the phone's, whose + list has no room for a row of group cards; see Android's HostsScreen. + + What a heading also was is the drop target, and that moved to the group cards rather than + going away with it — see the note on them above, and the code-behind for the gesture. A host is + taken back out of a group through its own editor, which is the one place "no group" can be said + plainly. + + Which group a card is in is on the card, as a chip. That is what the heading above it used to + say, and one chip per card says it without an accordion. + + SelectedItem is still SelectedSidebarRow and deliberately not SelectedHost, though every item + here is now a host. That property swallows the null the ListBox writes back whenever the + collection is rebuilt — which is every filter keystroke and every background sync — where + binding the host selection directly would let that null through, and a null host selection + closes the drawer and disarms a delete somebody is in the middle of confirming. See the + property, and RebuildVisibleHosts on where the real clearing is done. --> @@ -279,7 +312,8 @@ one per card: a ContextMenu inside a DataTemplate would have the row for its data context, and the commands here are the vault's. The code-behind selects whatever was right-clicked before the menu opens, so the selection-based commands act on the card under the pointer, - and cancels the menu outright over a group heading, which has no host to act on. + and cancels the menu outright over the space around the cards, where there is no host to act + on. --> @@ -290,35 +324,9 @@ - + - - - - - - + @@ -389,6 +397,26 @@ VerticalAlignment="Top" Margin="6,3,0,0" /> + + + + +