Public Access
The chosen-hosts set has been in VaultViewModel since the phone's connect card became a contextual action bar: a set of entity ids, a tick on the row, and seven things that can be done to it. Only one head could fill it. The desktop's grid answered a press with one selection — the card the drawer, CONNECT and the context menu are about — so filing eleven imported machines under a heading was eleven drags, and clearing out a vault was eleven rounds of the deletion question. So the pointer gets three ways into the same set. Ctrl-clicks a card to tick it, Shift-clicks to tick the run between the anchor and the card, and drags a band out over the space between and below the cards to tick everything it touches. Esc, CLEAR, a plain click on a card and a click on the empty space each drop it, and Ctrl+A takes every card being drawn — VisibleHosts, so with something in the find box that is the ones on screen and not the ones it is hiding, which is the version of that shortcut whose result can be checked before Delete is pressed. TWO SELECTIONS ON ONE SCREEN, AND KEEPING THEM FROM DISAGREEING IS MOST OF THE CHANGE. Ctrl and Shift are answered on the tunnel and marked handled, so the ListBox never moves its own mark onto the card: a Ctrl-click that also selected would light the card it had just unticked and open the drawer on a machine somebody is removing from a set. A plain press drops the set unless it lands on a ticked card, and that case is deferred to the release, because the press may be the start of a drag of all of it. After any ordinary click exactly one card is in play, which is what makes every command on the screen unambiguous again. The context menu is where the seven live, and it is one markup with two halves gated on IsChoosingHosts. Connect, Browse files and Edit… are drawn only for a single ticked host, as the phone's sheet collapses them and for the same reason; the other four read better for a count. A right click on a card outside the set drops the set first, so a Delete… about the card under the pointer can never be offered while six sit ticked behind the menu — the same rule OnContextRequested has always enforced for the selection, reached from the other direction. No bar of buttons: the phone raises one because it has no other way to hold seven entries, and a strip repeating a menu that already exists would be a second home for the wording that matters most. What the desktop gains instead is a count beside the HOSTS heading, CLEAR, and a sentence saying where the actions are. A drag that starts on a ticked card carries every ticked card. The payload is a list rather than a row now, and a drop of more than one goes through FileChosenHostsUnder, which makes the refusals once — an open editor, and a group belonging to one keychain — and reports a count instead of forty status lines. Moving whichever card the pointer happened to be holding and leaving the other five where they are is a gesture that quietly does a fraction of what it looks like it does, and the five left behind look filed. The three panels the set's actions raise had never been drawn in a window: the vault picker with its key question, the group picker, and the deletion question. All three sit above the grid rather than over it, which is the arrangement the GROUPS section and the phone's list already use and for the reason written there — the ticked cards are the information the question exists to give, so the grid shortens instead. A DEFECT FOUND BEHIND IT, AND IT WAS ALREADY LIVE ON THE PHONE. The deletion question names a count and the run that answers it reads the set again, and nothing kept the two the same set: the panel is deliberately above a live list, so one more tick between "Delete these 6 hosts?" and pressing DELETE deleted seven, with the seventh named in nothing the user had read. It needed a deliberate act on a phone and a second's work with a band, which is what turned it up. VaultViewModel now remembers which hosts the question was asked about and drops the question when the set stops being them — the question rather than the set, because what somebody has just chosen is what they meant. It also covers the case nobody performs: a colleague's deletion arriving mid-question and shrinking the set under it. VERIFIED. 354 tests in App.Tests and 111 in App.Layout.Tests, build clean, no new warnings. Six gesture tests drive real pointer and key input through the headless window — the modifier click and what it must not do to the selection, the run and its re-measurement from the anchor, the band and the click that drops the set, Ctrl+A under a filter, and the menu's two halves — plus a DragOver carrying two hosts. Four layout tests measure the strip and the three panels at the window's minimum; the vault panel binds a key to its host first, or it would measure the short shape and certify the tall one. Two flow tests cover the multi-drop's write and its refusal, and the deletion question dropping itself. manual-checks gains 7.6a for dragging a set, which no test can see for the reason 7.6 gives, and 7.7a for the gestures — the rectangle actually being painted and the tick and the fill being legible together are the two things the harness cannot look at.
962 lines
52 KiB
XML
962 lines
52 KiB
XML
<Application xmlns="https://github.com/avaloniaui"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
x:Class="DodoSSH.Client.App.DodoSshApp"
|
|
RequestedThemeVariant="Dark">
|
|
|
|
<!--
|
|
Dark by default rather than following the system. A terminal is a dark surface either way, and
|
|
a light chrome around a dark terminal is the worst of both: the eye adapts to the bright frame
|
|
and then cannot read the text.
|
|
-->
|
|
|
|
<Application.Resources>
|
|
|
|
<!--
|
|
The palette and the font stack, shared with the Android head. They moved into DodoSSH.Client.Shell
|
|
when there were two heads that had to agree on them; Theme/Palette.axaml carries the reasoning behind
|
|
the names, the five near-black surfaces and the font substitution.
|
|
-->
|
|
<ResourceDictionary>
|
|
<ResourceDictionary.MergedDictionaries>
|
|
<ResourceInclude Source="avares://DodoSSH.Client.Shell/Theme/Palette.axaml" />
|
|
</ResourceDictionary.MergedDictionaries>
|
|
</ResourceDictionary>
|
|
|
|
</Application.Resources>
|
|
|
|
<Application.Styles>
|
|
<FluentTheme />
|
|
|
|
<!--
|
|
Application-wide rather than on MainWindow, which is where the first three lived until the vault column
|
|
became its own control. A Window's styles reach its whole tree, so the move changes nothing about how
|
|
the application renders — but a control laid out on its own, as the layout harness lays the column out,
|
|
would otherwise lose them. That matters more than it looks: "hint" carries TextWrapping, and text that
|
|
wraps in the application and does not wrap in the harness makes every measured height too small, which
|
|
is the one way this kind of test lies quietly.
|
|
-->
|
|
<!--
|
|
◆ THE BASE SIZE, and it is set here rather than on the window.
|
|
|
|
Every explicit FontSize below is a step off a base nothing was stating: a bare TextBlock took
|
|
TextElement's own default of 12, which meant the one number the whole scale is measured from lived in
|
|
Avalonia rather than in this file. Raising the scale meant naming it.
|
|
|
|
Declared on Window *and* on UserControl, which is not redundancy. FontSize inherits, so the window
|
|
alone would reach everything the application draws — but the layout harness hosts a UserControl in a
|
|
plain `new Window()` it constructs itself, and that window has no style of this application's on it.
|
|
Without the second selector the harness would measure every screen a point smaller than it ships,
|
|
which is the failure mode App.axaml's own note at the top of this block was written about: a test that
|
|
lays out text smaller than the application does reports heights that are all slightly too small, and
|
|
it does it silently.
|
|
|
|
A selector on TextBlock would have been the obvious way and is wrong. Style setters beat inherited
|
|
values in Avalonia, so `Selector="TextBlock"` would win over the size a Button sets on itself and
|
|
reach the TextBlock inside that button's template — collapsing every deliberate step below back to one
|
|
number.
|
|
-->
|
|
<Style Selector="Window">
|
|
<Setter Property="FontSize" Value="13" />
|
|
</Style>
|
|
<Style Selector="UserControl">
|
|
<Setter Property="FontSize" Value="13" />
|
|
</Style>
|
|
|
|
<Style Selector="TextBlock.hint">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
<Setter Property="TextWrapping" Value="Wrap" />
|
|
</Style>
|
|
<Style Selector="TextBlock.heading">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="FontSize" Value="19" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
</Style>
|
|
<Style Selector="Border.card">
|
|
<Setter Property="Background" Value="{StaticResource Chrome}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="12" />
|
|
<Setter Property="Padding" Value="24" />
|
|
<Setter Property="MaxWidth" Value="520" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
|
</Style>
|
|
|
|
<!--
|
|
Monospace, which in this design is not a decoration for code. Host names, fingerprints, addresses and
|
|
counts are all things people compare character by character, and a proportional font is what makes two
|
|
similar fingerprints look alike.
|
|
-->
|
|
<Style Selector="TextBlock.mono">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
</Style>
|
|
<Style Selector="SelectableTextBlock.mono">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The small tracked-out capitals the design uses for every section heading. Letter spacing rather than
|
|
bold: at this size weight closes the counters up and the word turns into a smudge, whereas spacing is
|
|
what makes six characters read as a label instead of as an abbreviation.
|
|
-->
|
|
<Style Selector="TextBlock.label">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
<Setter Property="LetterSpacing" Value="1.2" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
|
|
<!--
|
|
A chip: a bordered scrap of text stating one fact. The design uses them for tags, roles, scopes and
|
|
shortcut hints, which have nothing in common except that each is a noun the eye should be able to skip.
|
|
-->
|
|
<Style Selector="Border.chip">
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Padding" Value="6,2" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
<Style Selector="Border.chip > TextBlock">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11" />
|
|
<Setter Property="FontWeight" Value="Medium" />
|
|
<Setter Property="LetterSpacing" Value="0.5" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Border.chip.accent">
|
|
<Setter Property="BorderBrush" Value="{StaticResource AccentSoft}" />
|
|
</Style>
|
|
<!--
|
|
AccentText rather than Accent, which is the v2 split: the fill colour is too saturated to read as
|
|
small text on a dark surface, and a chip is small text. See the remark on the pair in Palette.axaml.
|
|
-->
|
|
<Style Selector="Border.chip.accent > TextBlock">
|
|
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
|
|
</Style>
|
|
<!--
|
|
A chip that is also a button: the tag picker in the host editor. It borrows Border.chip's geometry
|
|
rather than inheriting it, because a Button is not a Border and Avalonia's selectors are structural —
|
|
the numbers are repeated here and the two have to move together, which is why they sit adjacent.
|
|
|
|
Worn is filled, unworn is outlined, and the difference is the accent rather than a tick: a row of
|
|
these has to read as "the tags this host has" at a glance, and a checkbox column reads as a form.
|
|
-->
|
|
<Style Selector="Button.chiptoggle">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Padding" Value="6,2" />
|
|
<Setter Property="MinHeight" Value="0" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontWeight" Value="Medium" />
|
|
</Style>
|
|
<Style Selector="Button.chiptoggle.worn">
|
|
<Setter Property="BorderBrush" Value="{StaticResource AccentSoft}" />
|
|
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
|
|
</Style>
|
|
<Style Selector="Button.chiptoggle.worn /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource AccentWash}" />
|
|
</Style>
|
|
<Style Selector="Button.chiptoggle:pointerover /template/ ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
</Style>
|
|
|
|
<Style Selector="Border.chip.warn">
|
|
<Setter Property="BorderBrush" Value="{StaticResource WarnSoft}" />
|
|
</Style>
|
|
<Style Selector="Border.chip.warn > TextBlock">
|
|
<Setter Property="Foreground" Value="{StaticResource Warn}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The buttons. Fluent's own chrome is a rounded grey slab, and one of those beside a hairline-bordered
|
|
chip reads as belonging to a different application — so the three shapes this design uses are declared
|
|
here rather than restyled per view.
|
|
|
|
Each sets its brushes on the content presenter, not on the button. That is where the Fluent theme puts
|
|
its own, so a Background set on the button is a TemplateBinding the theme's :pointerover rule overrides
|
|
— which shows up as a button that is styled until the pointer arrives and then is not.
|
|
-->
|
|
<Style Selector="Button.ghost /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="7" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Button.ghost:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderHover}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
<Style Selector="Button.ghost:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderFaint}" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
|
|
<Style Selector="Button.accent /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Accent}" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="CornerRadius" Value="7" />
|
|
<Setter Property="Foreground" Value="{StaticResource AccentInk}" />
|
|
</Style>
|
|
<Style Selector="Button.accent:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Accent}" />
|
|
<Setter Property="Opacity" Value="0.85" />
|
|
<Setter Property="Foreground" Value="{StaticResource AccentInk}" />
|
|
</Style>
|
|
<!--
|
|
A disabled accent button keeps its shape and loses its fill. Fluent's disabled state greys the
|
|
foreground and leaves the background, which on a green slab is unreadable rather than merely dim.
|
|
-->
|
|
<Style Selector="Button.accent:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderFaint}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
|
|
<Style Selector="Button.danger /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource DangerSoft}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="7" />
|
|
<Setter Property="Foreground" Value="{StaticResource Danger}" />
|
|
</Style>
|
|
<Style Selector="Button.danger:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource DangerWash}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Danger}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Danger}" />
|
|
</Style>
|
|
<Style Selector="Button.danger:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderFaint}" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
|
|
<!-- Every button in this window is small, mono and tracked out; only the colours differ. -->
|
|
<Style Selector="Button.ghost, Button.accent, Button.danger">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11.5" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
<Setter Property="LetterSpacing" Value="0.8" />
|
|
<Setter Property="Padding" Value="10,5" />
|
|
<Setter Property="MinHeight" Value="0" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
|
|
<!--
|
|
Buttons that are not shaped like buttons: a group heading, a nav-rail destination, a terminal tab, a
|
|
window control. All four are clickable regions that read as part of the chrome, so they carry no
|
|
border and no fill until the pointer is over them — and all four are buttons rather than borders with
|
|
a gesture, because a button is focusable, keyboard-activatable and announced as a control.
|
|
-->
|
|
<Style Selector="Button.flat">
|
|
<Setter Property="MinHeight" Value="0" />
|
|
<Setter Property="MinWidth" Value="0" />
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
</Style>
|
|
<Style Selector="Button.flat /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
</Style>
|
|
<Style Selector="Button.flat:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
|
|
</Style>
|
|
<Style Selector="Button.flat:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
|
|
</Style>
|
|
|
|
<!--
|
|
Button.grouphead was here: the padding on the fold-away group heading that used to sit between the host
|
|
cards. The headings went when the grid became cards and the group cards above it became the thing that
|
|
names a group — see HostsScreen.axaml — and a style with nothing wearing it is a shape somebody will
|
|
later reintroduce by reaching for it.
|
|
-->
|
|
|
|
<!-- A category in the vault's rail: the same accent strip a selected row carries, at full row height. -->
|
|
<Style Selector="Button.cat">
|
|
<Setter Property="Padding" Value="0,6,14,6" />
|
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11" />
|
|
<Setter Property="FontWeight" Value="Medium" />
|
|
<Setter Property="LetterSpacing" Value="0.5" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Button.cat /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Button.cat.active /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource AccentWash}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
<!--
|
|
The nav rail. The active destination is marked with an accent bar down its left edge and a wash
|
|
behind it, which is the design's whole idiom for "you are here" — the same two marks a selected list
|
|
row carries, so the window has one vocabulary for selection rather than one per control.
|
|
-->
|
|
<!--
|
|
A destination in the sidebar. v2 turns these from 54-pixel stacked labels into 32-pixel rows with a
|
|
glyph, a word and a count, and the active one becomes a filled rounded row rather than a label with an
|
|
accent bar down its left edge. The bar is gone because a filled row at this width already reads as
|
|
chosen, and the bar was carrying that on its own when there was no room for a fill.
|
|
-->
|
|
<Style Selector="Button.nav">
|
|
<Setter Property="Height" Value="32" />
|
|
<Setter Property="Padding" Value="10,0" />
|
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="FontWeight" Value="Medium" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Button.nav /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
</Style>
|
|
<Style Selector="Button.nav:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
</Style>
|
|
<Style Selector="Button.nav.active /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
|
|
<Setter Property="Background" Value="{StaticResource Active}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The three parts of a sidebar row. Separate classes rather than inline setters because nine rows draw
|
|
them and the day one of the three moves is the day eight of them would not have.
|
|
|
|
The glyph and the label inherit the row's own foreground, so they light with it; the count does not,
|
|
because a number that lit with its row would compete with the word beside it for the same emphasis.
|
|
-->
|
|
<Style Selector="TextBlock.navicon">
|
|
<Setter Property="FontSize" Value="14" />
|
|
<Setter Property="Width" Value="20" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
<Style Selector="TextBlock.navlabel">
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
|
</Style>
|
|
<Style Selector="TextBlock.navcount">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
|
|
<!--
|
|
A terminal tab, and since v2 a pill rather than a filing-cabinet tab: 30 tall inside a 42 strip, its
|
|
own rounded outline, and the active one filled instead of marked along an edge. The edge mark is gone
|
|
because a pill has no edge to share with its neighbour — the gap between two of them is the divider
|
|
the old top-and-right border was standing in for.
|
|
-->
|
|
<Style Selector="Button.tab">
|
|
<!-- Less on the right than the left: the close box lives inside the tab and brings its own margin. -->
|
|
<Setter Property="Padding" Value="12,0,8,0" />
|
|
<Setter Property="Height" Value="30" />
|
|
<Setter Property="Margin" Value="0,0,6,0" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="FontWeight" Value="Medium" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ ORDER IS THE BEHAVIOUR HERE. The base pill comes first, then its hover, then the exceptions — and
|
|
that ordering is the fix for a bug this arrangement had when v2 first drew it.
|
|
|
|
A pill paints its own Background, which the flat tab it replaced did not. That one detail moves where
|
|
the hover has to live: `Button.flat:pointerover` is declared far above and used to supply it, and the
|
|
moment `.tab`'s template rule set a Background of its own, the later declaration won and every tab in
|
|
the strip lost its pointer feedback silently. The `+` lost more than that — it sits below as an
|
|
exception, so a `.tab` rule declared after it was overriding the very thing that made it an exception,
|
|
and it drew as a filled outlined pill contradicting the comment above it.
|
|
|
|
Avalonia has no specificity; the later declaration wins. The same trap is recorded further down this
|
|
file for Border.rowmark. Anything added below must be an exception to what is above it, never a
|
|
restatement of the base.
|
|
-->
|
|
<Style Selector="Button.tab /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
<Setter Property="Background" Value="{StaticResource Panel}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
</Style>
|
|
<Style Selector="Button.tab:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
<!--
|
|
One of the strip's three fixed tabs — Vaults, SFTP, S3. A pill in every respect except that it has no
|
|
close box, so it takes its padding back on the right: the base rule is short there to leave room for
|
|
the cross a terminal tab carries inside itself, and a fixed tab with the same asymmetry sits visibly
|
|
off-centre beside one that has a reason for it.
|
|
|
|
Nothing else differs, deliberately. These are tabs and have to read as tabs — the whole point of the
|
|
strip is that "where the window is" is one row of one kind of control.
|
|
-->
|
|
<Style Selector="Button.tab.fixed">
|
|
<Setter Property="Padding" Value="12,0" />
|
|
</Style>
|
|
|
|
<!--
|
|
The button that opens a connection. A tab in every respect but the marks a tab carries: no active
|
|
state, because it is never the thing showing, and no outline, because it is not one of the things
|
|
being chosen between. Both of those are cleared rather than merely omitted — the base rule above sets
|
|
a fill and a border, so an exception has to say so.
|
|
-->
|
|
<Style Selector="Button.tab.plus">
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="Width" Value="28" />
|
|
<Setter Property="Height" Value="28" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
<Style Selector="Button.tab.plus /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
</Style>
|
|
<Style Selector="Button.tab.plus:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
</Style>
|
|
|
|
<Style Selector="Button.tab.active /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Active}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The two halves of the Vaults tab: the tab itself, and the caret that opens its menu. Two buttons
|
|
because they do two things, drawn as one pill because they are one destination — so the pair meets in
|
|
the middle with no gap, no doubled border down the join, and the outer corners rounded as any tab's
|
|
are.
|
|
|
|
Both the Button and its ContentPresenter carry a CornerRadius above, so both have to be squared here:
|
|
setting only one leaves a rounded outline inside a square hit area, which shows as a hairline of the
|
|
strip's background cutting through the join.
|
|
|
|
After Button.tab.fixed rather than beside it, because .caret takes that rule's padding back to zero
|
|
and Avalonia has no specificity — the later declaration is the one that wins. See the ordering note
|
|
above.
|
|
-->
|
|
<Style Selector="Button.tab.split">
|
|
<Setter Property="Margin" Value="0" />
|
|
<Setter Property="CornerRadius" Value="8,0,0,8" />
|
|
</Style>
|
|
<Style Selector="Button.tab.split /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="8,0,0,8" />
|
|
<Setter Property="BorderThickness" Value="1,1,0,1" />
|
|
</Style>
|
|
<Style Selector="Button.tab.caret">
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
|
</Style>
|
|
<Style Selector="Button.tab.caret /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
|
</Style>
|
|
|
|
<!--
|
|
A pair of buttons standing in for a two-way choice, inside a pane rather than down a rail. Not the
|
|
.cat style, which stretches to fill a 176-pixel rail row and would be wrong at this width — and which
|
|
the category rail's own test counts, so borrowing it would have made this a fourth category.
|
|
-->
|
|
<Style Selector="Button.choice">
|
|
<Setter Property="Padding" Value="10,5" />
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="10.5" />
|
|
<Setter Property="LetterSpacing" Value="0.5" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
<Style Selector="Button.choice /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Background" Value="{StaticResource Raised}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="Button.choice:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
<Style Selector="Button.choice.active /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource AccentWash}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Accent}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
<!-- The close box on a tab, and the window controls. Square, quiet, and red only where it means it. -->
|
|
<Style Selector="Button.close /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
<Style Selector="Button.close:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource DangerWash}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Danger}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The one inside a tab, as opposed to the ones in the titlebar. Rounded and small, because a square
|
|
full-height red panel inside a tab reads as a divider between two tabs rather than as part of one —
|
|
which is what it looked like while it was a sibling of the tab instead of a child.
|
|
-->
|
|
<Style Selector="Button.close.inline /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="3" />
|
|
</Style>
|
|
|
|
<!--
|
|
Text input. Fluent draws a filled box with a thick focus underline; this design draws a hairline field
|
|
that changes border colour, and the two do not sit together in one row.
|
|
-->
|
|
<Style Selector="TextBox">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="SelectionBrush" Value="{StaticResource AccentSoft}" />
|
|
<Setter Property="CaretBrush" Value="{StaticResource Accent}" />
|
|
<Setter Property="MinHeight" Value="30" />
|
|
<Setter Property="Padding" Value="8,4" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
</Style>
|
|
<Style Selector="TextBox /template/ Border#PART_BorderElement">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
</Style>
|
|
<Style Selector="TextBox:pointerover /template/ Border#PART_BorderElement">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
</Style>
|
|
<Style Selector="TextBox:focus /template/ Border#PART_BorderElement">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource AccentSoft}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
</Style>
|
|
<Style Selector="TextBox /template/ TextBlock#PART_Watermark">
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
|
|
<!--
|
|
Lists. The rows in this design are drawn by their own templates and marked as selected with an accent
|
|
strip down the left edge, so the theme's rounded blue selection block has to go — it would sit behind
|
|
every row as a second, disagreeing highlight.
|
|
-->
|
|
<Style Selector="ListBox">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="Padding" Value="0" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem">
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="MinHeight" Value="0" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
<Setter Property="Padding" Value="0" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource AccentWash}" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource AccentWash}" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ A LIST OF TILES, which is the hosts screen's grid of group and host cards.
|
|
|
|
The three rules above paint the *item* — a full-bleed rectangle behind whatever the template draws —
|
|
and that is right for a row and wrong for a card: a square wash behind a 10-pixel-rounded tile shows
|
|
as four grey corners, and it does it on hover as well as on selection, which is most of the time the
|
|
pointer is anywhere near the grid. So a tiles list clears all three and the tile paints its own
|
|
states.
|
|
|
|
Declared after them and not before. Avalonia has no specificity: two rules matching one element are
|
|
settled by declaration order, so an exception stated above the rule it excepts does nothing at all.
|
|
That trap is recorded twice more in this file, for Button.tab and for Border.rowmark.
|
|
-->
|
|
<Style Selector="ListBox.tiles > ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
<Style Selector="ListBox.tiles > ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
<Style Selector="ListBox.tiles > ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
|
|
<!--
|
|
The card itself. A fixed width and a free height, which is the pair that makes a WrapPanel of these
|
|
into a grid: equal columns, and a card that grew a third line of tags is taller than its neighbours
|
|
rather than narrower.
|
|
|
|
◆ 232 IS DERIVED, and the arithmetic is written out because getting it wrong is invisible. The grid's
|
|
column at the window's minimum is 1016 less the rail's 190 and the drawer's 304, which is 522. The
|
|
scrolling stack inside it takes 16 of margin on each side, and the vertical scrollbar takes its own —
|
|
call the usable width 490. A WrapPanel fits floor(490 / (Width + 10)) per row, so two columns needs
|
|
Width no more than 235.
|
|
|
|
The first number here was 248, from the same reasoning with the two margins left out. It laid out
|
|
cleanly and the layout harness passed it, because the harness asks whether a control is inside the
|
|
window and not how many of them fit on a line — so the grid quietly became one column wide at exactly
|
|
the size this application guarantees, which is the shape the cards exist to avoid.
|
|
-->
|
|
<Style Selector="Border.tile">
|
|
<Setter Property="Background" Value="{StaticResource Raised}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="10" />
|
|
<Setter Property="Padding" Value="12,10" />
|
|
<Setter Property="Width" Value="232" />
|
|
<Setter Property="Margin" Value="0,0,10,10" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:pointerover Border.tile">
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderHover}" />
|
|
</Style>
|
|
<!--
|
|
Selected is the accent outline *and* the filled surface, which is the same pair the nav rail and the
|
|
tab strip use for "you are here". A tile marked by its border alone is legible on a card you are
|
|
looking at and invisible in peripheral vision, which is where a selected card usually is once the
|
|
drawer beside it has opened.
|
|
-->
|
|
<Style Selector="ListBoxItem:selected Border.tile">
|
|
<Setter Property="Background" Value="{StaticResource Active}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Accent}" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ A CARD IN THE CHOSEN SET, which is not the same thing as the selected one.
|
|
|
|
The list holds one selection — the card the drawer and CONNECT are about — and the set is what Ctrl,
|
|
Shift and a band dragged across the grid build on top of it. Six ticked cards with one of them also
|
|
selected is the ordinary shape, so the two marks have to be legible together and legible apart.
|
|
|
|
The fill and the tick on the card are the mark; the border is deliberately left to the rule above. A
|
|
chosen card that also drew an accent outline would be indistinguishable from the selected one, and the
|
|
question "which of these is the drawer showing" would have no answer. Declared after that rule anyway,
|
|
because it sets the same Background and Avalonia settles two matching rules by declaration order —
|
|
the trap this file records for Button.tab, Border.rowmark and the drop target below.
|
|
-->
|
|
<Style Selector="Border.tile.chosen">
|
|
<Setter Property="Background" Value="{StaticResource Active}" />
|
|
</Style>
|
|
|
|
<!-- The square a tile carries on its left: a group's mark, or a host's prompt. -->
|
|
<Style Selector="Border.tileicon">
|
|
<Setter Property="Width" Value="32" />
|
|
<Setter Property="Height" Value="32" />
|
|
<Setter Property="CornerRadius" Value="8" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ THE PENCIL ON A HOST CARD, which is the only way the drawer opens from the grid.
|
|
|
|
It exists because selecting a host stopped opening it. A card that took 304 pixels off the grid the
|
|
moment it was touched charged the price of the pane for the act of choosing — most of which is arrowing
|
|
past machines you did not want — so opening is now a thing you ask for and this is the asking. See
|
|
VaultViewModel.IsHostPaneOpen.
|
|
|
|
Hidden until the pointer is on the card, and shown on the selected one whether or not the pointer is
|
|
there, because the selected card is the one whose pane you are most likely to want and a control that
|
|
vanished from under the card you just clicked would have to be hunted for. Both are declared after the
|
|
base rule: Avalonia has no specificity and settles two matching rules by declaration order, which this
|
|
file records three times over.
|
|
|
|
IsVisible rather than Opacity. A button at zero opacity is still a button — it takes the click, and the
|
|
card underneath does not — so half the grid would swallow presses aimed at selecting a host. The space
|
|
is held open by the Panel around it in the item template rather than by this, so the card does not
|
|
reflow as the pointer crosses it; see HostsScreen.axaml.
|
|
-->
|
|
<Style Selector="Button.rowedit">
|
|
<Setter Property="Width" Value="24" />
|
|
<Setter Property="Height" Value="24" />
|
|
<Setter Property="CornerRadius" Value="6" />
|
|
<Setter Property="FontSize" Value="12" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
<Setter Property="IsVisible" Value="False" />
|
|
</Style>
|
|
<Style Selector="Button.rowedit /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="6" />
|
|
</Style>
|
|
<Style Selector="Button.rowedit:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
|
|
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:pointerover Button.rowedit">
|
|
<Setter Property="IsVisible" Value="True" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:selected Button.rowedit">
|
|
<Setter Property="IsVisible" Value="True" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ THE DRAWER'S FURNITURE: a card, its heading, and the rows inside it.
|
|
|
|
The hosts drawer is a column of grouped fields rather than a run of labelled controls — address here,
|
|
the general facts here, the connection and its credentials here — which is what the design draws and
|
|
what a 304-pixel column needs to stay readable: three cards is three things to find, where fourteen
|
|
stacked controls is fourteen.
|
|
|
|
Raised on Sidebar, which is the one pairing that works in this column. Panel and Chrome are the same
|
|
value as each other and near enough Sidebar to vanish against it, and Field is the well a control sits
|
|
in — a card drawn in it would read as one enormous input.
|
|
-->
|
|
<Style Selector="Border.section">
|
|
<Setter Property="Background" Value="{StaticResource Raised}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="10" />
|
|
<Setter Property="Padding" Value="12" />
|
|
</Style>
|
|
|
|
<!--
|
|
A card's heading, and deliberately not TextBlock.label. That style is the small tracked-out capitals
|
|
this design uses for a *section of a screen* — GROUPS, HOSTS, the vault's categories — and three of
|
|
them inside a 304-pixel column would claim the same weight as the screen headings beside it. These are
|
|
captions on a card, so they are sentence case at the body size.
|
|
-->
|
|
<Style Selector="TextBlock.sectionhead">
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
<!--
|
|
◆ A ROW IN THE DETAIL PANE, AND IT IS A BUTTON THAT LOOKS LIKE A FIELD.
|
|
|
|
The design draws every fact about a host as a filled box, which is the shape of an input — so a reader
|
|
arrives expecting to type in one. They cannot: saving here is a whole-host operation that validates the
|
|
key-or-credential exclusion and writes one encrypted payload, so a box that committed on every
|
|
keystroke would be a save per character and a half-typed hostname on the wire.
|
|
|
|
Rather than draw an input that refuses the pointer, each row *is* the door to the editor. Pressing any
|
|
of them opens the host editor, which is the same three cards with real boxes in them — so the thing
|
|
that looked editable turns out to be editable, one step further along, and nothing on screen is a
|
|
control that does nothing. The alternative was a flat read-only pane with one EDIT button, which is
|
|
what this replaced.
|
|
|
|
It borrows the TextBox's own geometry rather than sharing it, for the reason Button.chiptoggle borrows
|
|
Border.chip's: a Button is not a TextBox and Avalonia's selectors are structural. The numbers are
|
|
repeated and the two have to move together, which is why they are adjacent in this file.
|
|
-->
|
|
<Style Selector="Button.fieldrow">
|
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
<Setter Property="MinHeight" Value="32" />
|
|
<Setter Property="Padding" Value="8,5" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
<Style Selector="Button.fieldrow /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
<Style Selector="Button.fieldrow:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Hover}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderHover}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The glyph at the head of such a row. A fixed width so that four rows in a card line their text up, and
|
|
the faintest step in the ramp because it is a mark to skip rather than a fact to read: what the row
|
|
says is to its right.
|
|
-->
|
|
<Style Selector="TextBlock.fieldglyph">
|
|
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
|
|
<Setter Property="FontSize" Value="11" />
|
|
<Setter Property="Width" Value="16" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
|
|
<!--
|
|
The two buttons in the drawer's header: the overflow menu and the arrow that puts the drawer away.
|
|
Chrome rather than controls — they are the frame around whichever panel is showing, which is why they
|
|
are flat and quiet until the pointer finds them.
|
|
-->
|
|
<Style Selector="Button.paneicon">
|
|
<Setter Property="Width" Value="26" />
|
|
<Setter Property="Height" Value="26" />
|
|
<Setter Property="CornerRadius" Value="6" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
|
|
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
</Style>
|
|
<Style Selector="Button.paneicon /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="CornerRadius" Value="6" />
|
|
</Style>
|
|
<Style Selector="Button.paneicon:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The status dot, in one place rather than as a converter in code.
|
|
|
|
Two states and not the design's three. Green means connected — a terminal is open on this host, or a
|
|
connection to the server is held with nothing waiting to be sent. Grey means it is not. The design also
|
|
has an amber dot, which would have to mean "reachable but not connected", and nothing in this
|
|
application ever checks whether a host is reachable; a colour for a state nobody computes would be
|
|
decoration that reads as information.
|
|
|
|
A style rather than a value converter so that the two colours are the palette's, once. A converter
|
|
would have had to name #3DDC97 in C#, which is how a green ends up one shade off in the fourth place
|
|
it appears.
|
|
|
|
Live rather than Accent, and that is a v2 change with meaning rather than a recolour. The accent is
|
|
blue now, and the green survives as the colour of a fact — see the remark on Live in Palette.axaml.
|
|
Filling this with Accent would say a connected host is a thing to press.
|
|
-->
|
|
<Style Selector="Ellipse.dot">
|
|
<Setter Property="Width" Value="6" />
|
|
<Setter Property="Height" Value="6" />
|
|
<Setter Property="Fill" Value="{StaticResource TextFaint}" />
|
|
</Style>
|
|
<Style Selector="Ellipse.dot.live">
|
|
<Setter Property="Fill" Value="{StaticResource Live}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The accent strip a selected row carries, drawn by the row template rather than by the item, because the
|
|
item's presenter is the thing the theme keeps repainting.
|
|
-->
|
|
<Style Selector="Border.rowmark">
|
|
<Setter Property="Width" Value="2" />
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
<Style Selector="ListBoxItem:selected Border.rowmark">
|
|
<Setter Property="Background" Value="{StaticResource Accent}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The group card a host is about to be dropped on. A wash rather than the accent strip a selection
|
|
carries, because it is not a selection: it says "let go here", it lasts as long as the pointer is over
|
|
the card, and it has to be legible on top of whatever that card already looks like — including the
|
|
selected one, which is the group the grid is currently narrowed to and so often the one being dragged
|
|
onto.
|
|
|
|
Painted on the tile rather than on the item's own presenter, which is where this used to go when the
|
|
target was a full-width heading. A wash behind a 10-pixel-rounded tile is a square of colour with the
|
|
card floating on it — the same four grey corners the tiles list clears the theme's own hover and
|
|
selection brushes to avoid. Declared after that rule and after the selected-tile rule, because
|
|
Avalonia has no specificity and settles two matching rules by declaration order.
|
|
|
|
◆ THE DOUBLED BORDER IS THE MARK, not the fill, and that is what the move to the tile forced. The fill
|
|
a selected tile already carries is the strongest one this palette has, so a target that only filled
|
|
would be invisible on the card the grid is narrowed to — which is the card a host most often has to be
|
|
dragged off, into another group. Two pixels of accent is legible on both.
|
|
|
|
Set from the code-behind rather than by a binding, for the reason the whole gesture is code: the rows
|
|
are rebuilt from scratch on every filter keystroke and every sync pass, so a flag on the view model
|
|
would be state the list throws away halfway through the drag. See HostsScreen.axaml.cs.
|
|
-->
|
|
<Style Selector="ListBoxItem.droptarget Border.tile">
|
|
<Setter Property="Background" Value="{StaticResource Active}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Accent}" />
|
|
<Setter Property="BorderThickness" Value="2" />
|
|
</Style>
|
|
|
|
<!--
|
|
A context menu, in this window's palette rather than the theme's. The Fluent default is a lighter grey
|
|
than anything else here, which on a near-black chrome reads as a dialog from another application.
|
|
|
|
FlyoutPresenter is in the same rule rather than one of its own: the vault menu on the tab strip is a
|
|
plain Flyout, and a popup that came out of this window in a different grey from the context menu two
|
|
screens away would read as two applications rather than one.
|
|
-->
|
|
<Style Selector="MenuFlyoutPresenter, ContextMenu, FlyoutPresenter">
|
|
<Setter Property="Background" Value="{StaticResource Chrome}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
</Style>
|
|
<Style Selector="MenuItem">
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
<!--
|
|
The same strip on the vault's category rail, which is buttons rather than list items — so the class
|
|
the rail sets stands in for the :selected pseudo-class.
|
|
|
|
It has to come after Border.rowmark, and that is not tidiness. Avalonia has no CSS specificity: two
|
|
class selectors matching one element carry the same priority, and the later declaration wins. Declared
|
|
above, "Background: Transparent" from Border.rowmark overrode this and the active category had no
|
|
strip at all — which looks exactly like a rail that was never given one.
|
|
-->
|
|
<Style Selector="Button.cat Border.catmark">
|
|
<Setter Property="Background" Value="Transparent" />
|
|
</Style>
|
|
<Style Selector="Button.cat.active Border.catmark">
|
|
<Setter Property="Background" Value="{StaticResource Accent}" />
|
|
</Style>
|
|
|
|
<Style Selector="ComboBox">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="MinHeight" Value="30" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
</Style>
|
|
<Style Selector="NumericUpDown">
|
|
<Setter Property="Background" Value="{StaticResource Field}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="CornerRadius" Value="4" />
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="MinHeight" Value="30" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
</Style>
|
|
<!--
|
|
The foreground goes on the content presenter as well as on the control, for the same reason the
|
|
buttons' brushes do: the Fluent theme sets its own there, and a Foreground set only on the CheckBox is
|
|
a TemplateBinding the theme's own value wins over — which showed up as a label in the theme's blue-grey
|
|
beside a hint in this palette's.
|
|
-->
|
|
<Style Selector="CheckBox">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
<Setter Property="FontSize" Value="13" />
|
|
<Setter Property="MinHeight" Value="0" />
|
|
</Style>
|
|
<Style Selector="CheckBox /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
|
|
</Style>
|
|
<Style Selector="CheckBox:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
</Style>
|
|
|
|
</Application.Styles>
|
|
|
|
</Application>
|