Update README.md with new navigation structure and UI adjustments for video library manager. Enhance LibraryStyles.axaml to improve layout of filter and navigation bars. Revise MainWindow.axaml to accommodate updated UI components and ensure better user experience across different screen sizes.
This commit is contained in:
@@ -148,6 +148,10 @@ dotnet test
|
|||||||
захотел бы владеть этой компоновкой целиком. Четыре вкладки сущностей делят одну панель:
|
захотел бы владеть этой компоновкой целиком. Четыре вкладки сущностей делят одну панель:
|
||||||
различается только вид метки, и четыре почти одинаковых разметки разошлись бы при первой же
|
различается только вид метки, и четыре почти одинаковых разметки разошлись бы при первой же
|
||||||
правке.
|
правке.
|
||||||
|
Живут они уровнем ниже оконной панели, а не в ней: сверху то, что верно для всего
|
||||||
|
приложения (поиск, тема, настройки, добавить папку), под ним — навигация и органы управления
|
||||||
|
текущей страницей. В один ряд это не влезало: вкладки съедали ширину у поиска, и на 1200 px
|
||||||
|
они наезжали друг на друга. Сортировка уехала туда же — она про страницу, а не про окно.
|
||||||
- **Метки лежат на карточке, а не запрашиваются.** Отбор по тегу, актёру или студии — это
|
- **Метки лежат на карточке, а не запрашиваются.** Отбор по тегу, актёру или студии — это
|
||||||
предикат, который DynamicData прогоняет по каждой карточке в фоновом потоке; ходить оттуда
|
предикат, который DynamicData прогоняет по каждой карточке в фоновом потоке; ходить оттуда
|
||||||
в базу значило бы запрос на карточку. Поэтому `GetLibraryAsync` грузит видео вместе с
|
в базу значило бы запрос на карточку. Поэтому `GetLibraryAsync` грузит видео вместе с
|
||||||
|
|||||||
@@ -112,9 +112,13 @@
|
|||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- A row of controls that belongs to the page below it rather than to the window. -->
|
<!--
|
||||||
<Style Selector="Border.filterBar">
|
A row of controls that belongs to the page below it rather than to the window. Lighter
|
||||||
<Setter Property="Padding" Value="20,10" />
|
than the window bars on purpose: it sits inside the content, and giving it the same weight
|
||||||
|
would make the window read as three stacked strips of chrome.
|
||||||
|
-->
|
||||||
|
<Style Selector="Border.filterBar, Border.navBar">
|
||||||
|
<Setter Property="Padding" Value="20,9" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -156,24 +156,6 @@
|
|||||||
<TextBlock Classes="sectionTitle" Text="Видеотека" />
|
<TextBlock Classes="sectionTitle" Text="Видеотека" />
|
||||||
<TextBlock Classes="cardMeta" Text="{Binding Videos.Count, StringFormat='{}{0} видео'}" />
|
<TextBlock Classes="cardMeta" Text="{Binding Videos.Count, StringFormat='{}{0} видео'}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Navigation. Text tabs rather than icons: "Актёры" and "Студии" have no icon a
|
|
||||||
reader would guess, and a wrong guess costs a click to find out. -->
|
|
||||||
<ItemsControl ItemsSource="{Binding Sections}" VerticalAlignment="Center" Margin="10,0,0,0">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="2" />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:SectionTabViewModel">
|
|
||||||
<Button Classes="sectionTab"
|
|
||||||
Classes.active="{Binding IsSelected}"
|
|
||||||
Command="{Binding SelectCommand}"
|
|
||||||
Content="{Binding Title}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBox Grid.Column="1"
|
<TextBox Grid.Column="1"
|
||||||
@@ -181,21 +163,11 @@
|
|||||||
MaxWidth="420"
|
MaxWidth="420"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
PlaceholderText="Поиск по названию…"
|
PlaceholderText="Поиск по названию, тегу, актёру…"
|
||||||
Text="{Binding SearchText}" />
|
Text="{Binding SearchText}" />
|
||||||
|
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||||
|
|
||||||
<ComboBox MinWidth="180"
|
|
||||||
ItemsSource="{Binding SortOptions}"
|
|
||||||
SelectedItem="{Binding SelectedSort}">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:SortOption">
|
|
||||||
<TextBlock Text="{Binding Label}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ComboBox.ItemTemplate>
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<Button Command="{Binding ScanCommand}"
|
<Button Command="{Binding ScanCommand}"
|
||||||
IsEnabled="{Binding !IsScanning}"
|
IsEnabled="{Binding !IsScanning}"
|
||||||
ToolTip.Tip="Пересканировать библиотеку">
|
ToolTip.Tip="Пересканировать библиотеку">
|
||||||
@@ -235,125 +207,168 @@
|
|||||||
<!-- Библиотека -->
|
<!-- Библиотека -->
|
||||||
<Panel IsVisible="{Binding !IsPlayerOpen}">
|
<Panel IsVisible="{Binding !IsPlayerOpen}">
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*" IsVisible="{Binding IsVideosSection}">
|
<Grid RowDefinitions="Auto,*">
|
||||||
|
|
||||||
<!-- The label the grid is narrowed to. Present only while one is active, so the row
|
<!--
|
||||||
costs nothing when the grid shows everything. -->
|
Navigation and the controls that belong to whichever page is open. Below the window
|
||||||
<Border Grid.Row="0"
|
chrome rather than in it: the bar above holds what is true of the whole application,
|
||||||
Classes="filterBar"
|
and cramming the tabs in beside the search left neither enough room.
|
||||||
IsVisible="{Binding ActiveLabel, Converter={x:Static ObjectConverters.IsNotNull}}">
|
-->
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
<Border Grid.Row="0" Classes="navBar">
|
||||||
<TextBlock Classes="cardMeta" VerticalAlignment="Center" Text="Отбор:" />
|
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="14">
|
||||||
<TextBlock Classes="panelTitle"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding ActiveLabel.Name}" />
|
|
||||||
<Button Classes="transport"
|
|
||||||
Padding="4"
|
|
||||||
Command="{Binding ClearLabelFilterCommand}"
|
|
||||||
ToolTip.Tip="Показать все видео">
|
|
||||||
<icons:MaterialIcon Kind="Close" Width="13" Height="13" />
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1" Padding="20,18" HorizontalScrollBarVisibility="Disabled">
|
<!-- Text tabs rather than icons: "Актёры" and "Студии" have no icon a reader
|
||||||
<ItemsRepeater ItemsSource="{Binding Videos}" ItemTemplate="{StaticResource VideoCardTemplate}">
|
would guess, and a wrong guess costs a click to find out. -->
|
||||||
<ItemsRepeater.Layout>
|
<ItemsControl Grid.Column="0" ItemsSource="{Binding Sections}" VerticalAlignment="Center">
|
||||||
<UniformGridLayout ItemsStretch="Fill"
|
<ItemsControl.ItemsPanel>
|
||||||
MinItemWidth="230"
|
<ItemsPanelTemplate>
|
||||||
MinItemHeight="212"
|
<StackPanel Orientation="Horizontal" Spacing="2" />
|
||||||
MinColumnSpacing="16"
|
</ItemsPanelTemplate>
|
||||||
MinRowSpacing="16" />
|
</ItemsControl.ItemsPanel>
|
||||||
</ItemsRepeater.Layout>
|
<ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
<DataTemplate x:DataType="vm:SectionTabViewModel">
|
||||||
</ScrollViewer>
|
<Button Classes="sectionTab"
|
||||||
</Grid>
|
Classes.active="{Binding IsSelected}"
|
||||||
|
Command="{Binding SelectCommand}"
|
||||||
<!-- ================= Теги, актёры, студии, коллекции ================= -->
|
Content="{Binding Title}" />
|
||||||
<Grid RowDefinitions="Auto,*" IsVisible="{Binding IsEntitySection}">
|
|
||||||
|
|
||||||
<Border Grid.Row="0" Classes="filterBar">
|
|
||||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
|
|
||||||
<TextBox Grid.Column="0"
|
|
||||||
MaxWidth="360"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
PlaceholderText="Поиск…"
|
|
||||||
Text="{Binding EntitySearchText}" />
|
|
||||||
<ComboBox Grid.Column="1"
|
|
||||||
MinWidth="180"
|
|
||||||
ItemsSource="{Binding EntitySortOptions}"
|
|
||||||
SelectedItem="{Binding SelectedEntitySort}">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:EntitySortOption">
|
|
||||||
<TextBlock Text="{Binding Label}" />
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ComboBox>
|
</ItemsControl>
|
||||||
|
|
||||||
|
<!-- The label the grid is narrowed to; absent while it shows everything. -->
|
||||||
|
<StackPanel Grid.Column="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="6"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsVisible="{Binding ActiveLabel, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
|
<Border Classes="badge">
|
||||||
|
<TextBlock Text="{Binding ActiveLabel.Name}" />
|
||||||
|
</Border>
|
||||||
|
<Button Classes="transport"
|
||||||
|
Padding="3"
|
||||||
|
Command="{Binding ClearLabelFilterCommand}"
|
||||||
|
ToolTip.Tip="Показать все видео">
|
||||||
|
<icons:MaterialIcon Kind="Close" Width="12" Height="12" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<TextBox Width="200"
|
||||||
|
PlaceholderText="Поиск…"
|
||||||
|
Text="{Binding EntitySearchText}"
|
||||||
|
IsVisible="{Binding IsEntitySection}" />
|
||||||
|
|
||||||
|
<ComboBox MinWidth="170"
|
||||||
|
ItemsSource="{Binding EntitySortOptions}"
|
||||||
|
SelectedItem="{Binding SelectedEntitySort}"
|
||||||
|
IsVisible="{Binding IsEntitySection}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:EntitySortOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<ComboBox MinWidth="190"
|
||||||
|
ItemsSource="{Binding SortOptions}"
|
||||||
|
SelectedItem="{Binding SelectedSort}"
|
||||||
|
IsVisible="{Binding IsVideosSection}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SortOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1" Padding="20,18" HorizontalScrollBarVisibility="Disabled">
|
<!-- One page at a time; the empty-state prompt sits over the grid, not over the bar. -->
|
||||||
<ItemsRepeater ItemsSource="{Binding Entities}">
|
<Panel Grid.Row="1">
|
||||||
<ItemsRepeater.Layout>
|
|
||||||
<UniformGridLayout ItemsStretch="Fill"
|
<ScrollViewer Padding="20,18"
|
||||||
MinItemWidth="220"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
MinItemHeight="56"
|
IsVisible="{Binding IsVideosSection}">
|
||||||
MinColumnSpacing="12"
|
<ItemsRepeater ItemsSource="{Binding Videos}" ItemTemplate="{StaticResource VideoCardTemplate}">
|
||||||
MinRowSpacing="12" />
|
<ItemsRepeater.Layout>
|
||||||
</ItemsRepeater.Layout>
|
<UniformGridLayout ItemsStretch="Fill"
|
||||||
<ItemsRepeater.ItemTemplate>
|
MinItemWidth="230"
|
||||||
<DataTemplate x:DataType="vm:LabelSummaryViewModel">
|
MinItemHeight="212"
|
||||||
<Button Classes="entity" Command="{Binding OpenCommand}">
|
MinColumnSpacing="16"
|
||||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="10">
|
MinRowSpacing="16" />
|
||||||
<TextBlock Grid.Column="0"
|
</ItemsRepeater.Layout>
|
||||||
VerticalAlignment="Center"
|
</ItemsRepeater>
|
||||||
Text="{Binding Name}"
|
</ScrollViewer>
|
||||||
TextTrimming="CharacterEllipsis"
|
|
||||||
ToolTip.Tip="{Binding Name}"
|
<!-- ============= Теги, актёры, студии, коллекции ============= -->
|
||||||
FontSize="13"
|
<ScrollViewer Padding="20,18"
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}" />
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
<Border Grid.Column="1" Classes="badge" VerticalAlignment="Center">
|
IsVisible="{Binding IsEntitySection}">
|
||||||
<TextBlock Text="{Binding CountText}" />
|
<ItemsRepeater ItemsSource="{Binding Entities}">
|
||||||
</Border>
|
<ItemsRepeater.Layout>
|
||||||
</Grid>
|
<UniformGridLayout ItemsStretch="Fill"
|
||||||
</Button>
|
MinItemWidth="220"
|
||||||
</DataTemplate>
|
MinItemHeight="56"
|
||||||
</ItemsRepeater.ItemTemplate>
|
MinColumnSpacing="12"
|
||||||
</ItemsRepeater>
|
MinRowSpacing="12" />
|
||||||
</ScrollViewer>
|
</ItemsRepeater.Layout>
|
||||||
|
<ItemsRepeater.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:LabelSummaryViewModel">
|
||||||
|
<Button Classes="entity" Command="{Binding OpenCommand}">
|
||||||
|
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="10">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding Name}"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
ToolTip.Tip="{Binding Name}"
|
||||||
|
FontSize="13"
|
||||||
|
Foreground="{DynamicResource TextPrimaryBrush}" />
|
||||||
|
<Border Grid.Column="1" Classes="badge" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="{Binding CountText}" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- ===================== Метаданные ===================== -->
|
||||||
|
<ContentControl Content="{Binding MetadataScan}"
|
||||||
|
ContentTemplate="{StaticResource MetadataScanTemplate}"
|
||||||
|
IsVisible="{Binding IsMetadataSection}" />
|
||||||
|
|
||||||
|
<!-- Empty state -->
|
||||||
|
<StackPanel HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Spacing="14"
|
||||||
|
MaxWidth="420"
|
||||||
|
IsVisible="{Binding IsLibraryPromptVisible}">
|
||||||
|
<icons:MaterialIcon Kind="VideoBoxOff"
|
||||||
|
Width="52"
|
||||||
|
Height="52"
|
||||||
|
Opacity="0.35"
|
||||||
|
Foreground="{DynamicResource TextTertiaryBrush}"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
<TextBlock Classes="sectionTitle"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="Здесь пока пусто" />
|
||||||
|
<TextBlock Classes="subtle"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="Укажите папку с видеофайлами — PLib пройдётся по ней, соберёт превью и покажет всё карточками." />
|
||||||
|
<Button Classes="Primary"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Command="{Binding AddFolderCommand}"
|
||||||
|
Content="Выбрать папку" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- ======================= Метаданные ======================= -->
|
|
||||||
<ContentControl Content="{Binding MetadataScan}"
|
|
||||||
ContentTemplate="{StaticResource MetadataScanTemplate}"
|
|
||||||
IsVisible="{Binding IsMetadataSection}" />
|
|
||||||
|
|
||||||
<!-- Empty state -->
|
|
||||||
<StackPanel HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Spacing="14"
|
|
||||||
MaxWidth="420"
|
|
||||||
IsVisible="{Binding IsLibraryPromptVisible}">
|
|
||||||
<icons:MaterialIcon Kind="VideoBoxOff"
|
|
||||||
Width="52"
|
|
||||||
Height="52"
|
|
||||||
Opacity="0.35"
|
|
||||||
Foreground="{DynamicResource TextTertiaryBrush}"
|
|
||||||
HorizontalAlignment="Center" />
|
|
||||||
<TextBlock Classes="sectionTitle"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Text="Здесь пока пусто" />
|
|
||||||
<TextBlock Classes="subtle"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
TextAlignment="Center"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
Text="Укажите папку с видеофайлами — PLib пройдётся по ней, соберёт превью и покажет всё карточками." />
|
|
||||||
<Button Classes="Primary"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Command="{Binding AddFolderCommand}"
|
|
||||||
Content="Выбрать папку" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<!-- Страница медиа. Content is cleared when the page closes, which detaches the
|
<!-- Страница медиа. Content is cleared when the page closes, which detaches the
|
||||||
|
|||||||
Reference in New Issue
Block a user