Public Access
Give hosts and terminals their own screen, and the rest of the vault another
Rebuilds the client's shell from an imported design: a titlebar and nav rail it draws itself, real multi-session tabs over the one WebView, a Ctrl+K host search, and a vault screen that merges keys, passwords and pinned host keys into one table. Hosts left the vault column for their own screen beside the terminal, which is what the design asks for and turned out to be the better split anyway. Two screens the design shows have nothing behind them yet — file transfer and teams — and say so plainly rather than rendering invented data; every other gap between the design and this build is recorded in docs/design-import-gaps.md.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace DodoSSH.Client.App.Views;
|
||||
|
||||
/// <summary>
|
||||
/// The window's own titlebar, drawn because the design draws one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Everything here is a window operation, which is why it is code-behind and not a command on a view model:
|
||||
/// dragging, maximising and closing are properties of the <see cref="Window"/> this control happens to be
|
||||
/// inside, and a view model that knew about them would be a view model that could not be tested without one.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The window is found by walking up rather than injected, so this control drops into any window — including
|
||||
/// the bare one the layout harness hosts it in, where <see cref="Host"/> is simply a different window and
|
||||
/// every handler still has something to act on.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal sealed partial class TitleBar : UserControl
|
||||
{
|
||||
public TitleBar() => InitializeComponent();
|
||||
|
||||
private Window? Host => TopLevel.GetTopLevel(this) as Window;
|
||||
|
||||
/// <remarks>
|
||||
/// Left button only, and only on a press that has not already been handled by something inside the bar —
|
||||
/// otherwise dragging would start from the close button and swallow the click that was meant to close
|
||||
/// the window.
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Both the button and a double-click on the bar arrive here, which is the convention Windows sets and
|
||||
/// the one people reach for without thinking about it.
|
||||
/// </remarks>
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user