Files
Libation/Source/LibationAvalonia/Views/ProcessQueueControl.axaml
T
Allamagoosa 1eddccbe63 Bound concurrency by the hard limit and say what the machine will do
Taking the alternative offered in review. The spinner's maximum is the flat
hard limit now, the same number in both UIs and on every machine, and
machine capability is applied only at the point of use.

Bounding the control by capability made it lie in two directions at once.
Below the stored value, a two-way spinner coerces its display down to its
maximum and writes that back, so opening the panel on a smaller machine
overwrote an 8 chosen on a larger one. Raising the bound to meet the stored
value fixed that but left a stored 8 displaying 8 on a two-core box that
will only ever run 2. The two UIs had also drifted apart: Avalonia bound
Maximum and ratcheted down, WinForms set it once in the constructor, so
within a session you could lower and re-raise in one and not the other.

What is left is the gap between what the setting says and what runs, and
that is now stated rather than hidden. Chardonnay shows "(2 on this
machine)" beside the spinner, in the spare column the settings row already
had. Classic's settings table is full at three columns with no width to
spare, so it says the same thing in the control's tooltip.
2026-08-20 17:13:02 -07:00

202 lines
8.7 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="*,72">
<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>
<!-- The pane is a fixed 400px, which is not enough for the settings and both buttons
on one line - Cancel All was the one that got clipped, down to "Ca". The two
spinners get a full-width row of their own, and the buttons sit under them in
Auto columns so that neither can be squeezed by whatever is beside it. -->
<Grid Grid.Column="0" Grid.Row="1" RowDefinitions="Auto,*">
<Grid.Styles>
<Style Selector="NumericUpDown#PART_Spinner">
<Setter Property="Background" Value="Black"/>
</Style>
<Style Selector="TextBlock.settingLabel">
<Setter Property="FontSize" Value="11" />
<Setter Property="Margin" Value="0,0,6,0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</Grid.Styles>
<Grid Grid.Row="0" Margin="0,0,0,4" VerticalAlignment="Center"
ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<TextBlock Grid.Column="0" Classes="settingLabel" Text="At once" />
<NumericUpDown
Grid.Column="1"
Classes="SmallNumericUpDown"
MinWidth="86"
FontSize="12"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
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" />
<!-- Sits in the spare column between the two spinners, and only when this machine
cannot keep up with the number chosen. -->
<TextBlock
Grid.Column="2"
Classes="settingLabel"
HorizontalAlignment="Left"
Margin="6,0,6,0"
Opacity="0.7"
TextTrimming="CharacterEllipsis"
ToolTip.Tip="Fewer books download at once than the number chosen, because this machine has fewer processors to decrypt them."
IsVisible="{Binding ConcurrencyHint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text="{Binding ConcurrencyHint}" />
<TextBlock Grid.Column="3" Classes="settingLabel" Text="DL Limit" />
<NumericUpDown
Grid.Column="4"
Classes="SmallNumericUpDown"
MinWidth="86"
FontSize="12"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
TextConverter="{StaticResource myConverter}"
Value="{Binding SpeedLimit, Mode=TwoWay}"
Minimum="0"
KeyDown="NumericUpDown_KeyDown"
Increment="{Binding SpeedLimitIncrement}"
Maximum="999" />
</Grid>
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto">
<Button Name="cancelAllBtn" Grid.Column="0" FontSize="12" VerticalAlignment="Stretch" Click="CancelAllBtn_Click">Cancel All</Button>
<CheckBox
Grid.Column="1"
FontSize="12"
Margin="10,0,10,0"
VerticalAlignment="Center"
IsChecked="{Binding AutoScrollQueue, Mode=TwoWay}"
ToolTip.Tip="Keep active downloads scrolled into view."
Content="Auto-scroll" />
<Button Grid.Column="3" FontSize="12" VerticalAlignment="Stretch" Click="ClearFinishedBtn_Click">Clear Finished</Button>
</Grid>
</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>