Public Access
Give the window its v5b chrome and each session surface its own shell
This commit is contained in:
1 parent
1b76c51fbb
commit
43c939b697
30 files changed
+4433
-1772
No files matched your search
@@ -9,15 +9,14 @@
|
||||
Icon="/Assets/dodossh.ico"
|
||||
Width="1180"
|
||||
Height="760"
|
||||
MinWidth="1016"
|
||||
MinHeight="574"
|
||||
MinWidth="1081"
|
||||
MinHeight="583"
|
||||
Background="{StaticResource Canvas}"
|
||||
SystemDecorations="BorderOnly"
|
||||
Focusable="True">
|
||||
|
||||
<!--
|
||||
The shell window: a titlebar it draws itself, a sidebar, a tab strip, one surface at a time, and a
|
||||
status bar.
|
||||
The shell window: a titlebar it draws itself, a nav rail, one surface at a time, and a status bar.
|
||||
|
||||
THE MINIMUM GREW, and it grew by exactly what v2 added rather than by a round number somebody liked.
|
||||
The sidebar went from 54 pixels to 190 and the chrome from 72 tall to 86, so 880x560 became 1016x574 —
|
||||
@@ -26,6 +25,17 @@
|
||||
fitting at 690 wide. Widening the sidebar without widening the window would have quietly broken them
|
||||
somewhere nobody was looking.
|
||||
|
||||
v5b moves it again, by exactly the same reasoning: the titlebar's fidelity pass takes it from 44 to 53
|
||||
and the rail's from 190 to 255, so 1016x574 becomes 1081x583 — nine pixels and sixty-five pixels, added
|
||||
straight onto the minimum rather than absorbed by shrinking a screen. <c>LayoutHarness.ScreenWidth</c>
|
||||
stays unchanged at 826, because the rail is the only thing beside a page that grew.
|
||||
|
||||
<c>ScreenHeight</c> did move, and in the other direction: v5b's own fidelity pass also retires the
|
||||
window-wide tab strip this comment used to describe — see the paragraph below — and every full-bleed
|
||||
page gets that 42 pixels back rather than the window losing height to compensate. A screen this suite
|
||||
measures is 826 pixels wide and taller than it was, by exactly the strip's own height; see
|
||||
<c>LayoutHarness</c>'s own remark on the budget for the arithmetic.
|
||||
|
||||
Windows is asked for a resize border and nothing else, so TitleBar does the dragging, the maximising and
|
||||
the closing. That is a real cost, and the reason it is paid is that a stock grey system bar above a
|
||||
near-black application is the one part of the window that would look borrowed.
|
||||
@@ -37,12 +47,19 @@
|
||||
removes the caption and keeps the resize border and the drop shadow, which is the half of the system
|
||||
chrome worth having.
|
||||
|
||||
TWO SURFACES, ONE RECTANGLE.
|
||||
TWO SURFACES, ONE RECTANGLE — AND EACH ONE OWNS ITS OWN TAB ROW NOW.
|
||||
|
||||
The tab strip is above everything the nav rail leads to, so a terminal opened from any screen stays
|
||||
visible and reachable from every other one. What that costs is that the terminal and the pages now share
|
||||
the area beneath the strip, and exactly one of them may occupy it. That is the whole of ShellSurface: an
|
||||
enum rather than two flags, so there is no way to write the state where both are showing.
|
||||
v5b retires the tab strip this file used to draw above the whole window — Vaults, SFTP and S3 left it
|
||||
for the rail's own switcher in an earlier pass, and this one moves the remaining pills, one per open
|
||||
terminal, off the window's own chrome entirely. Each of the two screens that carries a tab row —
|
||||
<c>SessionTabRow.axaml</c> — draws its own, 38 pixels, inside its own 26-pixel padded column, per the
|
||||
design; see the terminal surface's own Grid below and the SFTP one inside the pages Panel. A tab still
|
||||
survives navigating away from either screen — that is what makes the terminal reachable from anywhere —
|
||||
it simply is not drawn as chrome above every screen while it does.
|
||||
|
||||
The terminal and the pages still share the one rectangle beside the rail, and exactly one of them may
|
||||
occupy it at a time: that is the whole of ShellSurface, an enum rather than two flags, so there is no way
|
||||
to write the state where both are showing.
|
||||
|
||||
THE OCCLUSION RULE, which every arrangement in this file obeys.
|
||||
|
||||
@@ -67,7 +84,9 @@
|
||||
template swap — detaches it, and detaching destroys the native control and the whole WebView2 process
|
||||
tree, so every unlock would pay a cold start. Hoisting the binding to an ancestor looks tidier and is
|
||||
unverified: NativeControlHost does watch ancestors, but NativeWebView's own bounds-and-scaling re-push
|
||||
fires only for its own IsVisible.
|
||||
fires only for its own IsVisible. v5b nests the WebView three levels deeper than it used to sit, inside
|
||||
the terminal surface's own session shell — see that Grid's own remark below for why the rule still holds
|
||||
with the control that much further from the Panel that used to be its only parent.
|
||||
-->
|
||||
|
||||
<Grid RowDefinitions="Auto,*,Auto,Auto">
|
||||
@@ -77,209 +96,227 @@
|
||||
<Panel Grid.Row="1">
|
||||
|
||||
<!-- The unlocked application. -->
|
||||
<Grid RowDefinitions="Auto,*" IsVisible="{Binding IsUnlocked}">
|
||||
<Grid ColumnDefinitions="Auto,*" IsVisible="{Binding IsUnlocked}">
|
||||
|
||||
<!--
|
||||
◆ THE STRIP IS ABOVE THE RAIL, and it used to be beside it.
|
||||
|
||||
It was the other way round for a reason that stopped being true: while every tab was a terminal,
|
||||
the strip navigated only the area to the right of a full-height rail, and putting it over the rail
|
||||
would have been a row of tabs above a column of destinations they had nothing to do with.
|
||||
|
||||
The three fixed tabs are what changed that. The rail is now one tab's contents rather than the
|
||||
window's own furniture — Vaults owns it, SFTP and S3 do not have it, and a terminal does not
|
||||
either — so a rail drawn beside the strip would outrank the thing that decides whether it is
|
||||
showing at all. Above and full width is the arrangement that matches what selects what.
|
||||
The window's own furniture, at 255 pixels — see NavRail.axaml's own v5b remark. It no longer sits
|
||||
under a strip: v5b retired the window-wide tab strip entirely, and with it the row this Grid used
|
||||
to give up its own first row to. See the remark below on where a session's tabs live now.
|
||||
-->
|
||||
<views:TerminalTabs Grid.Row="0" />
|
||||
<views:NavRail Grid.Column="0" />
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
|
||||
<Panel Grid.Column="1">
|
||||
|
||||
<!--
|
||||
The Vaults tab's own navigation, and it collapses with that tab. Its width is 190 either way, so
|
||||
SFTP, S3 and a terminal each get the full window rather than the 826 a page gets.
|
||||
-->
|
||||
<views:NavRail Grid.Column="0" IsVisible="{Binding IsVaultsTab}" />
|
||||
|
||||
<Grid Grid.Column="1" RowDefinitions="Auto,*">
|
||||
<!-- ============ THE PAGES ============ -->
|
||||
<Panel IsVisible="{Binding IsShowingPages}">
|
||||
|
||||
<!--
|
||||
◆ THE PIN STRIP, ABOVE THE TERMINAL AND NOTHING ELSE.
|
||||
|
||||
A Grid row rather than a sibling in the Panel below it — the terminal, the pages and the
|
||||
connecting card are all layered on top of one another there, which is right for three things
|
||||
that occupy the same rectangle and wrong for a row that is supposed to sit above it. Auto height
|
||||
and IsVisible="False" collapse to nothing when there is nothing to show, so a page screen or an
|
||||
empty terminal loses no height to a row it never draws — see ShowsPinStrip, which is false on
|
||||
every page and every tab whose host has no pins.
|
||||
|
||||
ActiveTabPinnedPaths is MainWindowViewModel's, not the vault's: which tab is selected and
|
||||
whether its session is live are the shell's business, and RefreshConnectedHosts is where the
|
||||
two lists — Tabs and Vault.Hosts — are already walked together to paint the status dots. This
|
||||
rides along on the same walk rather than opening a subscription of its own.
|
||||
Wrapped, like every other screen here. It used to be bound directly — its data context was
|
||||
the shell's, so IsHostsScreen resolved on the same element — and that was only so it could
|
||||
hand the vault to the host sidebar from inside its own markup. The sidebar is gone and the
|
||||
drawer that replaced it is a plain child, so the screen takes the vault like the rest and
|
||||
its visibility goes on the wrapper.
|
||||
-->
|
||||
<Border Grid.Row="0" Padding="14,8" Background="{StaticResource Panel}"
|
||||
BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1"
|
||||
IsVisible="{Binding ShowsPinStrip, FallbackValue=False}">
|
||||
<ItemsControl ItemsSource="{Binding ActiveTabPinnedPaths}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate><StackPanel Orientation="Horizontal" Spacing="6" /></ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<!--
|
||||
A single level of $parent[ItemsControl] is enough to reach back to the shell: this
|
||||
ItemsControl's own DataContext is MainWindowViewModel, the window's root, so one hop up
|
||||
from the chip's string DataContext lands on it directly — no #Board-style named-element
|
||||
indirection needed, because there is no second level of template nesting here.
|
||||
-->
|
||||
<Button Classes="ghost" Padding="8,4" ToolTip.Tip="{Binding}"
|
||||
Command="{Binding $parent[ItemsControl].((vm:MainWindowViewModel)DataContext).OpenPinnedPathCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="" FontFamily="{StaticResource IconFont}" FontSize="12" />
|
||||
<TextBlock FontFamily="{StaticResource MonoFont}" Text="{Binding}" FontSize="11.5"
|
||||
MaxWidth="220" TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
|
||||
<Panel Grid.Row="1">
|
||||
|
||||
<!-- ============ THE PAGES ============ -->
|
||||
<Panel IsVisible="{Binding IsShowingPages}">
|
||||
|
||||
<!--
|
||||
Wrapped, like every other screen here. It used to be bound directly — its data context was
|
||||
the shell's, so IsHostsScreen resolved on the same element — and that was only so it could
|
||||
hand the vault to the host sidebar from inside its own markup. The sidebar is gone and the
|
||||
drawer that replaced it is a plain child, so the screen takes the vault like the rest and
|
||||
its visibility goes on the wrapper.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsHostsScreen}">
|
||||
<views:HostsScreen x:Name="HostsPane" DataContext="{Binding Vault}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ SFTP ============ -->
|
||||
<!--
|
||||
Inside this Panel although it is a tab rather than a rail screen, and that is not an
|
||||
oversight. IsShowingPages means "the Avalonia page area, not the WebView", which is the
|
||||
occlusion question and is true of all three fixed tabs; which of them is showing is the
|
||||
separate question each child below answers. Keeping the two apart is what lets the terminal
|
||||
stay collapsed under one rule rather than under four.
|
||||
|
||||
What differs from a rail screen is only the rail: NavRail collapses on IsVaultsTab above, so
|
||||
this screen is laid out at the full window width.
|
||||
|
||||
Wrapped rather than bound directly, for the same reason the vault screen is: this element's
|
||||
visibility is the shell's business and its data context is the transfers view model, and
|
||||
putting both on one element resolves IsVisible against that view model, where
|
||||
IsTransfersScreen does not exist.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsTransfersScreen}">
|
||||
<views:TransfersScreen DataContext="{Binding Transfers}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ S3 ============ -->
|
||||
<!--
|
||||
The same screen as SFTP above, over the same view model, because an object store and an
|
||||
SFTP host are both an IRemoteFileStore and everything below the picker was written once.
|
||||
What differs is which picker is offered, and that is decided by the destination rather than
|
||||
by a toggle inside the screen — see ShowFiles, and the tab in the strip that calls it.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsBucketsScreen}">
|
||||
<views:TransfersScreen DataContext="{Binding Transfers}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ KEYCHAIN ============ -->
|
||||
<!--
|
||||
Wrapped rather than bound directly, for the reason the vault column always was: this
|
||||
element's visibility is the shell's business and its data context is the vault, and put both
|
||||
on one element and IsVisible resolves against the vault as well, where IsKeychainScreen does
|
||||
not exist.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsKeychainScreen}">
|
||||
<views:KeychainScreen x:Name="VaultPane" DataContext="{Binding Vault}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ HOST KEYS ============ -->
|
||||
<!--
|
||||
Wrapped, like the two above and for the same reason: its data context is the screen's own
|
||||
view model, where IsKnownHostsScreen does not exist.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsKnownHostsScreen}">
|
||||
<views:KnownHostsScreen x:Name="PinsPane" DataContext="{Binding KnownHostsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ SNIPPETS ============ -->
|
||||
<!-- Wrapped, like the others whose data context is their own view model. -->
|
||||
<Panel IsVisible="{Binding IsSnippetsScreen}">
|
||||
<views:SnippetsScreen x:Name="SnippetsPane" DataContext="{Binding SnippetsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ LOGS ============ -->
|
||||
<!-- Wrapped, like the others whose data context is their own view model. -->
|
||||
<Panel IsVisible="{Binding IsLogsScreen}">
|
||||
<views:LogsScreen x:Name="LogsPane" DataContext="{Binding LogsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ VAULTS ============ -->
|
||||
<!--
|
||||
Wrapped, for the reason the keychain and transfers screens are: the visibility is the shell's
|
||||
business and the data context is the vaults view model, and both on one element would resolve
|
||||
IsVaultsScreen against a type that does not have it.
|
||||
|
||||
Bound to Vaults, which is the vaults themselves and the people in them — not to Vault, which
|
||||
is one vault's contents and is what the keychain and hosts screens above draw.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsVaultsScreen}">
|
||||
<views:VaultsScreen DataContext="{Binding Vaults}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ PREFERENCES ============ -->
|
||||
<views:PreferencesScreen IsVisible="{Binding IsPreferencesScreen}" />
|
||||
|
||||
<!-- ============ IMPORT ============ -->
|
||||
<!--
|
||||
Reached from preferences rather than from the rail; see ShellScreen.Import. Wrapped, like
|
||||
the others whose data context is their own view model.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsImportScreen}">
|
||||
<views:ImportScreen x:Name="ImportPane" DataContext="{Binding ImportScreen}" />
|
||||
</Panel>
|
||||
|
||||
<Panel IsVisible="{Binding IsHostsScreen}">
|
||||
<views:HostsScreen x:Name="HostsPane" DataContext="{Binding Vault}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ SFTP ============ -->
|
||||
<!--
|
||||
The other thing that can be in the terminal's rectangle: a tab whose session does not exist
|
||||
yet, or never will. Exclusive with the WebView below by construction — a selected tab either
|
||||
has a session or it does not — which is what makes drawing it here safe under the occlusion
|
||||
rule, the same way the page area is. See ConnectingCard.axaml.
|
||||
-->
|
||||
<views:ConnectingCard x:Name="ConnectingPane"
|
||||
IsVisible="{Binding IsConnectingShowing, FallbackValue=False}" />
|
||||
◆ v5b's session shell, wrapping this screen's existing content rather than replacing it — the
|
||||
two-pane grid and the queue inside TransfersScreen.axaml are untouched; wave C restyles their
|
||||
internals. What is new here is everything design-notes/v5b-fidelity-notes.md calls the session
|
||||
shell: a 26px padded column, an in-screen tab row, a bordered rounded-bottom container holding
|
||||
a host header and a status bar around the screen's own content, and a 300px sidebar.
|
||||
|
||||
Gated on IsTransfersScreen exactly as before — IsShowingPages means "the Avalonia page area,
|
||||
not the WebView", which is the occlusion question, and which mode the switcher is on is this
|
||||
wrapper's own separate question.
|
||||
|
||||
The tab row's own click does not select a terminal tab — there is no per-tab SFTP session in
|
||||
this application, and building one is out of this wave's scope; see the notes' own open
|
||||
question and MainWindowViewModel.SelectFilesHostCommand for how this resolves it: a click
|
||||
reuses the same "Browse files" plumbing a pin click already does, honestly opening (or
|
||||
reusing) a second, SFTP-specific connection to that tab's host rather than pretending a session
|
||||
exists that does not.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsTransfersScreen}">
|
||||
<Grid RowDefinitions="Auto,*" Margin="26">
|
||||
<views:SessionTabRow Grid.Row="0" Classes="sftp"
|
||||
TabCommand="{Binding SelectFilesHostCommand}" />
|
||||
<Border Grid.Row="1" BorderBrush="{StaticResource Border}" BorderThickness="1"
|
||||
CornerRadius="0,0,12,12" ClipToBounds="True">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Grid Grid.Column="0" RowDefinitions="Auto,*,Auto">
|
||||
<views:SessionHeader Grid.Row="0"
|
||||
OpenLabel="Open terminal"
|
||||
OpenCommand="{Binding OpenTerminalForFilesHostCommand}"
|
||||
EmptyText="{Binding Transfers.Status}" />
|
||||
<views:TransfersScreen Grid.Row="1" DataContext="{Binding Transfers}" />
|
||||
<views:SessionStatusBar Grid.Row="2" />
|
||||
</Grid>
|
||||
<!--
|
||||
Hides when no session is active — see ShowsQuickAccessSidebar — rather than always
|
||||
drawn: the SFTP surface reaches this Panel before a host is chosen, and a sidebar with
|
||||
an empty QUICK ACCESS and no host name to print would be furniture with nothing to say.
|
||||
Collapsing frees the "Auto" column it sits in, so the pane column takes the width back.
|
||||
-->
|
||||
<views:SessionSidebar Grid.Column="1"
|
||||
IsVisible="{Binding ShowsQuickAccessSidebar, FallbackValue=False}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Panel>
|
||||
|
||||
<!-- ============ S3 ============ -->
|
||||
<!--
|
||||
One WebView hosting every terminal. Not one per tab: each WebView2 is a separate browser
|
||||
process tree, so twenty tabs would cost twenty of them.
|
||||
The same screen as SFTP above, over the same view model, because an object store and an
|
||||
SFTP host are both an IRemoteFileStore and everything below the picker was written once.
|
||||
What differs is which picker is offered, and that is decided by the destination rather than
|
||||
by a toggle inside the screen — see ShowFiles, and the rail's own switcher segment that
|
||||
calls it now; see NavRail.axaml.
|
||||
|
||||
A sibling of the page area rather than a child of any screen, which is the structural half of
|
||||
the tab rework: the terminal belongs to the window now, not to the hosts screen.
|
||||
|
||||
FallbackValue, because a compiled binding with no DataContext yields UnsetValue, IsVisible
|
||||
then falls back to its default of true, and the occlusion comes back silently. Not reachable
|
||||
at runtime — the DataContext is set before the window is shown — but it is what the previewer
|
||||
does.
|
||||
Deliberately not given the session shell above: a bucket is not a host, has no terminal tab
|
||||
to be the other end of a cross-surface button, and pins nothing the sidebar's QUICK ACCESS
|
||||
could show. Wave B's own scope is the terminal and SFTP surfaces; S3 stays the plain screen it
|
||||
already was, and this is the recorded reason rather than an oversight.
|
||||
-->
|
||||
<NativeWebView x:Name="Terminal"
|
||||
IsVisible="{Binding IsTerminalShowing, FallbackValue=False}" />
|
||||
<Panel IsVisible="{Binding IsBucketsScreen}">
|
||||
<views:TransfersScreen DataContext="{Binding Transfers}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ KEYCHAIN ============ -->
|
||||
<!--
|
||||
Wrapped rather than bound directly, for the reason the vault column always was: this
|
||||
element's visibility is the shell's business and its data context is the vault, and put both
|
||||
on one element and IsVisible resolves against the vault as well, where IsKeychainScreen does
|
||||
not exist.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsKeychainScreen}">
|
||||
<views:KeychainScreen x:Name="VaultPane" DataContext="{Binding Vault}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ HOST KEYS ============ -->
|
||||
<!--
|
||||
Wrapped, like the two above and for the same reason: its data context is the screen's own
|
||||
view model, where IsKnownHostsScreen does not exist.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsKnownHostsScreen}">
|
||||
<views:KnownHostsScreen x:Name="PinsPane" DataContext="{Binding KnownHostsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ SNIPPETS ============ -->
|
||||
<!-- Wrapped, like the others whose data context is their own view model. -->
|
||||
<Panel IsVisible="{Binding IsSnippetsScreen}">
|
||||
<views:SnippetsScreen x:Name="SnippetsPane" DataContext="{Binding SnippetsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ LOGS ============ -->
|
||||
<!-- Wrapped, like the others whose data context is their own view model. -->
|
||||
<Panel IsVisible="{Binding IsLogsScreen}">
|
||||
<views:LogsScreen x:Name="LogsPane" DataContext="{Binding LogsScreen}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ VAULTS ============ -->
|
||||
<!--
|
||||
Wrapped, for the reason the keychain and transfers screens are: the visibility is the shell's
|
||||
business and the data context is the vaults view model, and both on one element would resolve
|
||||
IsVaultsScreen against a type that does not have it.
|
||||
|
||||
Bound to Vaults, which is the vaults themselves and the people in them — not to Vault, which
|
||||
is one vault's contents and is what the keychain and hosts screens above draw.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsVaultsScreen}">
|
||||
<views:VaultsScreen DataContext="{Binding Vaults}" />
|
||||
</Panel>
|
||||
|
||||
<!-- ============ PREFERENCES ============ -->
|
||||
<views:PreferencesScreen IsVisible="{Binding IsPreferencesScreen}" />
|
||||
|
||||
<!-- ============ IMPORT ============ -->
|
||||
<!--
|
||||
Reached from preferences rather than from the rail; see ShellScreen.Import. Wrapped, like
|
||||
the others whose data context is their own view model.
|
||||
-->
|
||||
<Panel IsVisible="{Binding IsImportScreen}">
|
||||
<views:ImportScreen x:Name="ImportPane" DataContext="{Binding ImportScreen}" />
|
||||
</Panel>
|
||||
|
||||
</Panel>
|
||||
|
||||
<!-- ============ TERMINAL ============ -->
|
||||
<!--
|
||||
◆ v5b's session shell for the terminal surface, built the same way SFTP's own wrapper above is —
|
||||
around the real terminal control rather than around a page.
|
||||
|
||||
Gated on IsTerminalSurface rather than on IsShowingPages: the two are always exclusive, because
|
||||
Surface is a single ShellSurface value, so this Grid and the pages Panel above it are never both
|
||||
visible at once. That is what keeps the occlusion rule intact with the WebView now nested inside
|
||||
a padded column, a bordered container and a header row rather than sitting directly beside the
|
||||
page area the way it used to.
|
||||
|
||||
THE RULE ITSELF DID NOT MOVE. NativeWebView's own IsVisible binding, below, is unchanged and is
|
||||
not hoisted to this Grid — hoisting it is the one alternative the pin-strip era of this file
|
||||
called out as unverified, and nesting the control deeper without touching its own binding is not
|
||||
that: IsTerminalShowing already depends on IsTerminalSurface, so this Grid's own visibility and
|
||||
the WebView's own visibility flip together on every surface change, driven by the same property
|
||||
change rather than one waiting on the other.
|
||||
|
||||
The tab row's own click selects a terminal tab exactly as the old window-wide strip's did; see
|
||||
SelectTabCommand. "+" keeps its current meaning, quick connect, on both this row and SFTP's own.
|
||||
-->
|
||||
<Grid RowDefinitions="Auto,*" Margin="26" IsVisible="{Binding IsTerminalSurface}">
|
||||
<views:SessionTabRow Grid.Row="0" TabCommand="{Binding SelectTabCommand}" />
|
||||
<Border Grid.Row="1" BorderBrush="{StaticResource Border}" BorderThickness="1"
|
||||
CornerRadius="0,0,12,12" ClipToBounds="True">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Grid Grid.Column="0" RowDefinitions="Auto,*,Auto">
|
||||
<views:SessionHeader Grid.Row="0"
|
||||
OpenLabel="Open SFTP"
|
||||
OpenCommand="{Binding SelectFilesHostCommand}"
|
||||
OpenCommandParameter="{Binding SelectedTab}"
|
||||
EmptyText="no terminals open · press + or Ctrl+K, or choose a host and press Connect" />
|
||||
|
||||
<Panel Grid.Row="1" Background="{StaticResource Pane}">
|
||||
|
||||
<!--
|
||||
The other thing that can be in the terminal's rectangle: a tab whose session does not
|
||||
exist yet, or never will. Exclusive with the WebView below by construction — a
|
||||
selected tab either has a session or it does not — which is what makes drawing it here
|
||||
safe under the occlusion rule, the same way the page area is. See ConnectingCard.axaml.
|
||||
-->
|
||||
<views:ConnectingCard x:Name="ConnectingPane"
|
||||
IsVisible="{Binding IsConnectingShowing, FallbackValue=False}" />
|
||||
|
||||
<!--
|
||||
One WebView hosting every terminal. Not one per tab: each WebView2 is a separate
|
||||
browser process tree, so twenty tabs would cost twenty of them.
|
||||
|
||||
FallbackValue, because a compiled binding with no DataContext yields UnsetValue,
|
||||
IsVisible then falls back to its default of true, and the occlusion comes back
|
||||
silently. Not reachable at runtime — the DataContext is set before the window is shown
|
||||
— but it is what the previewer does.
|
||||
-->
|
||||
<NativeWebView x:Name="Terminal"
|
||||
IsVisible="{Binding IsTerminalShowing, FallbackValue=False}" />
|
||||
|
||||
</Panel>
|
||||
|
||||
<views:SessionStatusBar Grid.Row="2" ShowsEncoding="True" />
|
||||
</Grid>
|
||||
<!--
|
||||
Hides when no session is active — see ShowsQuickAccessSidebar — rather than always drawn:
|
||||
the terminal surface is reachable with no tab open at all (the rail's own SSH segment), and
|
||||
a sidebar naming no host would be furniture with nothing to say. Collapsing frees the
|
||||
"Auto" column it sits in, so the pane column takes the width back.
|
||||
-->
|
||||
<views:SessionSidebar Grid.Column="1" ShowsSnips="True"
|
||||
IsVisible="{Binding ShowsQuickAccessSidebar, FallbackValue=False}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Panel>
|
||||
</Grid>
|
||||
|
||||
<!--
|
||||
|
||||
Reference in new issue
Block a user