Public Access
Desktop: drop the VAULT columns that had nothing in them, and the snippet buttons with nothing to act on
This commit is contained in:
1 parent
22b4d82ba8
commit
3ccafdd53b
5 files changed
+118
-22
No files matched your search
@@ -256,7 +256,8 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- ============ The table ============ -->
|
<!-- ============ The table ============ -->
|
||||||
<Grid Grid.Column="1" RowDefinitions="Auto,Auto,*" Background="{StaticResource Pane}">
|
<Grid Grid.Column="1" RowDefinitions="Auto,Auto,*" Background="{StaticResource Pane}"
|
||||||
|
Grid.IsSharedSizeScope="True">
|
||||||
|
|
||||||
<Border Grid.Row="0" Padding="16,0" Height="44"
|
<Border Grid.Row="0" Padding="16,0" Height="44"
|
||||||
BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1">
|
BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1">
|
||||||
@@ -275,12 +276,24 @@
|
|||||||
item this application stores carries a timestamp; see the file-level remark above and
|
item this application stores carries a timestamp; see the file-level remark above and
|
||||||
docs/design-import-gaps.md.
|
docs/design-import-gaps.md.
|
||||||
-->
|
-->
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="2,3*,1.1*,0.9*,1.2*" Margin="0,10,16,6"
|
<!--
|
||||||
|
VAULT is sized to what is in it, and shared between this heading and every row so the two agree:
|
||||||
|
with one vault no row names one, and a fixed column was a heading over a stripe of nothing.
|
||||||
|
-->
|
||||||
|
<Grid Grid.Row="1" Margin="0,10,16,6"
|
||||||
IsVisible="{Binding HasVaultItems}">
|
IsVisible="{Binding HasVaultItems}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="2" />
|
||||||
|
<ColumnDefinition Width="3*" />
|
||||||
|
<ColumnDefinition Width="1.1*" />
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="KeychainVault" />
|
||||||
|
<ColumnDefinition Width="1.2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="1" Classes="label" Text="NAME" FontSize="9.5" LetterSpacing="1"
|
<TextBlock Grid.Column="1" Classes="label" Text="NAME" FontSize="9.5" LetterSpacing="1"
|
||||||
Margin="12,0,8,0" />
|
Margin="12,0,8,0" />
|
||||||
<TextBlock Grid.Column="2" Classes="label" Text="TYPE" FontSize="9.5" LetterSpacing="1" />
|
<TextBlock Grid.Column="2" Classes="label" Text="TYPE" FontSize="9.5" LetterSpacing="1" />
|
||||||
<TextBlock Grid.Column="3" Classes="label" Text="VAULT" FontSize="9.5" LetterSpacing="1" />
|
<TextBlock Grid.Column="3" Classes="label" Text="VAULT" FontSize="9.5" LetterSpacing="1"
|
||||||
|
Margin="0,0,16,0" IsVisible="{Binding ShowsVaultColumn}" />
|
||||||
<TextBlock Grid.Column="4" Classes="label" Text="USED BY" FontSize="9.5" LetterSpacing="1" />
|
<TextBlock Grid.Column="4" Classes="label" Text="USED BY" FontSize="9.5" LetterSpacing="1" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -290,7 +303,14 @@
|
|||||||
Margin="8,0,8,10">
|
Margin="8,0,8,10">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:VaultItemRowViewModel">
|
<DataTemplate x:DataType="vm:VaultItemRowViewModel">
|
||||||
<Grid ColumnDefinitions="2,3*,1.1*,0.9*,1.2*" Height="40" Margin="6,0,10,0">
|
<Grid Height="40" Margin="6,0,10,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="2" />
|
||||||
|
<ColumnDefinition Width="3*" />
|
||||||
|
<ColumnDefinition Width="1.1*" />
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="KeychainVault" />
|
||||||
|
<ColumnDefinition Width="1.2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
<!--
|
<!--
|
||||||
A Grid rather than a horizontal StackPanel: the StackPanel measured the name with infinite
|
A Grid rather than a horizontal StackPanel: the StackPanel measured the name with infinite
|
||||||
width, so TextTrimming never fired and a long name printed over the TYPE and USED BY
|
width, so TextTrimming never fired and a long name printed over the TYPE and USED BY
|
||||||
@@ -307,7 +327,7 @@
|
|||||||
<TextBlock Grid.Column="2" Classes="mono" Text="{Binding Type}" FontSize="10.5"
|
<TextBlock Grid.Column="2" Classes="mono" Text="{Binding Type}" FontSize="10.5"
|
||||||
Foreground="{StaticResource TextGhost}" VerticalAlignment="Center"
|
Foreground="{StaticResource TextGhost}" VerticalAlignment="Center"
|
||||||
TextTrimming="CharacterEllipsis" />
|
TextTrimming="CharacterEllipsis" />
|
||||||
<Border Grid.Column="3" Classes="chip" HorizontalAlignment="Left"
|
<Border Grid.Column="3" Classes="chip" HorizontalAlignment="Left" Margin="0,0,16,0"
|
||||||
VerticalAlignment="Center" IsVisible="{Binding HasVaultBadge}">
|
VerticalAlignment="Center" IsVisible="{Binding HasVaultBadge}">
|
||||||
<TextBlock Text="{Binding VaultBadge}" FontSize="10" />
|
<TextBlock Text="{Binding VaultBadge}" FontSize="10" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -81,11 +81,15 @@
|
|||||||
IsVisible="{Binding Vault, Converter={x:Static ObjectConverters.IsNotNull}}">
|
IsVisible="{Binding Vault, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="1.4*,*,*,Auto" Margin="20,14,20,10">
|
<!--
|
||||||
|
No VAULT column, although the design draws one: TagRowViewModel does not carry which vault a tag
|
||||||
|
lives in (tags are read from the active vault alone; see VaultViewModel.NewTag's own remark), so
|
||||||
|
the column was a heading over nothing on every row.
|
||||||
|
-->
|
||||||
|
<Grid ColumnDefinitions="1.4*,2*,Auto" Margin="20,14,20,10">
|
||||||
<TextBlock Grid.Column="0" Classes="label" Text="TAG" />
|
<TextBlock Grid.Column="0" Classes="label" Text="TAG" />
|
||||||
<TextBlock Grid.Column="1" Classes="label" Text="USED BY" />
|
<TextBlock Grid.Column="1" Classes="label" Text="USED BY" />
|
||||||
<TextBlock Grid.Column="2" Classes="label" Text="VAULT" />
|
<TextBlock Grid.Column="2" Classes="label" Text=" " />
|
||||||
<TextBlock Grid.Column="3" Classes="label" Text=" " />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding Vault.Tags}">
|
<ItemsControl ItemsSource="{Binding Vault.Tags}">
|
||||||
@@ -97,7 +101,7 @@
|
|||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:TagRowViewModel">
|
<DataTemplate x:DataType="vm:TagRowViewModel">
|
||||||
<Border Classes="settingsrow last" Padding="20,10">
|
<Border Classes="settingsrow last" Padding="20,10">
|
||||||
<Grid ColumnDefinitions="1.4*,*,*,Auto">
|
<Grid ColumnDefinitions="1.4*,2*,Auto">
|
||||||
|
|
||||||
<Border Grid.Column="0" Classes="chip" HorizontalAlignment="Left" VerticalAlignment="Center">
|
<Border Grid.Column="0" Classes="chip" HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{Binding Label}" />
|
<TextBlock Text="{Binding Label}" />
|
||||||
@@ -107,14 +111,7 @@
|
|||||||
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center"
|
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center"
|
||||||
Text="{Binding Description}" />
|
Text="{Binding Description}" />
|
||||||
|
|
||||||
<!--
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||||
No vault chip here — unlike a group, TagRowViewModel does not carry which vault it lives
|
|
||||||
in (tags are read from the active vault alone; see VaultViewModel.NewTag's own remark),
|
|
||||||
so there is no second vault this column could honestly name.
|
|
||||||
-->
|
|
||||||
<TextBlock Grid.Column="2" />
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
|
||||||
<Button Classes="paneicon" Width="30" Height="30" FontSize="13"
|
<Button Classes="paneicon" Width="30" Height="30" FontSize="13"
|
||||||
Command="{Binding #Root.((vm:MainWindowViewModel)DataContext).Vault.EditTagRowCommand}"
|
Command="{Binding #Root.((vm:MainWindowViewModel)DataContext).Vault.EditTagRowCommand}"
|
||||||
CommandParameter="{Binding}" ToolTip.Tip="Rename this tag.">
|
CommandParameter="{Binding}" ToolTip.Tip="Rename this tag.">
|
||||||
|
|||||||
@@ -167,8 +167,12 @@
|
|||||||
TextAlignment="Center" MaxWidth="360"
|
TextAlignment="Center" MaxWidth="360"
|
||||||
IsVisible="{Binding !HasVisible}" />
|
IsVisible="{Binding !HasVisible}" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Only with a snippet chosen. Three greyed buttons under a list nobody has clicked in read as
|
||||||
|
something broken rather than something waiting, and the pane on the right already says what to do.
|
||||||
|
-->
|
||||||
<Border Grid.Row="1" Padding="16,10" BorderBrush="{StaticResource BorderSubtle}"
|
<Border Grid.Row="1" Padding="16,10" BorderBrush="{StaticResource BorderSubtle}"
|
||||||
BorderThickness="0,1,0,0">
|
BorderThickness="0,1,0,0" IsVisible="{Binding HasSelection}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
<Button Classes="ghost" Content="Edit" Command="{Binding EditCommand}"
|
<Button Classes="ghost" Content="Edit" Command="{Binding EditCommand}"
|
||||||
IsEnabled="{Binding HasSelection}" />
|
IsEnabled="{Binding HasSelection}" />
|
||||||
|
|||||||
@@ -2404,6 +2404,14 @@ internal sealed partial class VaultViewModel(
|
|||||||
|
|
||||||
internal bool HasVaultItems => VaultItems.Count > 0;
|
internal bool HasVaultItems => VaultItems.Count > 0;
|
||||||
|
|
||||||
|
/// <summary>Whether any row in the keychain table names a vault, which is whether its VAULT column has a heading.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Read off the rows rather than off how many vaults there are, because a row's badge is what the column
|
||||||
|
/// holds: the two agree today — see <see cref="VaultItemRowViewModel.VaultBadge"/> — and if they ever stop
|
||||||
|
/// agreeing, a heading over nothing is the thing this exists to avoid.
|
||||||
|
/// </remarks>
|
||||||
|
internal bool ShowsVaultColumn => VaultItems.Any(row => row.HasVaultBadge);
|
||||||
|
|
||||||
internal bool HasSelectedVaultItem => SelectedVaultItem is not null;
|
internal bool HasSelectedVaultItem => SelectedVaultItem is not null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -13457,6 +13465,16 @@ internal sealed partial class VaultViewModel(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Re-reads what the keychain table says about its rows as a whole.</summary>
|
||||||
|
private void RaiseVaultItemFacts()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(SectionSummary));
|
||||||
|
OnPropertyChanged(nameof(HasVaultItems));
|
||||||
|
OnPropertyChanged(nameof(ShowsVaultColumn));
|
||||||
|
OnPropertyChanged(nameof(TotalItemCount));
|
||||||
|
OnPropertyChanged(nameof(EmptySectionMessage));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>── v5b ── Narrows <see cref="VaultItems"/> to rows <see cref="ItemFilter"/> matches.</summary>
|
/// <summary>── v5b ── Narrows <see cref="VaultItems"/> to rows <see cref="ItemFilter"/> matches.</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Its own method for the reason <see cref="AddTagRows"/> is out of <see cref="RebuildVaultItems"/> —
|
/// Its own method for the reason <see cref="AddTagRows"/> is out of <see cref="RebuildVaultItems"/> —
|
||||||
@@ -13552,10 +13570,7 @@ internal sealed partial class VaultViewModel(
|
|||||||
// sync every minute would otherwise move the detail pane out from under whoever was reading it.
|
// sync every minute would otherwise move the detail pane out from under whoever was reading it.
|
||||||
SelectedVaultItem = VaultItems.FirstOrDefault(row => row.EntityId == selectedId);
|
SelectedVaultItem = VaultItems.FirstOrDefault(row => row.EntityId == selectedId);
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SectionSummary));
|
RaiseVaultItemFacts();
|
||||||
OnPropertyChanged(nameof(HasVaultItems));
|
|
||||||
OnPropertyChanged(nameof(TotalItemCount));
|
|
||||||
OnPropertyChanged(nameof(EmptySectionMessage));
|
|
||||||
|
|
||||||
// A reload replaces every row object, and the selection is restored by id — so the setter above may
|
// A reload replaces every row object, and the selection is restored by id — so the setter above may
|
||||||
// not have fired even though the row this answers about is a different instance. Asked again here,
|
// not have fired even though the row this answers about is a different instance. Asked again here,
|
||||||
|
|||||||
@@ -2296,6 +2296,66 @@ public sealed class ScreenLayoutTests : IAsyncLifetime
|
|||||||
},
|
},
|
||||||
Token);
|
Token);
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// Three greyed buttons under a list nobody has clicked in read as something broken rather than something
|
||||||
|
/// waiting. The row appears with a snippet to act on, and goes with it.
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task TheSnippetButtonsAppearWithASnippetToActOn()
|
||||||
|
{
|
||||||
|
await SeedSnippetsAsync();
|
||||||
|
|
||||||
|
var snippets = NewSnippetsScreen();
|
||||||
|
|
||||||
|
await LayoutHarness.OnTheUiThreadAsync(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var screen = new SnippetsScreen { DataContext = snippets };
|
||||||
|
|
||||||
|
var window = LayoutHarness.HostAtMinimumSize(
|
||||||
|
screen, LayoutHarness.ScreenWidth, LayoutHarness.ScreenHeight);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var edit = screen.GetVisualDescendants().OfType<Button>()
|
||||||
|
.Single(button => button.Content is "Edit");
|
||||||
|
|
||||||
|
snippets.Selected = null;
|
||||||
|
LayoutHarness.Settle(window, LayoutHarness.ScreenWidth, LayoutHarness.ScreenHeight);
|
||||||
|
edit.IsEffectivelyVisible.ShouldBeFalse("nothing is chosen for it to edit");
|
||||||
|
|
||||||
|
snippets.Selected = snippets.Visible.First();
|
||||||
|
LayoutHarness.Settle(window, LayoutHarness.ScreenWidth, LayoutHarness.ScreenHeight);
|
||||||
|
edit.IsEffectivelyVisible.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// With one vault no row names one, and a fixed VAULT column was a heading over a stripe of nothing
|
||||||
|
/// between TYPE and USED BY. It is sized to what its rows hold now, and its heading goes with them.
|
||||||
|
/// </remarks>
|
||||||
|
[Fact]
|
||||||
|
public async Task TheKeychainsVaultColumn_TakesNoRoomWithOneVault()
|
||||||
|
{
|
||||||
|
await OnTheVaultAsync((screen, _) =>
|
||||||
|
{
|
||||||
|
vault.HasVaultItems.ShouldBeTrue("an empty table has no columns to judge");
|
||||||
|
vault.ShowsVaultColumn.ShouldBeFalse("this suite's keychain has one vault");
|
||||||
|
|
||||||
|
var heading = screen.GetVisualDescendants().OfType<TextBlock>()
|
||||||
|
.Single(block => block.Text is "VAULT");
|
||||||
|
|
||||||
|
heading.IsEffectivelyVisible.ShouldBeFalse();
|
||||||
|
heading.Parent.ShouldBeOfType<Grid>().ColumnDefinitions[3].ActualWidth.ShouldBe(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private SnippetsViewModel NewSnippetsScreen(InsertTarget? target = null) =>
|
private SnippetsViewModel NewSnippetsScreen(InsertTarget? target = null) =>
|
||||||
new(
|
new(
|
||||||
vault,
|
vault,
|
||||||
|
|||||||
Reference in new issue
Block a user