merge branch 'feature/ui-polish' into feature/totp

This commit is contained in:
2026-03-27 17:16:14 +03:00
7 changed files with 208 additions and 167 deletions

View File

@@ -1,93 +1,94 @@
<Window xmlns="https://github.com/avaloniaui" <Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="KeyKeeper.Views.CreateVaultFileWindow" x:Class="KeyKeeper.Views.CreateVaultDialog"
Title="Create New Vault" Title="Create New Vault"
Background="#fff" Background="#fff"
Focusable="True"
Icon="/Assets/icon.ico" Icon="/Assets/icon.ico"
Width="600" Height="450" Width="600" Height="450"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
CanResize="False" CanResize="False"
x:Name="ThisWindow"> x:Name="ThisWindow">
<Grid ColumnDefinitions="1.5*, 2*"> <Grid ColumnDefinitions="1.5*, 2*">
<!-- Левая синяя панель --> <!-- Левая синяя панель -->
<Border Background="#2328C4" Grid.Column="0"> <Border Background="#2328C4" Grid.Column="0">
<StackPanel VerticalAlignment="Center" Margin="20"> <StackPanel VerticalAlignment="Center" Margin="20">
<TextBlock Text="Choose where to save your password database and set a master password" <TextBlock Text="Choose where to save your password database and set a master password"
TextWrapping="Wrap" TextWrapping="Wrap"
Foreground="#E0E0FF" Foreground="#E0E0FF"
FontSize="20" FontSize="20"
TextAlignment="Center"/> TextAlignment="Center"/>
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Правая белая панель с формой --> <!-- Правая белая панель с формой -->
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto"> <ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
<StackPanel Margin="30" VerticalAlignment="Center" Spacing="20"> <StackPanel Margin="30" VerticalAlignment="Center" Spacing="20">
<TextBlock Text="Create new vault" <TextBlock Text="Create new vault"
FontSize="22" FontSize="22"
FontWeight="Bold" FontWeight="Bold"
Foreground="#2328C4"/> Foreground="#2328C4"/>
<!-- Выбор файла --> <!-- Выбор файла -->
<StackPanel Spacing="10"> <StackPanel Spacing="10">
<TextBlock Text="File location" FontWeight="SemiBold" Foreground="Black" /> <TextBlock Text="File location" FontWeight="SemiBold" Foreground="Black" />
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="10"> <Grid ColumnDefinitions="*, Auto" ColumnSpacing="10">
<TextBox x:Name="FilePathTextBox" <TextBox x:Name="FilePathTextBox"
Grid.Column="0" Grid.Column="0"
Watermark="Select file path..." Watermark="Select file path..."
Padding="10,8"/> Padding="10,8"/>
<Button x:Name="BrowseButton" <Button x:Name="BrowseButton"
Grid.Column="1" Grid.Column="1"
Content="Browse..." Content="Browse..."
Classes="secondaryButton" Classes="secondaryButton"
Padding="15,8" Padding="15,8"
Click="BrowseButton_Click"/> Click="BrowseButton_Click"/>
</Grid> </Grid>
<TextBlock x:Name="PathWarning" <TextBlock x:Name="PathWarning"
FontSize="12" FontSize="12"
Foreground="Orange" Foreground="Orange"
Text=" "/> Text=" "/>
</StackPanel> </StackPanel>
<!-- Ввод мастер-пароля --> <!-- Ввод мастер-пароля -->
<StackPanel Spacing="10"> <StackPanel Spacing="10">
<TextBlock Text="Master password" FontWeight="SemiBold" Foreground="Black" /> <TextBlock Text="Master password" FontWeight="SemiBold" Foreground="Black" />
<TextBox x:Name="PasswordBox" <TextBox x:Name="PasswordBox"
PasswordChar="*" PasswordChar="*"
Watermark="Enter password" Watermark="Enter password"
Padding="10,8"/> Padding="10,8"/>
</StackPanel> </StackPanel>
<StackPanel Spacing="10"> <StackPanel Spacing="10">
<TextBlock Text="Confirm password" FontWeight="SemiBold" Foreground="Black" /> <TextBlock Text="Confirm password" FontWeight="SemiBold" Foreground="Black" />
<TextBox x:Name="ConfirmPasswordBox" <TextBox x:Name="ConfirmPasswordBox"
PasswordChar="*" PasswordChar="*"
Watermark="Confirm password" Watermark="Confirm password"
Padding="10,8"/> Padding="10,8"/>
</StackPanel> </StackPanel>
<!-- Сообщение об ошибке пароля --> <!-- Сообщение об ошибке пароля -->
<TextBlock x:Name="PasswordErrorText" <TextBlock x:Name="PasswordErrorText"
FontSize="12" FontSize="12"
Foreground="Red" Foreground="Red"
Text="" Text=""
IsVisible="False"/> IsVisible="False"/>
<!-- Кнопки действий --> <!-- Кнопки действий -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10" Margin="0,20,0,0"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10" Margin="0,20,0,0">
<Button Content="Cancel" <Button Content="Cancel"
Classes="secondaryButton" Classes="secondaryButton"
Width="80" Width="80"
Click="CancelButton_Click"/> Click="CancelButton_Click"/>
<Button x:Name="CreateButton" <Button x:Name="CreateButton"
Content="Create" Content="Create"
Classes="accentButton" Classes="accentButton"
Width="80" Width="80"
IsEnabled="False" IsEnabled="False"
Click="CreateButton_Click"/> Click="CreateButton_Click"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
</Window> </Window>

View File

@@ -1,19 +1,18 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using System;
using System.Threading.Tasks;
namespace KeyKeeper.Views namespace KeyKeeper.Views
{ {
public partial class CreateVaultFileWindow : Window public partial class CreateVaultDialog : Window
{ {
public string FilePath { get; private set; } = string.Empty; public string FilePath { get; private set; } = string.Empty;
public string Password { get; private set; } = string.Empty; public string Password { get; private set; } = string.Empty;
public bool Success { get; private set; } public bool Success { get; private set; }
public CreateVaultFileWindow() public CreateVaultDialog()
{ {
InitializeComponent(); InitializeComponent();
#if DEBUG #if DEBUG
@@ -22,6 +21,8 @@ namespace KeyKeeper.Views
FilePathTextBox.TextChanged += OnTextChanged; FilePathTextBox.TextChanged += OnTextChanged;
PasswordBox.TextChanged += OnPasswordTextChanged; PasswordBox.TextChanged += OnPasswordTextChanged;
ConfirmPasswordBox.TextChanged += OnPasswordTextChanged; ConfirmPasswordBox.TextChanged += OnPasswordTextChanged;
KeyDown += CreateVaultDialog_KeyDown;
} }
private async void OnTextChanged(object? sender, TextChangedEventArgs e) private async void OnTextChanged(object? sender, TextChangedEventArgs e)
@@ -60,6 +61,18 @@ namespace KeyKeeper.Views
CreateButton.IsEnabled = pathValid && passwordsEntered; CreateButton.IsEnabled = pathValid && passwordsEntered;
} }
private void CreateVaultDialog_KeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
Close();
}
else if (e.Key == Key.Enter && CreateButton.IsEnabled)
{
Submit();
}
}
private async void BrowseButton_Click(object? sender, RoutedEventArgs e) private async void BrowseButton_Click(object? sender, RoutedEventArgs e)
{ {
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
@@ -83,6 +96,11 @@ namespace KeyKeeper.Views
} }
private void CreateButton_Click(object? sender, RoutedEventArgs e) private void CreateButton_Click(object? sender, RoutedEventArgs e)
{
Submit();
}
private void Submit()
{ {
string path = FilePathTextBox.Text ?? ""; string path = FilePathTextBox.Text ?? "";
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))

View File

@@ -4,30 +4,30 @@
x:Class="KeyKeeper.Views.EntryEditWindow" x:Class="KeyKeeper.Views.EntryEditWindow"
Title="Add Entry" Title="Add Entry"
CanResize="False" CanResize="False"
Width="540" Width="400"
Height="300" Height="250"
Background="White"> Background="White">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="*,*" Margin="5"> <Grid RowDefinitions="Auto,Auto,Auto,Auto,*" ColumnDefinitions="1*,3*" Margin="5">
<TextBlock Text="Add New Password Entry" FontSize="20" HorizontalAlignment="Center" <TextBlock Text="Add New Password Entry" FontSize="20" HorizontalAlignment="Center"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/> Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,20"/>
<TextBlock Text="Entry name:" HorizontalAlignment="Right" <TextBlock Text="Entry name:" HorizontalAlignment="Right"
Grid.Row="1" Grid.Column="0" Margin="5" /> Grid.Row="1" Grid.Column="0" Margin="5" />
<TextBox Name="EntryNameEdit" Grid.Row="1" Grid.Column="1" Margin="5" /> <TextBox Name="EntryNameEdit" Grid.Row="1" Grid.Column="1" Margin="5" />
<TextBlock Text="Username:" HorizontalAlignment="Right" <TextBlock Text="Username:" HorizontalAlignment="Right"
Grid.Row="2" Grid.Column="0" Margin="5" /> Grid.Row="2" Grid.Column="0" Margin="5" />
<TextBox Name="UsernameEdit" Grid.Row="2" Grid.Column="1" Margin="5" /> <TextBox Name="UsernameEdit" Grid.Row="2" Grid.Column="1" Margin="5" />
<TextBlock Text="Password:" HorizontalAlignment="Right" <TextBlock Text="Password:" HorizontalAlignment="Right"
Grid.Row="3" Grid.Column="0" Margin="5" /> Grid.Row="3" Grid.Column="0" Margin="5" />
<!-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> --> <!-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> -->
<Grid Grid.Row="3" Grid.Column="1" Margin="5" RowDefinitions="Auto,Auto"> <Grid Grid.Row="3" Grid.Column="1" Margin="5" RowDefinitions="Auto,Auto">
<TextBox Name="PasswordEdit" Grid.Row="0" PasswordChar="*" /> <TextBox Name="PasswordEdit" Grid.Row="0" PasswordChar="*" />
<!-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> --> <!-- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> -->
<Border Name="PasswordStrengthIndicator" Grid.Row="1" <Border Name="PasswordStrengthIndicator" Grid.Row="1"
Height="4" CornerRadius="2" Margin="0,3,0,0" Height="4" CornerRadius="2" Margin="0,3,0,0"
Background="#ddd"> Background="#ddd">
@@ -38,9 +38,9 @@
</Border> </Border>
</Grid> </Grid>
<Button Content="Add!" HorizontalAlignment="Center" <Button Content="Done" HorizontalAlignment="Center" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"
Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Bottom"
Background="#aaa" Click="AddButton_Click" /> Background="#aaa" Click="AddButton_Click" />
</Grid> </Grid>
<Window.Styles> <Window.Styles>
@@ -50,12 +50,5 @@
<Style Selector="TextBox"> <Style Selector="TextBox">
<Setter Property="Foreground" Value="Black" /> <Setter Property="Foreground" Value="Black" />
</Style> </Style>
<Style Selector="Button /template/ ContentPresenter">
<Setter Property="Foreground" Value="Black" />
</Style>
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="#ccc" />
</Style>
</Window.Styles> </Window.Styles>
</Window> </Window>

View File

@@ -56,7 +56,7 @@
<Grid Grid.Column="1" VerticalAlignment="Center"> <Grid Grid.Column="1" VerticalAlignment="Center">
<Grid RowDefinitions="Auto,Auto"> <Grid RowDefinitions="Auto,Auto">
<TextBlock Text="Recent Database" <TextBlock Text="Recently opened"
FontSize="38" FontSize="38"
Foreground="#2328C4" Foreground="#2328C4"
FontWeight="SemiBold" FontWeight="SemiBold"

View File

@@ -22,7 +22,7 @@ namespace KeyKeeper.Views
private async void CreateNewVault_Click(object sender, RoutedEventArgs e) private async void CreateNewVault_Click(object sender, RoutedEventArgs e)
{ {
var createVaultDialog = new CreateVaultFileWindow(); var createVaultDialog = new CreateVaultDialog();
await createVaultDialog.ShowDialog(this); await createVaultDialog.ShowDialog(this);
if (createVaultDialog.Success && if (createVaultDialog.Success &&

View File

@@ -4,7 +4,7 @@
xmlns:i="using:Avalonia.Interactivity" xmlns:i="using:Avalonia.Interactivity"
xmlns:kkp="using:KeyKeeper.Views" xmlns:kkp="using:KeyKeeper.Views"
x:Class="KeyKeeper.Views.RepositoryWindow" x:Class="KeyKeeper.Views.RepositoryWindow"
Closing="RepositoryWindow_Closing" Closing="RepositoryWindow_Closing"
Title="KeyKeeper - Password store" Title="KeyKeeper - Password store"
CanResize="False" CanResize="False"
Width="800" Width="800"
@@ -12,59 +12,60 @@
Background="White" Background="White"
x:DataType="vm:RepositoryWindowViewModel"> x:DataType="vm:RepositoryWindowViewModel">
<Window.DataTemplates> <Window.DataTemplates>
<DataTemplate DataType="{x:Type vm:UnlockedRepositoryViewModel}"> <DataTemplate DataType="{x:Type vm:UnlockedRepositoryViewModel}">
<Grid> <Grid>
<!-- Синий левый край --> <!-- Синий левый край -->
<Border Width="200" <Border Width="224"
Background="#2328C4" Background="#2A2ABB"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Stretch"/> VerticalAlignment="Stretch">
<StackPanel Margin="20" HorizontalAlignment="Left">
<!-- Надпись KeyKeeper --> <StackPanel Margin="20" HorizontalAlignment="Left">
<TextBlock Text="KeyKeeper"
FontSize="32" <Svg Path="/Assets/logo_en.svg"
FontWeight="Bold" Stretch="Uniform" />
HorizontalAlignment="Left"
Margin="0,0,0,20"/>
<!-- Таймер блокировки под надписью KeyKeeper --> <!-- Таймер блокировки под надписью KeyKeeper -->
<Border Background="#CC000000" <Border Background="#CC000000"
CornerRadius="6" CornerRadius="6"
Padding="8,4" Padding="8,4"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,0,0,12" Margin="0,12,0,12"
IsVisible="{Binding $parent[Window].DataContext.LockTimerDisplay, IsVisible="{Binding $parent[Window].DataContext.LockTimerDisplay,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}"> Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
<!-- Иконка замка (Material Design lock outline) --> <!-- Иконка замка (Material Design lock outline) -->
<Path Fill="White" <Path Fill="White"
VerticalAlignment="Center" VerticalAlignment="Center"
Width="13" Height="13" Width="13" Height="13"
Stretch="Uniform" Stretch="Uniform"
Data="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/> Data="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>
<TextBlock Text="{Binding $parent[Window].DataContext.LockTimerDisplay}" <TextBlock Text="{Binding $parent[Window].DataContext.LockTimerDisplay}"
Foreground="White" Foreground="White"
FontSize="13" FontSize="13"
FontWeight="SemiBold" FontWeight="SemiBold"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Рамочка --> <!-- Рамочка -->
<!-- <Border BorderBrush="Gray" <!-- <Border BorderBrush="Gray"
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
Padding="20" Padding="20"
Background="#F5F5F5" Background="#F5F5F5"
HorizontalAlignment="Left"> HorizontalAlignment="Left">
<StackPanel HorizontalAlignment="Left"> <StackPanel HorizontalAlignment="Left">
<Button Content="All Passwords" <Button Content="All Passwords"
Width="120" Width="120"
Height="30" Height="30"
HorizontalAlignment="Left"/> HorizontalAlignment="Left"/>
</StackPanel> </StackPanel>
</Border> --> </Border> -->
<!-- Save Passwords --> <!-- Save Passwords -->
@@ -75,93 +76,104 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,20,0,0"/> Margin="0,20,0,0"/>
<!-- New Entry --> <!-- New Entry -->
<Button Content="New Entry" <Button Content="New Entry"
Classes="accentSidebarButton" Classes="accentSidebarButton"
Click="AddEntryButton_Click" Click="AddEntryButton_Click"
Height="30" Height="30"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,20,0,0"/> Margin="0,20,0,0"/>
<!-- Edit Selected Entry --> <!-- Edit Selected Entry -->
<Button Content="Edit Selected Entry" <Button Content="Edit Selected Entry"
Classes="accentSidebarButton" Classes="accentSidebarButton"
Click="EditEntryButton_Click" Click="EditEntryButton_Click"
Height="30" Height="30"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,20,0,0"/> Margin="0,20,0,0"/>
</StackPanel> </StackPanel>
</Border>
<!-- ListBox с паролями --> <!-- ListBox с паролями -->
<ListBox x:Name="PasswordsListBox" <ListBox x:Name="PasswordsListBox"
Width="580" Margin="234 10 10 10"
Margin="210 10 10 10"
ItemsSource="{Binding Passwords}" ItemsSource="{Binding Passwords}"
Background="Transparent" Background="Transparent"
SelectionMode="Single"> SelectionMode="Single"
<ListBox.ItemsPanel> KeyDown="PasswordsListBox_KeyDown">
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate> <ListBox.ItemsPanel>
<DataTemplate> <ItemsPanelTemplate>
<Border Background="Transparent" DoubleTapped="Entry_DoubleTapped"> <WrapPanel Orientation="Horizontal"/>
<StackPanel Width="100" </ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="Transparent"
DoubleTapped="Entry_DoubleTapped">
<StackPanel Width="100"
Margin="10" Margin="10"
HorizontalAlignment="Center"> HorizontalAlignment="Center">
<Svg Path="{Binding IconPath}" Width="48" Height="48"/>
<TextBlock Text="{Binding Name}" <Svg Path="{Binding IconPath}" Width="48" Height="48"/>
<TextBlock Text="{Binding Name}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Foreground="Black" /> Foreground="Black" />
<TextBlock Text="{Binding Username.Value}" <TextBlock Text="{Binding Username.Value}"
Foreground="#666" Foreground="#666"
HorizontalAlignment="Center" /> HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
<Border.ContextMenu> <Border.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Name="entryCtxMenuCopyUsername" Header="Copy username" Click="EntryContextMenuItem_Click"/> <MenuItem Name="entryCtxMenuCopyUsername" Header="Copy username" Click="EntryContextMenuItem_Click"/>
<MenuItem Name="entryCtxMenuCopyPassword" Header="Copy password" Click="EntryContextMenuItem_Click"/> <MenuItem Name="entryCtxMenuCopyPassword" Header="Copy password" Click="EntryContextMenuItem_Click"/>
<!-- Новый пункт меню "Edit" --> <MenuItem Name="entryCtxMenuEdit" Header="Edit" Click="EntryContextMenuItem_Click"/>
<MenuItem Name="entryCtxMenuEdit" Header="Edit" Click="EntryContextMenuItem_Click"/> <MenuItem Name="entryCtxMenuDelete" Header="Delete" Click="EntryContextMenuItem_Click"/>
<MenuItem Name="entryCtxMenuDelete" Header="Delete" Click="EntryContextMenuItem_Click"/> </ContextMenu>
</ContextMenu> </Border.ContextMenu>
</Border.ContextMenu> </Border>
</Border> </DataTemplate>
</DataTemplate> </ListBox.ItemTemplate>
</ListBox.ItemTemplate> </ListBox>
</ListBox>
<kkp:ToastNotificationHost x:Name="NotificationHost" <kkp:ToastNotificationHost x:Name="NotificationHost"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="20" Margin="20"
Duration="0:0:2" /> Duration="0:0:2" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type vm:LockedRepositoryViewModel}"> <DataTemplate DataType="{x:Type vm:LockedRepositoryViewModel}">
<StackPanel Margin="20" <StackPanel Margin="20"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Spacing="10"> Spacing="10">
<TextBlock Text="Enter credentials to unlock"
<TextBlock Text="Enter credentials to unlock"
Foreground="#2328C4" Foreground="#2328C4"
FontSize="32" /> FontSize="32" />
<TextBox x:Name="UnlockPasswordEdit" <TextBox x:Name="UnlockPasswordEdit"
Text="{Binding UnlockPassword, Mode=TwoWay}" Text="{Binding UnlockPassword, Mode=TwoWay}"
PasswordChar="*" PasswordChar="*"
Width="450" /> Width="450"
Loaded="UnlockPasswordEdit_Loaded">
<Button x:Name="UnlockButton" <TextBox.KeyBindings>
<KeyBinding Gesture="Return" Command="{Binding TryUnlock}"/>
</TextBox.KeyBindings>
</TextBox>
<Button x:Name="UnlockButton"
Command="{Binding TryUnlock}" Command="{Binding TryUnlock}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Content="Unlock!" /> Content="Unlock!" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</Window.DataTemplates> </Window.DataTemplates>
<ContentControl Content="{Binding CurrentPage}"/> <ContentControl Content="{Binding CurrentPage}"/>
</Window> </Window>

View File

@@ -39,6 +39,11 @@ public partial class RepositoryWindow : Window
vm.ResetLockTimer(); vm.ResetLockTimer();
} }
private void UnlockPasswordEdit_Loaded(object? sender, RoutedEventArgs e)
{
(sender as TextBox)?.Focus();
}
private async void RepositoryWindow_Closing(object? sender, WindowClosingEventArgs e) private async void RepositoryWindow_Closing(object? sender, WindowClosingEventArgs e)
{ {
if (allowClose || closeConfirmationShown) if (allowClose || closeConfirmationShown)
@@ -150,6 +155,18 @@ public partial class RepositoryWindow : Window
} }
} }
private void PasswordsListBox_KeyDown(object sender, KeyEventArgs args)
{
if (args.Key == Key.C && args.KeyModifiers == KeyModifiers.Control)
{
if (sender is ListBox list && list.SelectedItem is PassStoreEntryPassword pwd)
{
Clipboard!.SetTextAsync(pwd.Password.Value);
this.FindControlRecursive<ToastNotificationHost>("NotificationHost")?.Show("Password copied to clipboard");
}
}
}
private async void EntryContextMenuItem_Click(object sender, RoutedEventArgs args) private async void EntryContextMenuItem_Click(object sender, RoutedEventArgs args)
{ {
if (args.Source is StyledElement s && s.DataContext is PassStoreEntryPassword pwd) if (args.Source is StyledElement s && s.DataContext is PassStoreEntryPassword pwd)