Public Access
Merge branch 'claude/host-detail-pane-design-e98621'
This commit is contained in:
@@ -16,7 +16,12 @@
|
||||
|
||||
Cards split the two jobs. The grid is about all of them — how many groups there are, which machines are
|
||||
in one, which have a shell open — and it gets the full width to say so. The drawer is about one, and it
|
||||
is only there when there is one. See HostDrawer and VaultViewModel.IsDrawerOpen.
|
||||
is only there when it has been asked for. See HostDrawer and VaultViewModel.IsDrawerOpen.
|
||||
|
||||
◆ ASKED FOR, AND IT USED TO ARRIVE WITH THE SELECTION. Touching any card opened the drawer, so choosing
|
||||
a host cost 304 pixels of grid every time — including for every card passed over on the way to the one
|
||||
somebody wanted. The pencil on a card is what opens it now; selecting is free again. See the item
|
||||
template below and VaultViewModel.IsHostPaneOpen.
|
||||
|
||||
── WHAT IS DELIBERATELY NOT HERE ────────────────────────────────────────────────────────────────────
|
||||
The design this was drawn from puts a view-mode switch, a tag filter, a calendar and a share control in
|
||||
@@ -357,9 +362,17 @@
|
||||
and cancels the menu outright over the space around the cards, where there is no host to act
|
||||
on.
|
||||
-->
|
||||
<!--
|
||||
Details is here as well as on the pencil, and that is not a duplicate for its own sake: the
|
||||
pencil appears on hover, so it is unreachable without a pointer, and this menu is what a
|
||||
keyboard and the context key can get to. It passes no parameter — the code-behind has
|
||||
already selected whatever was right-clicked, which is exactly what OpenHostPane falls back
|
||||
to.
|
||||
-->
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Connect" Command="{Binding ConnectCommand}" />
|
||||
<MenuItem Header="Details…" Command="{Binding OpenHostPaneCommand}" />
|
||||
<MenuItem Header="Edit…" Command="{Binding EditSelectedHostCommand}" />
|
||||
<Separator />
|
||||
<MenuItem Header="Delete…" Command="{Binding DeleteHostCommand}" />
|
||||
@@ -369,116 +382,148 @@
|
||||
<ListBox.ItemTemplate>
|
||||
|
||||
<DataTemplate x:DataType="vm:HostRowViewModel">
|
||||
<Border Classes="tile">
|
||||
<StackPanel Spacing="6">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<!--
|
||||
The address is on the tooltip and nowhere else on the card. It used to be the subtitle;
|
||||
see the note on the summary line below for what took its place and why it is not lost.
|
||||
-->
|
||||
<Border Classes="tile" ToolTip.Tip="{Binding Address}">
|
||||
<!--
|
||||
A Panel so the pencil below can be drawn *over* the card rather than in a column of
|
||||
its own. A column would have cost the name 30 pixels of a 232-pixel tile — permanently,
|
||||
for a control that is only there while the pointer is — and the name is the thing the
|
||||
grid exists to be scanned by.
|
||||
-->
|
||||
<Panel>
|
||||
<StackPanel Spacing="6">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
|
||||
<Border Grid.Column="0" Classes="tileicon" Background="{StaticResource Chip}">
|
||||
<TextBlock Classes="mono" Text=">_" FontSize="11" FontWeight="Bold"
|
||||
Foreground="{StaticResource AccentText}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border Grid.Column="0" Classes="tileicon" Background="{StaticResource Chip}">
|
||||
<TextBlock Classes="mono" Text=">_" FontSize="11" FontWeight="Bold"
|
||||
Foreground="{StaticResource AccentText}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center"
|
||||
Spacing="2">
|
||||
<!--
|
||||
◆ A GRID AND NOT A HORIZONTAL StackPanel, on both of these rows, and the two
|
||||
look interchangeable until the text is too long for the card.
|
||||
<StackPanel Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center"
|
||||
Spacing="2">
|
||||
<!--
|
||||
◆ A GRID AND NOT A HORIZONTAL StackPanel, on both of these rows, and the two
|
||||
look interchangeable until the text is too long for the card.
|
||||
|
||||
A horizontal StackPanel measures every child with infinite width, so a
|
||||
TextBlock inside one never learns it is short of room and TextTrimming never
|
||||
fires — the text simply runs on past the card's border and over the card beside
|
||||
it. The card is a fixed 248 and a host name is not, so that is the ordinary
|
||||
case rather than the edge one.
|
||||
A horizontal StackPanel measures every child with infinite width, so a
|
||||
TextBlock inside one never learns it is short of room and TextTrimming never
|
||||
fires — the text simply runs on past the card's border and over the card beside
|
||||
it. The card is a fixed 248 and a host name is not, so that is the ordinary
|
||||
case rather than the edge one.
|
||||
|
||||
The star column is the part that gives way and the Auto column is the part that
|
||||
must not: a badge saying "not synced" and the word that says how a host
|
||||
authenticates are both short and both meaningless trimmed, where a hostname
|
||||
with its tail cut is still the machine you were looking for.
|
||||
-->
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Grid.Column="0" Text="{Binding Label}" FontWeight="Medium"
|
||||
Foreground="{StaticResource Text}"
|
||||
The star column is the part that gives way and the Auto column is the part that
|
||||
must not: a badge saying "not synced" and the word that says how a host
|
||||
authenticates are both short and both meaningless trimmed, where a hostname
|
||||
with its tail cut is still the machine you were looking for.
|
||||
-->
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Grid.Column="0" Text="{Binding Label}" FontWeight="Medium"
|
||||
Foreground="{StaticResource Text}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<Border Grid.Column="1" Classes="chip warn" Padding="4,0" Margin="6,0,0,0"
|
||||
IsVisible="{Binding Badge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock Text="{Binding Badge}" FontSize="9.5" />
|
||||
</Border>
|
||||
</Grid>
|
||||
<!--
|
||||
◆ ONE COMMA-SEPARATED LINE: the transport, the account, and every tag this host
|
||||
wears. It replaces two things — the address-and-authentication row that was here,
|
||||
and the row of tag chips that used to sit under the whole card.
|
||||
|
||||
What went is the address, and it went to the tooltip above rather than nowhere.
|
||||
A card is read while scanning forty machines, and at that moment the name and
|
||||
what kind of machine it is are what is being looked for; an address is what you
|
||||
read once you have found it, which is what the drawer is for. The word saying
|
||||
how it authenticates went with it, for a smaller reason: it was there to warn
|
||||
that a password box would be needed, and the box is in the pane beside that
|
||||
warning now.
|
||||
|
||||
See HostRowViewModel.Summary, and the remark there on why "ssh" is printed at
|
||||
all when every host in this product is one.
|
||||
-->
|
||||
<TextBlock Text="{Binding Summary}" FontSize="10.5"
|
||||
Foreground="{StaticResource TextFaint}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<Border Grid.Column="1" Classes="chip warn" Padding="4,0" Margin="6,0,0,0"
|
||||
IsVisible="{Binding Badge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock Text="{Binding Badge}" FontSize="9.5" />
|
||||
</Border>
|
||||
</Grid>
|
||||
<!--
|
||||
Which vault this host is in, and only when there is more than one to be in. It
|
||||
decides who else can see the host and where an edit goes back to, so on a grid
|
||||
spanning several vaults it is not decoration.
|
||||
-->
|
||||
<TextBlock Classes="mono" Text="{Binding VaultBadge}" FontSize="10.5"
|
||||
Foreground="{StaticResource TextFaint}"
|
||||
IsVisible="{Binding HasVaultBadge}" />
|
||||
</StackPanel>
|
||||
|
||||
<!--
|
||||
The address and how it authenticates, on one line. Which of the three ways is
|
||||
here because only one of them wants a password typed, and an empty password box
|
||||
on a key-authenticated host is otherwise indistinguishable from one somebody
|
||||
forgot to fill in.
|
||||
Connected, and nothing more. Green means a terminal is open on this host right
|
||||
now; grey means there is not one. It is deliberately not reachability — nothing
|
||||
here pings anything, and a dot that meant "up" would be a claim this application
|
||||
never checks.
|
||||
-->
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Grid.Column="0" Classes="mono" Text="{Binding Address}"
|
||||
FontSize="10.5" Foreground="{StaticResource TextFaint}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Grid.Column="1" Classes="mono" Text="{Binding Authentication}"
|
||||
FontSize="10.5" Margin="6,0,0,0"
|
||||
Foreground="{StaticResource TextFaint}" />
|
||||
</Grid>
|
||||
<!--
|
||||
Which vault this host is in, and only when there is more than one to be in. It
|
||||
decides who else can see the host and where an edit goes back to, so on a grid
|
||||
spanning several vaults it is not decoration.
|
||||
-->
|
||||
<TextBlock Classes="mono" Text="{Binding VaultBadge}" FontSize="10.5"
|
||||
Foreground="{StaticResource TextFaint}"
|
||||
IsVisible="{Binding HasVaultBadge}" />
|
||||
</StackPanel>
|
||||
<Ellipse Grid.Column="2" Classes="dot" Classes.live="{Binding IsConnected}"
|
||||
VerticalAlignment="Top" Margin="6,3,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!--
|
||||
Connected, and nothing more. Green means a terminal is open on this host right
|
||||
now; grey means there is not one. It is deliberately not reachability — nothing
|
||||
here pings anything, and a dot that meant "up" would be a claim this application
|
||||
never checks.
|
||||
The group this host is filed under, and only when it is filed under one — which is
|
||||
the same rule the group cards above follow, and the reason a keychain nobody has
|
||||
filed anything in looks exactly as it did before groups existed.
|
||||
|
||||
This is what the fold-away heading over a run of cards used to say. A chip on the
|
||||
card says it per host instead, which is the shape the rest of this card already is
|
||||
and the shape a wrap of cards can carry: it survives the cards being reflowed, it
|
||||
needs no row of its own, and it is what changes when a card is dropped onto a group.
|
||||
|
||||
Accent rather than plain, so the one chip that is a *place* is not read as another
|
||||
tag. A group whose name the vault cannot resolve leaves this empty rather than
|
||||
printing an id, for the reason a tag does. See HostRowViewModel.GroupLabel.
|
||||
-->
|
||||
<Ellipse Grid.Column="2" Classes="dot" Classes.live="{Binding IsConnected}"
|
||||
VerticalAlignment="Top" Margin="6,3,0,0" />
|
||||
</Grid>
|
||||
<Border Classes="chip accent" Padding="5,0" HorizontalAlignment="Left"
|
||||
IsVisible="{Binding HasGroup}">
|
||||
<TextBlock Text="{Binding GroupLabel}" FontSize="9.5"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Border>
|
||||
|
||||
<!--
|
||||
The group this host is filed under, and only when it is filed under one — which is
|
||||
the same rule the group cards above follow, and the reason a keychain nobody has
|
||||
filed anything in looks exactly as it did before groups existed.
|
||||
<!--
|
||||
A wrapped row of tag chips was here. The tags are in the summary line above now,
|
||||
after the account, which is what took the card from three rows and a wrap down to
|
||||
two: a chip and a comma-separated name say the same thing, and only one of them
|
||||
needs a row of its own. Chips are still what a tag looks like where it is *chosen* —
|
||||
the editor's picker in the drawer, and the phone's own list. See
|
||||
HostRowViewModel.Summary.
|
||||
-->
|
||||
</StackPanel>
|
||||
|
||||
This is what the fold-away heading over a run of cards used to say. A chip on the
|
||||
card says it per host instead, which is the shape the rest of this card already is
|
||||
and the shape a wrap of cards can carry: it survives the cards being reflowed, it
|
||||
needs no row of its own, and it is what changes when a card is dropped onto a group.
|
||||
<!--
|
||||
◆ THE PENCIL, AND THE ONLY WAY THE DRAWER OPENS FROM THIS GRID.
|
||||
|
||||
Accent rather than plain, so the one chip that is a *place* is not read as another
|
||||
tag. A group whose name the vault cannot resolve leaves this empty rather than
|
||||
printing an id, for the reason a tag does. See HostRowViewModel.GroupLabel.
|
||||
-->
|
||||
<Border Classes="chip accent" Padding="5,0" HorizontalAlignment="Left"
|
||||
IsVisible="{Binding HasGroup}">
|
||||
<TextBlock Text="{Binding GroupLabel}" FontSize="9.5"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Border>
|
||||
Selecting a card no longer opens it. That used to be automatic, which meant the grid
|
||||
gave up 304 pixels the moment anybody touched a card — including every card arrowed
|
||||
past on the way to the one they wanted. Opening the pane is a thing you ask for now,
|
||||
and this is where the asking is. See VaultViewModel.IsHostPaneOpen.
|
||||
|
||||
<!--
|
||||
The tags this host wears. Under the summary rather than beside it, because a host
|
||||
can wear several and the line above is three fixed-width facts where these are not.
|
||||
Bottom right, under the status dot rather than beside it, and over the card rather
|
||||
than in a column: the two marks a card can carry on that edge are "a shell is open
|
||||
here" and "open this one's pane", and stacking them is what keeps both out of the
|
||||
name's width. It appears on hover and on the selected card; see Button.rowedit in
|
||||
App.axaml for why that is IsVisible rather than an opacity.
|
||||
|
||||
Names, not ids: a tag the vault cannot resolve is left out rather than drawn. See
|
||||
HostRowViewModel.TagLabels.
|
||||
-->
|
||||
<ItemsControl ItemsSource="{Binding TagLabels}" IsVisible="{Binding HasTags}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<Border Classes="chip" Padding="5,0" Margin="0,0,4,2">
|
||||
<TextBlock Text="{Binding}" FontSize="9.5" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
The row is the command's parameter rather than being left to the selection. A button
|
||||
inside a ListBoxItem takes the press itself, and whether the item is also selected by
|
||||
it is the theme's business — so a command reading SelectedHost would open the pane on
|
||||
whichever card happened to be lit, which on the first click of a session is none of
|
||||
them.
|
||||
-->
|
||||
<Button Classes="flat rowedit" Content="✎"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
Command="{Binding $parent[ListBox].((vm:VaultViewModel)DataContext).OpenHostPaneCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
ToolTip.Tip="Opens this host's pane beside the grid" />
|
||||
</Panel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -503,8 +548,9 @@
|
||||
</Grid>
|
||||
|
||||
<!--
|
||||
The drawer, and it takes its 304 pixels only while there is something in it — which is what gives the
|
||||
grid the full width for most of the time anybody is looking at it.
|
||||
The drawer, and it takes its 304 pixels only while it has been opened — which is what gives the grid
|
||||
the full width for most of the time anybody is looking at it, now including all the time somebody is
|
||||
choosing between forty machines.
|
||||
|
||||
Wrapped so that its own IsVisible is the shell's binding and its data context is the vault; see
|
||||
MainWindow.axaml's note on why the two cannot be put on one element.
|
||||
|
||||
Reference in New Issue
Block a user