Public Access
Let the connecting card's host name and address be taken away
The card that fills the terminal's rectangle while a connection is being made prints two lines saying which machine and at what address, and both were TextBlocks — which in Avalonia is a drawing of a string rather than a string. It is the one place the shell says what it is dialling *before* anything has answered, and on a refusal it is the only copy of that on screen: there is no prompt yet and there may never be one. That is exactly when the value is wanted somewhere else — in a ticket, in a message to whoever owns the host, in the ssh command being written in the window beside this one — and retyping it off the screen is how a digit goes missing from an address that is mostly digits. SelectableTextBlock, the same change the session column's own address got in "Let the session address be selected, and copied whole" and for the same reason. Nothing else about either line moves: same font, size, weight, colour, trimming and centring. The refusal underneath them was already selectable, which made the pair above it the odd ones out. Both lines rather than only the name. They are two lines of one identifier, and a pair where the first can be taken and the second cannot is a worse answer than either. Tooltips are new, and they are what makes the trimming safe rather than a trap. A drag reaches only the glyphs that were drawn, so a name or an address wearing an ellipsis has to be taken with select-all — which takes the whole value, because the ellipsis is a drawing and not a shorter string. The hover says what the whole value is, so nobody copies half of one without having been told there is a half. Same argument, same shape, as the sidebar's. The test asserts that half rather than the easy one, on values long enough to be trimmed inside the card's 460 pixels; values that fit would pass whether the trimming were render-only or not. Confirmed it bites by putting the two TextBlocks back and watching it fail. The phone draws the same two lines in the same state — TerminalScreen.axaml's IsConnectingShowing block — and is deliberately left alone. Its captions are styled by TextBlock.title and TextBlock.detail selectors in Phone.axaml, so the same substitution there silently drops the styling until those rules gain SelectableTextBlock siblings, which is a theme change rather than this one. Worth doing; not worth smuggling in here. Also: WindowDecorations on the splash instead of SystemDecorations, which Avalonia 12 has deprecated in favour of it. That warning was mine, added two commits ago; the main window's own is older and is not this change's to move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d1f0a4b53
commit
dec2cf164a
3 files changed
+108
-9
No files matched your search
@@ -942,6 +942,49 @@ public sealed class ScreenLayoutTests : IAsyncLifetime
|
||||
await MeasureConnectingAsync(faults => faults.ShouldBeEmpty("with a refusal to explain"), tab);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The card is the one place the shell says what it is dialling <em>before</em> anything answers, and on a
|
||||
/// refusal it is the only copy of that on screen — so both lines are there to be taken away, and a
|
||||
/// <c>TextBlock</c> in Avalonia is a drawing of a string rather than a string. Same change, same argument,
|
||||
/// as the session column's own address; see <c>SessionSidebarTests</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Asserted on values long enough to be trimmed, which is the half that could quietly break: an ellipsis
|
||||
/// is a drawing and not a shorter string, so select-all has to reach the whole name and the whole address
|
||||
/// rather than the part that happened to fit inside the card's 460 pixels. Values that fit would pass
|
||||
/// whether that were true or not.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task TheConnectingCardsNameAndAddressSelectAndCopyWhole()
|
||||
{
|
||||
const string label = "customer-production-database-01.eu-west-1.internal.example.company";
|
||||
const string address = "a-very-long-deploy-account@db-primary.eu-west-1.internal.example:22022";
|
||||
|
||||
await OnTheConnectingCardAsync(
|
||||
card =>
|
||||
{
|
||||
// Found by their text rather than by position: the order is the visual tree's, and a third
|
||||
// selectable block is already in there — the refusal reason, hidden while this tab is only
|
||||
// connecting. Single rather than First, so a duplicate would be a question rather than a
|
||||
// silent pick.
|
||||
var takeable = card.GetVisualDescendants().OfType<SelectableTextBlock>().ToList();
|
||||
|
||||
var name = takeable.Single(block => block.Text == label);
|
||||
var endpoint = takeable.Single(block => block.Text == address);
|
||||
|
||||
name.SelectAll();
|
||||
name.SelectedText.ShouldBe(label, "the ellipsis is a drawing, not a shorter string");
|
||||
name.CanCopy.ShouldBeTrue("Ctrl+C and the right-click Copy item are both gated on this");
|
||||
|
||||
endpoint.SelectAll();
|
||||
endpoint.SelectedText.ShouldBe(address, "the ellipsis is a drawing, not a shorter string");
|
||||
endpoint.CanCopy.ShouldBeTrue("Ctrl+C and the right-click Copy item are both gated on this");
|
||||
},
|
||||
new TerminalTabViewModel(label, address));
|
||||
}
|
||||
|
||||
// ---- The logs screen ----
|
||||
|
||||
[Fact]
|
||||
@@ -2043,6 +2086,40 @@ public sealed class ScreenLayoutTests : IAsyncLifetime
|
||||
},
|
||||
Token);
|
||||
|
||||
/// <summary>Lays the connecting card out and hands it over, rather than measuring it.</summary>
|
||||
/// <remarks>
|
||||
/// The same rectangle <see cref="MeasureConnectingAsync"/> uses and for the same reasons — see its
|
||||
/// remark. Separate because the claim is about a control's own state rather than about where anything was
|
||||
/// arranged, and threading an assertion over the card through a method whose parameter is a fault list
|
||||
/// would make both callers harder to read than two methods are.
|
||||
/// </remarks>
|
||||
private Task OnTheConnectingCardAsync(Action<ConnectingCard> body, TerminalTabViewModel tab) =>
|
||||
LayoutHarness.OnTheUiThreadAsync(
|
||||
() =>
|
||||
{
|
||||
shell.State = ShellState.Unlocked;
|
||||
shell.Tabs.Clear();
|
||||
shell.Tabs.Add(tab);
|
||||
shell.SelectedTab = tab;
|
||||
|
||||
var card = new ConnectingCard { DataContext = shell };
|
||||
|
||||
var window = LayoutHarness.HostAtMinimumSize(
|
||||
card,
|
||||
LayoutHarness.SessionScreenWidth(sidebarVisible: true),
|
||||
LayoutHarness.SessionScreenHeight);
|
||||
|
||||
try
|
||||
{
|
||||
body(card);
|
||||
}
|
||||
finally
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
},
|
||||
Token);
|
||||
|
||||
/// <summary>Lays the host-key decision out in the rectangle it is drawn over.</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
|
||||
Reference in new issue
Block a user