Public Access
Give a shell the whole phone, and one bar to leave it by
A connected phone was drawing five rows of chrome around the thing the user opened it for. The vault header at 56, the terminal's own tab strip at 52, a connection line at 36, the shells strip at 46 and the four-entry bottom bar at 64: at 360dp that is about a third of the display, and every row of it was about somewhere the user was not. What replaces them is one 52-pixel bar drawn by the surface itself — back on the left, the session pills, and a `+` across from them — and then the terminal. Three of those rows belong to `PhoneShell` and each is now bound on `IsShowingPages`. That is the same question asked once rather than three conditions that could drift: the surface is either a page or a terminal, and these are the chrome a page has. The header needed a wrapper because Avalonia's bindings have no "and" and it already had a condition of its own; the strip needed one for the same reason. The bottom bar had none and is bound directly. The back arrow goes to the page the terminal was opened over rather than to Hosts by name, because the system back gesture already picks that and an arrow landing somewhere else would be the second of two answers to one question. The bar's `+` raises a sheet offering the three connections this application can make — a shell, a host's files over SFTP, a bucket — since SFTP and S3 used to be two taps through the bottom bar's MORE and the bar is not on screen here. A control that replaced it and led to one of the three would have quietly removed the other two. Two things moved rather than being dropped. The text-size buttons are pinned at the right-hand end of the accessory key row, outside its scroller: the connection line existed to keep them from scrolling out of reach, and being outside the scroller answers that argument rather than abandoning it. The dialled address moved onto the connecting card, which is the moment it is worth reading — what is being connected to, before anything has answered — and after that the shell's own prompt says it more accurately than a header derived from the keychain ever did. The sheet collapses the renderer rather than covering it. Whether Android's `WebView` composites above Avalonia content the way Win32's child window does is still unverified — `docs/android-port.md` has said so since the port — so this follows the desktop's palette and gives up the rectangle outright, which is correct under either answer. It collapses `IsTerminalShowing` and not `IsTerminalSurface`, because the bar the sheet was raised from is part of that surface and dropping it would take the bar, the tabs and the whole arrangement with it, leaving the sheet floating over the page underneath. `OnSurfaceChanged` is the one place the flag is lowered, and that is the load- bearing half. Every way out of a terminal ends there — a destination, the files screen, the palette connecting to a host, closing the last tab, a lock — and each of them would otherwise leave a sheet set over a page. Not merely untidy: the flag holds the renderer blank, so the next return to the terminal would draw the menu again over a rectangle kept blank by it. Opening is refused off the terminal surface for the same reason from the other direction. The back gesture gains a guard above the switch, in the shape of the editor guard that arrived with the phone's `+`. It is nearer than any of them: with no header and no bottom bar, while the menu is up that gesture is the only way off it other than the scrim and CANCEL. The bottom bar's Terminal entry lost its `IsCurrent` binding. The bar is collapsed on that surface, so the binding could only ever be read as false, and a rule about a state the control cannot be in is a claim that it can. Three tests in `ShellFlowTests`, which is where shared state-machine behaviour for this head goes: the collapse and its recovery, the refusal to open over a page, and the sheet lowering both by a menu entry and by a route it was never wired to. Everything visual needs a device, so it is phase 11 of `docs/manual-checks.md` — and 11.2 is the check that would finally settle the compositing question this head has carried as unverified since the port.
This commit is contained in:
@@ -21,6 +21,16 @@
|
||||
|
||||
The order is the design's rather than the rail's. Terminal sits second, beside Hosts, because those two
|
||||
are the pair a session moves between; on the desktop the terminal is not a rail entry at all.
|
||||
|
||||
── a terminal gets the screen ─────────────────────────────────────────────────────────────────────────
|
||||
Three of the four rows below stand down while a shell is showing: the header, the shells strip and the
|
||||
bottom bar itself. All three are bound on IsShowingPages, which is the same question asked once — the
|
||||
surface is either a page or a terminal, and these are the chrome a page has.
|
||||
|
||||
The arithmetic is why. Header 56, strip 46, bar 64, and the terminal's own two rows on top of that: at
|
||||
360dp the shell was framed by about a third of the display, all of it about somewhere the user was not.
|
||||
What takes their place is one 52-pixel bar drawn by the surface itself, carrying back on the left and
|
||||
the sessions and a + across from it. See TerminalScreen.axaml.
|
||||
-->
|
||||
|
||||
<!--
|
||||
@@ -49,45 +59,54 @@
|
||||
Hidden behind MORE, and that is the design's arrangement rather than a saving. v2 gives every screen
|
||||
one header carrying that screen's own name and its own actions — a back arrow, an add, a refresh —
|
||||
so the five hub screens draw their own and this one stands down rather than stacking a second row of
|
||||
chrome above theirs. It stays on the three destinations that are the product's top level, where the
|
||||
chrome above theirs. It stays on the two destinations that are the product's top level, where the
|
||||
vault's name and the sync light are the most useful thing a header could say.
|
||||
|
||||
Wrapped rather than given a second condition, because Avalonia's bindings have no "and": the wrapper
|
||||
collapses it over a terminal, where the surface draws its own bar and the vault's name is not what
|
||||
the user is looking at. That is one of three rows this Grid stands down while a shell is showing —
|
||||
see the strip and the bottom bar below.
|
||||
-->
|
||||
<Border Grid.Row="0" Background="{StaticResource Chrome}" BorderBrush="{StaticResource Border}"
|
||||
BorderThickness="0,0,0,1" Padding="14,0" Height="56" IsVisible="{Binding !IsMoreSurface}">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
<Panel Grid.Row="0" IsVisible="{Binding IsShowingPages}">
|
||||
<Border Background="{StaticResource Chrome}" BorderBrush="{StaticResource Border}"
|
||||
BorderThickness="0,0,0,1" Padding="14,0" Height="56"
|
||||
IsVisible="{Binding !IsMoreSurface}">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto,Auto">
|
||||
|
||||
<!--
|
||||
Filled rather than outlined since v2. The mark is the one thing on this header that is not a
|
||||
fact about the vault, and the design gives it the accent as a solid tile — which is also what
|
||||
the launcher icon draws, so the two agree.
|
||||
-->
|
||||
<Border Grid.Column="0" Width="26" Height="26" CornerRadius="8"
|
||||
Background="{StaticResource Accent}" VerticalAlignment="Center">
|
||||
<TextBlock Text=">_" Foreground="{StaticResource AccentInk}"
|
||||
FontFamily="{StaticResource MonoFont}" FontSize="10" FontWeight="Bold"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<!--
|
||||
Filled rather than outlined since v2. The mark is the one thing on this header that is not a
|
||||
fact about the vault, and the design gives it the accent as a solid tile — which is also what
|
||||
the launcher icon draws, so the two agree.
|
||||
-->
|
||||
<Border Grid.Column="0" Width="26" Height="26" CornerRadius="8"
|
||||
Background="{StaticResource Accent}" VerticalAlignment="Center">
|
||||
<TextBlock Text=">_" Foreground="{StaticResource AccentInk}"
|
||||
FontFamily="{StaticResource MonoFont}" FontSize="10" FontWeight="Bold"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Column="1" Classes="heading" Margin="10,0,8,0" FontSize="16"
|
||||
Text="{Binding Vault.VaultName}" TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Grid.Column="1" Classes="heading" Margin="10,0,8,0" FontSize="16"
|
||||
Text="{Binding Vault.VaultName}" TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<!--
|
||||
The sync light, and it is green only when it has earned it — see SyncLabel. The design draws a
|
||||
permanently green "Synced" here, which is the one claim on that mock-up this application will
|
||||
not make.
|
||||
-->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
|
||||
<Ellipse Classes="dot" Classes.live="{Binding IsFullySynced}" Width="6" Height="6"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Classes="label" FontSize="9" Text="{Binding SyncLabel}" />
|
||||
</StackPanel>
|
||||
<!--
|
||||
The sync light, and it is green only when it has earned it — see SyncLabel. The design draws
|
||||
a permanently green "Synced" here, which is the one claim on that mock-up this application
|
||||
will not make.
|
||||
-->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
|
||||
<Ellipse Classes="dot" Classes.live="{Binding IsFullySynced}" Width="6" Height="6"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Classes="label" FontSize="9" Text="{Binding SyncLabel}" />
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Column="3" Classes="icon" Margin="4,0,0,0" Command="{Binding LockCommand}"
|
||||
ToolTip.Tip="Lock the keychain">
|
||||
<TextBlock Text="LOCK" Classes="label" FontSize="8.5" Foreground="{StaticResource TextDim}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Button Grid.Column="3" Classes="icon" Margin="4,0,0,0" Command="{Binding LockCommand}"
|
||||
ToolTip.Tip="Lock the keychain">
|
||||
<TextBlock Text="LOCK" Classes="label" FontSize="8.5"
|
||||
Foreground="{StaticResource TextDim}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Panel>
|
||||
|
||||
<!-- The screens. Only one draws; which one is the shell's business. -->
|
||||
<Panel Grid.Row="1">
|
||||
@@ -180,41 +199,57 @@
|
||||
v2 draws the sessions as pills rather than as a labelled row, and drops the word SHELLS: with a
|
||||
rounded chip carrying a live dot and a name, the label was spending nine characters of a 360dp row
|
||||
saying what the row already looks like.
|
||||
|
||||
On every screen except the one it names. The terminal draws these same sessions in its own bar, and
|
||||
two rows of the same pills — one of them 46 pixels of it — is the arrangement this surface exists to
|
||||
stop. Wrapped rather than given a second condition, because the strip's own visibility is about
|
||||
whether there are any tabs and this one is about which surface is up.
|
||||
-->
|
||||
<Border Grid.Row="2" IsVisible="{Binding HasTabs}" Background="{StaticResource Sidebar}"
|
||||
BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0" Height="46">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
|
||||
<ItemsControl ItemsSource="{Binding Tabs}" Margin="12,0" VerticalAlignment="Center">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate><StackPanel Orientation="Horizontal" Spacing="6" /></ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:TerminalTabViewModel">
|
||||
<Button Classes="row" MinHeight="34" Padding="13,0" CornerRadius="9"
|
||||
Background="{StaticResource Panel}" BorderBrush="{StaticResource BorderMid}"
|
||||
BorderThickness="1"
|
||||
Command="{Binding $parent[views:PhoneShell].((vm:MainWindowViewModel)DataContext).SelectTabCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7" VerticalAlignment="Center">
|
||||
<!--
|
||||
Green only while there is a shell behind the tab. It used to be lit unconditionally,
|
||||
which was true when a tab could not exist without a session; one can now — connecting
|
||||
opens the tab first — and a dot that was green before anything had answered would be
|
||||
the one thing on this strip claiming something untrue.
|
||||
-->
|
||||
<Ellipse Classes="dot" Classes.live="{Binding IsLive}" Width="6" Height="6"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Classes="mono" FontSize="11" Text="{Binding Label}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
<Panel Grid.Row="2" IsVisible="{Binding IsShowingPages}">
|
||||
<Border IsVisible="{Binding HasTabs}" Background="{StaticResource Sidebar}"
|
||||
BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0" Height="46">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
|
||||
<ItemsControl ItemsSource="{Binding Tabs}" Margin="12,0" VerticalAlignment="Center">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate><StackPanel Orientation="Horizontal" Spacing="6" /></ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:TerminalTabViewModel">
|
||||
<Button Classes="row" MinHeight="34" Padding="13,0" CornerRadius="9"
|
||||
Background="{StaticResource Panel}" BorderBrush="{StaticResource BorderMid}"
|
||||
BorderThickness="1"
|
||||
Command="{Binding $parent[views:PhoneShell].((vm:MainWindowViewModel)DataContext).SelectTabCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7" VerticalAlignment="Center">
|
||||
<!--
|
||||
Green only while there is a shell behind the tab. It used to be lit unconditionally,
|
||||
which was true when a tab could not exist without a session; one can now —
|
||||
connecting opens the tab first — and a dot that was green before anything had
|
||||
answered would be the one thing on this strip claiming something untrue.
|
||||
-->
|
||||
<Ellipse Classes="dot" Classes.live="{Binding IsLive}" Width="6" Height="6"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Classes="mono" FontSize="11" Text="{Binding Label}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Panel>
|
||||
|
||||
<!-- ============ navigation ============ -->
|
||||
<Border Grid.Row="3" Background="{StaticResource Chrome}" BorderBrush="{StaticResource Border}"
|
||||
<!--
|
||||
Gone while a terminal is showing, which is the whole of that surface's arrangement: the bar's four
|
||||
destinations are replaced by a back arrow and a + that leads to three of them, both in the terminal's
|
||||
own bar. See TerminalScreen.axaml.
|
||||
|
||||
This one is bound directly rather than wrapped — its visibility is a single question and it has no
|
||||
second condition of its own to keep separate.
|
||||
-->
|
||||
<Border Grid.Row="3" IsVisible="{Binding IsShowingPages}"
|
||||
Background="{StaticResource Chrome}" BorderBrush="{StaticResource Border}"
|
||||
BorderThickness="0,1,0,0" Height="64">
|
||||
<Grid ColumnDefinitions="*,*,*,*">
|
||||
|
||||
@@ -225,8 +260,13 @@
|
||||
<!--
|
||||
The terminal is a surface rather than a page — see ShellSurface — so this one does not go
|
||||
through ShowScreen. Its own command is on the shell.
|
||||
|
||||
The only entry here that never lights, and deliberately no longer tries: this bar is collapsed
|
||||
while the terminal is showing, so IsCurrent could only ever be read as false. Binding it anyway
|
||||
would be a rule about a state this control cannot be in. What marks the terminal as current is
|
||||
the surface filling the screen.
|
||||
-->
|
||||
<views:NavButton Grid.Column="1" Label="Terminal" Glyph="⌗" IsCurrent="{Binding IsTerminalSurface}"
|
||||
<views:NavButton Grid.Column="1" Label="Terminal" Glyph="⌗"
|
||||
Command="{Binding ShowTerminalCommand}" />
|
||||
|
||||
<!--
|
||||
|
||||
Reference in New Issue
Block a user