Files
Libation/Source/LibationAvalonia/Views/ProcessQueueControl.axaml
T
Allamagoosa 986dda5eaa Review #1885: Avalonia parity, and cap concurrency by processor count
Chardonnay had parallel downloads with no way to configure them, since
the queue logic lives in shared UI code but each UI supplies its own
controls. Adds the Auto-scroll toggle and the 'At once' spinner to
Chardonnay's queue panel, bound to the same view model properties the
WinForms panel uses.

Also uses Environment.ProcessorCount as the spinner's ceiling rather
than its default: min(ProcessorCount, 10). Downloading is bound by
Audible's license throttling rather than local CPU, so core count says
nothing about how many concurrent downloads will succeed - it only
bounds how many decrypts can usefully run at once. The default stays 3.

Spinner bounds are bound rather than hardcoded, so the two UIs cannot
drift apart.
2026-08-16 14:08:49 -07:00

178 lines
7.6 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LibationAvalonia.Views"
xmlns:viewModels="clr-namespace:LibationAvalonia.ViewModels"
xmlns:vmbase="clr-namespace:LibationUiBase.ProcessQueue;assembly=LibationUiBase"
x:DataType="vmbase:ProcessQueueViewModel"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="650"
Background="{DynamicResource SystemRegionColor}"
x:Class="LibationAvalonia.Views.ProcessQueueControl">
<UserControl.Resources>
<views:DecimalConverter x:Key="myConverter" />
</UserControl.Resources>
<Grid RowDefinitions="*,Auto">
<TabControl Grid.Row="0">
<TabControl.Styles>
<Style Selector="TabControl /template/ ItemsPresenter#PART_ItemsPresenter">
<Setter Property="Height" Value="33"/>
</Style>
<Style Selector="TabItem /template/ Border#PART_LayoutRoot">
<Setter Property="Height" Value="33"/>
</Style>
</TabControl.Styles>
<!-- Queue Tab -->
<TabItem>
<TabItem.Header>
<TextBlock FontSize="14" VerticalAlignment="Center">Process Queue</TextBlock>
</TabItem.Header>
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemRegionColor}">
<ScrollViewer
Name="scroller"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
AllowAutoHide="False">
<ItemsControl Name="QueueListControl" ItemsSource="{Binding Queue}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:ProcessBookControl DataContext="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
<Grid Grid.Column="0" Grid.Row="1" ColumnDefinitions="*,Auto,Auto,Auto">
<Button Name="cancelAllBtn" Grid.Column="0" FontSize="12" HorizontalAlignment="Left" Click="CancelAllBtn_Click">Cancel All</Button>
<StackPanel Orientation="Horizontal" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center">
<CheckBox
FontSize="12"
Margin="0,0,10,0"
VerticalAlignment="Center"
IsChecked="{Binding AutoScrollQueue, Mode=TwoWay}"
ToolTip.Tip="Keep active downloads scrolled into view."
Content="Auto-scroll" />
<TextBlock Margin="0,0,6,0" FontSize="11" Text="At&#xA;once" VerticalAlignment="Center" />
<NumericUpDown
Classes="SmallNumericUpDown"
MinWidth="90"
FontSize="12"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="{Binding #cancelAllBtn.DesiredSize.Height}"
ToolTip.Tip="How many books to download and decrypt at the same time. 1 downloads one at a time."
Value="{Binding MaxConcurrentDownloads, Mode=TwoWay}"
Minimum="{Binding MinConcurrentDownloads}"
Maximum="{Binding MaxAllowedConcurrentDownloads}"
Increment="1"
FormatString="0" />
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="2" Margin="0,0,10,0" >
<StackPanel.Styles>
<Style Selector="NumericUpDown#PART_Spinner">
<Setter Property="Background" Value="Black"/>
</Style>
</StackPanel.Styles>
<TextBlock Margin="0,0,6,0" FontSize="11" Text="DL&#xA;Limit" VerticalAlignment="Center" />
<NumericUpDown
Classes="SmallNumericUpDown"
MinWidth="100"
FontSize="12"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
TextConverter="{StaticResource myConverter}"
Height="{Binding #cancelAllBtn.DesiredSize.Height}"
Value="{Binding SpeedLimit, Mode=TwoWay}"
Minimum="0"
KeyDown="NumericUpDown_KeyDown"
Increment="{Binding SpeedLimitIncrement}"
Maximum="999" />
</StackPanel>
<Button Grid.Column="3" FontSize="12" HorizontalAlignment="Right" Click="ClearFinishedBtn_Click">Clear Finished</Button>
</Grid>
</Grid>
</TabItem>
<!-- Log Tab -->
<TabItem>
<TabItem.Header>
<TextBlock FontSize="14" VerticalAlignment="Center">Queue Log</TextBlock>
</TabItem.Header>
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemRegionColor}">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding LogEntries}">
<DataGrid.Columns>
<DataGridTextColumn SortMemberPath="LogDate" Header="Timestamp" CanUserSort="True" Binding="{Binding LogDateString}" Width="90"/>
<DataGridTemplateColumn SortMemberPath="LogMessage" Width="*" Header="Message" CanUserSort="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="3">
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding LogMessage}" />
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Border>
<Grid Grid.Column="0" Grid.Row="1" ColumnDefinitions="*,Auto">
<Button Grid.Column="0" FontSize="12" HorizontalAlignment="Left" Click="LogCopyBtn_Click">Copy Log Entries to Clipboard</Button>
<Button Grid.Column="1" FontSize="12" HorizontalAlignment="Right" Click="ClearLogBtn_Click">Clear Log</Button>
</Grid>
</Grid>
</TabItem>
</TabControl>
<!-- Queue Status -->
<Grid Grid.Row="1" Margin="5,0,0,0" ColumnDefinitions="Auto,*,Auto">
<Panel Grid.Column="0">
<Panel.Styles>
<Style Selector="ProgressBar:horizontal">
<Setter Property="MinWidth" Value="100" />
</Style>
</Panel.Styles>
<ProgressBar IsVisible="{Binding ProgressBarVisible}" Value="{Binding Progress}" ShowProgressText="True" />
</Panel>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<StackPanel.Styles>
<Style Selector="StackPanel">
<Setter Property="Height" Value="15" />
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="Orientation" Value="Horizontal" />
<Style Selector="^ > Path">
<Setter Property="Stretch" Value="Uniform" />
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
</Style>
<Style Selector="^ > TextBlock">
<Setter Property="Margin" Value="3,0,0,0" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Style>
</StackPanel.Styles>
<StackPanel IsVisible="{Binding AnyQueued}">
<Path Data="{StaticResource QueuedIcon}" />
<TextBlock Text="{Binding QueuedCount}" />
</StackPanel>
<StackPanel IsVisible="{Binding AnyCompleted}">
<Path Data="{StaticResource QueueCompletedIcon}" />
<TextBlock Text="{Binding CompletedCount}" />
</StackPanel>
<StackPanel IsVisible="{Binding AnyErrors}">
<Path Data="{StaticResource QueueErrorIcon}"/>
<TextBlock Text="{Binding ErrorCount}" />
</StackPanel>
</StackPanel>
<Panel Grid.Column="2" Margin="0,0,5,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock Text="{Binding RunningTime}" />
</Panel>
</Grid>
</Grid>
</UserControl>