mirror of
https://github.com/Pixeval/Pixeval.git
synced 2025-01-05 10:17:19 +08:00
AOT部分支持 (#502)
* 替换WebApiClientCore * 添加sg优化 * 公开类型 * 修复图标错位 * 更新AOT支持 * 暂存 * 解决项目内json反射问题 * set pubxml * 修复枚举和资源相关的反射 * fix all CSWinrt1028 * 修复收藏bug * asynclazy可重入性 * 修复novel动画 * 调整SettingsEntryAttribute * 添加aot条件 * 修复备份 * update workflow
This commit is contained in:
parent
ee70b396de
commit
356ef9326f
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@ -1,11 +1,11 @@
|
||||
name: Pixeval Build Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'dependabot**'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
# push:
|
||||
# branches-ignore:
|
||||
# - 'dependabot**'
|
||||
# tags-ignore:
|
||||
# - '**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
@ -23,9 +23,9 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
fail-fast: true
|
||||
matrix:
|
||||
configuration: [Debug]
|
||||
configuration: [Release]
|
||||
platform: ['x64','x86','arm64']
|
||||
|
||||
env:
|
||||
@ -70,8 +70,8 @@ jobs:
|
||||
shell: pwsh
|
||||
run: 'nuget restore $env:SOLUTION_NAME'
|
||||
|
||||
- name: Build Files (Debug)
|
||||
id: build_app_with_debug
|
||||
- name: Build Files (Release)
|
||||
id: build_app_with_release
|
||||
shell: pwsh
|
||||
run: |
|
||||
msbuild $env:PACKAGE_PROJECT_NAME `
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -184,7 +184,7 @@ publish/
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
# *.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31808.319
|
||||
|
@ -35,23 +35,19 @@
|
||||
<Project Path="src\Pixeval.Controls\Pixeval.Controls.csproj">
|
||||
<Configuration Solution="*|arm64" Project="*|arm64" />
|
||||
<Configuration Solution="*|x64" Project="*|x64" />
|
||||
<Configuration Solution="*|x86" Project="*|x86" />
|
||||
</Project>
|
||||
<Project Path="src\Pixeval.CoreApi\Pixeval.CoreApi.csproj">
|
||||
<Configuration Solution="*|arm64" Project="*|arm64" />
|
||||
<Configuration Solution="*|x64" Project="*|x64" />
|
||||
<Configuration Solution="*|x86" Project="*|x86" />
|
||||
</Project>
|
||||
<Project Path="src\Pixeval.SourceGen\Pixeval.SourceGen.csproj" />
|
||||
<Project Path="src\Pixeval.Utilities\Pixeval.Utilities.csproj">
|
||||
<Configuration Solution="*|arm64" Project="*|arm64" />
|
||||
<Configuration Solution="*|x64" Project="*|x64" />
|
||||
<Configuration Solution="*|x86" Project="*|x86" />
|
||||
</Project>
|
||||
<Project Path="src\Pixeval\Pixeval.csproj">
|
||||
<Configuration Solution="*|*" Project="*|*|Deploy" />
|
||||
<Configuration Solution="*|arm64" Project="*|arm64|Deploy" />
|
||||
<Configuration Solution="*|x64" Project="*|x64|Deploy" />
|
||||
<Configuration Solution="*|x86" Project="*|x86|Deploy" />
|
||||
</Project>
|
||||
</Solution>
|
@ -24,6 +24,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
[LocalizationMetadata(typeof(AdvancedItemsViewResources))]
|
||||
public enum ItemsViewLayoutType
|
||||
{
|
||||
[LocalizedResource(typeof(AdvancedItemsViewResources), nameof(AdvancedItemsViewResources.LinedFlow))]
|
||||
|
@ -33,12 +33,13 @@ using Windows.Foundation;
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using CommunityToolkit.WinUI.Helpers;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pixeval.Collections;
|
||||
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
public class AdvancedObservableCollection<T> : IList<T>, IList, IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<T> where T : class
|
||||
public class AdvancedObservableCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : IList<T>, IList, IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<T> where T : class
|
||||
{
|
||||
private readonly Dictionary<string, PropertyInfo> _sortProperties = [];
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
#region Copyright
|
||||
|
||||
// GPL v3 License
|
||||
//
|
||||
// Pixeval/Pixeval.Controls
|
||||
// Copyright (c) 2024 Pixeval.Controls/ColorHelper.cs
|
||||
// Pixeval/Pixeval
|
||||
// Copyright (c) 2024 Pixeval/EnumLocalizationMetadataAttribute.cs
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@ -17,31 +16,23 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using Windows.UI;
|
||||
using System;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
namespace Pixeval.Attributes;
|
||||
|
||||
public static class ColorHelper
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
|
||||
public class LocalizationMetadataAttribute(Type resourceType) : Attribute
|
||||
{
|
||||
public static unsafe Color GetAlphaColor(this uint color)
|
||||
{
|
||||
var ptr = &color;
|
||||
var c = (byte*)ptr;
|
||||
return Color.FromArgb(c[3], c[2], c[1], c[0]);
|
||||
}
|
||||
public Type ResourceType { get; } = resourceType;
|
||||
|
||||
public static unsafe uint GetAlphaUInt(this Color color)
|
||||
{
|
||||
uint ret;
|
||||
var ptr = &ret;
|
||||
var c = (byte*)ptr;
|
||||
c[0] = color.B;
|
||||
c[1] = color.G;
|
||||
c[2] = color.R;
|
||||
c[3] = color.A;
|
||||
return ret;
|
||||
}
|
||||
public bool IsPartial { get; init; }
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class AttachedLocalizationMetadataAttribute<T>(Type resourceType) : Attribute
|
||||
{
|
||||
public Type ResourceType { get; } = resourceType;
|
||||
}
|
@ -24,7 +24,7 @@ using System;
|
||||
namespace Pixeval.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class LocalizedResource(Type resourceLoader, string key, object? formatKey = null) : Attribute
|
||||
public class LocalizedResourceAttribute(Type resourceLoader, string key, object? formatKey = null) : Attribute
|
||||
{
|
||||
public Type ResourceLoader { get; } = resourceLoader;
|
||||
|
||||
@ -32,3 +32,11 @@ public class LocalizedResource(Type resourceLoader, string key, object? formatKe
|
||||
|
||||
public object? FormatKey { get; } = formatKey;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class AttachedLocalizedResourceAttribute(string fieldName, string key) : Attribute
|
||||
{
|
||||
public string FieldName { get; } = fieldName;
|
||||
|
||||
public string Key { get; } = key;
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public static class C
|
||||
return Color.FromArgb(c[3], c[2], c[1], c[0]);
|
||||
}
|
||||
|
||||
public static SolidColorBrush ToSolidColorBrush(uint value) => new(value.GetAlphaColor());
|
||||
public static SolidColorBrush ToSolidColorBrush(uint value) => new(ToAlphaColor(value));
|
||||
|
||||
public static unsafe uint ToAlphaUInt(Color color)
|
||||
{
|
||||
|
@ -7,9 +7,11 @@
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>Enable</Nullable>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultLanguage>zh-cn</DefaultLanguage>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -17,20 +19,21 @@
|
||||
<PackageReference Include="FluentIcons.WinUI" Version="1.1.242" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240428000" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240607001" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Triggers" Version="8.1.240606-rc" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Collections" Version="8.1.240606-rc" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.1.240606-rc" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.1.240606-rc" />
|
||||
<PackageReference Include="WinUI3Utilities" Version="1.1.7" />
|
||||
<PackageReference Include="WinUI3Utilities" Version="1.1.7.6" />
|
||||
|
||||
<ProjectReference Include="..\Pixeval.Utilities\Pixeval.Utilities.csproj" />
|
||||
<ProjectReference Include="..\Pixeval.SourceGen\Pixeval.SourceGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="False" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--Exposes PRI resources-->
|
||||
|
@ -20,11 +20,14 @@
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Text.Json;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Pixeval.CoreApi.Global.Enum;
|
||||
using Pixeval.CoreApi.Model;
|
||||
using Pixeval.CoreApi.Net.Request;
|
||||
using Pixeval.CoreApi.Net.Response;
|
||||
using Pixeval.CoreApi.Preference;
|
||||
|
||||
namespace Pixeval.CoreApi;
|
||||
|
||||
@ -32,7 +35,7 @@ namespace Pixeval.CoreApi;
|
||||
[JsonSerializable(typeof(PixivBookmarkTagResponse))]
|
||||
[JsonSerializable(typeof(PixivCommentResponse))]
|
||||
[JsonSerializable(typeof(PixivIllustrationResponse))]
|
||||
[JsonSerializable(typeof(PixivNextUrlResponse<>))]
|
||||
// [JsonSerializable(typeof(PixivNextUrlResponse<>))]
|
||||
[JsonSerializable(typeof(PixivNovelResponse))]
|
||||
[JsonSerializable(typeof(PixivRelatedUsersResponse))]
|
||||
[JsonSerializable(typeof(PixivSingleIllustResponse))]
|
||||
@ -138,4 +141,32 @@ namespace Pixeval.CoreApi;
|
||||
[JsonSerializable(typeof(WorkSortOption))]
|
||||
[JsonSerializable(typeof(WorkType))]
|
||||
[JsonSerializable(typeof(SimpleWorkType))]
|
||||
internal partial class AppJsonSerializerContext : JsonSerializerContext;
|
||||
|
||||
[JsonSerializable(typeof(string[]))]
|
||||
[JsonSerializable(typeof(Tag[]))]
|
||||
[JsonSerializable(typeof(MetaPage[]))]
|
||||
[JsonSerializable(typeof(NovelIllustInfo[]))]
|
||||
[JsonSerializable(typeof(NovelImage[]))]
|
||||
[JsonSerializable(typeof(NovelReplaceableGlossary[]))]
|
||||
[JsonSerializable(typeof(long[]))]
|
||||
[JsonSerializable(typeof(NovelTag[]))]
|
||||
[JsonSerializable(typeof(BookmarkTag[]))]
|
||||
[JsonSerializable(typeof(Comment[]))]
|
||||
[JsonSerializable(typeof(Illustration[]))]
|
||||
[JsonSerializable(typeof(Novel[]))]
|
||||
[JsonSerializable(typeof(User[]))]
|
||||
[JsonSerializable(typeof(SpotlightBody[]))]
|
||||
[JsonSerializable(typeof(PixivisionTag[]))]
|
||||
[JsonSerializable(typeof(Illust[]))]
|
||||
[JsonSerializable(typeof(RelatedArticle[]))]
|
||||
[JsonSerializable(typeof(Spotlight[]))]
|
||||
[JsonSerializable(typeof(Result[]))]
|
||||
[JsonSerializable(typeof(TrendingTag[]))]
|
||||
[JsonSerializable(typeof(Frame[]))]
|
||||
[JsonSerializable(typeof(UserSpecifiedBookmarkTag[]))]
|
||||
[JsonSerializable(typeof(Work[]))]
|
||||
|
||||
[JsonSerializable(typeof(Session))]
|
||||
public partial class AppJsonSerializerContext : JsonSerializerContext;
|
||||
|
||||
public class SnakeCaseLowerEnumConverter<T>() : JsonStringEnumConverter<T>(JsonNamingPolicy.SnakeCaseLower) where T : struct, Enum;
|
||||
|
@ -21,6 +21,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Pixeval.CoreApi.Global.Exception;
|
||||
using Pixeval.CoreApi.Model;
|
||||
@ -43,7 +44,7 @@ namespace Pixeval.CoreApi.Engine;
|
||||
/// <typeparam name="TFetchEngine">The fetch engine</typeparam>
|
||||
public abstract class AbstractPixivAsyncEnumerator<TEntity, TRawEntity, TFetchEngine>(TFetchEngine pixivFetchEngine,
|
||||
MakoApiKind apiKind) : IAsyncEnumerator<TEntity>
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
where TEntity : class, IEntry
|
||||
where TRawEntity : class
|
||||
{
|
||||
@ -100,13 +101,12 @@ public abstract class AbstractPixivAsyncEnumerator<TEntity, TRawEntity, TFetchEn
|
||||
return Result<TRawEntity>.AsFailure(await MakoNetworkException.FromHttpResponseMessageAsync(responseMessage, MakoClient.Configuration.DomainFronting).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
var result = (await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false)).FromJson<TRawEntity>();
|
||||
var str = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
return result is null
|
||||
? Result<TRawEntity>.AsFailure()
|
||||
: ValidateResponse(result)
|
||||
? Result<TRawEntity>.AsSuccess(result)
|
||||
: Result<TRawEntity>.AsFailure();
|
||||
if (JsonSerializer.Deserialize(str, typeof(TRawEntity), AppJsonSerializerContext.Default) is TRawEntity result)
|
||||
if (ValidateResponse(result))
|
||||
return Result<TRawEntity>.AsSuccess(result);
|
||||
return Result<TRawEntity>.AsFailure();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -29,9 +29,9 @@ namespace Pixeval.CoreApi.Engine;
|
||||
|
||||
internal abstract class RecursivePixivAsyncEnumerator<TEntity, TRawEntity, TFetchEngine>(TFetchEngine pixivFetchEngine, MakoApiKind makoApiKind)
|
||||
: AbstractPixivAsyncEnumerator<TEntity, TRawEntity, TFetchEngine>(pixivFetchEngine, makoApiKind)
|
||||
where TEntity : class, IEntry
|
||||
where TEntity : class, IEntry
|
||||
where TRawEntity : class
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
{
|
||||
private TRawEntity? RawEntity { get; set; }
|
||||
|
||||
@ -112,9 +112,9 @@ internal static class RecursivePixivAsyncEnumerators
|
||||
{
|
||||
public abstract class BaseRecursivePixivAsyncEnumerator<TEntity, TRawEntity, TFetchEngine>(TFetchEngine pixivFetchEngine, MakoApiKind makoApiKind, string initialUrl)
|
||||
: RecursivePixivAsyncEnumerator<TEntity, TRawEntity, TFetchEngine>(pixivFetchEngine, makoApiKind)
|
||||
where TEntity : class, IEntry
|
||||
where TRawEntity : PixivNextUrlResponse<TEntity>
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
where TEntity : class, IEntry
|
||||
where TRawEntity : class, IPixivNextUrlResponse<TEntity>
|
||||
where TFetchEngine : class, IFetchEngine<TEntity>
|
||||
{
|
||||
protected override string InitialUrl => initialUrl;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Pixeval.CoreApi.Global.Enum;
|
||||
|
||||
@ -27,13 +27,12 @@ namespace Pixeval.CoreApi.Global.Enum;
|
||||
/// The privacy policy of Pixiv, be aware that the <see cref="Private" /> option
|
||||
/// is only permitted when the ID is pointing to yourself
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(SnakeCaseLowerEnumConverter<PrivacyPolicy>))]
|
||||
public enum PrivacyPolicy
|
||||
{
|
||||
[Description("public")]
|
||||
[EnumMember(Value = "public")]
|
||||
Public,
|
||||
|
||||
[Description("private")]
|
||||
[EnumMember(Value = "private")]
|
||||
Private
|
||||
}
|
||||
|
@ -90,27 +90,3 @@ public enum RankOption
|
||||
[Description("day_r18_ai")]
|
||||
DayR18Ai
|
||||
}
|
||||
|
||||
public static class RankOptionExtension
|
||||
{
|
||||
public static readonly RankOption[] NovelRankOptions =
|
||||
[
|
||||
RankOption.Day,
|
||||
RankOption.Week,
|
||||
// RankOption.Month,
|
||||
RankOption.DayMale,
|
||||
RankOption.DayFemale,
|
||||
// RankOption.DayManga,
|
||||
// RankOption.WeekManga,
|
||||
// RankOption.MonthManga,
|
||||
RankOption.WeekOriginal,
|
||||
RankOption.WeekRookie,
|
||||
RankOption.DayR18,
|
||||
RankOption.DayMaleR18,
|
||||
RankOption.DayFemaleR18,
|
||||
RankOption.WeekR18,
|
||||
RankOption.WeekR18G,
|
||||
RankOption.DayAi,
|
||||
RankOption.DayR18Ai
|
||||
];
|
||||
}
|
||||
|
@ -19,17 +19,16 @@
|
||||
#endregion
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Pixeval.CoreApi.Global.Enum;
|
||||
|
||||
[JsonConverter(typeof(SnakeCaseLowerEnumConverter<TargetFilter>))]
|
||||
public enum TargetFilter
|
||||
{
|
||||
[Description("for_android")]
|
||||
[EnumMember(Value = "for_android")]
|
||||
ForAndroid,
|
||||
|
||||
[Description("for_ios")]
|
||||
[EnumMember(Value = "for_ios")]
|
||||
ForIos
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public partial class MakoClient
|
||||
|
||||
var span = contentHtml[startIndex..endIndex];
|
||||
|
||||
return JsonSerializer.Deserialize<NovelContent>(span)!;
|
||||
return (NovelContent)JsonSerializer.Deserialize(span, typeof(NovelContent), AppJsonSerializerContext.Default)!;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
@ -101,7 +102,7 @@ public partial class MakoClient
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> RunWithLoggerAsync(Func<Task<HttpResponseMessage>> task)
|
||||
private async Task<HttpResponseMessage> RunWithLoggerAsync(Func<Task<HttpResponseMessage>> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -149,13 +150,14 @@ public partial class MakoClient
|
||||
|
||||
internal void LogException(Exception e) => Logger.LogError("MakoClient Exception", e);
|
||||
|
||||
[DynamicDependency("ConstructSystemProxy", "SystemProxyInfo", "System.Net.Http")]
|
||||
static MakoClient()
|
||||
{
|
||||
var type = typeof(HttpClient).Assembly.GetType("System.Net.Http.SystemProxyInfo");
|
||||
var method = type?.GetMethod("ConstructSystemProxy");
|
||||
var @delegate = method?.CreateDelegate<Func<IWebProxy>>();
|
||||
|
||||
_getCurrentSystemProxy = @delegate ?? ThrowUtils.Throw<MissingMethodException, Func<IWebProxy>>("Unable to find proxy functions");
|
||||
_getCurrentSystemProxy = @delegate ?? ThrowUtils.Throw<MissingMethodException, Func<IWebProxy>>(new("Unable to find proxy functions"));
|
||||
HttpClient.DefaultProxy = _getCurrentSystemProxy();
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,9 @@ public partial class MakoClient : ICancellable, IDisposable, IAsyncDisposable
|
||||
makoClient.Session = (await makoClient.Provider.GetRequiredService<IAuthEndPoint>().RefreshAsync(new RefreshSessionRequest(refreshToken)).ConfigureAwait(false)).ToSession();
|
||||
return makoClient;
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError("Login error", e);
|
||||
await makoClient.DisposeAsync();
|
||||
return null;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public partial record Comment : IIdEntry
|
||||
public required long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("comment")]
|
||||
public required string CommentContent { get; set; }
|
||||
public required string CommentContent { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("date")]
|
||||
public required DateTimeOffset Date { get; set; }
|
||||
@ -52,7 +52,7 @@ public partial record Stamp
|
||||
public required long StampId { get; set; }
|
||||
|
||||
[JsonPropertyName("stamp_url")]
|
||||
public required string StampUrl { get; set; }
|
||||
public required string StampUrl { get; set; } = DefaultImageUrls.ImageNotAvailable;
|
||||
}
|
||||
|
||||
[Factory]
|
||||
@ -62,10 +62,10 @@ public partial record CommentUser
|
||||
public required long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
public required string Name { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("account")]
|
||||
public required string Account { get; set; }
|
||||
public required string Account { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("profile_image_urls")]
|
||||
public required ProfileImageUrls ProfileImageUrls { get; set; }
|
||||
|
@ -301,8 +301,8 @@ internal class SpecialDictionaryConverter<T> : JsonConverter<T[]>
|
||||
return ThrowUtils.Json<T[]>();
|
||||
case JsonTokenType.PropertyName:
|
||||
{
|
||||
var jsonConverter = (JsonConverter<T>)options.GetConverter(typeof(T));
|
||||
list.Add(jsonConverter.Read(ref reader, typeof(T), options)!);
|
||||
var propertyValue = (T)JsonSerializer.Deserialize(ref reader, typeof(T), AppJsonSerializerContext.Default)!;
|
||||
list.Add(propertyValue);
|
||||
break;
|
||||
}
|
||||
case JsonTokenType.EndObject:
|
||||
|
@ -31,19 +31,19 @@ namespace Pixeval.CoreApi.Model;
|
||||
public partial record TokenResponse
|
||||
{
|
||||
[JsonPropertyName("access_token")]
|
||||
public required string AccessToken { get; set; }
|
||||
public required string AccessToken { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("expires_in")]
|
||||
public required long ExpiresIn { get; set; }
|
||||
|
||||
[JsonPropertyName("token_type")]
|
||||
public required string TokenType { get; set; }
|
||||
public required string TokenType { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("scope")]
|
||||
public required string Scope { get; set; }
|
||||
public required string Scope { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("refresh_token")]
|
||||
public required string RefreshToken { get; set; }
|
||||
public required string RefreshToken { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("user")]
|
||||
public required TokenUser User { get; set; }
|
||||
@ -79,13 +79,13 @@ public partial record TokenUser
|
||||
public required long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
public required string Name { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("account")]
|
||||
public required string Account { get; set; }
|
||||
public required string Account { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("mail_address")]
|
||||
public required string MailAddress { get; set; }
|
||||
public required string MailAddress { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("is_premium")]
|
||||
public required bool IsPremium { get; set; }
|
||||
@ -104,11 +104,11 @@ public partial record TokenUser
|
||||
public partial record TokenProfileImageUrls
|
||||
{
|
||||
[JsonPropertyName("px_16x16")]
|
||||
public required string Px16X16 { get; set; }
|
||||
public required string Px16X16 { get; set; } = DefaultImageUrls.NoProfile;
|
||||
|
||||
[JsonPropertyName("px_50x50")]
|
||||
public required string Px50X50 { get; set; }
|
||||
public required string Px50X50 { get; set; } = DefaultImageUrls.NoProfile;
|
||||
|
||||
[JsonPropertyName("px_170x170")]
|
||||
public required string Px170X170 { get; set; }
|
||||
public required string Px170X170 { get; set; } = DefaultImageUrls.NoProfile;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public interface IAppApiEndPoint
|
||||
Task<PixivSingleNovelResponse> GetSingleNovelAsync([AliasAs("novel_id")] long id);
|
||||
|
||||
[HttpGet("/webview/v2/novel")]
|
||||
Task<string> GetNovelContentAsync([AliasAs("id")] long id, [AliasAs("raw")] bool raw = false);
|
||||
Task<string> GetNovelContentAsync(long id, bool raw = false);
|
||||
/*
|
||||
[AliasAs("viewer_version")] string viewerVersion = "20221031_ai",
|
||||
[AliasAs("font")] string x1 = "mincho",
|
||||
@ -68,7 +68,7 @@ public interface IAppApiEndPoint
|
||||
*/
|
||||
|
||||
[HttpGet("/v1/user/related")]
|
||||
Task<PixivRelatedUsersResponse> RelatedUserAsync([AliasAs("filter")] TargetFilter filter, [AliasAs("seed_user_id")] long userId);
|
||||
Task<PixivRelatedUsersResponse> RelatedUserAsync(TargetFilter filter, [AliasAs("seed_user_id")] long userId);
|
||||
|
||||
[HttpPost("/v1/user/follow/add")]
|
||||
Task<HttpResponseMessage> FollowUserAsync([FormContent] FollowUserRequest request);
|
||||
@ -77,10 +77,10 @@ public interface IAppApiEndPoint
|
||||
Task<HttpResponseMessage> RemoveFollowUserAsync([FormContent] RemoveFollowUserRequest request);
|
||||
|
||||
[HttpGet("/v1/trending-tags/illust")]
|
||||
Task<TrendingTagResponse> GetTrendingTagsAsync([AliasAs("filter")] TargetFilter filter);
|
||||
Task<TrendingTagResponse> GetTrendingTagsAsync(TargetFilter filter);
|
||||
|
||||
[HttpGet("/v1/trending-tags/novel")]
|
||||
Task<TrendingTagResponse> GetTrendingTagsForNovelAsync([AliasAs("filter")] TargetFilter filter);
|
||||
Task<TrendingTagResponse> GetTrendingTagsForNovelAsync(TargetFilter filter);
|
||||
|
||||
[HttpGet("/v1/ugoira/metadata")]
|
||||
Task<UgoiraMetadataResponse> GetUgoiraMetadataAsync([AliasAs("illust_id")] long id);
|
||||
|
@ -26,8 +26,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivBookmarkTagResponse : PixivNextUrlResponse<BookmarkTag>
|
||||
public partial record PixivBookmarkTagResponse : IPixivNextUrlResponse<BookmarkTag>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("bookmark_tags")]
|
||||
public override required BookmarkTag[] Entities { get; set; } = [];
|
||||
public /*override*/ required BookmarkTag[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivCommentResponse : PixivNextUrlResponse<Comment>
|
||||
public partial record PixivCommentResponse : IPixivNextUrlResponse<Comment>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("comments")]
|
||||
public override required Comment[] Entities { get; set; } = [];
|
||||
public /*override*/ required Comment[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivIllustrationResponse : PixivNextUrlResponse<Illustration>
|
||||
public partial record PixivIllustrationResponse : IPixivNextUrlResponse<Illustration>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("illusts")]
|
||||
public override required Illustration[] Entities { get; set; } = [];
|
||||
public /*override*/ required Illustration[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -25,10 +25,10 @@ using Pixeval.CoreApi.Model;
|
||||
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
public abstract record PixivNextUrlResponse<TEntity> where TEntity : class, IEntry
|
||||
public interface IPixivNextUrlResponse<TEntity> where TEntity : class, IEntry
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; } = null;
|
||||
string? NextUrl { get; set; }
|
||||
|
||||
public abstract TEntity[] Entities { get; set; }
|
||||
TEntity[] Entities { get; set; }
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivNovelResponse : PixivNextUrlResponse<Novel>
|
||||
public partial record PixivNovelResponse : IPixivNextUrlResponse<Novel>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("novels")]
|
||||
public override required Novel[] Entities { get; set; } = [];
|
||||
public /*override*/ required Novel[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivSpotlightResponse : PixivNextUrlResponse<Spotlight>
|
||||
public partial record PixivSpotlightResponse : IPixivNextUrlResponse<Spotlight>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("spotlight_articles")]
|
||||
public override required Spotlight[] Entities { get; set; } = [];
|
||||
public /*override*/ required Spotlight[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.CoreApi.Net.Response;
|
||||
|
||||
[Factory]
|
||||
public partial record PixivUserResponse : PixivNextUrlResponse<User>
|
||||
public partial record PixivUserResponse : IPixivNextUrlResponse<User>
|
||||
{
|
||||
[JsonPropertyName("next_url")]
|
||||
public required string? NextUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("user_previews")]
|
||||
public override required User[] Entities { get; set; } = [];
|
||||
public /*override*/ required User[] Entities { get; set; } = [];
|
||||
}
|
||||
|
@ -6,13 +6,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="WebApiClientCore" Version="2.1.1" PrivateAssets="True" />
|
||||
<PackageReference Include="WebApiClientCore.Extensions.SourceGenerator" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -19,8 +19,8 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Pixeval.Utilities;
|
||||
|
||||
namespace Pixeval.CoreApi.Preference;
|
||||
|
||||
@ -46,5 +46,5 @@ public record Session(
|
||||
[property: JsonPropertyName("account")] string Account,
|
||||
[property: JsonPropertyName("isPremium")] bool IsPremium)
|
||||
{
|
||||
public override string? ToString() => this.ToJson();
|
||||
public override string ToString() => JsonSerializer.Serialize(this, typeof(Session), AppJsonSerializerContext.Default);
|
||||
}
|
||||
|
@ -32,8 +32,12 @@ public class FactoryGenerator : IIncrementalGenerator
|
||||
IdentifierName(symbol.Name),
|
||||
syntax.Initializer is { } init
|
||||
? init.Value
|
||||
: symbol.Type.NullableAnnotation is NullableAnnotation.NotAnnotated && symbol.Type.GetAttributes().Any(i => i.AttributeClass?.MetadataName == AttributeName)
|
||||
? InvocationExpression(symbol.Type.GetStaticMemberAccessExpression(createDefault))
|
||||
: symbol.Type.NullableAnnotation is NullableAnnotation.NotAnnotated && symbol.Type
|
||||
.GetAttributes().Any(i => i.AttributeClass?.MetadataName == AttributeName)
|
||||
? InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(symbol.Type.ToDisplayString()),
|
||||
IdentifierName(createDefault)))
|
||||
: DefaultExpression(symbol.Type.GetTypeSyntax(false)));
|
||||
});
|
||||
|
||||
@ -70,12 +74,10 @@ public class FactoryGenerator : IIncrementalGenerator
|
||||
AttributeFullName,
|
||||
(_, _) => true,
|
||||
(syntaxContext, _) => syntaxContext
|
||||
).Combine(context.CompilationProvider);
|
||||
);
|
||||
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, tuple) =>
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, ga) =>
|
||||
{
|
||||
var (ga, compilation) = tuple;
|
||||
|
||||
if (ga.TargetSymbol is not INamedTypeSymbol symbol)
|
||||
return;
|
||||
|
||||
|
313
src/Pixeval.SourceGen/LocalizationMetadataGenerator.cs
Normal file
313
src/Pixeval.SourceGen/LocalizationMetadataGenerator.cs
Normal file
@ -0,0 +1,313 @@
|
||||
#region Copyright
|
||||
// GPL v3 License
|
||||
//
|
||||
// Pixeval/Pixeval.SourceGen
|
||||
// Copyright (c) 2024 Pixeval.SourceGen/EnumLocalizationMetadataGenerator.cs
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using static Pixeval.SourceGen.SyntaxHelper;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pixeval.SourceGen;
|
||||
|
||||
public abstract class LocalizationMetadataGeneratorBase
|
||||
{
|
||||
public static ClassDeclarationSyntax GetClassDeclaration(string typeFullName, string name, string resourceTypeName, List<(string Name, string ResourceName)> members, bool isPartial)
|
||||
{
|
||||
const string stringRepresentableItemName = "global::Pixeval.Controls.StringRepresentableItem";
|
||||
const string valuesTable = "ValuesTable";
|
||||
const string getResource = "GetResource";
|
||||
return ClassDeclaration(name)
|
||||
.AddModifiers(Token(SyntaxKind.StaticKeyword),
|
||||
isPartial ? Token(SyntaxKind.PartialKeyword) : Token(SyntaxKind.PublicKeyword))
|
||||
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
|
||||
.AddMembers(
|
||||
MethodDeclaration(
|
||||
ParseTypeName(
|
||||
$"global::System.Collections.Generic.IReadOnlyList<{stringRepresentableItemName}>"),
|
||||
"GetItems")
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithBody(
|
||||
Block(List<StatementSyntax>([
|
||||
LocalDeclarationStatement(
|
||||
VariableDeclaration(ParseTypeName("var"), SeparatedList([
|
||||
VariableDeclarator("array").WithInitializer(EqualsValueClause(
|
||||
ArrayCreationExpression(
|
||||
ArrayType(
|
||||
ParseTypeName(stringRepresentableItemName),
|
||||
List([
|
||||
ArrayRankSpecifier(SeparatedList([
|
||||
(ExpressionSyntax)MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(valuesTable),
|
||||
IdentifierName("Count"))
|
||||
]))
|
||||
])))))
|
||||
]))),
|
||||
LocalDeclarationStatement(
|
||||
VariableDeclaration(ParseTypeName("var"), SeparatedList([
|
||||
VariableDeclarator("i").WithInitializer(EqualsValueClause(
|
||||
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))))
|
||||
]))),
|
||||
ForEachStatement(ParseTypeName("var"), Identifier("t"), IdentifierName(valuesTable),
|
||||
Block(List<StatementSyntax>([
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
|
||||
ElementAccessExpression(IdentifierName("array"),
|
||||
BracketedArgumentList(SeparatedList([Argument(IdentifierName("i"))]))),
|
||||
ObjectCreationExpression(ParseTypeName(stringRepresentableItemName),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("t"),
|
||||
IdentifierName("Key"))),
|
||||
Argument(InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(resourceTypeName),
|
||||
IdentifierName(getResource)),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("t"),
|
||||
IdentifierName("Value")))
|
||||
]))))
|
||||
])), null))),
|
||||
ExpressionStatement(PrefixUnaryExpression(SyntaxKind.PreIncrementExpression,
|
||||
IdentifierName("i")))
|
||||
]))),
|
||||
ReturnStatement(IdentifierName("array"))
|
||||
]))),
|
||||
MethodDeclaration(ParseTypeName("string"), getResource)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithParameterList(ParameterList(SeparatedList([
|
||||
Parameter(Identifier("id")).WithType(ParseTypeName("string"))
|
||||
])))
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(resourceTypeName), IdentifierName(getResource)),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(IdentifierName("id"))
|
||||
])))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
|
||||
MethodDeclaration(ParseTypeName("string"), getResource)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithParameterList(ParameterList(SeparatedList([
|
||||
Parameter(Identifier("value")).WithType(ParseTypeName(typeFullName))
|
||||
])))
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(resourceTypeName), IdentifierName(getResource)),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(ElementAccessExpression(
|
||||
IdentifierName(valuesTable), BracketedArgumentList(SeparatedList([
|
||||
Argument(IdentifierName("value"))
|
||||
]))))
|
||||
])))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
|
||||
PropertyDeclaration(
|
||||
ParseTypeName("global::System.Collections.Frozen.FrozenDictionary<string, string>"),
|
||||
"NamesTable")
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("global::System.Collections.Frozen.FrozenDictionary"),
|
||||
IdentifierName("ToFrozenDictionary")),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(
|
||||
ObjectCreationExpression(
|
||||
ParseTypeName("global::System.Collections.Generic.Dictionary<string, string>"),
|
||||
null,
|
||||
InitializerExpression(SyntaxKind.ObjectInitializerExpression,
|
||||
SeparatedList(members.Select(member =>
|
||||
(ExpressionSyntax)AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
ImplicitElementAccess(BracketedArgumentList(SeparatedList(
|
||||
[Argument(NameOfExpression(member.Name))]
|
||||
))),
|
||||
NameOfExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(resourceTypeName),
|
||||
IdentifierName(member.ResourceName)))))))))
|
||||
])))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
|
||||
PropertyDeclaration(
|
||||
ParseTypeName($"{FrozenDictionaryTypeName}<{typeFullName}, string>"),
|
||||
valuesTable)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(FrozenDictionaryTypeName),
|
||||
IdentifierName("ToFrozenDictionary")),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(
|
||||
ObjectCreationExpression(
|
||||
ParseTypeName(
|
||||
$"global::System.Collections.Generic.Dictionary<{typeFullName}, string>"),
|
||||
null,
|
||||
InitializerExpression(SyntaxKind.ObjectInitializerExpression,
|
||||
SeparatedList(members.Select(member =>
|
||||
(ExpressionSyntax)AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
ImplicitElementAccess(BracketedArgumentList(SeparatedList(
|
||||
[Argument(IdentifierName(member.Name))]
|
||||
))),
|
||||
NameOfExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(resourceTypeName),
|
||||
IdentifierName(member.ResourceName)))))))))
|
||||
])))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)))
|
||||
.WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken));
|
||||
}
|
||||
}
|
||||
|
||||
[Generator]
|
||||
public class LocalizationMetadataGenerator : LocalizationMetadataGeneratorBase, IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "LocalizationMetadataAttribute";
|
||||
|
||||
private const string SubAttributeName = "LocalizedResourceAttribute";
|
||||
|
||||
private const string AttributeNamespace = nameof(Pixeval) + ".Attributes";
|
||||
|
||||
private const string AttributeFullName = AttributeNamespace + "." + AttributeName;
|
||||
|
||||
private const string SubAttributeFullName = AttributeNamespace + "." + SubAttributeName;
|
||||
|
||||
internal string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray<AttributeData> attributeList)
|
||||
{
|
||||
if (attributeList is not [{ ConstructorArguments: [{ Value: INamedTypeSymbol resourceType }, ..] }])
|
||||
return null;
|
||||
|
||||
var isPartial = attributeList is [{ NamedArguments: [{ Key: "IsPartial", Value.Value: true }] }];
|
||||
|
||||
List<(string Name, string ResourceName)> members = [];
|
||||
|
||||
foreach (var member in typeSymbol.GetMembers())
|
||||
{
|
||||
if (member is not IFieldSymbol field)
|
||||
continue;
|
||||
|
||||
if (field.GetAttribute(SubAttributeFullName) is not { ConstructorArguments: [_, { Value: string resourceName }, ..] })
|
||||
continue;
|
||||
|
||||
members.Add((typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + field.Name, resourceName));
|
||||
}
|
||||
|
||||
var generatedType = GetClassDeclaration(typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), $"{typeSymbol.Name}Extension", resourceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), members, isPartial);
|
||||
|
||||
var generatedNamespace = GetFileScopedNamespaceDeclaration(typeSymbol.ContainingNamespace.ToDisplayString(), generatedType, true);
|
||||
var compilationUnit = CompilationUnit()
|
||||
.AddMembers(generatedNamespace)
|
||||
.NormalizeWhitespace();
|
||||
return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString();
|
||||
}
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
|
||||
AttributeFullName,
|
||||
(_, _) => true,
|
||||
(syntaxContext, _) => syntaxContext
|
||||
);
|
||||
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, ga) =>
|
||||
{
|
||||
if (ga.TargetSymbol is not INamedTypeSymbol symbol)
|
||||
return;
|
||||
|
||||
if (TypeWithAttribute(symbol, ga.Attributes) is { } source)
|
||||
spc.AddSource(
|
||||
// 不能重名
|
||||
$"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs",
|
||||
source);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Generator]
|
||||
public class AttachedLocalizationMetadataGenerator : LocalizationMetadataGeneratorBase, IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "AttachedLocalizationMetadataAttribute`1";
|
||||
|
||||
private const string SubAttributeName = "AttachedLocalizedResourceAttribute";
|
||||
|
||||
private const string AttributeNamespace = nameof(Pixeval) + ".Attributes";
|
||||
|
||||
private const string AttributeFullName = AttributeNamespace + "." + AttributeName;
|
||||
|
||||
private const string SubAttributeFullName = AttributeNamespace + "." + SubAttributeName;
|
||||
|
||||
internal string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray<AttributeData> attributeList)
|
||||
{
|
||||
if (attributeList is not [{ ConstructorArguments: [{ Value: INamedTypeSymbol resourceType }, ..], AttributeClass.TypeArguments: [INamedTypeSymbol attachedType, ..] }])
|
||||
return null;
|
||||
|
||||
List<(string Name, string ResourceName)> members = [];
|
||||
|
||||
foreach (var attribute in typeSymbol.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() is not SubAttributeFullName)
|
||||
continue;
|
||||
|
||||
if (attribute is not { ConstructorArguments: [{ Value: string fieldName }, { Value: string resourceName }, ..] })
|
||||
continue;
|
||||
|
||||
members.Add((attachedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + fieldName, resourceName));
|
||||
}
|
||||
|
||||
var generatedType = GetClassDeclaration(attachedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), typeSymbol.Name, resourceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), members, true);
|
||||
|
||||
var generatedNamespace = GetFileScopedNamespaceDeclaration(typeSymbol.ContainingNamespace.ToDisplayString(), generatedType, true);
|
||||
var compilationUnit = CompilationUnit()
|
||||
.AddMembers(generatedNamespace)
|
||||
.NormalizeWhitespace();
|
||||
return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString();
|
||||
}
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
|
||||
AttributeFullName,
|
||||
(_, _) => true,
|
||||
(syntaxContext, _) => syntaxContext
|
||||
);
|
||||
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, ga) =>
|
||||
{
|
||||
if (ga.TargetSymbol is not INamedTypeSymbol symbol)
|
||||
return;
|
||||
|
||||
if (TypeWithAttribute(symbol, ga.Attributes) is { } source)
|
||||
spc.AddSource(
|
||||
// 不能重名
|
||||
$"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs",
|
||||
source);
|
||||
});
|
||||
}
|
||||
}
|
97
src/Pixeval.SourceGen/MetaPathMacroGenerator.cs
Normal file
97
src/Pixeval.SourceGen/MetaPathMacroGenerator.cs
Normal file
@ -0,0 +1,97 @@
|
||||
#region Copyright
|
||||
// GPL v3 License
|
||||
//
|
||||
// Pixeval/Pixeval.SourceGen
|
||||
// Copyright (c) 2024 Pixeval.SourceGen/MetaPathMacroGenerator.cs
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using static Pixeval.SourceGen.SyntaxHelper;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace Pixeval.SourceGen;
|
||||
|
||||
[Generator]
|
||||
public class MetaPathMacroGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "MetaPathMacroAttribute`1";
|
||||
|
||||
private const string AttributeNamespace = nameof(Pixeval) + ".Download.Macros";
|
||||
|
||||
private const string AttributeFullName = AttributeNamespace + "." + AttributeName;
|
||||
|
||||
internal string TypeWithAttribute(ImmutableArray<GeneratorAttributeSyntaxContext> gas, out string fileName)
|
||||
{
|
||||
var dictionary = new Dictionary<ITypeSymbol, List<INamedTypeSymbol>>(SymbolEqualityComparer.Default);
|
||||
foreach (var ga in gas)
|
||||
{
|
||||
if (ga is not { TargetSymbol: INamedTypeSymbol symbol, Attributes: var attributeList })
|
||||
continue;
|
||||
foreach (var attributeData in attributeList)
|
||||
if (attributeData.AttributeClass?.TypeArguments is [{ } type])
|
||||
if (dictionary.TryGetValue(type, out var list))
|
||||
list.Add(symbol);
|
||||
else
|
||||
dictionary[type] = [symbol];
|
||||
}
|
||||
|
||||
const string getAttachedTypeInstances = "Get{0}Instances";
|
||||
|
||||
fileName = "MetaPathMacroAttributeHelper.g.cs";
|
||||
var generatedType = ClassDeclaration("MetaPathMacroAttributeHelper")
|
||||
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
|
||||
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
|
||||
.AddMembers(dictionary.Select(t =>
|
||||
(MemberDeclarationSyntax)MethodDeclaration(
|
||||
ParseTypeName("global::System.Collections.Generic.IReadOnlyList<global::Pixeval.Download.MacroParser.IMacro>"),
|
||||
string.Format(getAttachedTypeInstances, t.Key.Name))
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithExpressionBody(ArrowExpressionClause(CollectionExpression(SeparatedList(t.Value.Select(v =>
|
||||
(CollectionElementSyntax)ExpressionElement(ObjectCreationExpression(v.GetTypeSyntax(false))
|
||||
.WithArgumentList(ArgumentList()))))
|
||||
)))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))).ToArray()
|
||||
)
|
||||
.WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken));
|
||||
var generatedNamespace = GetFileScopedNamespaceDeclaration(AttributeNamespace, generatedType, true);
|
||||
var compilationUnit = CompilationUnit()
|
||||
.AddMembers(generatedNamespace)
|
||||
.NormalizeWhitespace();
|
||||
return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString();
|
||||
}
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
|
||||
AttributeFullName,
|
||||
(_, _) => true,
|
||||
(syntaxContext, _) => syntaxContext
|
||||
).Collect();
|
||||
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, gas) =>
|
||||
{
|
||||
if (gas.Length > 0)
|
||||
if (TypeWithAttribute(gas, out var name) is { } source)
|
||||
spc.AddSource(name, source);
|
||||
});
|
||||
}
|
||||
}
|
@ -6,16 +6,17 @@
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<PublishAot>false</PublishAot>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" PrivateAssets="all" />
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
123
src/Pixeval.SourceGen/SettingsEntryGenerator.cs
Normal file
123
src/Pixeval.SourceGen/SettingsEntryGenerator.cs
Normal file
@ -0,0 +1,123 @@
|
||||
#region Copyright
|
||||
|
||||
// GPL v3 License
|
||||
//
|
||||
// Pixeval/Pixeval.SourceGen
|
||||
// Copyright (c) 2024 Pixeval.SourceGen/SettingsEntryGenerator.cs
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Pixeval.SourceGen.SyntaxHelper;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace Pixeval.SourceGen;
|
||||
|
||||
// [Generator]
|
||||
public class SettingsEntryGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "SettingsEntryAttribute";
|
||||
|
||||
private const string AttributeNamespace = nameof(Pixeval) + ".Attributes";
|
||||
|
||||
private const string AttributeFullName = AttributeNamespace + "." + AttributeName;
|
||||
|
||||
internal string TypeWithAttribute(ImmutableArray<GeneratorAttributeSyntaxContext> gas, out string fileName)
|
||||
{
|
||||
var list = new List<(IPropertySymbol Property, (int, string, string) Attribute)>();
|
||||
foreach (var ga in gas)
|
||||
if (ga is
|
||||
{
|
||||
TargetSymbol: IPropertySymbol symbol,
|
||||
Attributes: [{ ConstructorArguments: [{ Value: int a }, { Value: string b }, { Value: string c }] }]
|
||||
})
|
||||
list.Add((symbol, (a, b, c)));
|
||||
|
||||
const string metadataTable = "MetadataTable";
|
||||
|
||||
fileName = "SettingsEntryAttribute.g.cs";
|
||||
var generatedType = ClassDeclaration("SettingsEntryAttribute")
|
||||
.AddModifiers(Token(SyntaxKind.PartialKeyword))
|
||||
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
|
||||
.AddMembers(
|
||||
PropertyDeclaration(
|
||||
ParseTypeName($"{FrozenDictionaryTypeName}<string, global::{AttributeFullName}>"),
|
||||
metadataTable)
|
||||
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
|
||||
.WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(FrozenDictionaryTypeName),
|
||||
IdentifierName("ToFrozenDictionary")),
|
||||
ArgumentList(SeparatedList([
|
||||
Argument(
|
||||
ObjectCreationExpression(
|
||||
ParseTypeName(
|
||||
$"global::System.Collections.Generic.Dictionary<string, global::{AttributeFullName}>"),
|
||||
null,
|
||||
InitializerExpression(SyntaxKind.ObjectInitializerExpression,
|
||||
SeparatedList(list.Select(elem =>
|
||||
(ExpressionSyntax)AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
ImplicitElementAccess(BracketedArgumentList(SeparatedList(
|
||||
[
|
||||
Argument(NameOfExpression(
|
||||
elem.Property.ContainingType.ToDisplayString(
|
||||
SymbolDisplayFormat.FullyQualifiedFormat) + "." +
|
||||
elem.Property.Name))
|
||||
]
|
||||
))),
|
||||
ObjectCreationExpression(ParseTypeName("global::" + AttributeFullName))
|
||||
.WithArgumentList(ArgumentList(SeparatedList([
|
||||
Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal(elem.Attribute.Item1))),
|
||||
Argument(LiteralExpression(SyntaxKind.StringLiteralExpression,
|
||||
Literal(elem.Attribute.Item2))),
|
||||
Argument(LiteralExpression(SyntaxKind.StringLiteralExpression,
|
||||
Literal(elem.Attribute.Item3)))
|
||||
])))))))))
|
||||
])))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
|
||||
)
|
||||
.WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken));
|
||||
var generatedNamespace = GetFileScopedNamespaceDeclaration(AttributeNamespace, generatedType, true);
|
||||
var compilationUnit = CompilationUnit()
|
||||
.AddMembers(generatedNamespace)
|
||||
.NormalizeWhitespace();
|
||||
return SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText().ToString();
|
||||
}
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
|
||||
AttributeFullName,
|
||||
(_, _) => true,
|
||||
(syntaxContext, _) => syntaxContext
|
||||
).Collect();
|
||||
|
||||
context.RegisterSourceOutput(generatorAttributes, (spc, gas) =>
|
||||
{
|
||||
if (gas.Length > 0)
|
||||
if (TypeWithAttribute(gas, out var name) is { } source)
|
||||
spc.AddSource(name, source);
|
||||
});
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ public static class SyntaxHelper
|
||||
{
|
||||
internal const string AttributeNamespace = "WinUI3Utilities.Attributes.";
|
||||
internal const string DisableSourceGeneratorAttribute = AttributeNamespace + "DisableSourceGeneratorAttribute";
|
||||
internal const string FrozenDictionaryTypeName = "global::System.Collections.Frozen.FrozenDictionary";
|
||||
|
||||
public static bool HasAttribute(this ISymbol s, string attributeFqName)
|
||||
{
|
||||
@ -43,19 +44,6 @@ public static class SyntaxHelper
|
||||
return mds.GetAttributes().FirstOrDefault(attr => attr?.AttributeClass?.ToDisplayString() == attributeFqName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缩进(伪tab)
|
||||
/// </summary>
|
||||
/// <param name="n">tab数量</param>
|
||||
/// <returns>4n个space</returns>
|
||||
internal static string Spacing(int n)
|
||||
{
|
||||
var temp = "";
|
||||
for (var i = 0; i < n; ++i)
|
||||
temp += " ";
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the following code
|
||||
/// <code>
|
||||
@ -69,13 +57,23 @@ public static class SyntaxHelper
|
||||
return isNullable ? NullableType(typeName) : typeName;
|
||||
}
|
||||
|
||||
internal static MemberAccessExpressionSyntax GetStaticMemberAccessExpression(this ITypeSymbol typeSymbol, string name)
|
||||
{
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(typeSymbol.ToDisplayString()),
|
||||
IdentifierName(name));
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate the following code
|
||||
/// <code>
|
||||
/// nameof(<paramref name="name" />)
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <returns>NameOfExpression</returns>
|
||||
internal static InvocationExpressionSyntax NameOfExpression(string name) => NameOfExpression(IdentifierName(name));
|
||||
|
||||
/// <summary>
|
||||
/// Generate the following code
|
||||
/// <code>
|
||||
/// nameof(<paramref name="expressionSyntax" />)
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <returns>NameOfExpression</returns>
|
||||
internal static InvocationExpressionSyntax NameOfExpression(ExpressionSyntax expressionSyntax) => InvocationExpression(IdentifierName("nameof"), ArgumentList().AddArguments(Argument(expressionSyntax)));
|
||||
|
||||
internal static IEnumerable<IPropertySymbol> GetProperties(this ITypeSymbol typeSymbol, INamedTypeSymbol attribute)
|
||||
{
|
||||
@ -114,31 +112,6 @@ public static class SyntaxHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取某<paramref name="symbol"/>的namespace并加入<paramref name="namespaces"/>集合
|
||||
/// </summary>
|
||||
/// <param name="namespaces">namespaces集合</param>
|
||||
/// <param name="usedTypes">已判断过的types</param>
|
||||
/// <param name="contextType">上下文所在的类</param>
|
||||
/// <param name="symbol">type的symbol</param>
|
||||
internal static void UseNamespace(this HashSet<string> namespaces, HashSet<ITypeSymbol> usedTypes, INamedTypeSymbol contextType, ITypeSymbol symbol)
|
||||
{
|
||||
if (usedTypes.Contains(symbol))
|
||||
return;
|
||||
|
||||
_ = usedTypes.Add(symbol);
|
||||
|
||||
if (symbol.ContainingNamespace is not { } ns)
|
||||
return;
|
||||
|
||||
if (!SymbolEqualityComparer.Default.Equals(ns, contextType.ContainingNamespace))
|
||||
_ = namespaces.Add(ns.ToDisplayString());
|
||||
|
||||
if (symbol is INamedTypeSymbol { IsGenericType: true } genericSymbol)
|
||||
foreach (var a in genericSymbol.TypeArguments)
|
||||
namespaces.UseNamespace(usedTypes, contextType, a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the following code
|
||||
/// <code>
|
||||
@ -163,21 +136,21 @@ public static class SyntaxHelper
|
||||
/// <summary>
|
||||
/// Generate the following code
|
||||
/// <code>
|
||||
/// partial <paramref name="symbol" /> <paramref name="name" /><br/>
|
||||
/// partial <paramref name="originalSymbol" /> <paramref name="name" /><br/>
|
||||
/// {<br/>
|
||||
/// <paramref name="member" /><br/>
|
||||
/// }
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <returns>TypeDeclaration</returns>
|
||||
internal static TypeDeclarationSyntax GetDeclaration(string name, INamedTypeSymbol symbol, params MemberDeclarationSyntax[] member)
|
||||
internal static TypeDeclarationSyntax GetDeclaration(string name, INamedTypeSymbol originalSymbol, params MemberDeclarationSyntax[] member)
|
||||
{
|
||||
TypeDeclarationSyntax typeDeclarationTemp = symbol.TypeKind switch
|
||||
TypeDeclarationSyntax typeDeclarationTemp = originalSymbol.TypeKind switch
|
||||
{
|
||||
TypeKind.Class when !symbol.IsRecord => ClassDeclaration(name),
|
||||
TypeKind.Struct when !symbol.IsRecord => StructDeclaration(name),
|
||||
TypeKind.Class or TypeKind.Struct when symbol.IsRecord => RecordDeclaration(Token(SyntaxKind.RecordKeyword), name),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(symbol.TypeKind))
|
||||
TypeKind.Class when !originalSymbol.IsRecord => ClassDeclaration(name),
|
||||
TypeKind.Struct when !originalSymbol.IsRecord => StructDeclaration(name),
|
||||
TypeKind.Class or TypeKind.Struct when originalSymbol.IsRecord => RecordDeclaration(Token(SyntaxKind.RecordKeyword), name),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(originalSymbol.TypeKind))
|
||||
};
|
||||
return typeDeclarationTemp.AddModifiers(Token(SyntaxKind.PartialKeyword))
|
||||
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pixeval.Utilities;
|
||||
@ -37,6 +38,8 @@ public class AsyncLazy<T>
|
||||
/// </summary>
|
||||
private T? _value;
|
||||
|
||||
private readonly SemaphoreSlim _slimLock = new(1, 1);
|
||||
|
||||
public AsyncLazy(T value)
|
||||
{
|
||||
_value = value;
|
||||
@ -52,7 +55,18 @@ public class AsyncLazy<T>
|
||||
public ValueTask<T> ValueAsync => ValueAsyncInternal();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private async ValueTask<T> ValueAsyncInternal() => IsValueCreated ? _value! : await CreateValueAsync();
|
||||
private async ValueTask<T> ValueAsyncInternal()
|
||||
{
|
||||
await _slimLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
return IsValueCreated ? _value! : await CreateValueAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = _slimLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<T> CreateValueAsync()
|
||||
{
|
||||
|
@ -20,13 +20,14 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Pixeval.Utilities;
|
||||
|
||||
public static class DescriptionHelper
|
||||
{
|
||||
public static string GetDescription<TEnum>(this TEnum @enum) where TEnum : Enum
|
||||
public static string GetDescription<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum>(this TEnum @enum) where TEnum : Enum
|
||||
{
|
||||
return (typeof(TEnum).GetField(@enum.ToString())?.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute)?.Description ?? ThrowUtils.InvalidOperation<string>("Attribute not found");
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
@ -28,10 +27,8 @@ using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.HighPerformance;
|
||||
using CommunityToolkit.HighPerformance.Buffers;
|
||||
|
||||
namespace Pixeval.Utilities;
|
||||
@ -118,39 +115,6 @@ public static class Objects
|
||||
return bytes.Select(b => b.ToString("x2")).Aggregate(string.Concat);
|
||||
}
|
||||
|
||||
public static async Task<string?> ToJsonAsync<TEntity>(this TEntity? obj, Action<JsonSerializerOptions>? serializerOptionConfigure = null)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await using var memoryStream = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(memoryStream, obj, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))).ConfigureAwait(false);
|
||||
return memoryStream.ToArray().GetString();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string? ToJson(this object? obj, Action<JsonSerializerOptions>? serializerOptionConfigure = null)
|
||||
{
|
||||
return obj?.Let(o => JsonSerializer.Serialize(o, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))));
|
||||
}
|
||||
|
||||
public static async ValueTask<TEntity?> FromJsonAsync<TEntity>(this IMemoryOwner<byte> bytes, Action<JsonSerializerOptions>? serializerOptionConfigure = null)
|
||||
{
|
||||
using (bytes)
|
||||
{
|
||||
await using var stream = bytes.Memory.AsStream();
|
||||
return await JsonSerializer.DeserializeAsync<TEntity>(stream, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TEntity? FromJson<TEntity>(this string str, Action<JsonSerializerOptions>? serializerOptionConfigure = null)
|
||||
{
|
||||
return JsonSerializer.Deserialize<TEntity>(str, new JsonSerializerOptions().Apply(option => serializerOptionConfigure?.Invoke(option)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsIgnoreCase(this string str1, string str2)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
<Platforms>x86;x64;arm64</Platforms>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
|
||||
|
@ -39,8 +39,8 @@ public static class ThrowUtils
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static TResult Throw<TException, TResult>(params object[] parameters) where TException : Exception
|
||||
=> throw ((Exception)Activator.CreateInstance(typeof(TException), parameters)!);
|
||||
public static TResult Throw<TException, TResult>(TException e) where TException : Exception
|
||||
=> throw e;
|
||||
|
||||
/// <exception cref="InvalidOperationException"/>
|
||||
[DoesNotReturn]
|
||||
|
@ -55,6 +55,7 @@ public partial class App
|
||||
public App()
|
||||
{
|
||||
_ = this.UseSegoeMetrics();
|
||||
SettingsValueConverter.Context = SettingsSerializeContext.Default;
|
||||
AppViewModel = new AppViewModel(this);
|
||||
BookmarkTag.AllCountedTagString = MiscResources.AllCountedTagName;
|
||||
AppInfo.SetNameResolvers(AppViewModel.AppSettings);
|
||||
|
@ -22,6 +22,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json.Serialization;
|
||||
using Windows.Foundation;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Xaml;
|
||||
@ -301,3 +302,7 @@ public partial record AppSettings() : IWindowSettings
|
||||
: $"@{{if_illust={picPath}}}@{{if_novel={docPath}}}";
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(string[]))]
|
||||
[JsonSerializable(typeof(HashSet<string>))]
|
||||
public partial class SettingsSerializeContext : JsonSerializerContext;
|
||||
|
@ -49,7 +49,7 @@ public class Versioning
|
||||
AppReleaseModels = null;
|
||||
if (client.DefaultRequestHeaders.UserAgent.Count is 0)
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0");
|
||||
if (await client.GetFromJsonAsync<GitHubRelease[]>("https://api.github.com/repos/Pixeval/Pixeval/releases") is { Length: > 0 } gitHubReleases)
|
||||
if (await client.GetFromJsonAsync("https://api.github.com/repos/Pixeval/Pixeval/releases", typeof(GitHubRelease[]), GitHubReleaseSerializeContext.Default) is GitHubRelease[] { Length: > 0 } gitHubReleases)
|
||||
{
|
||||
var appReleaseModels = new List<AppReleaseModel>(gitHubReleases.Length);
|
||||
foreach (var release in gitHubReleases)
|
||||
@ -102,7 +102,11 @@ public record AppReleaseModel(
|
||||
}
|
||||
}
|
||||
|
||||
file class GitHubRelease
|
||||
[JsonSerializable(typeof(GitHubRelease[]))]
|
||||
[JsonSerializable(typeof(Assets[]))]
|
||||
public partial class GitHubReleaseSerializeContext : JsonSerializerContext;
|
||||
|
||||
public class GitHubRelease
|
||||
{
|
||||
[JsonPropertyName("tag_name")]
|
||||
public required string TagName { get; init; }
|
||||
@ -114,7 +118,7 @@ file class GitHubRelease
|
||||
public required string Notes { get; init; }
|
||||
}
|
||||
|
||||
file class Assets
|
||||
public class Assets
|
||||
{
|
||||
[JsonPropertyName("browser_download_url")]
|
||||
public required string BrowserDownloadUrl { get; init; }
|
||||
|
@ -37,7 +37,7 @@ using Windows.Storage;
|
||||
|
||||
namespace Pixeval;
|
||||
|
||||
public class AppViewModel(App app) : IDisposable
|
||||
public partial class AppViewModel(App app) : IDisposable
|
||||
{
|
||||
private bool _activatedByProtocol;
|
||||
|
||||
|
@ -20,18 +20,23 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using FluentIcons.Common;
|
||||
using Pixeval.AppManagement;
|
||||
using Pixeval.Util;
|
||||
using Pixeval.Utilities;
|
||||
using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, string? resourceKeyDescription) : Attribute
|
||||
public partial class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, string? resourceKeyDescription) : Attribute
|
||||
{
|
||||
public SettingsEntryAttribute(int symbol, string resourceKeyHeader, string? resourceKeyDescription)
|
||||
: this((Symbol)symbol, resourceKeyHeader, resourceKeyDescription)
|
||||
{
|
||||
}
|
||||
|
||||
public static readonly SettingsEntryAttribute AutoUpdate = new(Symbol.Communication,
|
||||
nameof(SettingsPageResources.DownloadUpdateAutomaticallyEntryHeader),
|
||||
nameof(SettingsPageResources.DownloadUpdateAutomaticallyEntryDescription));
|
||||
@ -52,24 +57,22 @@ public class SettingsEntryAttribute(Symbol symbol, string resourceKeyHeader, str
|
||||
typeof(AppSettings).GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.SelectNotNull(f => f.GetCustomAttribute<SettingsEntryAttribute>()));
|
||||
|
||||
private static Type ResourceLoader => typeof(SettingsPageResources);
|
||||
|
||||
public Symbol Symbol { get; } = symbol;
|
||||
|
||||
public string LocalizedResourceHeader { get; } =
|
||||
LocalizedResourceAttributeHelper.GetLocalizedResourceContent(ResourceLoader, resourceKeyHeader) ?? "";
|
||||
SettingsPageResources.GetResource(resourceKeyHeader);
|
||||
|
||||
public string LocalizedResourceDescription { get; } =
|
||||
resourceKeyDescription is null
|
||||
? ""
|
||||
: LocalizedResourceAttributeHelper.GetLocalizedResourceContent(ResourceLoader, resourceKeyDescription) ?? "";
|
||||
: SettingsPageResources.GetResource(resourceKeyDescription);
|
||||
|
||||
public static SettingsEntryAttribute GetFromPropertyName(string propertyName)
|
||||
{
|
||||
return GetFromPropertyName<AppSettings>(propertyName);
|
||||
}
|
||||
|
||||
public static SettingsEntryAttribute GetFromPropertyName<T>(string propertyName)
|
||||
public static SettingsEntryAttribute GetFromPropertyName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string propertyName)
|
||||
{
|
||||
return typeof(T).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public)
|
||||
?.GetCustomAttribute<SettingsEntryAttribute>() ??
|
||||
|
@ -42,7 +42,7 @@ using QuestPDF.Infrastructure;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public class DocumentViewerViewModel(NovelContent novelContent) : ObservableObject, IDisposable, INovelParserViewModel<SoftwareBitmapSource>, INovelParserViewModel<Stream>
|
||||
public partial class DocumentViewerViewModel(NovelContent novelContent) : ObservableObject, IDisposable, INovelParserViewModel<SoftwareBitmapSource>, INovelParserViewModel<Stream>
|
||||
{
|
||||
/// <summary>
|
||||
/// 需要从外部Invoke
|
||||
|
@ -7,7 +7,7 @@ using Pixeval.Download.Models;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public class DownloadItemDataProvider : ObservableObject, IDisposable
|
||||
public partial class DownloadItemDataProvider : ObservableObject, IDisposable
|
||||
{
|
||||
public AdvancedObservableCollection<DownloadItemViewModel> View { get; } = [];
|
||||
|
||||
|
@ -36,7 +36,7 @@ using Pixeval.CoreApi.Model;
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class DownloadItemViewModel : WorkEntryViewModel<IWorkEntry>
|
||||
public sealed partial class DownloadItemViewModel : WorkEntryViewModel<IWorkEntry>
|
||||
{
|
||||
public DownloadTaskBase DownloadTask { get; }
|
||||
|
||||
|
@ -22,6 +22,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
[LocalizationMetadata(typeof(DownloadPageResources))]
|
||||
public enum DownloadListOption
|
||||
{
|
||||
[LocalizedResource(typeof(DownloadPageResources), nameof(DownloadPageResources.DownloadListOptionAllQueued))]
|
||||
|
@ -19,6 +19,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Pixeval.Collections;
|
||||
using Pixeval.CoreApi.Engine;
|
||||
@ -26,7 +27,7 @@ using Pixeval.CoreApi.Model;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public abstract class EntryViewViewModel<T, TViewModel>
|
||||
public abstract class EntryViewViewModel<T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TViewModel>
|
||||
: ObservableObject, IDisposable
|
||||
where T : class, IIdEntry
|
||||
where TViewModel : EntryViewModel<T>, IViewModelFactory<T, TViewModel>
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Pixeval.Collections;
|
||||
using Pixeval.CoreApi.Engine;
|
||||
@ -27,7 +28,7 @@ using Pixeval.CoreApi.Model;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public interface IDataProvider<T, TViewModel>
|
||||
public interface IDataProvider<T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TViewModel>
|
||||
: INotifyPropertyChanged, INotifyPropertyChanging, IDisposable
|
||||
where T : class, IIdEntry
|
||||
where TViewModel : class, IViewModelFactory<T, TViewModel>
|
||||
|
@ -21,6 +21,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Pixeval.Collections;
|
||||
@ -34,7 +35,7 @@ namespace Pixeval.Controls;
|
||||
/// 复用时调用<see cref="CloneRef"/>,<see cref="FetchEngineRef"/>和<see cref="EntrySourceRef"/>会在所有复用对象都Dispose时Dispose<br/>
|
||||
/// 初始化时调用<see cref="ResetEngine"/>
|
||||
/// </summary>
|
||||
public class SharableViewDataProvider<T, TViewModel>
|
||||
public partial class SharableViewDataProvider<T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TViewModel>
|
||||
: ObservableObject, IDataProvider<T, TViewModel>, IDisposable
|
||||
where T : class, IIdEntry
|
||||
where TViewModel : EntryViewModel<T>, IViewModelFactory<T, TViewModel>, IDisposable
|
||||
|
@ -19,6 +19,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Pixeval.Collections;
|
||||
@ -27,7 +28,7 @@ using Pixeval.CoreApi.Model;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public class SimpleViewDataProvider<T, TViewModel> : ObservableObject, IDataProvider<T, TViewModel>
|
||||
public partial class SimpleViewDataProvider<T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TViewModel> : ObservableObject, IDataProvider<T, TViewModel>
|
||||
where T : class, IIdEntry
|
||||
where TViewModel : class, IViewModelFactory<T, TViewModel>, IDisposable
|
||||
{
|
||||
|
@ -25,14 +25,12 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Markup;
|
||||
using Pixeval.CoreApi.Global.Enum;
|
||||
using Pixeval.Util;
|
||||
using WinUI3Utilities;
|
||||
using WinUI3Utilities.Attributes;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
[DependencyProperty<object>("SelectedEnum", DependencyPropertyDefaultValue.Default, nameof(OnSelectedEnumChanged), IsNullable = true)]
|
||||
[DependencyProperty<Array>("EnumSource", DependencyPropertyDefaultValue.Default, nameof(OnEnumSourceChanged))]
|
||||
public sealed partial class EnumComboBox : ComboBox
|
||||
{
|
||||
public new event EventHandler<SelectionChangedEventArgs>? SelectionChanged;
|
||||
@ -41,6 +39,12 @@ public sealed partial class EnumComboBox : ComboBox
|
||||
{
|
||||
Style = Application.Current.Resources["DefaultComboBoxStyle"] as Style;
|
||||
base.SelectionChanged += ComboBox_SelectionChanged;
|
||||
var token = RegisterPropertyChangedCallback(ItemsSourceProperty, (sender, _) =>
|
||||
{
|
||||
if (sender is EnumComboBox { ItemsSource: IEnumerable<StringRepresentableItem> enumerable } box)
|
||||
box.SelectedItem = enumerable.FirstOrDefault();
|
||||
});
|
||||
Unloaded += (sender, _) => sender.To<DependencyObject>().UnregisterPropertyChangedCallback(ItemsSourceProperty, token);
|
||||
}
|
||||
|
||||
public bool RaiseEventAfterLoaded { get; set; } = true;
|
||||
@ -65,33 +69,23 @@ public sealed partial class EnumComboBox : ComboBox
|
||||
var comboBox = sender.To<EnumComboBox>();
|
||||
comboBox.SelectedItem = comboBox.ItemsSource?.To<IEnumerable<StringRepresentableItem>>().FirstOrDefault(r => Equals(r.Item, comboBox.SelectedEnum));
|
||||
}
|
||||
|
||||
private static void OnEnumSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var comboBox = sender.To<EnumComboBox>();
|
||||
comboBox.ItemsSource = LocalizedResourceAttributeHelper.GetLocalizedResourceContents(comboBox.EnumSource);
|
||||
if (comboBox.SelectedEnum is null)
|
||||
comboBox.SelectedEnum = comboBox.EnumSource.GetValue(0);
|
||||
else
|
||||
comboBox.SelectedItem = comboBox.ItemsSource.To<IEnumerable<StringRepresentableItem>>().FirstOrDefault(r => Equals(r.Item, comboBox.SelectedEnum));
|
||||
}
|
||||
}
|
||||
|
||||
[MarkupExtensionReturnType(ReturnType = typeof(Array))]
|
||||
public sealed class EnumValuesExtension : MarkupExtension
|
||||
[MarkupExtensionReturnType(ReturnType = typeof(IReadOnlyList<StringRepresentableItem>))]
|
||||
public sealed partial class EnumValuesExtension : MarkupExtension
|
||||
{
|
||||
public EnumValuesEnum Type { get; set; }
|
||||
|
||||
protected override object ProvideValue()
|
||||
{
|
||||
return Enum.GetValues(Type switch
|
||||
return Type switch
|
||||
{
|
||||
EnumValuesEnum.WorkType => typeof(WorkType),
|
||||
EnumValuesEnum.SimpleWorkType => typeof(SimpleWorkType),
|
||||
EnumValuesEnum.WorkSortOption => typeof(WorkSortOption),
|
||||
EnumValuesEnum.PrivacyPolicy => typeof(PrivacyPolicy),
|
||||
EnumValuesEnum.WorkType => WorkTypeExtension.GetItems(),
|
||||
EnumValuesEnum.SimpleWorkType => SimpleWorkTypeExtension.GetItems(),
|
||||
EnumValuesEnum.WorkSortOption => WorkSortOptionExtension.GetItems(),
|
||||
EnumValuesEnum.PrivacyPolicy => PrivacyPolicyExtension.GetItems(),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Pixeval.CoreApi;
|
||||
using Pixeval.CoreApi.Global.Enum;
|
||||
using Pixeval.CoreApi.Net.Response;
|
||||
using WinUI3Utilities;
|
||||
@ -79,7 +80,7 @@ public sealed partial class CommentRepliesBlock
|
||||
|
||||
private async Task AddComment(HttpResponseMessage postCommentResponse)
|
||||
{
|
||||
if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync<PostCommentResponse>() is { Comment: { } comment })
|
||||
if (postCommentResponse.IsSuccessStatusCode && await postCommentResponse.Content.ReadFromJsonAsync(typeof(PostCommentResponse), AppJsonSerializerContext.Default) is PostCommentResponse { Comment: { } comment })
|
||||
ViewModel.AddComment(comment);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ using IllustrationViewDataProvider = Pixeval.Controls.SharableViewDataProvider<
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public sealed class IllustrationViewViewModel : SortableEntryViewViewModel<Illustration, IllustrationItemViewModel>
|
||||
public sealed partial class IllustrationViewViewModel : SortableEntryViewViewModel<Illustration, IllustrationItemViewModel>
|
||||
{
|
||||
public IllustrationViewViewModel(IllustrationViewViewModel viewModel) : this(viewModel.DataProvider.CloneRef())
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ using IllustratorViewDataProvider = Pixeval.Controls.SimpleViewDataProvider<
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public sealed class IllustratorViewViewModel : EntryViewViewModel<User, IllustratorItemViewModel>
|
||||
public sealed partial class IllustratorViewViewModel : EntryViewViewModel<User, IllustratorItemViewModel>
|
||||
{
|
||||
public override IllustratorViewDataProvider DataProvider { get; } = new();
|
||||
|
||||
|
@ -16,6 +16,10 @@
|
||||
PointerExited="NovelItem_OnPointerExited"
|
||||
mc:Ignorable="d">
|
||||
<Grid.Resources>
|
||||
<ExponentialEase
|
||||
x:Key="EasingFunction"
|
||||
EasingMode="EaseOut"
|
||||
Exponent="12" />
|
||||
<!-- ReSharper disable once Xaml.RedundantResource -->
|
||||
<Storyboard x:Key="ThumbnailStoryboard">
|
||||
<DoubleAnimation
|
||||
|
@ -27,7 +27,7 @@ using NovelViewDataProvider = Pixeval.Controls.SharableViewDataProvider<
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public sealed class NovelViewViewModel : SortableEntryViewViewModel<Novel, NovelItemViewModel>
|
||||
public sealed partial class NovelViewViewModel : SortableEntryViewViewModel<Novel, NovelItemViewModel>
|
||||
{
|
||||
public NovelViewViewModel(NovelViewViewModel viewModel) : this(viewModel.DataProvider.CloneRef())
|
||||
{
|
||||
|
@ -18,7 +18,6 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -26,13 +25,12 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Foundation;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Pixeval.Util;
|
||||
using WinUI3Utilities;
|
||||
using WinUI3Utilities.Attributes;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
[DependencyProperty<Array>("ItemsSource", DependencyPropertyDefaultValue.Default, nameof(OnItemsSourceChanged))]
|
||||
[DependencyProperty<object>("ItemsSource", DependencyPropertyDefaultValue.Default, nameof(OnItemsSourceChanged))]
|
||||
[DependencyProperty<object>("SelectedItem", propertyChanged: nameof(OnSelectedItemChanged))]
|
||||
[DependencyProperty<object>("Header")]
|
||||
public sealed partial class SettingRadioButtons : UserControl
|
||||
@ -60,7 +58,7 @@ public sealed partial class SettingRadioButtons : UserControl
|
||||
{
|
||||
var buttons = sender.To<SettingRadioButtons>();
|
||||
|
||||
buttons.Buttons.ItemsSource = LocalizedResourceAttributeHelper.GetLocalizedResourceContents(buttons.ItemsSource);
|
||||
buttons.Buttons.ItemsSource = buttons.ItemsSource;
|
||||
}
|
||||
|
||||
private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
|
||||
|
@ -16,7 +16,7 @@
|
||||
<fluent:SymbolIcon Symbol="{x:Bind Entry.HeaderIcon}" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
<controls1:EnumComboBox
|
||||
EnumSource="{x:Bind Entry.EnumValues}"
|
||||
ItemsSource="{x:Bind Entry.EnumItems}"
|
||||
SelectedEnum="{x:Bind Entry.Value, Mode=TwoWay}"
|
||||
SelectionChanged="EnumComboBox_OnSelectionChanged"
|
||||
Style="{StaticResource SettingsEnumComboBoxStyle}" />
|
||||
|
@ -16,7 +16,7 @@
|
||||
<fluent:SymbolIcon Symbol="{x:Bind Entry.HeaderIcon}" />
|
||||
</controls:SettingsExpander.HeaderIcon>
|
||||
<controls2:EnumComboBox
|
||||
EnumSource="{x:Bind Entry.EnumValues}"
|
||||
ItemsSource="{x:Bind Entry.EnumItems}"
|
||||
SelectedEnum="{x:Bind Entry.Value, Mode=TwoWay}"
|
||||
SelectionChanged="EnumComboBox_OnSelectionChanged"
|
||||
Style="{StaticResource SettingsEnumComboBoxStyle}" />
|
||||
|
@ -24,7 +24,7 @@ using Pixeval.Util;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public class SpotlightItemViewModel : ThumbnailEntryViewModel<Spotlight>, IViewModelFactory<Spotlight, SpotlightItemViewModel>
|
||||
public partial class SpotlightItemViewModel : ThumbnailEntryViewModel<Spotlight>, IViewModelFactory<Spotlight, SpotlightItemViewModel>
|
||||
{
|
||||
public static SpotlightItemViewModel CreateInstance(Spotlight entry) => new(entry);
|
||||
|
||||
|
@ -25,7 +25,7 @@ using SpotlightViewDataProvider = Pixeval.Controls.SimpleViewDataProvider<
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public sealed class SpotlightViewViewModel : EntryViewViewModel<Spotlight, SpotlightItemViewModel>
|
||||
public sealed partial class SpotlightViewViewModel : EntryViewViewModel<Spotlight, SpotlightItemViewModel>
|
||||
{
|
||||
public override SpotlightViewDataProvider DataProvider { get; } = new();
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
@ -29,7 +30,7 @@ using Pixeval.Utilities;
|
||||
|
||||
namespace Pixeval.Controls;
|
||||
|
||||
public abstract partial class SortableEntryViewViewModel<T, TViewModel>
|
||||
public abstract partial class SortableEntryViewViewModel<T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TViewModel>
|
||||
: EntryViewViewModel<T, TViewModel>, ISortableEntryViewViewModel
|
||||
where T : class, IWorkEntry
|
||||
where TViewModel : EntryViewModel<T>, IViewModelFactory<T, TViewModel>, IWorkViewModel
|
||||
|
@ -63,8 +63,7 @@ public sealed partial class WorkView : IEntryView<ISortableEntryViewViewModel>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(ViewModel);
|
||||
if (await viewModel.TryLoadThumbnailAsync(ViewModel))
|
||||
// TODO 不知道为什么NovelItem的Resource会有问题
|
||||
if (sender is IllustrationItem && sender.IsFullyOrPartiallyVisible(this))
|
||||
if (sender.IsFullyOrPartiallyVisible(this))
|
||||
sender.GetResource<Storyboard>("ThumbnailStoryboard").Begin();
|
||||
else
|
||||
sender.Opacity = 1;
|
||||
|
@ -33,7 +33,7 @@ using Pixeval.Utilities.Threading;
|
||||
|
||||
namespace Pixeval.Download;
|
||||
|
||||
public class DownloadManager<TDownloadTask> : IDisposable where TDownloadTask : DownloadTaskBase
|
||||
public partial class DownloadManager<TDownloadTask> : IDisposable where TDownloadTask : DownloadTaskBase
|
||||
{
|
||||
private readonly Channel<TDownloadTask> _downloadTaskChannel;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
@ -23,7 +23,7 @@ using Pixeval.Utilities.Threading;
|
||||
|
||||
namespace Pixeval.Download;
|
||||
|
||||
public class DownloadStartingDeferral : IDisposable
|
||||
public partial class DownloadStartingDeferral : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Set its result to <see langword="true" /> if you want the download to proceed, otherwise, <see langword="false" />
|
||||
|
@ -18,7 +18,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Pixeval.Controls;
|
||||
using Pixeval.Download.MacroParser;
|
||||
using Pixeval.Download.Macros;
|
||||
@ -30,9 +30,9 @@ public class IllustrationMetaPathParser : IMetaPathParser<IllustrationItemViewMo
|
||||
{
|
||||
private readonly MacroParser<IllustrationItemViewModel> _parser = new();
|
||||
|
||||
public static IMacro[] MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetAttachedTypeInstances<IllustrationItemViewModel>().ToArray();
|
||||
public static IReadOnlyList<IMacro> MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetIWorkViewModelInstances();
|
||||
|
||||
public IMacro[] MacroProvider => MacroProviderStatic;
|
||||
public IReadOnlyList<IMacro> MacroProvider => MacroProviderStatic;
|
||||
|
||||
public string Reduce(string raw, IllustrationItemViewModel context)
|
||||
{
|
||||
|
@ -18,9 +18,11 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pixeval.Download.MacroParser.Ast;
|
||||
|
||||
public interface IMetaPathNode<in TContext>
|
||||
{
|
||||
string Evaluate(IMacro[] env, TContext context);
|
||||
string Evaluate(IReadOnlyList<IMacro> env, TContext context);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Pixeval.Utilities;
|
||||
using WinUI3Utilities;
|
||||
@ -28,7 +29,7 @@ namespace Pixeval.Download.MacroParser.Ast;
|
||||
public record Macro<TContext>(PlainText<TContext> MacroName, OptionalMacroParameter<TContext>? OptionalParameters, bool IsNot)
|
||||
: SingleNode<TContext>
|
||||
{
|
||||
public override string Evaluate(IMacro[] env, TContext context)
|
||||
public override string Evaluate(IReadOnlyList<IMacro> env, TContext context)
|
||||
{
|
||||
var result = env.TryResolve(MacroName.Text, IsNot);
|
||||
return (result) switch
|
||||
|
@ -18,6 +18,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Pixeval.Download.MacroParser.Ast;
|
||||
@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast;
|
||||
[DebuggerDisplay("{Content}")]
|
||||
public record OptionalMacroParameter<TContext>(Sequence<TContext> Content) : IMetaPathNode<TContext>
|
||||
{
|
||||
public string Evaluate(IMacro[] env, TContext context)
|
||||
public string Evaluate(IReadOnlyList<IMacro> env, TContext context)
|
||||
{
|
||||
return Content.Evaluate(env, context);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Pixeval.Download.MacroParser.Ast;
|
||||
@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast;
|
||||
[DebuggerDisplay("{Text}")]
|
||||
public record PlainText<TContext>(string Text) : SingleNode<TContext>
|
||||
{
|
||||
public override string Evaluate(IMacro[] env, TContext context)
|
||||
public override string Evaluate(IReadOnlyList<IMacro> env, TContext context)
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Pixeval.Download.MacroParser.Ast;
|
||||
@ -25,7 +26,7 @@ namespace Pixeval.Download.MacroParser.Ast;
|
||||
[DebuggerDisplay("{First} {Remains}")]
|
||||
public record Sequence<TContext>(SingleNode<TContext> First, Sequence<TContext>? Remains) : IMetaPathNode<TContext>
|
||||
{
|
||||
public string Evaluate(IMacro[] env, TContext context)
|
||||
public string Evaluate(IReadOnlyList<IMacro> env, TContext context)
|
||||
{
|
||||
return First.Evaluate(env, context) + (Remains?.Evaluate(env, context) ?? string.Empty);
|
||||
}
|
||||
|
@ -18,9 +18,11 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pixeval.Download.MacroParser.Ast;
|
||||
|
||||
public abstract record SingleNode<TContext> : IMetaPathNode<TContext>
|
||||
{
|
||||
public abstract string Evaluate(IMacro[] env, TContext context);
|
||||
public abstract string Evaluate(IReadOnlyList<IMacro> env, TContext context);
|
||||
}
|
||||
|
@ -18,11 +18,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pixeval.Download.MacroParser;
|
||||
|
||||
public interface IMetaPathParser<in TContext>
|
||||
{
|
||||
IMacro[] MacroProvider { get; }
|
||||
IReadOnlyList<IMacro> MacroProvider { get; }
|
||||
|
||||
string Reduce(string raw, TContext context);
|
||||
}
|
||||
|
@ -19,33 +19,8 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Pixeval.Download.MacroParser;
|
||||
|
||||
namespace Pixeval.Download.Macros;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class MetaPathMacroAttribute<TContext> : Attribute;
|
||||
|
||||
public static class MetaPathMacroAttributeHelper
|
||||
{
|
||||
public static IEnumerable<IMacro> GetAttachedTypeInstances()
|
||||
{
|
||||
return Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where(t => t.GetCustomAttribute(typeof(MetaPathMacroAttribute<>))?.GetType() is not null)
|
||||
.Select(Activator.CreateInstance)
|
||||
.OfType<IMacro>();
|
||||
}
|
||||
|
||||
public static IEnumerable<IMacro> GetAttachedTypeInstances<TContext>()
|
||||
{
|
||||
return Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where(t =>
|
||||
t.GetCustomAttribute(typeof(MetaPathMacroAttribute<>))?.GetType()
|
||||
.GenericTypeArguments is [var type] && type.IsAssignableFrom(typeof(TContext)))
|
||||
.Select(Activator.CreateInstance)
|
||||
.OfType<IMacro>();
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class IllustrationDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration)
|
||||
public partial class IllustrationDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration)
|
||||
: DownloadTaskBase(entry)
|
||||
{
|
||||
public override IWorkViewModel ViewModel => IllustrationViewModel;
|
||||
|
@ -25,7 +25,7 @@ using Pixeval.Database;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public sealed class IntrinsicIllustrationDownloadTask : IllustrationDownloadTask
|
||||
public sealed partial class IntrinsicIllustrationDownloadTask : IllustrationDownloadTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The disposal of <paramref name="stream" /> is not handled
|
||||
|
@ -29,7 +29,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public sealed class IntrinsicMangaDownloadTask : MangaDownloadTask
|
||||
public sealed partial class IntrinsicMangaDownloadTask : MangaDownloadTask
|
||||
{
|
||||
public IntrinsicMangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustrationViewModel, IReadOnlyList<Stream> streams) : base(entry, illustrationViewModel)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ using Pixeval.Database;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public sealed class IntrinsicNovelDownloadTask : NovelDownloadTask
|
||||
public sealed partial class IntrinsicNovelDownloadTask : NovelDownloadTask
|
||||
{
|
||||
public IntrinsicNovelDownloadTask(DownloadHistoryEntry entry, NovelItemViewModel novel, DocumentViewerViewModel viewModel) :
|
||||
base(entry, novel, viewModel.NovelContent, viewModel) =>
|
||||
|
@ -29,7 +29,7 @@ using SixLabors.ImageSharp;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public sealed class IntrinsicUgoiraDownloadTask : UgoiraDownloadTask
|
||||
public sealed partial class IntrinsicUgoiraDownloadTask : UgoiraDownloadTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The disposal of <paramref name="stream" /> is not handled
|
||||
|
@ -25,7 +25,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class LazyInitializedIllustrationDownloadTask(DownloadHistoryEntry entry)
|
||||
public partial class LazyInitializedIllustrationDownloadTask(DownloadHistoryEntry entry)
|
||||
: IllustrationDownloadTask(entry, null!), ILazyLoadDownloadTask
|
||||
{
|
||||
public override async Task DownloadAsync(Downloader downloadStreamAsync)
|
||||
|
@ -27,7 +27,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class LazyInitializedMangaDownloadTask(DownloadHistoryEntry entry)
|
||||
public partial class LazyInitializedMangaDownloadTask(DownloadHistoryEntry entry)
|
||||
: MangaDownloadTask(entry, null!), ILazyLoadDownloadTask
|
||||
{
|
||||
public override async Task DownloadAsync(Downloader downloadStreamAsync)
|
||||
|
@ -27,7 +27,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class LazyInitializedNovelDownloadTask(DownloadHistoryEntry entry)
|
||||
public partial class LazyInitializedNovelDownloadTask(DownloadHistoryEntry entry)
|
||||
: NovelDownloadTask(entry, null!, null!, null!), ILazyLoadDownloadTask
|
||||
{
|
||||
public override async Task DownloadAsync(Downloader downloadStreamAsync)
|
||||
|
@ -25,7 +25,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class LazyInitializedUgoiraDownloadTask(DownloadHistoryEntry databaseEntry) : UgoiraDownloadTask(databaseEntry, null!, null!), ILazyLoadDownloadTask
|
||||
public partial class LazyInitializedUgoiraDownloadTask(DownloadHistoryEntry databaseEntry) : UgoiraDownloadTask(databaseEntry, null!, null!), ILazyLoadDownloadTask
|
||||
{
|
||||
public override async Task DownloadAsync(Downloader downloadStreamAsync)
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ using Pixeval.Util.IO;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class MangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration)
|
||||
public partial class MangaDownloadTask(DownloadHistoryEntry entry, IllustrationItemViewModel illustration)
|
||||
: IllustrationDownloadTask(entry, illustration)
|
||||
{
|
||||
protected int CurrentIndex { get; set; }
|
||||
|
@ -36,7 +36,7 @@ using WinUI3Utilities;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class NovelDownloadTask : DownloadTaskBase
|
||||
public partial class NovelDownloadTask : DownloadTaskBase
|
||||
{
|
||||
public NovelDownloadTask(
|
||||
DownloadHistoryEntry entry,
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Pixeval.Controls;
|
||||
using Pixeval.CoreApi.Net.Response;
|
||||
@ -31,7 +32,7 @@ using SixLabors.ImageSharp;
|
||||
|
||||
namespace Pixeval.Download.Models;
|
||||
|
||||
public class UgoiraDownloadTask(
|
||||
public partial class UgoiraDownloadTask(
|
||||
DownloadHistoryEntry entry,
|
||||
IllustrationItemViewModel illustration,
|
||||
UgoiraMetadataResponse metadata)
|
||||
@ -69,13 +70,8 @@ public class UgoiraDownloadTask(
|
||||
{
|
||||
if (App.AppViewModel.AppSettings.UgoiraDownloadFormat is UgoiraDownloadFormat.OriginalZip)
|
||||
{
|
||||
var dict = new Dictionary<string, Stream>();
|
||||
for (var i = 0; i < urls.Count; ++i)
|
||||
{
|
||||
var extension = Path.GetExtension(urls[i]);
|
||||
dict.Add($"{i}{extension}", streams[i]);
|
||||
}
|
||||
var zipStream = await IoHelper.WriteZipAsync(dict, Dispose);
|
||||
var names = urls.Select(Path.GetExtension).Select((extension, i) => $"{i}{extension}").ToArray();
|
||||
var zipStream = await IoHelper.WriteZipAsync(names, streams, Dispose);
|
||||
await zipStream.StreamSaveToFileAsync(destination);
|
||||
Report(100);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Pixeval.Controls;
|
||||
using Pixeval.Download.MacroParser;
|
||||
using Pixeval.Download.Macros;
|
||||
@ -32,9 +32,9 @@ public class NovelMetaPathParser : IMetaPathParser<NovelItemViewModel>
|
||||
{
|
||||
private readonly MacroParser<NovelItemViewModel> _parser = new();
|
||||
|
||||
public static IMacro[] MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetAttachedTypeInstances<NovelItemViewModel>().ToArray();
|
||||
public static IReadOnlyList<IMacro> MacroProviderStatic { get; } = MetaPathMacroAttributeHelper.GetIWorkViewModelInstances();
|
||||
|
||||
public IMacro[] MacroProvider => MacroProviderStatic;
|
||||
public IReadOnlyList<IMacro> MacroProvider => MacroProviderStatic;
|
||||
|
||||
public string Reduce(string raw, NovelItemViewModel context)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Options;
|
||||
|
||||
[LocalizationMetadata(typeof(MiscResources))]
|
||||
public enum FontWeightsOption
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -24,6 +24,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Options;
|
||||
|
||||
[LocalizationMetadata(typeof(MiscResources))]
|
||||
public enum IllustrationDownloadFormat
|
||||
{
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.Jpg))]
|
||||
|
@ -28,6 +28,7 @@ namespace Pixeval.Options;
|
||||
/// We require a strict matching between the value of the enum member and the order of the
|
||||
/// <see cref="NavigationViewItem" />in <see cref="MainPage" />
|
||||
/// </summary>
|
||||
[LocalizationMetadata(typeof(MainPageResources))]
|
||||
public enum MainPageTabItem
|
||||
{
|
||||
[LocalizedResource(typeof(MainPageResources), nameof(MainPageResources.RecommendationsTabContent))]
|
||||
|
@ -24,6 +24,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Options;
|
||||
|
||||
[LocalizationMetadata(typeof(MiscResources))]
|
||||
public enum NovelDownloadFormat
|
||||
{
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.Pdf))]
|
||||
|
@ -22,19 +22,45 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Options;
|
||||
|
||||
[LocalizationMetadata(typeof(ProxyTypeResources))]
|
||||
public enum ProxyType
|
||||
{
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.ProxyOptionSystem))]
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSystem))]
|
||||
System,
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.ProxyOptionNone))]
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionNone))]
|
||||
None,
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionHttp))]
|
||||
Http,
|
||||
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks4))]
|
||||
Socks4,
|
||||
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks4A))]
|
||||
Socks4A,
|
||||
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(ProxyTypeResources.ProxyOptionSocks5))]
|
||||
Socks5
|
||||
}
|
||||
|
||||
public static class ProxyTypeResources
|
||||
{
|
||||
public static string GetResource(string id) => id switch
|
||||
{
|
||||
nameof(ProxyType.Http) => ProxyOptionHttp,
|
||||
|
||||
nameof(ProxyType.Socks4) => ProxyOptionSocks4,
|
||||
nameof(ProxyType.Socks4A) => ProxyOptionSocks4A,
|
||||
nameof(ProxyType.Socks5) => ProxyOptionSocks5,
|
||||
nameof(ProxyType.System) => ProxyOptionSystem,
|
||||
_ => ProxyOptionNone
|
||||
};
|
||||
|
||||
public const string ProxyOptionHttp = "http/https";
|
||||
public const string ProxyOptionSocks4 = "socks4";
|
||||
public const string ProxyOptionSocks4A = "socks4a";
|
||||
public const string ProxyOptionSocks5 = "socks5";
|
||||
public static string ProxyOptionNone => MiscResources.ProxyOptionSystem;
|
||||
public static string ProxyOptionSystem => MiscResources.ProxyOptionNone;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#region Copyright (c) Pixeval/Pixeval
|
||||
#region Copyright (c) Pixeval/Pixeval
|
||||
// GPL v3 License
|
||||
//
|
||||
// Pixeval/Pixeval
|
||||
@ -22,6 +22,7 @@ using Pixeval.Attributes;
|
||||
|
||||
namespace Pixeval.Options;
|
||||
|
||||
[LocalizationMetadata(typeof(MiscResources))]
|
||||
public enum ThumbnailDirection
|
||||
{
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.ThumbnailDirectionLandscape))]
|
||||
@ -29,4 +30,4 @@ public enum ThumbnailDirection
|
||||
|
||||
[LocalizedResource(typeof(MiscResources), nameof(MiscResources.ThumbnailDirectionPortrait))]
|
||||
Portrait
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user