Refactor PLib video library manager's UI to implement a side panel for settings, enhancing accessibility and layout. Update MainWindow and SettingsView to accommodate the new design, ensuring the settings panel integrates seamlessly without overlaying the main content. Revise styles for app bars and status bars for consistency across the application. Update README.md to reflect these UI changes.
This commit is contained in:
@@ -9,9 +9,9 @@
|
||||
- Метаданные (длительность, разрешение, кодек) через ffprobe.
|
||||
- Постеры кадром из видео через ffmpeg, с кэшем на диске.
|
||||
- Виртуализированная сетка карточек, ленивая загрузка превью, поиск и сортировка.
|
||||
- Настройки — выдвижной панелью в том же окне: папки библиотеки (с удалением), параметры
|
||||
превью и сканирования, тема, очистка кэша. Всё пишется в `settings.json` и подхватывается
|
||||
без перезапуска.
|
||||
- Настройки — боковой панелью в том же окне (сетка сдвигается, а не перекрывается): папки
|
||||
библиотеки с удалением, параметры превью и сканирования, тема, очистка кэша. Всё пишется
|
||||
в `settings.json` и подхватывается без перезапуска.
|
||||
- Светлая, тёмная и системная темы; выбор запоминается.
|
||||
- Клик или Enter по карточке — открыть в системном плеере, правая кнопка — контекстное меню.
|
||||
|
||||
@@ -63,9 +63,12 @@ dotnet test
|
||||
декодированием в нужную ширину. Память зависит от размера окна, а не от размера библиотеки.
|
||||
- **Scope на операцию.** `DbContext` живёт ровно одну операцию — ViewModel берёт
|
||||
`IServiceScopeFactory` и создаёт scope на каждый вызов.
|
||||
- **Одно окно.** Настройки — панель поверх сетки, а не второе окно: библиотека остаётся
|
||||
видна, в alt-tab ничего не добавляется, и приложение остаётся переносимым на
|
||||
- **Одно окно.** Настройки — колонка макета, а не второе окно и не оверлей: открываясь, она
|
||||
сдвигает сетку, и та переливается в меньшее число столбцов, оставаясь целиком доступной.
|
||||
В alt-tab ничего не добавляется, и приложение остаётся переносимым на
|
||||
`ISingleViewApplicationLifetime`, где `ShowDialog` попросту не существует.
|
||||
Верхняя и нижняя полосы обеих половин заданы одной парой стилей, поэтому разделители
|
||||
читаются как сплошные линии через всё окно.
|
||||
- **Настройки — рабочая копия.** Панель правит снимок `AppSettings` и записывает его целиком
|
||||
только по «Сохранить», так что отмена не оставляет следов. Пересканирование запускается
|
||||
только если изменилось то, что влияет на состав библиотеки, — смена темы или ширины кадра
|
||||
|
||||
@@ -104,56 +104,57 @@
|
||||
<Setter Property="FontSize" Value="12.5" />
|
||||
</Style>
|
||||
|
||||
<!-- ======================== Settings overlay ======================== -->
|
||||
<!-- ========================= Window bars ========================= -->
|
||||
|
||||
<!--
|
||||
The overlay stays in the tree and hides behind opacity rather than IsVisible: a collapsed
|
||||
element has nothing to animate from, and the slide-in is the whole point. Nothing inside
|
||||
is focusable while it is closed, because the panel's content is null until it opens.
|
||||
The library and the settings panel sit side by side, so their top and bottom bars have to
|
||||
line up: the separators read as one continuous line across the window. Fixing the heights
|
||||
here, in one place, is what keeps them from drifting apart as either side gains controls.
|
||||
-->
|
||||
<Style Selector="Panel.settingsOverlay">
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
<Setter Property="IsHitTestVisible" Value="False" />
|
||||
<Setter Property="Transitions">
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="0:0:0.16" Easing="CubicEaseOut" />
|
||||
</Transitions>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Panel.settingsOverlay.open">
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<Setter Property="IsHitTestVisible" Value="True" />
|
||||
</Style>
|
||||
|
||||
<!-- Click-anywhere-to-dismiss backdrop. A Button gets the click handling for free. -->
|
||||
<Style Selector="Button.scrim">
|
||||
<Setter Property="Background" Value="{DynamicResource OverlayBrush}" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Cursor" Value="Arrow" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="{TemplateBinding Background}" />
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border.drawer">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="Width" Value="470" />
|
||||
<Setter Property="Background" Value="{DynamicResource PageBackgroundBrush}" />
|
||||
<Style Selector="Border.appBar">
|
||||
<Setter Property="Height" Value="62" />
|
||||
<Setter Property="Padding" Value="20,0" />
|
||||
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border.statusBar">
|
||||
<Setter Property="Height" Value="50" />
|
||||
<Setter Property="Padding" Value="20,0" />
|
||||
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="0,1,0,0" />
|
||||
</Style>
|
||||
|
||||
<!-- ======================== Settings panel ======================== -->
|
||||
|
||||
<!--
|
||||
The panel is a column of the layout rather than an overlay, so opening it squeezes the
|
||||
grid instead of covering it. Width is animated because Avalonia has no transition for
|
||||
GridLength; the content inside keeps its full width and is simply clipped while the
|
||||
column grows, which stops the whole panel reflowing on every animation frame.
|
||||
-->
|
||||
<Style Selector="Border.settingsPanel">
|
||||
<Setter Property="Width" Value="0" />
|
||||
<Setter Property="ClipToBounds" Value="True" />
|
||||
<!--
|
||||
Its own backdrop and a stronger divider than the usual surface border: once the bars on
|
||||
both sides line up, a hairline in the ordinary border colour reads as one continuous
|
||||
bar and the panel stops looking like a separate region.
|
||||
-->
|
||||
<Setter Property="Background" Value="{DynamicResource PanelBackgroundBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PanelDividerBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1,0,0,0" />
|
||||
<Setter Property="RenderTransform" Value="translateX(30px)" />
|
||||
<Setter Property="Transitions">
|
||||
<Transitions>
|
||||
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" />
|
||||
<DoubleTransition Property="Width" Duration="0:0:0.22" Easing="CubicEaseOut" />
|
||||
</Transitions>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Panel.settingsOverlay.open Border.drawer">
|
||||
<Setter Property="RenderTransform" Value="none" />
|
||||
<Style Selector="Border.settingsPanel.open">
|
||||
<Setter Property="Width" Value="400" />
|
||||
</Style>
|
||||
|
||||
<!-- ============================ Badge ============================ -->
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<SolidColorBrush x:Key="CardBorderBrush" Color="#242A34" />
|
||||
<SolidColorBrush x:Key="CardHoverBorderBrush" Color="#3F4B5E" />
|
||||
<SolidColorBrush x:Key="ThumbnailPlaceholderBrush" Color="#1C222B" />
|
||||
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#111419" />
|
||||
<SolidColorBrush x:Key="PanelDividerBrush" Color="#333C4A" />
|
||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#E8EDF4" />
|
||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#97A2B2" />
|
||||
<SolidColorBrush x:Key="TextTertiaryBrush" Color="#69737F" />
|
||||
@@ -34,6 +36,8 @@
|
||||
<SolidColorBrush x:Key="CardBorderBrush" Color="#E4E7EC" />
|
||||
<SolidColorBrush x:Key="CardHoverBorderBrush" Color="#B9C3D3" />
|
||||
<SolidColorBrush x:Key="ThumbnailPlaceholderBrush" Color="#E9EDF2" />
|
||||
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FAFBFD" />
|
||||
<SolidColorBrush x:Key="PanelDividerBrush" Color="#C6CFDC" />
|
||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#111621" />
|
||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#586274" />
|
||||
<SolidColorBrush x:Key="TextTertiaryBrush" Color="#8A93A2" />
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
Title="PLib — видеотека"
|
||||
Width="1180"
|
||||
Height="760"
|
||||
MinWidth="640"
|
||||
MinWidth="1000"
|
||||
MinHeight="420"
|
||||
Background="{DynamicResource PageBackgroundBrush}"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
@@ -95,24 +95,22 @@
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
|
||||
<Grid Grid.Column="0" RowDefinitions="Auto,*,Auto">
|
||||
|
||||
<!-- ======================= Header ======================= -->
|
||||
<Border Grid.Row="0"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="20,14">
|
||||
<Border Grid.Row="0" Classes="appBar">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="20">
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<Border Width="34"
|
||||
Height="34"
|
||||
<Border Width="32"
|
||||
Height="32"
|
||||
CornerRadius="9"
|
||||
Background="{DynamicResource AccentSoftBrush}">
|
||||
<icons:MaterialIcon Kind="VideoVintage"
|
||||
Width="19"
|
||||
Height="19"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Foreground="{DynamicResource AccentBrush}" />
|
||||
</Border>
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
@@ -122,6 +120,7 @@
|
||||
</StackPanel>
|
||||
|
||||
<TextBox Grid.Column="1"
|
||||
MinWidth="150"
|
||||
MaxWidth="420"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
@@ -154,7 +153,10 @@
|
||||
<icons:MaterialIcon Kind="CogOutline" Width="17" Height="17" />
|
||||
</Button>
|
||||
|
||||
<Button Classes="Primary" Command="{Binding AddFolderCommand}">
|
||||
<!-- Redundant while the panel is open, and the header needs the room there. -->
|
||||
<Button Classes="Primary"
|
||||
Command="{Binding AddFolderCommand}"
|
||||
IsVisible="{Binding !IsSettingsOpen}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<icons:MaterialIcon Kind="FolderPlusOutline" Width="17" Height="17" />
|
||||
<TextBlock Text="Добавить папку" />
|
||||
@@ -209,11 +211,7 @@
|
||||
</Panel>
|
||||
|
||||
<!-- ======================= Status bar ======================= -->
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="20,9">
|
||||
<Border Grid.Row="2" Classes="statusBar">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="14">
|
||||
|
||||
<ProgressBar Grid.Column="0"
|
||||
@@ -239,17 +237,14 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- ======================= Settings panel ======================= -->
|
||||
<Panel Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
Classes="settingsOverlay"
|
||||
Classes.open="{Binding IsSettingsOpen}">
|
||||
<Button Classes="scrim" Command="{Binding CloseSettingsCommand}" />
|
||||
<Border Classes="drawer">
|
||||
<ContentControl Content="{Binding SettingsPanel}"
|
||||
ContentTemplate="{StaticResource SettingsTemplate}" />
|
||||
</Border>
|
||||
</Panel>
|
||||
<Border Grid.Column="1" Classes="settingsPanel" Classes.open="{Binding IsSettingsOpen}">
|
||||
<ContentControl Width="400"
|
||||
Content="{Binding SettingsPanel}"
|
||||
ContentTemplate="{StaticResource SettingsTemplate}" />
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -31,11 +31,7 @@
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
|
||||
<!-- ======================= Header ======================= -->
|
||||
<Border Grid.Row="0"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="20,14">
|
||||
<Border Grid.Row="0" Classes="appBar">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<Border Grid.Column="0"
|
||||
Width="32"
|
||||
@@ -227,11 +223,7 @@
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- ======================= Footer ======================= -->
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource SurfaceBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceBorderBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="20,12">
|
||||
<Border Grid.Row="2" Classes="statusBar">
|
||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="14">
|
||||
<TextBlock Grid.Column="0"
|
||||
Classes="hint"
|
||||
|
||||
Reference in New Issue
Block a user