Implement theme management and settings dialog in PLib video library manager. Add theme service for light/dark/system themes, integrate settings dialog for user preferences, and update app configuration to include appearance options. Enhance thumbnail caching with new methods for cache size and clearing. Update README.md to reflect new features and settings.
This commit is contained in:
@@ -145,6 +145,10 @@
|
||||
<icons:MaterialIcon Kind="ThemeLightDark" Width="17" Height="17" />
|
||||
</Button>
|
||||
|
||||
<Button Command="{Binding OpenSettingsCommand}" ToolTip.Tip="Настройки">
|
||||
<icons:MaterialIcon Kind="CogOutline" Width="17" Height="17" />
|
||||
</Button>
|
||||
|
||||
<Button Classes="Primary" Command="{Binding AddFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<icons:MaterialIcon Kind="FolderPlusOutline" Width="17" Height="17" />
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:icons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:vm="clr-namespace:PLib.Desktop.ViewModels"
|
||||
x:Class="PLib.Desktop.Views.SettingsWindow"
|
||||
x:DataType="vm:SettingsViewModel"
|
||||
Title="Настройки"
|
||||
Width="620"
|
||||
Height="700"
|
||||
MinWidth="520"
|
||||
MinHeight="520"
|
||||
Background="{DynamicResource PageBackgroundBrush}"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
|
||||
<Window.Styles>
|
||||
<!-- One settings block: a titled surface holding a few related controls. -->
|
||||
<Style Selector="Border.section">
|
||||
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
<Setter Property="Padding" Value="16" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBlock.label">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextSecondaryBrush}" />
|
||||
<Setter Property="FontSize" Value="12.5" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBlock.hint">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextTertiaryBrush}" />
|
||||
<Setter Property="FontSize" Value="11.5" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
|
||||
<!-- ======================= Header ======================= -->
|
||||
<Border Grid.Row="0"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="20,14">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Border Width="32"
|
||||
Height="32"
|
||||
CornerRadius="9"
|
||||
Background="{DynamicResource AccentSoftBrush}">
|
||||
<icons:MaterialIcon Kind="CogOutline"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Foreground="{DynamicResource AccentBrush}" />
|
||||
</Border>
|
||||
<TextBlock Classes="sectionTitle" Text="Настройки" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- ======================= Body ======================= -->
|
||||
<ScrollViewer Grid.Row="1" Padding="20,18">
|
||||
<StackPanel Spacing="14">
|
||||
|
||||
<!-- Folders -->
|
||||
<Border Classes="section">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Classes="sectionTitle" FontSize="14" Text="Папки библиотеки" />
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Folders}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FolderEntryViewModel">
|
||||
<Border Background="{DynamicResource CardBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource CardBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="10,6"
|
||||
Margin="0,0,0,6">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="9">
|
||||
<icons:MaterialIcon Grid.Column="0"
|
||||
Kind="FolderOutline"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Foreground="{DynamicResource TextTertiaryBrush}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding Path}"
|
||||
ToolTip.Tip="{Binding Path}"
|
||||
VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
FontSize="12.5" />
|
||||
<Button Grid.Column="2"
|
||||
Command="{Binding RemoveCommand}"
|
||||
Padding="6"
|
||||
ToolTip.Tip="Убрать из библиотеки">
|
||||
<icons:MaterialIcon Kind="Close" Width="14" Height="14" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<TextBlock Classes="hint"
|
||||
IsVisible="{Binding !HasFolders}"
|
||||
Text="Пока не добавлено ни одной папки." />
|
||||
|
||||
<Button HorizontalAlignment="Left" Command="{Binding AddFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<icons:MaterialIcon Kind="FolderPlusOutline" Width="16" Height="16" />
|
||||
<TextBlock Text="Добавить папку" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Thumbnails -->
|
||||
<Border Classes="section">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="sectionTitle" FontSize="14" Text="Превью" />
|
||||
|
||||
<Grid ColumnDefinitions="200,*" RowDefinitions="Auto,Auto" RowSpacing="12">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Classes="label" Text="Ширина кадра, px" />
|
||||
<NumericUpDown Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Minimum="160"
|
||||
Maximum="1920"
|
||||
Increment="40"
|
||||
FormatString="0"
|
||||
Value="{Binding ThumbnailWidth}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Classes="label" Text="Кадр на отметке, %" />
|
||||
<Grid Grid.Row="1" Grid.Column="1" ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
||||
<Slider Grid.Column="0"
|
||||
Minimum="0"
|
||||
Maximum="90"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding ThumbnailPositionPercent}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Classes="label"
|
||||
MinWidth="36"
|
||||
TextAlignment="Right"
|
||||
Text="{Binding ThumbnailPositionPercent, StringFormat='{}{0:0} %'}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Classes="hint"
|
||||
Text="Самый первый кадр почти всегда чёрный или с логотипом, поэтому кадр берётся чуть дальше от начала. Изменения применятся к новым превью — для пересборки старых очистите кэш." />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Scanning -->
|
||||
<Border Classes="section">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="sectionTitle" FontSize="14" Text="Сканирование" />
|
||||
|
||||
<Grid ColumnDefinitions="200,*" RowDefinitions="Auto,Auto" RowSpacing="12">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Classes="label" Text="Файлов одновременно" />
|
||||
<NumericUpDown Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Minimum="1"
|
||||
Maximum="32"
|
||||
Increment="1"
|
||||
FormatString="0"
|
||||
Value="{Binding MaxIndexingConcurrency}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Classes="label" Text="Пропускать файлы меньше, МБ" />
|
||||
<NumericUpDown Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Minimum="0"
|
||||
Maximum="1024"
|
||||
Increment="0.5"
|
||||
FormatString="0.##"
|
||||
Value="{Binding MinimumFileSizeMegabytes}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Appearance -->
|
||||
<Border Classes="section">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="sectionTitle" FontSize="14" Text="Внешний вид" />
|
||||
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Тема" />
|
||||
<ComboBox Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding ThemeOptions}"
|
||||
SelectedItem="{Binding SelectedTheme}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:ThemeOption">
|
||||
<TextBlock Text="{Binding Label}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Cache -->
|
||||
<Border Classes="section">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="sectionTitle" FontSize="14" Text="Кэш превью" />
|
||||
|
||||
<Grid ColumnDefinitions="200,*,Auto" ColumnSpacing="12">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Занято на диске" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Classes="label"
|
||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
||||
Text="{Binding CacheSizeText}" />
|
||||
<Button Grid.Column="2"
|
||||
Command="{Binding ClearCacheCommand}"
|
||||
IsEnabled="{Binding !IsBusy}"
|
||||
Content="Очистить" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock Classes="hint"
|
||||
Text="Все кадры удаляются с диска, а библиотека забывает пути к ним. После закрытия настроек превью соберутся заново." />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- ======================= Footer ======================= -->
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="20,12">
|
||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="14">
|
||||
<TextBlock Grid.Column="0"
|
||||
Classes="hint"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Message}" />
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8">
|
||||
<Button Content="Отмена" Command="{Binding CancelCommand}" />
|
||||
<Button Classes="Primary" Content="Сохранить" Command="{Binding SaveCommand}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,9 @@
|
||||
using PLib.Desktop.ViewModels;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace PLib.Desktop.Views;
|
||||
|
||||
public sealed partial class SettingsWindow : ReactiveWindow<SettingsViewModel>
|
||||
{
|
||||
public SettingsWindow() => InitializeComponent();
|
||||
}
|
||||
Reference in New Issue
Block a user