mirror of
https://github.com/Pixeval/Pixeval.git
synced 2025-01-07 03:06:53 +08:00
实现扩展Upscale功能
This commit is contained in:
parent
157408905d
commit
548b99c1c5
@ -179,8 +179,6 @@ public partial record AppSettings() : IWindowSettings
|
||||
[AttributeIgnore(typeof(ResetAttribute))]
|
||||
public DateTimeOffset LastCheckedUpdate { get; set; } = DateTimeOffset.MinValue;
|
||||
|
||||
public bool ShowUpscalerTeachingTip { get; set; } = true;
|
||||
|
||||
[SettingsEntry(Symbol.Box, nameof(PixivNameResolverHeaderText), nameof(PixivNameResolverDescriptionText))]
|
||||
public string[] PixivAppApiNameResolver { get; set; } =
|
||||
[
|
||||
|
@ -72,7 +72,7 @@ public partial class AppViewModel(App app) : IDisposable
|
||||
var fileCache = await FileCache.CreateDefaultAsync();
|
||||
var memoryCache = await MemoryCache.CreateDefaultAsync(200);
|
||||
var extensionService = new ExtensionService();
|
||||
extensionService.LoadAllExtensions();
|
||||
extensionService.LoadAllHosts();
|
||||
return new ServiceCollection()
|
||||
.AddSingleton<IllustrationDownloadTaskFactory>()
|
||||
.AddSingleton<NovelDownloadTaskFactory>()
|
||||
@ -115,6 +115,7 @@ public partial class AppViewModel(App app) : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
AppServiceProvider?.GetRequiredService<LiteDatabase>().Dispose();
|
||||
AppServiceProvider?.GetRequiredService<ExtensionService>().Dispose();
|
||||
AppServiceProvider?.Dispose();
|
||||
DownloadManager?.Dispose();
|
||||
MakoClient?.Dispose();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using System.Runtime.InteropServices;
|
||||
using Pixeval.AppManagement;
|
||||
@ -17,7 +18,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Extensions;
|
||||
|
||||
public class ExtensionService
|
||||
public class ExtensionService : IDisposable
|
||||
{
|
||||
public IReadOnlyList<ExtensionsHostModel> ExtensionHosts => _extensionHosts;
|
||||
|
||||
@ -25,29 +26,43 @@ public class ExtensionService
|
||||
|
||||
public IEnumerable<KeyValuePair<ExtensionsHostModel, IReadOnlyList<IExtension>>> ActivePairs => ExtensionsLookup.Where(t => t.Key.IsActive);
|
||||
|
||||
public IReadOnlyList<ExtensionSettingsGroup> SettingsGroups => _settingsGroups;
|
||||
|
||||
public IReadOnlyList<IExtension> Extensions => ExtensionsLookup
|
||||
.Aggregate(new List<IExtension>(), (o, t) => o.Apply(p => p.AddRange(t.Value)));
|
||||
|
||||
public IReadOnlyList<IExtension> ActiveExtensions => ActivePairs
|
||||
.Aggregate(new List<IExtension>(), (o, t) => o.Apply(p => p.AddRange(t.Value)));
|
||||
|
||||
public IReadOnlyList<ExtensionSettingsGroup> SettingsGroups => _settingsGroups;
|
||||
public IEnumerable<IImageTransformerExtension> ActiveImageTransformers => ActiveExtensions.OfType<IImageTransformerExtension>();
|
||||
|
||||
public IEnumerable<IImageTransformerExtension> ImageTransformers => ActiveExtensions.OfType<IImageTransformerExtension>();
|
||||
|
||||
private readonly List<ExtensionsHostModel> _extensionHosts = [];
|
||||
private readonly ObservableCollection<ExtensionsHostModel> _extensionHosts = [];
|
||||
|
||||
private readonly Dictionary<ExtensionsHostModel, IReadOnlyList<IExtension>> _extensionsLookup = [];
|
||||
|
||||
private readonly List<ExtensionSettingsGroup> _settingsGroups = [];
|
||||
|
||||
public void LoadAllExtensions()
|
||||
public void LoadAllHosts()
|
||||
{
|
||||
foreach (var dll in AppKnownFolders.Extensions.GetFiles("*.dll"))
|
||||
foreach (var dll in AppKnownFolders.Extensions.GetFiles("*.dll"))
|
||||
_ = TryLoadHost(dll);
|
||||
}
|
||||
|
||||
public bool TryLoadHost(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LoadExtension(dll) is not { } host)
|
||||
continue;
|
||||
if (LoadExtension(path) is not { } host)
|
||||
return false;
|
||||
host.Initialize(AppSettings.CurrentCulture.Name, AppKnownFolders.Temp.FullPath);
|
||||
var model = new ExtensionsHostModel(host);
|
||||
_extensionHosts.Add(model);
|
||||
LoadExtensions(model);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,4 +206,11 @@ public class ExtensionService
|
||||
foreach (var imageTransformer in imageTransformers)
|
||||
imageTransformer.OnExtensionLoaded();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var extension in Extensions)
|
||||
extension.OnExtensionUnloaded();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:media="using:Microsoft.Xaml.Interactions.Media"
|
||||
xmlns:triggers="using:Pixeval.Util.Triggers"
|
||||
xmlns:winUi="using:FluentIcons.WinUI"
|
||||
xmlns:windowing="using:Pixeval.Controls.Windowing"
|
||||
KeyboardAcceleratorPlacementMode="Hidden"
|
||||
Loaded="IllustrationViewerPage_OnLoaded"
|
||||
@ -256,27 +255,6 @@
|
||||
Symbol="AppTitle"
|
||||
Text="{x:Bind _viewModel.CurrentIllustration.Title, Mode=OneWay}" />
|
||||
</CommandBar>
|
||||
|
||||
<!--<TextBlock
|
||||
Grid.Column="1"
|
||||
MaxHeight="45"
|
||||
Margin="310,0,375,0"
|
||||
Style="{StaticResource TextBlockStyle}"
|
||||
Text="{x:Bind _viewModel.AdditionalText, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="{x:Bind _viewModel.AdditionalTextBlockVisible, Mode=OneWay}" />
|
||||
|
||||
<StackPanel
|
||||
Margin="320,10,375,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind _viewModel.UpscalerProgressBarVisible, Mode=OneWay}">
|
||||
<ProgressBar Value="{x:Bind _viewModel.UpscalerProgress, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,2,0,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind _viewModel.UpscalerProgressText}" />
|
||||
</StackPanel>-->
|
||||
<CommandBar Grid.Column="2" HorizontalAlignment="Stretch">
|
||||
<AppBarElementContainer VerticalContentAlignment="Center">
|
||||
<TextBlock Style="{StaticResource TextBlockStyle}" Visibility="{x:Bind controls:C.ToVisibility(_viewModel.CurrentIllustration.IsManga), Mode=OneWay}">
|
||||
@ -286,20 +264,6 @@
|
||||
</TextBlock>
|
||||
</AppBarElementContainer>
|
||||
<AppBarSeparator />
|
||||
<AppBarButton
|
||||
x:Name="UpscaleButton"
|
||||
Click="UpscaleButton_OnTapped"
|
||||
Icon="{winUi:SymbolIcon Symbol=EyeTracking}"
|
||||
IsCompact="True"
|
||||
IsEnabled="False"
|
||||
LabelPosition="Collapsed" />
|
||||
<!--<TeachingTip
|
||||
x:Name="UpscaleTeachingTip"
|
||||
x:Uid="/EntryViewerPage/UpscaleTeachingTip"
|
||||
IconSource="{winUi:SymbolIconSource Symbol=CommentNote}"
|
||||
IsEnabled="False"
|
||||
IsLightDismissEnabled="True"
|
||||
Target="{x:Bind UpscaleButton}" />-->
|
||||
<AppBarSeparator Height="35" />
|
||||
<AppBarButton
|
||||
Command="{x:Bind _viewModel.FullScreenCommand}"
|
||||
|
@ -73,10 +73,6 @@ public sealed partial class IllustrationViewerPage
|
||||
// 此处this.XamlRoot为null
|
||||
_viewModel = HWnd.GetIllustrationViewerPageViewModelFromHandle(parameter);
|
||||
|
||||
//_viewModel.CurrentImage.UpscalerMessageChannel.Reader.OnReceive(
|
||||
// reader => reader == _viewModel.CurrentImage.UpscalerMessageChannel.Reader,
|
||||
// OnReceiveUpscalerMessage);
|
||||
|
||||
_viewModel.DetailedPropertyChanged += (sender, args) =>
|
||||
{
|
||||
var vm = sender.To<IllustrationViewerPageViewModel>();
|
||||
@ -105,10 +101,6 @@ public sealed partial class IllustrationViewerPage
|
||||
}
|
||||
|
||||
Navigate<ImageViewerPage>(IllustrationImageShowcaseFrame, vm.CurrentImage, info);
|
||||
|
||||
//vm.CurrentImage.UpscalerMessageChannel.Reader.OnReceive(
|
||||
// reader => reader == vm.CurrentImage.UpscalerMessageChannel.Reader,
|
||||
// OnReceiveUpscalerMessage);
|
||||
};
|
||||
|
||||
_viewModel.PropertyChanged += (sender, args) =>
|
||||
@ -129,31 +121,6 @@ public sealed partial class IllustrationViewerPage
|
||||
Navigate<ImageViewerPage>(IllustrationImageShowcaseFrame, _viewModel.CurrentImage);
|
||||
}
|
||||
|
||||
//[GeneratedRegex(@"\d+\.\d+%")]
|
||||
//private static partial Regex UpscalerMessagePercentageRegex();
|
||||
|
||||
//private void OnReceiveUpscalerMessage(string message)
|
||||
//{
|
||||
//_viewModel.UpscalerProgressBarVisible = UpscalerMessagePercentageRegex().IsMatch(message);
|
||||
//_viewModel.AdditionalTextBlockVisible = !_viewModel.UpscalerProgressBarVisible;
|
||||
//if (message == Upscaler.ProcessCompletedMark)
|
||||
//{
|
||||
// _viewModel.UpscalerProgressText = string.Empty;
|
||||
// _viewModel.UpscalerProgress = 0;
|
||||
// _viewModel.AdditionalText = $"{EntryViewerPageResources.AiUpscaled}";
|
||||
// return;
|
||||
//}
|
||||
|
||||
//if (UpscalerMessagePercentageRegex().IsMatch(message))
|
||||
//{
|
||||
// _viewModel.UpscalerProgressText = message;
|
||||
// _viewModel.UpscalerProgress = (int) double.Parse(message[..^1]);
|
||||
// return;
|
||||
//}
|
||||
|
||||
//_viewModel.AdditionalText = message;
|
||||
//}
|
||||
|
||||
private void IllustrationViewerPage_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!App.AppViewModel.AppSettings.BrowseOriginalImage)
|
||||
@ -281,30 +248,6 @@ public sealed partial class IllustrationViewerPage
|
||||
teachingTip.Target = appBarButton.IsInOverflow ? null : appBarButton;
|
||||
}
|
||||
|
||||
private async void UpscaleButton_OnTapped(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//if (!App.AppViewModel.AppSettings.ShowUpscalerTeachingTip)
|
||||
//{
|
||||
// _viewModel.CurrentImage.UpscaleCommand.Execute(null);
|
||||
// return;
|
||||
//}
|
||||
//UpscaleTeachingTip.IsOpen = true;
|
||||
//var dialog = await HWnd.CreateOkCancelAsync(EntryViewerPageResources.AiUpscalerWarningTitle,
|
||||
// EntryViewerPageResources.AiUpscalerWarningContent,
|
||||
// EntryViewerPageResources.AiUpscalerWarningOkButtonContent,
|
||||
// EntryViewerPageResources.AiUpscalerWarningCancelButtonContent);
|
||||
|
||||
//if (dialog == ContentDialogResult.Primary)
|
||||
//{
|
||||
// _viewModel.CurrentImage.UpscaleCommand.Execute(null);
|
||||
//}
|
||||
|
||||
//if (App.AppViewModel.AppSettings.ShowUpscalerTeachingTip)
|
||||
//{
|
||||
// App.AppViewModel.AppSettings.ShowUpscalerTeachingTip = false;
|
||||
//}
|
||||
}
|
||||
|
||||
public Visibility IsLogoVisible()
|
||||
{
|
||||
return WindowFactory.GetWindowForElement(this).HWnd != WindowFactory.RootWindow.HWnd
|
||||
|
@ -49,15 +49,6 @@ public partial class IllustrationViewerPageViewModel : DetailedUiObservableObjec
|
||||
[ObservableProperty]
|
||||
public partial bool AdditionalTextBlockVisible { get; set; } = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool UpscalerProgressBarVisible { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int UpscalerProgress { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? UpscalerProgressText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
@ -43,7 +43,10 @@ using Pixeval.CoreApi.Net.Response;
|
||||
using Pixeval.Download;
|
||||
using Pixeval.Util.ComponentModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Pixeval.Extensions;
|
||||
using Pixeval.Extensions.Common;
|
||||
using Pixeval.Util.IO.Caching;
|
||||
using Pixeval.Extensions.Common.Transformers;
|
||||
|
||||
namespace Pixeval.Pages.IllustrationViewer;
|
||||
|
||||
@ -51,8 +54,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
private bool _upscaled;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsMirrored { get; set; }
|
||||
|
||||
@ -82,17 +83,10 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
[ObservableProperty]
|
||||
public partial float Scale { get; set; } = 1;
|
||||
|
||||
//[ObservableProperty]
|
||||
//public partial bool Upscaling { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsFit))]
|
||||
public partial ZoomableImageMode ShowMode { get; set; }
|
||||
|
||||
//private Upscaler? _upscaler;
|
||||
|
||||
//public Channel<string> UpscalerMessageChannel { get; } = Channel.CreateUnbounded<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 由于多窗口,可能在加载图片后改变设置,所以此处缓存原图设置
|
||||
/// </summary>
|
||||
@ -199,9 +193,12 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
|
||||
private void AdvancePhase(LoadingPhase phase, double progress = 0)
|
||||
{
|
||||
LoadingText = phase is LoadingPhase.DownloadingImage
|
||||
? LoadingPhaseExtension.GetResource(LoadingPhase.DownloadingImage).Format((int) (LoadingProgress = progress))
|
||||
: LoadingPhaseExtension.GetResource(phase);
|
||||
LoadingProgress = progress;
|
||||
LoadingText = phase switch
|
||||
{
|
||||
LoadingPhase.DownloadingImage => LoadingPhaseExtension.GetResource(LoadingPhase.DownloadingImage).Format((int)progress),
|
||||
_ => LoadingPhaseExtension.GetResource(phase)
|
||||
};
|
||||
}
|
||||
|
||||
private async Task LoadImage()
|
||||
@ -230,6 +227,7 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
|
||||
var ugoiraUrl = metadata?.LargeUrl;
|
||||
object? source = null;
|
||||
// 原图动图(一张一张下)
|
||||
if (ugoiraUrl is not null && _isOriginal)
|
||||
{
|
||||
var urls = await IllustrationViewModel.UgoiraOriginalUrlsAsync();
|
||||
@ -253,12 +251,19 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
// 静图
|
||||
if (ugoiraUrl is null)
|
||||
{
|
||||
ugoiraUrl = IllustrationViewModel.StaticUrl(_isOriginal);
|
||||
if (await DownloadUrlAsync(ugoiraUrl) is { } s)
|
||||
{
|
||||
var newStream = await s.CopyToMemoryStreamAsync(false);
|
||||
s.Position = 0;
|
||||
source = (IReadOnlyList<Stream>) [s];
|
||||
ApplyImageTransformers(newStream);
|
||||
}
|
||||
}
|
||||
// 非原图动图(压缩包)
|
||||
else
|
||||
{
|
||||
source = await DownloadUrlAsync(ugoiraUrl);
|
||||
@ -290,6 +295,43 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
new Progress<double>(d => AdvancePhase(LoadingPhase.DownloadingImage, startProgress + ratio * d)),
|
||||
cancellationToken: ImageLoadingCancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
// 传入的s一定是新建的流,所以最后都会销毁s
|
||||
async void ApplyImageTransformers(Stream s)
|
||||
{
|
||||
var extensionService = App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>();
|
||||
var iStream = s.ToIStream();
|
||||
var isIllustrationOrFirstPageManga = IllustrationViewModel.MangaIndex is -1 or 0;
|
||||
var index = 1;
|
||||
var token = ImageLoadingCancellationTokenSource.Token;
|
||||
foreach (var transformer in extensionService.ActiveImageTransformers)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
if (isIllustrationOrFirstPageManga)
|
||||
HWnd.InfoGrowl(ImageViewerPageResources.ApplyingTransformerExtensionsFormatted.Format(index));
|
||||
// 运行扩展
|
||||
iStream.Seek(0, SeekOrigin.Begin, out _);
|
||||
var next = await transformer.TransformAsync(iStream);
|
||||
if (next is null)
|
||||
continue;
|
||||
// 最初的s会在最后一行销毁,从别的扩展拿到的iStream无法销毁,所以此处直接覆盖之前的流的行为没有问题
|
||||
iStream = next;
|
||||
// 下一次循环准备
|
||||
var newStream = iStream.ToStream();
|
||||
var memoryStream = await newStream.CopyToMemoryStreamAsync(false);
|
||||
iStream.Seek(0, SeekOrigin.Begin, out _);
|
||||
// 显示图片
|
||||
var last = OriginalStreamsSource;
|
||||
OriginalStreamsSource = (IReadOnlyList<Stream>)[memoryStream];
|
||||
if (last is IReadOnlyList<Stream> and [IDisposable disposable])
|
||||
disposable.Dispose();
|
||||
++index;
|
||||
}
|
||||
if (!token.IsCancellationRequested && isIllustrationOrFirstPageManga)
|
||||
HWnd.SuccessGrowl(ImageViewerPageResources.AllTransformerExtensionsFinished);
|
||||
await s.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,18 +376,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
HWnd.ShowShareUi();
|
||||
}
|
||||
|
||||
private async void UpscaleCommandOnExecuteRequested(XamlUICommand sender, ExecuteRequestedEventArgs args)
|
||||
{
|
||||
//_upscaled = true;
|
||||
//UpscaleCommand.NotifyCanExecuteChanged();
|
||||
//_upscaler ??= new Upscaler(new(OriginalStream!.Single(),
|
||||
// App.AppViewModel.AppSettings.UpscalerModel,
|
||||
// App.AppViewModel.AppSettings.UpscalerScaleRatio,
|
||||
// App.AppViewModel.AppSettings.UpscalerOutputType));
|
||||
//var newStream = await _upscaler.UpscaleAsync(UpscalerMessageChannel);
|
||||
//OriginalStream = [newStream];
|
||||
}
|
||||
|
||||
private void InitializeCommands()
|
||||
{
|
||||
PlayGifCommand.CanExecuteRequested += (_, e) => e.CanExecute = IllustrationViewModel.IsUgoira && LoadSuccessfully;
|
||||
@ -378,9 +408,6 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
|
||||
SetAsBackgroundCommand.CanExecuteRequested += IsNotUgoiraAndLoadingCompletedCanExecuteRequested;
|
||||
SetAsBackgroundCommand.ExecuteRequested += SetAsBackgroundCommandOnExecuteRequested;
|
||||
|
||||
//UpscaleCommand.CanExecuteRequested += IsUpscaleCommandCanExecutedRequested;
|
||||
//UpscaleCommand.ExecuteRequested += UpscaleCommandOnExecuteRequested;
|
||||
}
|
||||
|
||||
private void UpdateCommandCanExecute()
|
||||
@ -393,12 +420,8 @@ public partial class ImageViewerPageViewModel : UiObservableObject, IDisposable
|
||||
RotateCounterclockwiseCommand.NotifyCanExecuteChanged();
|
||||
MirrorCommand.NotifyCanExecuteChanged();
|
||||
ShareCommand.NotifyCanExecuteChanged();
|
||||
//UpscaleCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void IsUpscaleCommandCanExecutedRequested(XamlUICommand _, CanExecuteRequestedEventArgs args) =>
|
||||
args.CanExecute = LoadSuccessfully && !IllustrationViewModel.IsUgoira && !_upscaled;
|
||||
|
||||
private void LoadingCompletedCanExecuteRequested(XamlUICommand _, CanExecuteRequestedEventArgs args) => args.CanExecute = LoadSuccessfully;
|
||||
|
||||
private void IsNotUgoiraAndLoadingCompletedCanExecuteRequested(XamlUICommand sender, CanExecuteRequestedEventArgs args) => args.CanExecute = !IllustrationViewModel.IsUgoira && LoadSuccessfully;
|
||||
@ -459,5 +482,5 @@ public enum LoadingPhase
|
||||
DownloadingImage,
|
||||
|
||||
[LocalizedResource(typeof(ImageViewerPageResources), nameof(ImageViewerPageResources.LoadingImage))]
|
||||
LoadingImage
|
||||
LoadingImage,
|
||||
}
|
||||
|
@ -173,6 +173,7 @@
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.FooterMenuItems>
|
||||
<NavigationViewItem
|
||||
x:Name="ExtensionsTab"
|
||||
x:Uid="/MainPage/ExtensionsTab"
|
||||
Icon="{controls:BitmapImageIcon Uri=ms-appx:///Assets/Images/Icons/extensions.png}"
|
||||
Tag="{x:Bind _viewModel.ExtensionsTag}" />
|
||||
|
@ -132,7 +132,7 @@ public sealed partial class MainPage
|
||||
return;
|
||||
}
|
||||
|
||||
if (Equals(selectedItem, DownloadListTab) || Equals(selectedItem, SettingsTab) || Equals(selectedItem, TagsTab))
|
||||
if (Equals(selectedItem, DownloadListTab) || Equals(selectedItem, SettingsTab) || Equals(selectedItem, TagsTab)|| Equals(selectedItem, ExtensionsTab))
|
||||
Navigate(MainPageRootFrame, tag);
|
||||
else
|
||||
MainPageRootFrame.NavigateTag(tag, new SuppressNavigationTransitionInfo());
|
||||
|
@ -1,20 +1,27 @@
|
||||
<Page
|
||||
<controls:EnhancedWindowPage
|
||||
x:Class="Pixeval.Pages.Misc.ExtensionsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:controls="using:Pixeval.Controls"
|
||||
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:extensions="using:Pixeval.Extensions"
|
||||
xmlns:fluent="using:FluentIcons.WinUI"
|
||||
xmlns:local="using:Pixeval.Pages.Misc"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<controls:DockPanel VerticalSpacing="10">
|
||||
<controls1:DockPanel VerticalSpacing="10">
|
||||
<TextBlock
|
||||
x:Uid="/ExtensionsPage/TitleTextBlock"
|
||||
Margin="20,40,20,20"
|
||||
controls:DockPanel.Dock="Top"
|
||||
controls1:DockPanel.Dock="Top"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<CommandBar controls1:DockPanel.Dock="Top" DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
x:Uid="/ExtensionsPage/AddExtensionsButton"
|
||||
Click="AddExtensionsOnClick"
|
||||
Icon="{fluent:SymbolIcon Symbol=PlugConnectedAdd}" />
|
||||
</CommandBar>
|
||||
<ScrollView>
|
||||
<ItemsRepeater Margin="20,0,20,20" ItemsSource="{x:Bind Models}">
|
||||
<ItemsRepeater.Layout>
|
||||
@ -22,8 +29,8 @@
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="extensions:ExtensionsHostModel">
|
||||
<controls:SettingsCard Description="{x:Bind Description}" HeaderIcon="{fluent:SymbolIcon Symbol=PuzzlePiece}">
|
||||
<controls:SettingsCard.Header>
|
||||
<controls1:SettingsCard Description="{x:Bind Description}" HeaderIcon="{fluent:SymbolIcon Symbol=PuzzlePiece}">
|
||||
<controls1:SettingsCard.Header>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Text="{x:Bind Name}" />
|
||||
<Run
|
||||
@ -36,14 +43,14 @@
|
||||
FontWeight="Bold"
|
||||
Text="{x:Bind Author}" />
|
||||
</TextBlock>
|
||||
</controls:SettingsCard.Header>
|
||||
<controls:SettingsCard.Content>
|
||||
</controls1:SettingsCard.Header>
|
||||
<controls1:SettingsCard.Content>
|
||||
<ToggleSwitch IsOn="{x:Bind IsActive, Mode=TwoWay}" />
|
||||
</controls:SettingsCard.Content>
|
||||
</controls:SettingsCard>
|
||||
</controls1:SettingsCard.Content>
|
||||
</controls1:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ScrollView>
|
||||
</controls:DockPanel>
|
||||
</Page>
|
||||
</controls1:DockPanel>
|
||||
</controls:EnhancedWindowPage>
|
||||
|
@ -1,29 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Pixeval.AppManagement;
|
||||
using Pixeval.Controls;
|
||||
using Pixeval.Extensions;
|
||||
using Pixeval.Util.IO;
|
||||
using Pixeval.Util.UI;
|
||||
using Pixeval.Utilities;
|
||||
|
||||
namespace Pixeval.Pages.Misc;
|
||||
|
||||
public sealed partial class ExtensionsPage : Page
|
||||
public sealed partial class ExtensionsPage
|
||||
{
|
||||
private IReadOnlyList<ExtensionsHostModel> Models =>
|
||||
App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>().ExtensionHosts;
|
||||
|
||||
public ExtensionsPage()
|
||||
public ExtensionsPage() => InitializeComponent();
|
||||
|
||||
private async void AddExtensionsOnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InitializeComponent();
|
||||
var extensionService = App.AppViewModel.AppServiceProvider.GetRequiredService<ExtensionService>();
|
||||
var files = await HWnd.OpenMultipleDllsOpenPickerAsync();
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileInfo = new FileInfo(file.Path);
|
||||
var dllFileName = fileInfo.Name;
|
||||
var newDllPath = AppKnownFolders.Extensions.CombinePath(dllFileName);
|
||||
if (File.Exists(newDllPath))
|
||||
{
|
||||
HWnd.ErrorGrowl(ExtensionsPageResources.DllFileExistedErrorFormatted.Format(dllFileName));
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
fileInfo.CopyTo(newDllPath);
|
||||
if (extensionService.TryLoadHost(newDllPath))
|
||||
{
|
||||
HWnd.SuccessGrowl(ExtensionsPageResources.DllLoadedSuccessfullyFormatted.Format(dllFileName));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
HWnd.ErrorGrowl(ExtensionsPageResources.DllLoadFailedFormatted.Format(dllFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,18 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AddExtensionsButton.Label" xml:space="preserve">
|
||||
<value>添加扩展</value>
|
||||
</data>
|
||||
<data name="DllFileExistedErrorFormatted" xml:space="preserve">
|
||||
<value>名称为“{0}”的文件已在扩展文件夹中,将跳过该文件</value>
|
||||
</data>
|
||||
<data name="DllLoadedSuccessfullyFormatted" xml:space="preserve">
|
||||
<value>已加载DLL:“{0}”</value>
|
||||
</data>
|
||||
<data name="DllLoadFailedFormatted" xml:space="preserve">
|
||||
<value>加载DLL“{0}”失败</value>
|
||||
</data>
|
||||
<data name="TitleTextBlock.Text" xml:space="preserve">
|
||||
<value>Pixeval 扩展管理器</value>
|
||||
</data>
|
||||
|
@ -117,6 +117,12 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ApplyingTransformerExtensionsFormatted" xml:space="preserve">
|
||||
<value>正在应用图片转换扩展:第 {0} 个</value>
|
||||
</data>
|
||||
<data name="AllTransformerExtensionsFinished" xml:space="preserve">
|
||||
<value>所有图片转换扩展应用完成</value>
|
||||
</data>
|
||||
<data name="CheckingCache" xml:space="preserve">
|
||||
<value>正在检查缓存...</value>
|
||||
</data>
|
||||
@ -132,4 +138,4 @@
|
||||
<data name="MergingUgoiraFrames" xml:space="preserve">
|
||||
<value>正在合成动图...</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
@ -19,6 +19,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
@ -52,6 +53,7 @@ using Size = Windows.Foundation.Size;
|
||||
using Symbol = FluentIcons.Common.Symbol;
|
||||
using SymbolIcon = FluentIcons.WinUI.SymbolIcon;
|
||||
using SymbolIconSource = FluentIcons.WinUI.SymbolIconSource;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Pixeval.Util.UI;
|
||||
|
||||
@ -242,14 +244,19 @@ public static partial class UiHelper
|
||||
box.Document.SetText(TextSetOptions.None, "");
|
||||
}
|
||||
|
||||
public static IAsyncOperation<StorageFolder?> OpenFolderPickerAsync(this Window window) => window.PickSingleFolderAsync(PickerLocationId.PicturesLibrary);
|
||||
|
||||
public static IAsyncOperation<StorageFile?> OpenFileOpenPickerAsync(this Window window) => window.PickSingleFileAsync(PickerLocationId.PicturesLibrary);
|
||||
|
||||
public static IAsyncOperation<StorageFolder?> OpenFolderPickerAsync(this ulong hWnd) => WindowFactory.ForkedWindows[hWnd].PickSingleFolderAsync(PickerLocationId.PicturesLibrary);
|
||||
|
||||
public static IAsyncOperation<StorageFile?> OpenFileOpenPickerAsync(this ulong hWnd) => WindowFactory.ForkedWindows[hWnd].PickSingleFileAsync(PickerLocationId.PicturesLibrary);
|
||||
|
||||
public static IAsyncOperation<IReadOnlyList<StorageFile>> OpenMultipleDllsOpenPickerAsync(this ulong hWnd)
|
||||
{
|
||||
var fileOpenPicker = new FileOpenPicker();
|
||||
fileOpenPicker.FileTypeFilter.Add(".dll");
|
||||
fileOpenPicker.SuggestedStartLocation = PickerLocationId.Desktop;
|
||||
InitializeWithWindow.Initialize(fileOpenPicker, (nint)hWnd);
|
||||
return fileOpenPicker.PickMultipleFilesAsync();
|
||||
}
|
||||
|
||||
public static async Task<T> AwaitPageTransitionAsync<T>(this Frame root) where T : Page
|
||||
{
|
||||
await ThreadingHelper.SpinWaitAsync(() => root.Content is not T { IsLoaded: true });
|
||||
|
Loading…
Reference in New Issue
Block a user