From c8507b44feeac195b1b987680c50c744390e7880 Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Sat, 8 Aug 2026 14:17:19 +0200 Subject: [PATCH] Give the application a settings area built from what really exists --- src/DodoSSH.Client.App/App.axaml | 51 ++ .../Views/ImportScreen.axaml | 307 ++++++---- .../Views/ImportScreen.axaml.cs | 10 +- src/DodoSSH.Client.App/Views/MainWindow.axaml | 83 ++- .../Views/MainWindow.axaml.cs | 23 +- src/DodoSSH.Client.App/Views/NavRail.axaml | 17 +- src/DodoSSH.Client.App/Views/NavRail.axaml.cs | 22 +- .../Views/PreferencesScreen.axaml | 275 --------- .../Views/PreferencesScreen.axaml.cs | 9 - .../Views/SettingsAccountPage.axaml | 94 +++ .../Views/SettingsAccountPage.axaml.cs | 9 + .../Views/SettingsGeneralPage.axaml | 114 ++++ .../Views/SettingsGeneralPage.axaml.cs | 9 + .../Views/SettingsGroupsPage.axaml | 194 +++++++ .../Views/SettingsGroupsPage.axaml.cs | 9 + .../Views/SettingsNav.axaml | 116 ++++ .../Views/SettingsNav.axaml.cs | 9 + .../Views/SettingsPreferencesPage.axaml | 134 +++++ .../Views/SettingsPreferencesPage.axaml.cs | 9 + .../Views/SettingsSecurityPage.axaml | 126 ++++ .../Views/SettingsSecurityPage.axaml.cs | 9 + .../Views/SettingsTagsPage.axaml | 150 +++++ .../Views/SettingsTagsPage.axaml.cs | 9 + .../Views/SettingsTitleBar.axaml | 79 +++ .../Views/SettingsTitleBar.axaml.cs | 47 ++ .../Views/SettingsVaultsPage.axaml | 407 +++++++++++++ .../Views/SettingsVaultsPage.axaml.cs | 29 + .../Views/SettingsView.axaml | 68 +++ .../Views/SettingsView.axaml.cs | 12 + .../Views/VaultsScreen.axaml | 362 ------------ .../Views/VaultsScreen.axaml.cs | 9 - .../ViewModels/ImportViewModel.cs | 109 +++- .../ViewModels/KnownHostsViewModel.cs | 36 +- .../ViewModels/MainWindowViewModel.cs | 288 +++++++++- .../ViewModels/VaultViewModel.cs | 95 +++ .../ViewModels/VaultsViewModel.cs | 95 +++ .../LayoutHarness.cs | 39 ++ .../NavRailTests.cs | 45 +- .../ScreenLayoutTests.cs | 246 +------- .../SettingsPagesLayoutTests.cs | 541 ++++++++++++++++++ .../ShellFlowTests.cs | 519 +++++++++++++++++ .../VaultSharingTests.cs | 79 +++ 42 files changed, 3810 insertions(+), 1083 deletions(-) delete mode 100644 src/DodoSSH.Client.App/Views/PreferencesScreen.axaml delete mode 100644 src/DodoSSH.Client.App/Views/PreferencesScreen.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsAccountPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsAccountPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsGeneralPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsGeneralPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsGroupsPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsGroupsPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsNav.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsNav.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsPreferencesPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsPreferencesPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsSecurityPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsSecurityPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsVaultsPage.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsVaultsPage.axaml.cs create mode 100644 src/DodoSSH.Client.App/Views/SettingsView.axaml create mode 100644 src/DodoSSH.Client.App/Views/SettingsView.axaml.cs delete mode 100644 src/DodoSSH.Client.App/Views/VaultsScreen.axaml delete mode 100644 src/DodoSSH.Client.App/Views/VaultsScreen.axaml.cs create mode 100644 tests/DodoSSH.Client.App.Layout.Tests/SettingsPagesLayoutTests.cs diff --git a/src/DodoSSH.Client.App/App.axaml b/src/DodoSSH.Client.App/App.axaml index f906eb0..c36b9fc 100644 --- a/src/DodoSSH.Client.App/App.axaml +++ b/src/DodoSSH.Client.App/App.axaml @@ -1261,6 +1261,57 @@ + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/ImportScreen.axaml b/src/DodoSSH.Client.App/Views/ImportScreen.axaml index daff5e7..6c0c2d1 100644 --- a/src/DodoSSH.Client.App/Views/ImportScreen.axaml +++ b/src/DodoSSH.Client.App/Views/ImportScreen.axaml @@ -12,9 +12,10 @@ forty entries for machines that stopped existing years ago. So scanning writes nothing and the list says what each entry means; importing is a separate press on a set somebody has looked at. - Reachable from the preferences screen and not from the nav rail. It is a task rather than a - destination — done once, or once a year — and a seventh rail entry would cost every screen a slot for - something almost nobody is looking at. + v5c-3: restyled into Import.dc.html's own table over SettingsView's content column — SettingsNav stays + lit on Preferences while this is up, and the titlebar says "Back to preferences"; see + MainWindowViewModel.IsImportOpen. No longer reachable from the nav rail, exactly as before: it is a task + done once or once a year, reached from the Preferences page's own "OPEN IMPORTER" row. ── ◆ THE ONE TICK THAT READS PRIVATE KEYS ───────────────────────────────────────────────────────────── Below the list, off, and drawn only where the scan actually found an IdentityFile. It is the only control @@ -26,144 +27,212 @@ What comes back afterwards is the report under the list: one line per key file, saying which were stored, which are protected by a passphrase this cannot know, and which were not there at all. That is reported rather than previewed for the same reason — previewing would mean reading them. + + ── ◆ WHAT THIS MEANS ────────────────────────────────────────────────────────────────────────────────── + One chip per row rather than the old separate AUTHENTICATION/STATE columns, mapped off the two facts a + row actually carries: ImportRowViewModel.AlreadyPresent and HasWarnings. A skipped Host pattern (a + wildcard block) never becomes a row at all — see SshConfigImport.SkippedPatterns — so there is no third, + "skipped" state to draw here; a warned row is the amber case instead, and it wins over "already here" + because the warning is the more actionable of the two facts. See ImportRowViewModel.Meaning. --> - + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/SettingsGroupsPage.axaml.cs b/src/DodoSSH.Client.App/Views/SettingsGroupsPage.axaml.cs new file mode 100644 index 0000000..28edd4c --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsGroupsPage.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace DodoSSH.Client.App.Views; + +/// Groups: every group, and the hosts filed under each — see the remark at the top of the markup. +internal sealed partial class SettingsGroupsPage : UserControl +{ + public SettingsGroupsPage() => InitializeComponent(); +} diff --git a/src/DodoSSH.Client.App/Views/SettingsNav.axaml b/src/DodoSSH.Client.App/Views/SettingsNav.axaml new file mode 100644 index 0000000..4613531 --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsNav.axaml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/SettingsNav.axaml.cs b/src/DodoSSH.Client.App/Views/SettingsNav.axaml.cs new file mode 100644 index 0000000..e5112cb --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsNav.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace DodoSSH.Client.App.Views; + +/// Settings mode's own 340px rail — General/Vaults/Account, Security/Preferences, and Logout. +internal sealed partial class SettingsNav : UserControl +{ + public SettingsNav() => InitializeComponent(); +} diff --git a/src/DodoSSH.Client.App/Views/SettingsPreferencesPage.axaml b/src/DodoSSH.Client.App/Views/SettingsPreferencesPage.axaml new file mode 100644 index 0000000..4812d36 --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsPreferencesPage.axaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/SettingsSecurityPage.axaml.cs b/src/DodoSSH.Client.App/Views/SettingsSecurityPage.axaml.cs new file mode 100644 index 0000000..c1b9af3 --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsSecurityPage.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace DodoSSH.Client.App.Views; + +/// Settings mode's Security page: the E2E explainer, Windows Hello, and approved host keys. +internal sealed partial class SettingsSecurityPage : UserControl +{ + public SettingsSecurityPage() => InitializeComponent(); +} diff --git a/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml b/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml new file mode 100644 index 0000000..eb94b91 --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml.cs b/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml.cs new file mode 100644 index 0000000..9605def --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsTagsPage.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace DodoSSH.Client.App.Views; + +/// Tags: every tag, and how many hosts wear each — see the remark at the top of the markup. +internal sealed partial class SettingsTagsPage : UserControl +{ + public SettingsTagsPage() => InitializeComponent(); +} diff --git a/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml b/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml new file mode 100644 index 0000000..b1e151a --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml.cs b/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml.cs new file mode 100644 index 0000000..d48ec3e --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsTitleBar.axaml.cs @@ -0,0 +1,47 @@ +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace DodoSSH.Client.App.Views; + +/// Settings mode's own titlebar — see the remark in the markup for why it is not TitleBar itself. +internal sealed partial class SettingsTitleBar : UserControl +{ + public SettingsTitleBar() => InitializeComponent(); + + private Window? Host => TopLevel.GetTopLevel(this) as Window; + + /// Left button only, and only on a press nothing inside the bar has already handled. + private void OnDrag(object? sender, PointerPressedEventArgs e) + { + if (e.Handled || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + return; + } + + Host?.BeginMoveDrag(e); + } + + private void OnMinimise(object? sender, RoutedEventArgs e) + { + if (Host is { } window) + { + window.WindowState = WindowState.Minimized; + } + } + + /// Both the button and a double-click on the bar arrive here, as Windows convention expects. + private void OnToggleMaximised(object? sender, RoutedEventArgs e) + { + if (Host is not { } window) + { + return; + } + + window.WindowState = window.WindowState == WindowState.Maximized + ? WindowState.Normal + : WindowState.Maximized; + } + + private void OnClose(object? sender, RoutedEventArgs e) => Host?.Close(); +} diff --git a/src/DodoSSH.Client.App/Views/SettingsVaultsPage.axaml b/src/DodoSSH.Client.App/Views/SettingsVaultsPage.axaml new file mode 100644 index 0000000..a9b87f2 --- /dev/null +++ b/src/DodoSSH.Client.App/Views/SettingsVaultsPage.axaml @@ -0,0 +1,407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +