diff --git a/src/Pixeval.Controls/AppButtonItem.xaml b/src/Pixeval.Controls/AppButtonItem.xaml
index 0a0f627b..22e47ee1 100644
--- a/src/Pixeval.Controls/AppButtonItem.xaml
+++ b/src/Pixeval.Controls/AppButtonItem.xaml
@@ -16,14 +16,14 @@
HorizontalAlignment="Left"
controls:DockPanel.Dock="Top"
FontWeight="SemiBold"
- Foreground="{StaticResource TextSecondaryAccentColor}"
+ Foreground="{StaticResource TextFillColorSecondaryBrush}"
Style="{StaticResource BaseTextBlockStyle}"
Text="{x:Bind Title, Mode=OneWay}" />
value is not 0;
+ public static Visibility IsNotZeroToVisibility(int value) => value is not 0 ? Visibility.Visible : Visibility.Collapsed;
+
public static Visibility IsNotZeroDToVisibility(double value) => value is not 0 ? Visibility.Visible : Visibility.Collapsed;
public static unsafe Color ToAlphaColor(uint color)
diff --git a/src/Pixeval.Controls/DigitalSignalItem.xaml b/src/Pixeval.Controls/DigitalSignalItem.xaml
new file mode 100644
index 00000000..4d234da8
--- /dev/null
+++ b/src/Pixeval.Controls/DigitalSignalItem.xaml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/src/Pixeval.Controls/DigitalSignalItem.xaml.cs b/src/Pixeval.Controls/DigitalSignalItem.xaml.cs
new file mode 100644
index 00000000..09f04662
--- /dev/null
+++ b/src/Pixeval.Controls/DigitalSignalItem.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.UI.Xaml.Media;
+using WinUI3Utilities.Attributes;
+
+namespace Pixeval.Controls;
+
+[DependencyProperty("Text")]
+[DependencyProperty("Fill")]
+public sealed partial class DigitalSignalItem
+{
+ public DigitalSignalItem() => InitializeComponent();
+}
diff --git a/src/Pixeval.Controls/PersonView/PersonView.xaml b/src/Pixeval.Controls/PersonView/PersonView.xaml
index 5a12505d..58ab0189 100644
--- a/src/Pixeval.Controls/PersonView/PersonView.xaml
+++ b/src/Pixeval.Controls/PersonView/PersonView.xaml
@@ -30,7 +30,7 @@
Text="{x:Bind PersonNickname, Mode=OneWay}" />
diff --git a/src/Pixeval.Controls/Pixeval.Controls.csproj b/src/Pixeval.Controls/Pixeval.Controls.csproj
index 14439de6..cdda6971 100644
--- a/src/Pixeval.Controls/Pixeval.Controls.csproj
+++ b/src/Pixeval.Controls/Pixeval.Controls.csproj
@@ -16,14 +16,14 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
@@ -49,4 +49,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj b/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj
index 7d5459ea..0e840309 100644
--- a/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj
+++ b/src/Pixeval.CoreApi/Pixeval.CoreApi.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src/Pixeval/AppManagement/AppSettings.cs b/src/Pixeval/AppManagement/AppSettings.cs
index d3240e43..7691512a 100644
--- a/src/Pixeval/AppManagement/AppSettings.cs
+++ b/src/Pixeval/AppManagement/AppSettings.cs
@@ -88,7 +88,7 @@ public partial record AppSettings() : IWindowSettings
/// The max download tasks that are allowed to run concurrently
///
[SettingsEntry(Symbol.DeveloperBoardLightning, nameof(MaxDownloadConcurrencyLevelEntryHeader), nameof(MaxDownloadConcurrencyLevelEntryDescription))]
- public int MaxDownloadTaskConcurrencyLevel { get; set; } = Environment.ProcessorCount / 2;
+ public int MaxDownloadTaskConcurrencyLevel { get; set; } = Environment.ProcessorCount / 4;
[SettingsEntry(Symbol.SaveEdit, nameof(DownloadWhenBookmarkedEntryHeader), nameof(DownloadWhenBookmarkedEntryDescription))]
public bool DownloadWhenBookmarked { get; set; }
diff --git a/src/Pixeval/Controls/Comment/CommentItem.xaml b/src/Pixeval/Controls/Comment/CommentItem.xaml
index 57c38b6c..1f994616 100644
--- a/src/Pixeval/Controls/Comment/CommentItem.xaml
+++ b/src/Pixeval/Controls/Comment/CommentItem.xaml
@@ -69,7 +69,7 @@
diff --git a/src/Pixeval/Controls/Download/DownloadItem.xaml b/src/Pixeval/Controls/Download/DownloadItem.xaml
index 7547ba24..94f6069e 100644
--- a/src/Pixeval/Controls/Download/DownloadItem.xaml
+++ b/src/Pixeval/Controls/Download/DownloadItem.xaml
@@ -50,8 +50,25 @@
ShowError="{x:Bind ViewModel.IsError(ViewModel.DownloadTask.CurrentState), Mode=OneWay}"
ShowPaused="{x:Bind ViewModel.IsPaused(ViewModel.DownloadTask.CurrentState), Mode=OneWay}"
Value="{x:Bind ViewModel.DownloadTask.ProgressPercentage, Mode=OneWay}" />
+
+
+
+
+
ThrowHelper.ArgumentOutOfRange(state)
};
- public string ActionButtonContent(DownloadState state) => state switch
+ public string ActionButtonContent(DownloadState state) => ActionButtonSymbol(state) switch
{
- DownloadState.Queued or DownloadState.Pending => DownloadItemResources.ActionDownloadCancelled,
- DownloadState.Running => DownloadItemResources.ActionButtonContentPause,
- DownloadState.Cancelled or DownloadState.Error => DownloadItemResources.ActionButtonContentRetry,
- DownloadState.Completed => DownloadItemResources.ActionButtonContentOpen,
- DownloadState.Paused => DownloadItemResources.ActionButtonContentResume,
+ Symbol.Dismiss => DownloadItemResources.ActionDownloadCancelled,
+ Symbol.Pause => DownloadItemResources.ActionButtonContentPause,
+ Symbol.ArrowRepeatAll => DownloadItemResources.ActionButtonContentRetry,
+ Symbol.Open => DownloadItemResources.ActionButtonContentOpen,
+ Symbol.Play => DownloadItemResources.ActionButtonContentResume,
_ => ThrowHelper.ArgumentOutOfRange(state)
};
public Symbol ActionButtonSymbol(DownloadState state) => state switch
{
- DownloadState.Queued or DownloadState.Pending => Symbol.Dismiss,
- DownloadState.Running => Symbol.Pause,
+ DownloadState.Pending => Symbol.Dismiss,
+ DownloadState.Queued or DownloadState.Running => Symbol.Pause,
DownloadState.Cancelled or DownloadState.Error => Symbol.ArrowRepeatAll,
DownloadState.Completed => Symbol.Open,
DownloadState.Paused => Symbol.Play,
@@ -119,6 +121,15 @@ public sealed partial class DownloadItemViewModel(IDownloadTaskGroup downloadTas
public bool IsPaused(DownloadState state) => state is DownloadState.Paused;
+ public Visibility IsGroup(IDownloadTaskGroup group) => C.ToVisibility(group is DownloadTaskGroup);
+
+ public Brush CurrentStateBrush(DownloadState state) => Application.Current.GetResource(state switch
+ {
+ DownloadState.Paused => "SystemFillColorCautionBrush",
+ DownloadState.Cancelled => "SystemFillColorNeutralBrush",
+ _ => "SystemFillColorAttentionBrush"
+ });
+
#pragma warning restore CA1822
#region Not supported
diff --git a/src/Pixeval/Controls/Illustrator/IllustratorItem.xaml b/src/Pixeval/Controls/Illustrator/IllustratorItem.xaml
index dd655e3f..956fdcaa 100644
--- a/src/Pixeval/Controls/Illustrator/IllustratorItem.xaml
+++ b/src/Pixeval/Controls/Illustrator/IllustratorItem.xaml
@@ -45,10 +45,10 @@
VerticalAlignment="Center"
Content="{fluent:SymbolIcon Symbol=Guest,
FontSize=12}"
- Foreground="{StaticResource TextSecondaryAccentColor}" />
+ Foreground="{StaticResource TextFillColorSecondaryBrush}" />
+ SelectedColor="{x:Bind controls1:C.ToAlphaColor(Entry.Value), BindBack=ColorBindBack, Mode=TwoWay}" />
diff --git a/src/Pixeval/Controls/Settings/ColorSettingsCard.xaml.cs b/src/Pixeval/Controls/Settings/ColorSettingsCard.xaml.cs
index 4f2dee76..fc7ffe59 100644
--- a/src/Pixeval/Controls/Settings/ColorSettingsCard.xaml.cs
+++ b/src/Pixeval/Controls/Settings/ColorSettingsCard.xaml.cs
@@ -1,3 +1,4 @@
+using Windows.UI;
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -22,4 +23,6 @@ public sealed partial class ColorSettingsCard
{
Entry.ValueChanged?.Invoke(Entry.Value);
}
+
+ private void ColorBindBack(Color color) => Entry.Value = C.ToAlphaUInt(color);
}
diff --git a/src/Pixeval/Download/DownloadManager.cs b/src/Pixeval/Download/DownloadManager.cs
index 1140ba68..7a4055f7 100644
--- a/src/Pixeval/Download/DownloadManager.cs
+++ b/src/Pixeval/Download/DownloadManager.cs
@@ -28,16 +28,15 @@ using System.Threading.Tasks;
using Pixeval.CoreApi.Net;
using Pixeval.Download.Models;
using Pixeval.Utilities.Threading;
-using WinUI3Utilities;
namespace Pixeval.Download;
public partial class DownloadManager : IDisposable
{
///
- /// 正在下载的任务队列,数量不超过
+ /// 正在排队的任务队列
///
- private readonly Channel _downloadTaskChannel = Channel.CreateUnbounded();
+ private readonly Channel _downloadTaskChannel = Channel.CreateUnbounded();
///
/// 使用的
@@ -53,7 +52,7 @@ public partial class DownloadManager : IDisposable
private readonly ReenterableAwaiter _throttle = new(true, true);
///
- /// 中的数量
+ /// 正在下载中的数量
///
private int _workingTasks;
@@ -93,16 +92,17 @@ public partial class DownloadManager : IDisposable
///
/// intrinsic download task are not counted
///
- public void QueueTask(IDownloadTaskGroup task)
+ public void QueueTask(IDownloadTaskGroup taskGroup)
{
- if (_taskQuerySet.TryGetValue(task.Destination, out var v) && v == task)
+ if (_taskQuerySet.TryGetValue(taskGroup.Destination, out var v) && v == taskGroup)
return;
- _taskQuerySet[task.Destination] = task;
+ _taskQuerySet[taskGroup.Destination] = taskGroup;
if (v is not null)
_ = QueuedTasks.Remove(v);
- QueuedTasks.Insert(0, task);
- _ = _downloadTaskChannel.Writer.TryWrite(task);
+ QueuedTasks.Insert(0, taskGroup);
+ taskGroup.SubscribeProgress(_downloadTaskChannel.Writer);
+ _ = _downloadTaskChannel.Writer.TryWrite(taskGroup.GetToken());
}
///
@@ -111,40 +111,30 @@ public partial class DownloadManager : IDisposable
private async void PollTask()
{
while (await _downloadTaskChannel.Reader.WaitToReadAsync())
- while (await _throttle && _downloadTaskChannel.Reader.TryRead(out var queuedTask))
- switch (queuedTask)
- {
- case ImageDownloadTask imageTask:
- imageTask.DownloadTryResume += t => _downloadTaskChannel.Writer.TryWrite(t);
- imageTask.DownloadTryReset += t => _downloadTaskChannel.Writer.TryWrite(t);
- // 子任务入列时一定是处于Queued状态,需要直接运行的
- _ = DownloadAsync(imageTask);
- break;
- case IDownloadTaskGroup taskGroup:
- await taskGroup.InitializeTaskGroupAsync();
- foreach (var subTask in taskGroup)
- {
- subTask.DownloadTryResume += t => _downloadTaskChannel.Writer.TryWrite(t);
- subTask.DownloadTryReset += t => _downloadTaskChannel.Writer.TryWrite(t);
- // 任务组中的任务需要判断是否处于Queued状态才能运行
- if (subTask.CurrentState is DownloadState.Queued)
- {
- _ = DownloadAsync(subTask);
- _ = await _throttle;
- }
- }
- break;
- }
+ while (await _throttle
+ && _downloadTaskChannel.Reader.TryRead(out var taskToken)
+ && taskToken is { Token.IsCancellationRequested: false, Task: var taskGroup })
+ {
+ await taskGroup.InitializeTaskGroupAsync();
+ foreach (var subTask in taskGroup)
+ // 需要判断是否处于Queued状态才能运行
+ if (subTask.CurrentState is DownloadState.Queued)
+ {
+ await DownloadAsync(subTask);
+ _ = await _throttle;
+ }
+ }
}
///
/// 清除指定任务
///
- ///
- public void RemoveTask(IDownloadTaskGroup task)
+ ///
+ public void RemoveTask(IDownloadTaskGroup taskGroup)
{
- _ = _taskQuerySet.Remove(task.Destination);
- _ = QueuedTasks.Remove(task);
+ taskGroup.Cancel();
+ _ = _taskQuerySet.Remove(taskGroup.Destination);
+ _ = QueuedTasks.Remove(taskGroup);
}
///
@@ -152,9 +142,8 @@ public partial class DownloadManager : IDisposable
///
public void ClearTasks()
{
- // foreach (var task in QueuedTasks)
- // await task.CancelAsync();
-
+ foreach (var task in QueuedTasks)
+ task.Cancel();
QueuedTasks.Clear();
_taskQuerySet.Clear();
}
@@ -165,11 +154,12 @@ public partial class DownloadManager : IDisposable
///
/// Execute the task only if it's already queued
///
- public bool TryExecuteTaskGroupInline(IDownloadTaskGroup task)
+ public bool TryExecuteTaskGroupInline(IDownloadTaskGroup taskGroup)
{
- if (QueuedTasks.Contains(task) && task.CurrentState is DownloadState.Queued)
+ if (QueuedTasks.Contains(taskGroup) && taskGroup.CurrentState is DownloadState.Queued)
{
- _ = _downloadTaskChannel.Writer.TryWrite(task);
+ taskGroup.SubscribeProgress(_downloadTaskChannel.Writer);
+ _ = _downloadTaskChannel.Writer.TryWrite(taskGroup.GetToken());
return true;
}
@@ -179,8 +169,21 @@ public partial class DownloadManager : IDisposable
private async Task DownloadAsync(ImageDownloadTask task)
{
await IncrementCounterAsync();
- await DownloadInternalAsync(task);
- await DecrementCounterAsync();
+ _ = Download();
+
+ return;
+ async Task Download()
+ {
+ try
+ {
+ await task.StartAsync(_httpClient);
+ }
+ catch
+ {
+ // ignored
+ }
+ await DecrementCounterAsync();
+ }
}
private async Task IncrementCounterAsync()
@@ -194,21 +197,4 @@ public partial class DownloadManager : IDisposable
_ = Interlocked.Decrement(ref _workingTasks);
await _throttle.SetResultAsync(true);
}
-
- private async Task DownloadInternalAsync(ImageDownloadTask task)
- {
- try
- {
- await (task.CurrentState switch
- {
- DownloadState.Queued => task.StartAsync(_httpClient),
- DownloadState.Paused => task.ResumeAsync(_httpClient),
- _ => ThrowHelper.ArgumentOutOfRange(task.CurrentState)
- });
- }
- catch
- {
- // ignored
- }
- }
}
diff --git a/src/Pixeval/Download/Models/DownloadTaskGroup.cs b/src/Pixeval/Download/Models/DownloadTaskGroup.cs
index 3fc91f4d..5b10c0ce 100644
--- a/src/Pixeval/Download/Models/DownloadTaskGroup.cs
+++ b/src/Pixeval/Download/Models/DownloadTaskGroup.cs
@@ -25,6 +25,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
@@ -32,7 +33,6 @@ using Pixeval.Controls.Windowing;
using Pixeval.CoreApi.Model;
using Pixeval.Database;
using Pixeval.Database.Managers;
-using Pixeval.Util.UI;
using Pixeval.Utilities;
using WinUI3Utilities;
@@ -58,6 +58,10 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
var g = sender.To();
if (e.PropertyName is not nameof(CurrentState))
return;
+ // 子任务状态变化时,不一定需要任务组状态变化,也会进入此分支
+ OnPropertyChanged(nameof(ActiveCount));
+ OnPropertyChanged(nameof(CompletedCount));
+ OnPropertyChanged(nameof(ErrorCount));
if (g.CurrentState is DownloadState.Running or DownloadState.Paused or DownloadState.Pending)
return;
g.DatabaseEntry.State = g.CurrentState;
@@ -108,18 +112,23 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
if (CurrentState is not (DownloadState.Completed or DownloadState.Error or DownloadState.Cancelled))
return;
IsProcessing = true;
- TasksSet.ForEach(t => t.TryReset());
+ (CurrentState is DownloadState.Error
+ ? TasksSet.Where(t => t.CurrentState is DownloadState.Error)
+ : TasksSet)
+ .ForEach(t => t.TryReset());
if (CancellationTokenSource.IsCancellationRequested)
{
CancellationTokenSource.Dispose();
CancellationTokenSource = new();
}
+ DownloadTryReset?.Invoke(this);
IsProcessing = false;
}
public void Pause()
{
IsProcessing = true;
+ CancellationTokenSource.Cancel();
TasksSet.ForEach(t => t.Pause());
IsProcessing = false;
}
@@ -127,7 +136,13 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
public void TryResume()
{
IsProcessing = true;
+ if (CancellationTokenSource.IsCancellationRequested)
+ {
+ CancellationTokenSource.Dispose();
+ CancellationTokenSource = new();
+ }
TasksSet.ForEach(t => t.TryResume());
+ DownloadTryResume?.Invoke(this);
IsProcessing = false;
}
@@ -146,9 +161,8 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
public abstract void Delete();
- ///
- /// 用于的取消令牌
- ///
+ public DownloadToken GetToken() => new(this, CancellationTokenSource.Token);
+
private CancellationTokenSource CancellationTokenSource { get; set; } = new();
///
@@ -219,6 +233,10 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
public event Func? AfterAllDownloadAsync;
+ private event Action? DownloadTryResume;
+
+ private event Action? DownloadTryReset;
+
protected void AddToTasksSet(ImageDownloadTask task)
{
_tasksSet.Add(task);
@@ -228,31 +246,24 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
task.DownloadErrorAsync += x => ItemDownloadErrorAsync?.Invoke(x) ?? Task.CompletedTask;
task.PropertyChanged += (_, e) =>
{
- switch (e.PropertyName)
+ OnPropertyChanged(e.PropertyName switch
{
- case nameof(ImageDownloadTask.ProgressPercentage):
- {
- var time = DateTime.Now;
- if (time - _lastReportedTime < TimeSpan.FromMilliseconds(500))
- return;
- _lastReportedTime = time;
- OnPropertyChanged(nameof(ProgressPercentage));
- break;
- }
- case nameof(ImageDownloadTask.CurrentState):
- OnPropertyChanged(nameof(CurrentState));
- break;
- case nameof(ImageDownloadTask.ErrorCause):
- OnPropertyChanged(nameof(ErrorCause));
- break;
- }
+ nameof(ImageDownloadTask.ProgressPercentage) => nameof(ProgressPercentage),
+ nameof(ImageDownloadTask.CurrentState) => nameof(CurrentState),
+ nameof(ImageDownloadTask.ErrorCause) => nameof(ErrorCause),
+ _ => ""
+ });
};
}
- ///
- /// 降低汇报频率
- ///
- private DateTime _lastReportedTime = DateTime.MinValue;
+ public void SubscribeProgress(ChannelWriter writer)
+ {
+ DownloadTryResume += OnDownloadWrite;
+ DownloadTryReset += OnDownloadWrite;
+
+ return;
+ void OnDownloadWrite(DownloadTaskGroup o) => writer.TryWrite(o.GetToken());
+ }
public Exception? ErrorCause => TasksSet.FirstOrDefault(t => t.ErrorCause is not null)?.ErrorCause;
@@ -260,7 +271,20 @@ public abstract partial class DownloadTaskGroup(DownloadHistoryEntry entry) : Ob
public bool IsAnyError => TasksSet.Any(t => t.CurrentState is DownloadState.Error);
- public double ProgressPercentage => TasksSet.Count is 0 ? 100 : TasksSet.Average(t => t.ProgressPercentage);
+ public int ActiveCount => TasksSet.Count(t => t.CurrentState is DownloadState.Queued or DownloadState.Running or DownloadState.Pending or DownloadState.Paused or DownloadState.Cancelled);
+
+ public int CompletedCount => TasksSet.Count(t => t.CurrentState is DownloadState.Completed);
+
+ public int ErrorCount => TasksSet.Count(t => t.CurrentState is DownloadState.Error);
+
+ public double ProgressPercentage =>
+ IsCreateFromEntry
+ ? DatabaseEntry.State is DownloadState.Queued
+ ? 0
+ : 100
+ : TasksSet.Count is 0
+ ? 100
+ : TasksSet.Average(t => t.ProgressPercentage);
public IEnumerator GetEnumerator() => TasksSet.GetEnumerator();
diff --git a/src/Pixeval/Download/Models/IDownloadTaskGroup.cs b/src/Pixeval/Download/Models/IDownloadTaskGroup.cs
index 0ee5ddbf..e7b53989 100644
--- a/src/Pixeval/Download/Models/IDownloadTaskGroup.cs
+++ b/src/Pixeval/Download/Models/IDownloadTaskGroup.cs
@@ -23,6 +23,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Pixeval.CoreApi.Model;
using Pixeval.Database;
@@ -34,4 +36,16 @@ public interface IDownloadTaskGroup : IDownloadTaskBase, IIdEntry, INotifyProper
DownloadHistoryEntry DatabaseEntry { get; }
ValueTask InitializeTaskGroupAsync();
+
+ void SubscribeProgress(ChannelWriter writer);
+
+ DownloadToken GetToken();
+
+ int ActiveCount { get; }
+
+ int CompletedCount { get; }
+
+ int ErrorCount { get; }
}
+
+public readonly record struct DownloadToken(IDownloadTaskGroup Task, CancellationToken Token);
diff --git a/src/Pixeval/Download/Models/ImageDownloadTask.cs b/src/Pixeval/Download/Models/ImageDownloadTask.cs
index 23a19c2e..0657e382 100644
--- a/src/Pixeval/Download/Models/ImageDownloadTask.cs
+++ b/src/Pixeval/Download/Models/ImageDownloadTask.cs
@@ -39,6 +39,8 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
Uri = uri;
Destination = destination;
CurrentState = initState;
+ if (initState is DownloadState.Completed or DownloadState.Cancelled or DownloadState.Error)
+ ProgressPercentage = 100;
DownloadStartedAsync += DownloadStartedAsyncOverride;
DownloadStoppedAsync += DownloadStoppedAsyncOverride;
DownloadErrorAsync += DownloadErrorAsyncOverride;
@@ -66,13 +68,13 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
[ObservableProperty] private DownloadState _currentState;
- [ObservableProperty] private double _progressPercentage = 100;
+ [ObservableProperty] private double _progressPercentage;
[ObservableProperty] private Exception? _errorCause;
[ObservableProperty] private bool _isProcessing;
- private CancellationTokenSource CancellationTokenSource { get; set; } = new();
+ protected CancellationTokenSource CancellationTokenSource { get; private set; } = new();
private bool _isRunning;
@@ -199,11 +201,12 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
public void Pause()
{
- if (CurrentState is not DownloadState.Running)
+ if (CurrentState is not (DownloadState.Queued or DownloadState.Running))
return;
IsProcessing = true;
CancellationTokenSource.Cancel();
CurrentState = DownloadState.Paused;
+ DownloadPaused?.Invoke(this);
IsProcessing = false;
}
@@ -222,11 +225,6 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
IsProcessing = false;
}
- public async Task ResumeAsync(HttpClient httpClient)
- {
- await StartAsync(httpClient, true);
- }
-
public void Cancel()
{
if (CurrentState is not (DownloadState.Paused or DownloadState.Pending or DownloadState.Running or DownloadState.Queued))
@@ -234,6 +232,7 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
IsProcessing = true;
CancellationTokenSource.Cancel();
CurrentState = DownloadState.Cancelled;
+ DownloadCancelled?.Invoke(this);
IsProcessing = false;
}
@@ -255,6 +254,10 @@ public partial class ImageDownloadTask : ObservableObject, IDownloadTaskBase, IP
public event Action? DownloadTryReset;
+ public event Action? DownloadPaused;
+
+ public event Action? DownloadCancelled;
+
public event Func AfterDownloadAsync;
void IProgress.Report(double value) => ProgressPercentage = value;
diff --git a/src/Pixeval/Download/Models/MangaDownloadTaskGroup.cs b/src/Pixeval/Download/Models/MangaDownloadTaskGroup.cs
index 4f7ace52..b0081e13 100644
--- a/src/Pixeval/Download/Models/MangaDownloadTaskGroup.cs
+++ b/src/Pixeval/Download/Models/MangaDownloadTaskGroup.cs
@@ -85,7 +85,15 @@ public class MangaDownloadTaskGroup : DownloadTaskGroup, IImageDownloadTaskGroup
private IllustrationDownloadFormat IllustrationDownloadFormat { get; }
- public override string OpenLocalDestination => Path.GetDirectoryName(TasksSet[0].Destination)!;
+ public override string OpenLocalDestination
+ {
+ get
+ {
+ if (TasksSet.Count is 0)
+ SetTasksSet();
+ return Path.GetDirectoryName(TasksSet[0].Destination)!;
+ }
+ }
public override void Delete()
{
diff --git a/src/Pixeval/Download/Models/SingleImageDownloadTaskGroup.cs b/src/Pixeval/Download/Models/SingleImageDownloadTaskGroup.cs
index 067b995a..e313ff06 100644
--- a/src/Pixeval/Download/Models/SingleImageDownloadTaskGroup.cs
+++ b/src/Pixeval/Download/Models/SingleImageDownloadTaskGroup.cs
@@ -24,6 +24,7 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Pixeval.CoreApi.Model;
@@ -40,7 +41,11 @@ public class SingleImageDownloadTaskGroup : ImageDownloadTask, IImageDownloadTas
{
public DownloadHistoryEntry DatabaseEntry { get; }
- public ValueTask InitializeTaskGroupAsync() => ValueTask.CompletedTask;
+ public ValueTask InitializeTaskGroupAsync()
+ {
+ SetNotCreateFromEntry();
+ return ValueTask.CompletedTask;
+ }
public Illustration Entry => DatabaseEntry.Entry.To();
@@ -60,6 +65,8 @@ public class SingleImageDownloadTaskGroup : ImageDownloadTask, IImageDownloadTas
{
DatabaseEntry = entry;
CurrentState = entry.State;
+ if (entry.State is DownloadState.Completed or DownloadState.Cancelled or DownloadState.Error)
+ ProgressPercentage = 100;
IllustrationDownloadFormat = IoHelper.GetIllustrationFormat(Path.GetExtension(Destination));
}
@@ -92,6 +99,23 @@ public class SingleImageDownloadTaskGroup : ImageDownloadTask, IImageDownloadTas
await TagsManager.SetTagsAsync(Destination, Entry, token);
}
+ public DownloadToken GetToken() => new(this, CancellationTokenSource.Token);
+
+ public int ActiveCount => CurrentState is DownloadState.Queued or DownloadState.Running or DownloadState.Pending or DownloadState.Paused or DownloadState.Cancelled ? 1 : 0;
+
+ public int CompletedCount => CurrentState is DownloadState.Completed ? 1 : 0;
+
+ public int ErrorCount => CurrentState is DownloadState.Error ? 1 : 0;
+
+ public void SubscribeProgress(ChannelWriter writer)
+ {
+ DownloadTryResume += OnDownloadWrite;
+ DownloadTryReset += OnDownloadWrite;
+
+ return;
+ void OnDownloadWrite(ImageDownloadTask o) => writer.TryWrite(o.To().GetToken());
+ }
+
public int Count => 1;
public IEnumerator GetEnumerator() => ((IReadOnlyList)[this]).GetEnumerator();
diff --git a/src/Pixeval/Pages/MainPage.xaml b/src/Pixeval/Pages/MainPage.xaml
index 2353a717..af1d0fab 100644
--- a/src/Pixeval/Pages/MainPage.xaml
+++ b/src/Pixeval/Pages/MainPage.xaml
@@ -30,7 +30,7 @@
TextWrapping="WrapWholeWords" />
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
-
-
+
+
diff --git a/src/Pixeval/Themes/Colors.xaml b/src/Pixeval/Themes/Colors.xaml
index 5b2deb34..3ccf5f1a 100644
--- a/src/Pixeval/Themes/Colors.xaml
+++ b/src/Pixeval/Themes/Colors.xaml
@@ -12,12 +12,6 @@
TintOpacity="0.3" />
-
-
-
-
-
-
#FAFAFA
#F6F6F6
@@ -36,12 +30,6 @@
TintOpacity="0.3" />
-
-
-
-
-
-
#FAFAFA
#F6F6F6
@@ -60,13 +48,7 @@
TintOpacity="0.3" />
-
-
-
-
-
-
#302C2D
#323232
#323232
@@ -78,12 +60,6 @@
-
-
-
-
-
-
Cyan
#323232