diff --git a/Hollow.Core/Hollow.Core.csproj b/Hollow.Abstractions/Hollow.Abstractions.csproj similarity index 100% rename from Hollow.Core/Hollow.Core.csproj rename to Hollow.Abstractions/Hollow.Abstractions.csproj diff --git a/Hollow.Core/Helpers/JsonIntToStringConverter.cs b/Hollow.Abstractions/JsonConverters/JsonIntToStringConverter.cs similarity index 92% rename from Hollow.Core/Helpers/JsonIntToStringConverter.cs rename to Hollow.Abstractions/JsonConverters/JsonIntToStringConverter.cs index 8a706e4..b66f10b 100644 --- a/Hollow.Core/Helpers/JsonIntToStringConverter.cs +++ b/Hollow.Abstractions/JsonConverters/JsonIntToStringConverter.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Hollow.Core.Helpers; +namespace Hollow.Abstractions.JsonConverters; public class JsonIntToStringConverter: JsonConverter { diff --git a/Hollow/Models/AppInfo.cs b/Hollow.Abstractions/Models/AppInfo.cs similarity index 88% rename from Hollow/Models/AppInfo.cs rename to Hollow.Abstractions/Models/AppInfo.cs index bfe7cf4..124eb75 100644 --- a/Hollow/Models/AppInfo.cs +++ b/Hollow.Abstractions/Models/AppInfo.cs @@ -1,7 +1,6 @@ -using System.IO; -using System.Reflection; +using System.Reflection; -namespace Hollow.Models; +namespace Hollow.Abstractions.Models; public class AppInfo { diff --git a/Hollow/Models/Configs/AppConfig.cs b/Hollow.Abstractions/Models/Configs/AppConfig.cs similarity index 94% rename from Hollow/Models/Configs/AppConfig.cs rename to Hollow.Abstractions/Models/Configs/AppConfig.cs index 500742c..902d5e4 100644 --- a/Hollow/Models/Configs/AppConfig.cs +++ b/Hollow.Abstractions/Models/Configs/AppConfig.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Hollow.Models.Configs; +namespace Hollow.Abstractions.Models.Configs; public class AppConfig { diff --git a/Hollow.Core/Gacha/Common/GachaItem.cs b/Hollow.Abstractions/Models/HttpContrasts/Gacha/Common/GachaItem.cs similarity index 94% rename from Hollow.Core/Gacha/Common/GachaItem.cs rename to Hollow.Abstractions/Models/HttpContrasts/Gacha/Common/GachaItem.cs index d086f27..329142e 100644 --- a/Hollow.Core/Gacha/Common/GachaItem.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/Gacha/Common/GachaItem.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Hollow.Core.Gacha.Common; +namespace Hollow.Abstractions.Models.HttpContrasts.Gacha.Common; public class GachaItem { diff --git a/Hollow.Core/Gacha/GachaPage.cs b/Hollow.Abstractions/Models/HttpContrasts/Gacha/GachaPage.cs similarity index 74% rename from Hollow.Core/Gacha/GachaPage.cs rename to Hollow.Abstractions/Models/HttpContrasts/Gacha/GachaPage.cs index 9747d92..cdd754f 100644 --- a/Hollow.Core/Gacha/GachaPage.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/Gacha/GachaPage.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; -using Hollow.Core.Gacha.Common; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Common; -namespace Hollow.Core.Gacha; +namespace Hollow.Abstractions.Models.HttpContrasts.Gacha; public class GachaPage { diff --git a/Hollow.Core/Gacha/Uigf/GachaRecords.cs b/Hollow.Abstractions/Models/HttpContrasts/Gacha/Uigf/GachaRecords.cs similarity index 87% rename from Hollow.Core/Gacha/Uigf/GachaRecords.cs rename to Hollow.Abstractions/Models/HttpContrasts/Gacha/Uigf/GachaRecords.cs index d4d0f1d..5d7f84d 100644 --- a/Hollow.Core/Gacha/Uigf/GachaRecords.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/Gacha/Uigf/GachaRecords.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -using Hollow.Core.Gacha.Common; -using Hollow.Core.Helpers; +using Hollow.Abstractions.JsonConverters; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Common; -namespace Hollow.Core.Gacha.Uigf; +namespace Hollow.Abstractions.Models.HttpContrasts.Gacha.Uigf; public class GachaRecords { diff --git a/Hollow.Core/MiHoYoLauncher/Models/Common/ImageModel.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/Common/ImageModel.cs similarity index 75% rename from Hollow.Core/MiHoYoLauncher/Models/Common/ImageModel.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/Common/ImageModel.cs index 179edb4..4c43efb 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/Common/ImageModel.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/Common/ImageModel.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Hollow.Core.MiHoYoLauncher.Models.Common; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher.Common; public class ImageModel { diff --git a/Hollow.Core/MiHoYoLauncher/Models/ZzzAllGameBasicInfo.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAllGameBasicInfo.cs similarity index 84% rename from Hollow.Core/MiHoYoLauncher/Models/ZzzAllGameBasicInfo.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAllGameBasicInfo.cs index 31293ff..65bd188 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/ZzzAllGameBasicInfo.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAllGameBasicInfo.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; -using Hollow.Core.MiHoYoLauncher.Models.Common; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher.Common; -namespace Hollow.Core.MiHoYoLauncher.Models; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; public class ZzzAllGameBasicInfo { diff --git a/Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncement.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncement.cs similarity index 95% rename from Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncement.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncement.cs index f223c7b..7c11a2e 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncement.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncement.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Hollow.Core.MiHoYoLauncher.Models; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; public class ZzzAnnouncement { diff --git a/Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncementContent.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncementContent.cs similarity index 92% rename from Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncementContent.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncementContent.cs index 41bb776..bff8d89 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/ZzzAnnouncementContent.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzAnnouncementContent.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Hollow.Core.MiHoYoLauncher.Models; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; public class ZzzAnnouncementContent { diff --git a/Hollow.Core/MiHoYoLauncher/Models/ZzzGameContent.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameContent.cs similarity index 88% rename from Hollow.Core/MiHoYoLauncher/Models/ZzzGameContent.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameContent.cs index 79339be..b07e74e 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/ZzzGameContent.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameContent.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; -using Hollow.Core.MiHoYoLauncher.Models.Common; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher.Common; -namespace Hollow.Core.MiHoYoLauncher.Models; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; public class ZzzGameContent diff --git a/Hollow.Core/MiHoYoLauncher/Models/ZzzGameInfo.cs b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameInfo.cs similarity index 85% rename from Hollow.Core/MiHoYoLauncher/Models/ZzzGameInfo.cs rename to Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameInfo.cs index a9007ea..6679bff 100644 --- a/Hollow.Core/MiHoYoLauncher/Models/ZzzGameInfo.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/MiHoYoLauncher/ZzzGameInfo.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; -using Hollow.Core.MiHoYoLauncher.Models.Common; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher.Common; -namespace Hollow.Core.MiHoYoLauncher.Models; +namespace Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; public class ZzzGameInfo { diff --git a/Hollow/Models/Response.cs b/Hollow.Abstractions/Models/HttpContrasts/Response.cs similarity index 52% rename from Hollow/Models/Response.cs rename to Hollow.Abstractions/Models/HttpContrasts/Response.cs index e1645e3..05a1aea 100644 --- a/Hollow/Models/Response.cs +++ b/Hollow.Abstractions/Models/HttpContrasts/Response.cs @@ -1,6 +1,6 @@ -namespace Hollow.Models; +namespace Hollow.Abstractions.Models.HttpContrasts; -public struct Response(bool isSuccess, string message="") +public struct Response(bool isSuccess, string message = "") { public T Data { get; set; } public readonly bool IsSuccess = isSuccess; diff --git a/Hollow.Core/MiHoYoLauncher/GameId.cs b/Hollow.Abstractions/Models/MiHoYoLauncher/GameId.cs similarity index 54% rename from Hollow.Core/MiHoYoLauncher/GameId.cs rename to Hollow.Abstractions/Models/MiHoYoLauncher/GameId.cs index 38a615c..d930ccc 100644 --- a/Hollow.Core/MiHoYoLauncher/GameId.cs +++ b/Hollow.Abstractions/Models/MiHoYoLauncher/GameId.cs @@ -1,4 +1,4 @@ -namespace Hollow.Core.MiHoYoLauncher; +namespace Hollow.Abstractions.Models.MiHoYoLauncher; public class GameId { diff --git a/Hollow.Core/MiHoYoLauncher/LauncherId.cs b/Hollow.Abstractions/Models/MiHoYoLauncher/LauncherId.cs similarity index 58% rename from Hollow.Core/MiHoYoLauncher/LauncherId.cs rename to Hollow.Abstractions/Models/MiHoYoLauncher/LauncherId.cs index ff2e0fb..c40fcce 100644 --- a/Hollow.Core/MiHoYoLauncher/LauncherId.cs +++ b/Hollow.Abstractions/Models/MiHoYoLauncher/LauncherId.cs @@ -1,4 +1,4 @@ -namespace Hollow.Core.MiHoYoLauncher; +namespace Hollow.Abstractions.Models.MiHoYoLauncher; public class LauncherId { diff --git a/Hollow/Assets/Icons/small_icon.ico b/Hollow.Windows/Assets/Icons/small_icon.ico similarity index 100% rename from Hollow/Assets/Icons/small_icon.ico rename to Hollow.Windows/Assets/Icons/small_icon.ico diff --git a/Hollow.Windows/Hollow.Windows.csproj b/Hollow.Windows/Hollow.Windows.csproj new file mode 100644 index 0000000..58df675 --- /dev/null +++ b/Hollow.Windows/Hollow.Windows.csproj @@ -0,0 +1,27 @@ + + + + WinExe + net8.0-windows + enable + enable + app.manifest + true + true + true + Assets\Icons\small_icon.ico + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/Hollow.Windows/NativeMethods.txt b/Hollow.Windows/NativeMethods.txt new file mode 100644 index 0000000..a973580 --- /dev/null +++ b/Hollow.Windows/NativeMethods.txt @@ -0,0 +1,9 @@ +GetModuleHandle +RegisterClassEx +CreateWindowEx +PostQuitMessage +WM_DESTROY +DefWindowProc +DestroyWindow +GetWindowLong +SetLayeredWindowAttributes \ No newline at end of file diff --git a/Hollow/Program.cs b/Hollow.Windows/Program.cs similarity index 59% rename from Hollow/Program.cs rename to Hollow.Windows/Program.cs index 4b52634..7f9d91e 100644 --- a/Hollow/Program.cs +++ b/Hollow.Windows/Program.cs @@ -1,12 +1,13 @@ using Avalonia; -using System; -using System.IO; -using Hollow.Models; +using Hollow.Abstractions.Models; +using Hollow.Views.Controls.WebView; +using Hollow.Windows.Services; +using Microsoft.Extensions.DependencyInjection; using Serilog; -namespace Hollow; +namespace Hollow.Windows; -sealed class Program +public static class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized @@ -14,20 +15,14 @@ sealed class Program [STAThread] public static void Main(string[] args) { - Log.Logger = new LoggerConfiguration() - .Enrich.WithProperty("Version", AppInfo.AppVersion) - .MinimumLevel.Debug() - .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}") - .WriteTo.File(Path.Combine(AppInfo.LogDir, "log_.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, retainedFileCountLimit: null) - .CreateLogger(); - - Log.Information("Hollow is starting..."); - //TODO: Platform specific Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", AppInfo.CachesDir); - + try { + App.ConfigureServices(x => + x.AddTransient() + ); BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } catch (Exception e) diff --git a/Hollow.Windows/Services/WebView2Adapter.cs b/Hollow.Windows/Services/WebView2Adapter.cs new file mode 100644 index 0000000..ae092bb --- /dev/null +++ b/Hollow.Windows/Services/WebView2Adapter.cs @@ -0,0 +1,229 @@ +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using Hollow.Views.Controls.WebView; +using Microsoft.Web.WebView2.Core; +using Size = Avalonia.Size; + +namespace Hollow.Windows.Services; + +public class WebView2Adapter : IWebViewAdapter +{ + private CoreWebView2Controller? _controller; + private Action? _subscriptions; + private InvisibleWindow? _invisibleWindow; + + public IntPtr Handle { get; private set; } + public string HandleDescriptor => nameof(HWND); + + public bool CanGoBack => _controller?.CoreWebView2?.CanGoBack ?? false; + + public bool CanGoForward => _controller?.CoreWebView2?.CanGoForward ?? false; + + public Uri? Source + { + [return: MaybeNull] + get => Uri.TryCreate(_controller?.CoreWebView2?.Source, UriKind.Absolute, out var url) ? url : null!; + set => _controller?.CoreWebView2?.Navigate(value?.AbsoluteUri); + } + + public event EventHandler? NavigationCompleted; + public event EventHandler? NavigationStarted; + public event EventHandler? DomContentLoaded; + + public bool GoBack() + { + _controller?.CoreWebView2.GoBack(); + return true; + } + + public bool GoForward() + { + _controller?.CoreWebView2.GoForward(); + return true; + } + + public Task InvokeScript(string scriptName) + { + return _controller?.CoreWebView2?.ExecuteScriptAsync(scriptName) ?? Task.FromResult(null); + } + + public void Navigate(Uri url) + { + _controller?.CoreWebView2?.Navigate(url.AbsolutePath); + } + + public void NavigateToString(string text) + { + _controller?.CoreWebView2?.NavigateToString(text); + } + + public bool Refresh() + { + _controller?.CoreWebView2?.Reload(); + return true; + } + + public bool Stop() + { + _controller?.CoreWebView2?.Stop(); + return true; + } + + public async Task SetParentAsync(IntPtr handle) + { + if (handle == IntPtr.Zero) + { + // 如果父窗口为空,说明TabControl切换到了其他Tab,这时候需要隐藏WebView2,牺牲内存换取性能 + // 如果父窗口被关闭,那么WebView2也会被关闭,导致下一次显示时需要重新初始化 + _invisibleWindow ??= new InvisibleWindow(); + handle = _invisibleWindow.HWnd; + } + else + { + // Avalonia提供的宿主窗口默认为黑色背景,设置为透明 + // 由于 WebView2 不是GDI+,所以不会影响 WebView2 的渲染 + PInvoke.SetLayeredWindowAttributes((HWND)handle, new COLORREF(1), 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY); + } + + Handle = handle; + + if (_controller == null) + { + var env = await CoreWebView2Environment.CreateAsync(); + _controller = await env.CreateCoreWebView2ControllerAsync(Handle); + _controller.DefaultBackgroundColor = Color.Transparent; // 这里的文档提到 WebView2 是支持透明背景的 + _controller.IsVisible = true; + } + else + { + _controller.ParentWindow = Handle; // 重新设置父窗口 + } + + _controller.CoreWebView2.Settings.IsStatusBarEnabled = false; + _subscriptions = AddHandlers(_controller.CoreWebView2); + } + + public void HandleSizeChanged(Size newSize) + { + if (_controller is not null) + { + _controller.BoundsMode = CoreWebView2BoundsMode.UseRasterizationScale; + _controller.Bounds = new Rectangle(0, 0, (int)newSize.Width, (int)newSize.Height); + } + } + + private Action AddHandlers(CoreWebView2 webView) + { + webView.DOMContentLoaded += WebViewOnDomContentLoaded; + void WebViewOnDomContentLoaded(object? sender, CoreWebView2DOMContentLoadedEventArgs e) + { + DomContentLoaded?.Invoke(this, new WebViewDomContentLoadedEventArgs()); + } + + webView.WebResourceRequested += (_, e) => + { + if(e.Request.Uri.Contains("aaa")) + e.Response = webView.Environment.CreateWebResourceResponse(Stream.Null, 404, "Not Found", "Content-Type: text/html"); + }; + + webView.NavigationStarting += WebViewOnNavigationStarting; + void WebViewOnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e) + { + var args = new WebViewNavigationStartingEventArgs { Request = new Uri(e.Uri) }; + NavigationStarted?.Invoke(this, args); + if (args.Cancel) + { + e.Cancel = true; + } + } + + webView.NavigationCompleted += WebViewOnNavigationCompleted; + void WebViewOnNavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e) + { + NavigationCompleted?.Invoke(this, new WebViewNavigationCompletedEventArgs + { + Request = new Uri(((CoreWebView2)sender!).Source), + IsSuccess = e.IsSuccess + }); + } + + return () => + { + webView.NavigationStarting -= WebViewOnNavigationStarting; + webView.NavigationCompleted -= WebViewOnNavigationCompleted; + webView.DOMContentLoaded -= WebViewOnDomContentLoaded; + }; + } + + public void Dispose() + { + _invisibleWindow?.Dispose(); + _subscriptions?.Invoke(); + _controller?.Close(); + } + + private unsafe class InvisibleWindow : IDisposable + { + public HWND HWnd { get; private set; } + + // 防止委托被GC回收 + // ReSharper disable once NotAccessedField.Local + private readonly WNDPROC _wndProcDelegate; + + public InvisibleWindow() + { + fixed (char* invisible = "InvisibleWindow") + { + // Define window class + var wndClass = new WNDCLASSEXW + { + cbSize = (uint)Marshal.SizeOf(), + lpfnWndProc = _wndProcDelegate = WndProc, + hInstance = PInvoke.GetModuleHandle(new PCWSTR()), + lpszClassName = invisible + }; + + PInvoke.RegisterClassEx(wndClass); + + // Create window + HWnd = PInvoke.CreateWindowEx( + 0, + wndClass.lpszClassName, + invisible, + 0, + 0, + 0, + 0, + 0, + HWND.Null, + HMENU.Null, + wndClass.hInstance, + null); + } + } + + private LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case PInvoke.WM_DESTROY: + PInvoke.PostQuitMessage(0); + break; + } + + return PInvoke.DefWindowProc(hWnd, msg, wParam, lParam); + } + + public void Dispose() + { + if (HWnd == IntPtr.Zero) return; + + PInvoke.DestroyWindow(HWnd); + HWnd = default; + } + } +} diff --git a/Hollow/app.manifest b/Hollow.Windows/app.manifest similarity index 69% rename from Hollow/app.manifest rename to Hollow.Windows/app.manifest index d7f214d..ee3a777 100644 --- a/Hollow/app.manifest +++ b/Hollow.Windows/app.manifest @@ -3,7 +3,7 @@ - + @@ -15,4 +15,11 @@ + + + + true + PerMonitorV2 + + diff --git a/Hollow.sln b/Hollow.sln index e6f4962..0b68bc0 100644 --- a/Hollow.sln +++ b/Hollow.sln @@ -2,13 +2,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hollow", "Hollow\Hollow.csproj", "{6375F724-DD81-40FD-9AA3-7DE665081710}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hollow.Core", "Hollow.Core\Hollow.Core.csproj", "{1FB38667-CE4E-4AAD-A05E-FCB75D357A73}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hollow.Abstractions", "Hollow.Abstractions\Hollow.Abstractions.csproj", "{1FB38667-CE4E-4AAD-A05E-FCB75D357A73}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{7F12BC30-0491-4531-823E-11B7A4A4ABD5}" ProjectSection(SolutionItems) = preProject README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hollow.Windows", "Hollow.Windows\Hollow.Windows.csproj", "{B83D0035-224D-475B-9058-1AA3769B808D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,5 +25,9 @@ Global {1FB38667-CE4E-4AAD-A05E-FCB75D357A73}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FB38667-CE4E-4AAD-A05E-FCB75D357A73}.Release|Any CPU.ActiveCfg = Release|Any CPU {1FB38667-CE4E-4AAD-A05E-FCB75D357A73}.Release|Any CPU.Build.0 = Release|Any CPU + {B83D0035-224D-475B-9058-1AA3769B808D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B83D0035-224D-475B-9058-1AA3769B808D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B83D0035-224D-475B-9058-1AA3769B808D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B83D0035-224D-475B-9058-1AA3769B808D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Hollow/App.axaml b/Hollow/App.axaml index 1f3de22..19b3d34 100644 --- a/Hollow/App.axaml +++ b/Hollow/App.axaml @@ -15,14 +15,14 @@ - - - - - - - - + + + + + + + + diff --git a/Hollow/App.axaml.cs b/Hollow/App.axaml.cs index b7976db..c156d72 100644 --- a/Hollow/App.axaml.cs +++ b/Hollow/App.axaml.cs @@ -1,11 +1,10 @@ using System; using System.IO; -using System.Linq; using System.Net.Http; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Hollow.Models; +using Hollow.Abstractions.Models; using Hollow.Services.ConfigurationService; using Hollow.Services.GachaService; using Hollow.Services.GameService; @@ -23,19 +22,24 @@ namespace Hollow; public class App : Application { private static IServiceProvider? _provider; + public override void Initialize() { + Log.Logger = new LoggerConfiguration() + .Enrich.WithProperty("Version", AppInfo.AppVersion) + .MinimumLevel.Debug() + .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}") + .WriteTo.File(Path.Combine(AppInfo.LogDir, "log_.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, retainedFileCountLimit: null) + .CreateLogger(); + + Log.Information("Hollow is starting..."); + AvaloniaXamlLoader.Load(this); - _provider = ConfigureServices(); } - private static ServiceProvider ConfigureServices() + public static void ConfigureServices(Action? customServicesFactory = null) { - var viewLocator = Current?.DataTemplates.First(x => x is ViewLocator); var services = new ServiceCollection(); - - if (viewLocator is not null) - services.AddSingleton(viewLocator); services.AddSingleton(); services.AddSingleton(); @@ -64,11 +68,13 @@ private static ServiceProvider ConfigureServices() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + + customServicesFactory?.Invoke(services); - return services.BuildServiceProvider(); + _provider = services.BuildServiceProvider(); } - public static T GetService() where T : notnull => _provider!.GetRequiredService(); + public static T GetService() where T : notnull => (_provider ?? throw new InvalidOperationException("Services not Configured")).GetRequiredService(); public override void OnFrameworkInitializationCompleted() { diff --git a/Hollow/Controls/WebView/NativeWebView.cs b/Hollow/Controls/WebView/NativeWebView.cs deleted file mode 100644 index 0deaf83..0000000 --- a/Hollow/Controls/WebView/NativeWebView.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Platform; -using Hollow.Controls.WebView.Win; - -namespace Hollow.Controls.WebView; - -public class NativeWebView : NativeControlHost, IWebView -{ - private static readonly Uri EmptyPageLink = new("about:blank"); - - private IWebViewAdapter? _webViewAdapter; - private TaskCompletionSource _webViewReadyCompletion = new(); - - public event EventHandler? NavigationCompleted; - - public event EventHandler? NavigationStarted; - public event EventHandler? DomContentLoaded; - - public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source)); - - public Uri? Source - { - get => GetValue(SourceProperty); - set => SetValue(SourceProperty, value); - } - - public bool CanGoBack => _webViewAdapter?.CanGoBack ?? false; - - public bool CanGoForward => _webViewAdapter?.CanGoForward ?? false; - - public bool GoBack() => _webViewAdapter?.GoBack() ?? false; - - public bool GoForward() => _webViewAdapter?.GoForward() ?? false; - - public Task InvokeScript(string scriptName) - { - return _webViewAdapter is null - ? throw new InvalidOperationException("Control was not initialized") - : _webViewAdapter.InvokeScript(scriptName); - } - - public void Navigate(Uri url) - { - (_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized")) - .Navigate(url); - } - - public void NavigateToString(string text) - { - (_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized")) - .NavigateToString(text); - } - - public bool Refresh() => _webViewAdapter?.Refresh() ?? false; - - public bool Stop() => _webViewAdapter?.Stop() ?? false; - - protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && WebViewCapabilities.IsMsWebView2Available) - { - _webViewAdapter = new WebView2Adapter(base.CreateNativeControlCore(parent)); - } - else - { - return base.CreateNativeControlCore(parent); - } - - SubscribeOnEvents(); - - if (_webViewAdapter.IsInitialized) - { - WebViewAdapterOnInitialized(_webViewAdapter, EventArgs.Empty); - } - else - { - _webViewAdapter.Initialized += WebViewAdapterOnInitialized; - } - - return _webViewAdapter; - } - - private void SubscribeOnEvents() - { - if (_webViewAdapter is not null) - { - _webViewAdapter.NavigationStarted += WebViewAdapterOnNavigationStarted; - _webViewAdapter.NavigationCompleted += WebViewAdapterOnNavigationCompleted; - _webViewAdapter.DomContentLoaded += WebViewAdapterOnDomContentLoaded; - } - } - - private void WebViewAdapterOnDomContentLoaded(object? sender, WebViewDomContentLoadedEventArgs e) - { - DomContentLoaded?.Invoke(this, e); - } - - private void WebViewAdapterOnNavigationStarted(object? sender, WebViewNavigationStartingEventArgs e) - { - NavigationStarted?.Invoke(this, e); - } - - private void WebViewAdapterOnNavigationCompleted(object? sender, WebViewNavigationCompletedEventArgs e) - { - SetCurrentValue(SourceProperty, e.Request); - NavigationCompleted?.Invoke(this, e); - } - - private void WebViewAdapterOnInitialized(object? sender, EventArgs e) - { - var adapter = (IWebViewAdapter)sender!; - adapter.Initialized -= WebViewAdapterOnInitialized; - if (Source is not null) - { - adapter.Source = Source; - } - - _webViewReadyCompletion.TrySetResult(); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == SourceProperty) - { - if (_webViewAdapter is { IsInitialized: true }) - { - _webViewAdapter.Source = change.GetNewValue() ?? EmptyPageLink; - } - } - else if (change.Property == BoundsProperty) - { - _webViewAdapter?.SizeChanged(); - } - } - - protected override void DestroyNativeControlCore(IPlatformHandle control) - { - DestroyWebViewAdapter(); - } - - private void DestroyWebViewAdapter() - { - if (_webViewAdapter is not null) - { - _webViewReadyCompletion = new TaskCompletionSource(); - var adapter = _webViewAdapter; - _webViewAdapter = null; - adapter.DomContentLoaded -= WebViewAdapterOnDomContentLoaded; - adapter.NavigationStarted -= WebViewAdapterOnNavigationStarted; - adapter.NavigationCompleted -= WebViewAdapterOnNavigationCompleted; - adapter.Initialized -= WebViewAdapterOnInitialized; - adapter.Dispose(); - } - } -} diff --git a/Hollow/Controls/WebView/WebViewCapabilities.cs b/Hollow/Controls/WebView/WebViewCapabilities.cs deleted file mode 100644 index 3c24291..0000000 --- a/Hollow/Controls/WebView/WebViewCapabilities.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Hollow.Controls.WebView; - -public class WebViewCapabilities -{ - private static bool? _isMsWebView2Available; - public static bool IsMsWebView2Available => _isMsWebView2Available ??= IsMsWebView2AvailableInternal(); - - private static bool IsMsWebView2AvailableInternal() - { - try - { - var versionString = Microsoft.Web.WebView2.Core.CoreWebView2Environment.GetAvailableBrowserVersionString(); - return !string.IsNullOrWhiteSpace(versionString); - } - catch (Exception) - { - return false; - } - } -} diff --git a/Hollow/Controls/WebView/Win/WebView2Adapter.cs b/Hollow/Controls/WebView/Win/WebView2Adapter.cs deleted file mode 100644 index e6c3e69..0000000 --- a/Hollow/Controls/WebView/Win/WebView2Adapter.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Drawing; -using System.Runtime.Versioning; -using System.Threading.Tasks; -using Avalonia.Platform; -using Microsoft.Web.WebView2.Core; - -namespace Hollow.Controls.WebView.Win; - -[SupportedOSPlatform("Windows")] -internal class WebView2Adapter : IWebViewAdapter -{ - private CoreWebView2Controller? _controller; - private Action? _subscriptions; - - public WebView2Adapter(IPlatformHandle handle) - { - Handle = handle.Handle; - - Initialize(); - } - - public IntPtr Handle { get; } - public string HandleDescriptor => "HWND"; - - private async void Initialize() - { - var env = await CoreWebView2Environment.CreateAsync(); - var controller = await env.CreateCoreWebView2ControllerAsync(Handle); - controller.DefaultBackgroundColor = Color.FromArgb(49, 49, 49); - controller.IsVisible = true; - _controller = controller; - - SizeChanged(); - - _controller.CoreWebView2.Settings.IsStatusBarEnabled = false; - _subscriptions = AddHandlers(_controller.CoreWebView2); - - IsInitialized = true; - Initialized?.Invoke(this, EventArgs.Empty); - } - - public bool IsInitialized { get; private set; } - - public bool CanGoBack => _controller?.CoreWebView2?.CanGoBack ?? false; - - public bool CanGoForward => _controller?.CoreWebView2?.CanGoForward ?? false; - - public Uri? Source - { - [return: MaybeNull] - get => Uri.TryCreate(_controller?.CoreWebView2?.Source, UriKind.Absolute, out var url) ? url : null!; - set => _controller?.CoreWebView2?.Navigate(value?.AbsoluteUri); - } - - public event EventHandler? NavigationCompleted; - public event EventHandler? NavigationStarted; - public event EventHandler? DomContentLoaded; - public event EventHandler? Initialized; - - public void Dispose() - { - _subscriptions?.Invoke(); - _controller?.Close(); - } - - public bool GoBack() - { - _controller?.CoreWebView2.GoBack(); - return true; - } - - public bool GoForward() - { - _controller?.CoreWebView2.GoForward(); - return true; - } - - public Task InvokeScript(string scriptName) - { - return _controller?.CoreWebView2?.ExecuteScriptAsync(scriptName) ?? Task.FromResult(null); - } - - public void Navigate(Uri url) - { - _controller?.CoreWebView2?.Navigate(url.AbsolutePath); - } - - public void NavigateToString(string text) - { - _controller?.CoreWebView2?.NavigateToString(text); - } - - public bool Refresh() - { - _controller?.CoreWebView2?.Reload(); - return true; - } - - public bool Stop() - { - _controller?.CoreWebView2?.Stop(); - return true; - } - - public void SizeChanged() - { - WinApiHelpers.GetWindowRect(Handle, out var rect); - - if (_controller is not null) - { - _controller.BoundsMode = CoreWebView2BoundsMode.UseRawPixels; - _controller.Bounds = new System.Drawing.Rectangle(0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top); - } - } - - private Action AddHandlers(CoreWebView2 webView) - { - webView.DOMContentLoaded += WebViewOnDomContentLoaded; - void WebViewOnDomContentLoaded(object? sender, CoreWebView2DOMContentLoadedEventArgs e) - { - DomContentLoaded?.Invoke(this, new WebViewDomContentLoadedEventArgs()); - } - - webView.WebResourceRequested += (s, e) => - { - if(e.Request.Uri.Contains("aaa")) - e.Response = webView.Environment.CreateWebResourceResponse(System.IO.Stream.Null, 404, "Not Found", "Content-Type: text/html"); - }; - - webView.NavigationStarting += WebViewOnNavigationStarting; - void WebViewOnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e) - { - var args = new WebViewNavigationStartingEventArgs { Request = new Uri(e.Uri) }; - NavigationStarted?.Invoke(this, args); - if (args.Cancel) - { - e.Cancel = true; - } - } - - webView.NavigationCompleted += WebViewOnNavigationCompleted; - void WebViewOnNavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e) - { - NavigationCompleted?.Invoke(this, new WebViewNavigationCompletedEventArgs - { - Request = new Uri(((CoreWebView2)sender!).Source), - IsSuccess = e.IsSuccess - }); - } - - return () => - { - webView.NavigationStarting -= WebViewOnNavigationStarting; - webView.NavigationCompleted -= WebViewOnNavigationCompleted; - webView.DOMContentLoaded -= WebViewOnDomContentLoaded; - }; - } -} diff --git a/Hollow/Controls/WebView/Win/WinApiHelpers.cs b/Hollow/Controls/WebView/Win/WinApiHelpers.cs deleted file mode 100644 index b60a937..0000000 --- a/Hollow/Controls/WebView/Win/WinApiHelpers.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Hollow.Controls.WebView.Win; - -internal static class WinApiHelpers -{ - [DllImport("user32.dll", SetLastError=true)] - public static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect); - - [StructLayout(LayoutKind.Sequential)] - public struct Rect - { - public int Left; // x position of upper-left corner - public int Top; // y position of upper-left corner - public int Right; // x position of lower-right corner - public int Bottom; // y position of lower-right corner - } -} diff --git a/Hollow/Extensions/DispatcherExtension.cs b/Hollow/Extensions/DispatcherExtension.cs new file mode 100644 index 0000000..9162447 --- /dev/null +++ b/Hollow/Extensions/DispatcherExtension.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Avalonia.Threading; + +namespace Hollow.Extensions; + +public static class DispatcherExtension +{ + /// + /// 在指定的DispatcherFrame上运行一个Task,等待其完成,避免阻塞UI线程 + /// + /// + /// + /// + /// + public static void WaitOnDispatcherFrame(this Task task, Dispatcher? dispatcher = null) + { + var frame = new DispatcherFrame(); + AggregateException? capturedException = null; + + task.ContinueWith(t => + { + capturedException = t.Exception; + frame.Continue = false; // 结束消息循环 + }, + TaskContinuationOptions.AttachedToParent); + + dispatcher ??= Dispatcher.UIThread; + dispatcher.PushFrame(frame); + + if (capturedException != null) + { + throw capturedException; + } + } +} \ No newline at end of file diff --git a/Hollow/Helpers/GachaAnalyser.cs b/Hollow/Helpers/GachaAnalyser.cs index 904f1e4..369a233 100644 --- a/Hollow/Helpers/GachaAnalyser.cs +++ b/Hollow/Helpers/GachaAnalyser.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using Hollow.Core.Gacha.Common; -using Hollow.Core.Gacha.Uigf; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Common; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Uigf; using Hollow.Models.Pages.SignalSearch; namespace Hollow.Helpers; diff --git a/Hollow/Helpers/HollowJsonSerializer.cs b/Hollow/Helpers/HollowJsonSerializer.cs index 6d32554..37d1d54 100644 --- a/Hollow/Helpers/HollowJsonSerializer.cs +++ b/Hollow/Helpers/HollowJsonSerializer.cs @@ -1,7 +1,7 @@ using System.Text.Encodings.Web; using System.Text.Json; -namespace Hollow.Models; +namespace Hollow.Helpers; public class HollowJsonSerializer { diff --git a/Hollow/Helpers/ViewLocator.cs b/Hollow/Helpers/ViewLocator.cs deleted file mode 100644 index e22f226..0000000 --- a/Hollow/Helpers/ViewLocator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Linq; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Templates; - -namespace Hollow.Helpers; - -internal static class ViewLocator -{ - private static IDataTemplate? _locator; - - /// - /// Tries to build a suitable control using an appropriate DataTemplate provided by the App. - /// - /// - /// A valid control provided by a suitable ViewLocator if available, otherwise returns an error TextBlock. - internal static Control TryBuild(object? data) - { - if (data is string s) return new TextBlock() { Text = s }; - _locator ??= Application.Current?.DataTemplates.FirstOrDefault(); - return _locator?.Build(data) ?? new TextBlock() { Text = $"Unable to find suitable view for {data?.GetType().Name}" }; - } -} \ No newline at end of file diff --git a/Hollow/Hollow.csproj b/Hollow/Hollow.csproj index f7f0776..410068b 100644 --- a/Hollow/Hollow.csproj +++ b/Hollow/Hollow.csproj @@ -1,15 +1,8 @@  - WinExe net8.0 enable - true - app.manifest true - true - true - true - Assets\Icons\small_icon.ico @@ -39,7 +32,6 @@ - @@ -88,7 +80,7 @@ - + @@ -102,4 +94,20 @@ + + + + + + + + + + + + + + + + diff --git a/Hollow/Models/Pages/Announcement/AnnouncementModel.cs b/Hollow/Models/Pages/Announcement/AnnouncementModel.cs index e822e2d..22cc462 100644 --- a/Hollow/Models/Pages/Announcement/AnnouncementModel.cs +++ b/Hollow/Models/Pages/Announcement/AnnouncementModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Hollow.Core.MiHoYoLauncher.Models; namespace Hollow.Models.Pages.Announcement; diff --git a/Hollow/Services/ConfigurationService/ConfigurationService.cs b/Hollow/Services/ConfigurationService/ConfigurationService.cs index 822737f..a27da5f 100644 --- a/Hollow/Services/ConfigurationService/ConfigurationService.cs +++ b/Hollow/Services/ConfigurationService/ConfigurationService.cs @@ -2,8 +2,10 @@ using System.IO; using System.Text.Json; using Avalonia.Markup.Xaml.MarkupExtensions; +using Hollow.Abstractions.Models; +using Hollow.Abstractions.Models.Configs; +using Hollow.Helpers; using Hollow.Models; -using Hollow.Models.Configs; namespace Hollow.Services.ConfigurationService; diff --git a/Hollow/Services/ConfigurationService/IConfigurationService.cs b/Hollow/Services/ConfigurationService/IConfigurationService.cs index 559e0a8..a5f8623 100644 --- a/Hollow/Services/ConfigurationService/IConfigurationService.cs +++ b/Hollow/Services/ConfigurationService/IConfigurationService.cs @@ -1,4 +1,4 @@ -using Hollow.Models.Configs; +using Hollow.Abstractions.Models.Configs; namespace Hollow.Services.ConfigurationService; diff --git a/Hollow/Services/GachaService/GachaService.cs b/Hollow/Services/GachaService/GachaService.cs index 5ede070..3d416b4 100644 --- a/Hollow/Services/GachaService/GachaService.cs +++ b/Hollow/Services/GachaService/GachaService.cs @@ -6,9 +6,11 @@ using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Hollow.Core.Gacha; -using Hollow.Core.Gacha.Common; -using Hollow.Core.Gacha.Uigf; +using Hollow.Abstractions.Models; +using Hollow.Abstractions.Models.HttpContrasts; +using Hollow.Abstractions.Models.HttpContrasts.Gacha; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Common; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Uigf; using Hollow.Helpers; using Hollow.Models; using Hollow.Services.ConfigurationService; diff --git a/Hollow/Services/GachaService/IGachaService.cs b/Hollow/Services/GachaService/IGachaService.cs index 31af387..5b26a55 100644 --- a/Hollow/Services/GachaService/IGachaService.cs +++ b/Hollow/Services/GachaService/IGachaService.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Hollow.Core.Gacha.Uigf; +using Hollow.Abstractions.Models.HttpContrasts; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Uigf; using Hollow.Models; namespace Hollow.Services.GachaService; diff --git a/Hollow/Services/MiHoYoLauncherService/IMiHoYoLauncherService.cs b/Hollow/Services/MiHoYoLauncherService/IMiHoYoLauncherService.cs index 1b397a9..ba27881 100644 --- a/Hollow/Services/MiHoYoLauncherService/IMiHoYoLauncherService.cs +++ b/Hollow/Services/MiHoYoLauncherService/IMiHoYoLauncherService.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Hollow.Core.MiHoYoLauncher.Models; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; using Hollow.Models.Pages.Announcement; namespace Hollow.Services.MiHoYoLauncherService; diff --git a/Hollow/Services/MiHoYoLauncherService/MiHoYoLauncherService.cs b/Hollow/Services/MiHoYoLauncherService/MiHoYoLauncherService.cs index cb3d5d3..d1c54a5 100644 --- a/Hollow/Services/MiHoYoLauncherService/MiHoYoLauncherService.cs +++ b/Hollow/Services/MiHoYoLauncherService/MiHoYoLauncherService.cs @@ -3,8 +3,8 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Hollow.Core.MiHoYoLauncher; -using Hollow.Core.MiHoYoLauncher.Models; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; +using Hollow.Abstractions.Models.MiHoYoLauncher; using Hollow.Models.Pages.Announcement; using HtmlHelper = Hollow.Helpers.HtmlHelper; diff --git a/Hollow/ViewModels/MainWindowViewModel.cs b/Hollow/ViewModels/MainWindowViewModel.cs index d0198d5..a9781a6 100644 --- a/Hollow/ViewModels/MainWindowViewModel.cs +++ b/Hollow/ViewModels/MainWindowViewModel.cs @@ -16,7 +16,6 @@ public partial class MainWindowViewModel : ViewModelBase [ObservableProperty] private int _blur; [ObservableProperty] private double _coverageOpacity; - [ObservableProperty] private double _announcementCoverageOpacity; [ObservableProperty] private double _navigationOpacity = 1; [ObservableProperty] private Bitmap _gameIcon; @@ -53,7 +52,6 @@ private void OnNavigating() Blur = DisplayPageId == 0 ? 1 : 20; CoverageOpacity = DisplayPageId == 0 ? 0 : 1; NavigationOpacity = DisplayPageId == 0 ? 1 : 0; - AnnouncementCoverageOpacity = DisplayPageId == 1 ? 1 : 0; DisplayPageId = _navigationService.CurrentViewId; diff --git a/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs b/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs index 729f770..9ea42df 100644 --- a/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs +++ b/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs @@ -1,8 +1,8 @@ using System; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; -using Hollow.Controls.WebView; using Hollow.Helpers; +using Hollow.Views.Controls.WebView; using Hollow.Views.Pages; namespace Hollow.ViewModels.Pages; @@ -10,82 +10,83 @@ namespace Hollow.ViewModels.Pages; public partial class AnnouncementsViewModel : ViewModelBase, IViewModelBase { - private const string Script = """ - window.onload = function() { - let root = document.getElementById("root"); - root.style.backgroundColor = "#313131"; - root.style.backgroundRepeat = "no-repeat"; - root.style.backgroundPosition = "left bottom"; - root.style.backgroundSize = "cover"; - - let bodyhome = document.getElementsByClassName("home__body home__body--pc"); - if (bodyhome.length > 0) { - bodyhome[0].style.transform = "scale(1.33, 1.37)"; - } - - let home = document.getElementsByClassName("home"); - if (home.length > 0) { - home[0].style.background = "transparent"; - } - let homemask = document.getElementsByClassName("home__mask"); - if (homemask.length > 0) { - homemask[0].remove(); - } - - // Apply the MiSans font - var link = document.createElement('link'); - link.type = 'text/css'; - link.rel = 'stylesheet'; - document.head.appendChild(link); - link.href = 'https://cdn.jsdelivr.net/npm/misans@4.0.0/lib/Normal/MiSans-Medium.min.css'; - let innerann = document.getElementsByClassName("inner-ann"); - if (innerann.length > 0) { - innerann[0].style.fontFamily = 'MiSans'; - } - - // Remove the close button - let homeclose = document.getElementsByClassName("home__close"); - if (homeclose.length > 0) { - homeclose[0].remove(); - } - - // 重写所有 uniwebview URL 方案 - function rewriteUniWebViewUrls() { - document.querySelectorAll('a[href]').forEach(link => { - let href = link.getAttribute('href'); - if (href && href.startsWith('uniwebview://open_url?url=')) { - let newUrl = href.replace('uniwebview://open_url?url=', ''); - link.setAttribute('href', newUrl); - } - }); - } - - document.addEventListener('click', function(event) { - let target = event.target; - while (target && target.tagName !== 'A') { - target = target.parentNode; - } - if (target && target.tagName === 'A') { - let href = target.getAttribute('href'); - if (href && href.startsWith('javascript:miHoYoGameJSSDK.openInBrowser')) { - event.preventDefault(); - const regex = /javascript:miHoYoGameJSSDK\.openInBrowser\('([^']+)'\)/; - const match = href.match(regex); - if (match && match[1]) { - window.location.href = "inner:" + match[1]; - } - } - } - }); - }; - """; + private const string Script = + """ + window.onload = function() { + const root = document.getElementById("root"); + root.style.backgroundRepeat = "no-repeat"; + root.style.backgroundPosition = "left bottom"; + root.style.backgroundSize = "cover"; + + const bodyHome = document.getElementsByClassName("home__body home__body--pc"); + if (bodyHome.length > 0) { + bodyHome[0].style.transform = "scale(1.33, 1.37)"; + } + + const home = document.getElementsByClassName("home"); + if (home.length > 0) { + home[0].style.background = "transparent"; + } + + const homeMask = document.getElementsByClassName("home__mask"); + if (homeMask.length > 0) { + homeMask[0].remove(); + } + + // Apply the MiSans font + const link = document.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + document.head.appendChild(link); + link.href = 'https://cdn.jsdelivr.net/npm/misans@4.0.0/lib/Normal/MiSans-Medium.min.css'; + const innerAnn = document.getElementsByClassName("inner-ann"); + if (innerAnn.length > 0) { + innerAnn[0].style.fontFamily = 'MiSans'; + } + + // Remove the close button + const homeClose = document.getElementsByClassName("home__close"); + if (homeClose.length > 0) { + homeClose[0].remove(); + } + + // 重写所有 uniwebview URL 方案 + function rewriteUniWebViewUrls() { + document.querySelectorAll('a[href]').forEach(link => { + const href = link.getAttribute('href'); + if (href && href.startsWith('uniwebview://open_url?url=')) { + const newUrl = href.replace('uniwebview://open_url?url=', ''); + link.setAttribute('href', newUrl); + } + }); + } + + document.addEventListener('click', function(event) { + let target = event.target; + while (target && target.tagName !== 'A') { + target = target.parentNode; + } + if (target && target.tagName === 'A') { + const href = target.getAttribute('href'); + if (href && href.startsWith('javascript:miHoYoGameJSSDK.openInBrowser')) { + event.preventDefault(); + const regex = /javascript:miHoYoGameJSSDK\.openInBrowser\('([^']+)'\)/; + const match = href.match(regex); + if (match && match[1]) { + window.location.href = "inner:" + match[1]; + } + } + } + }); + }; + """; private const string AnnouncementUrl = "https://sdk.mihoyo.com/nap/announcement/index.html?auth_appid=announcement&authkey_ver=1&bundle_id=nap_cn&channel_id=1&game=nap&game_biz=nap_cn&lang=zh-cn&level=60&platform=pc®ion=prod_gf_cn&sdk_presentation_style=fullscreen&sdk_screen_transparent=true&sign_type=2&uid=100000000&version=2.27#/"; public void Navigated() { - + } [ObservableProperty] private string? _announcementHtmlContent; diff --git a/Hollow/ViewModels/Pages/GameSettingsViewModel.cs b/Hollow/ViewModels/Pages/GameSettingsViewModel.cs index 85a4a08..690627e 100644 --- a/Hollow/ViewModels/Pages/GameSettingsViewModel.cs +++ b/Hollow/ViewModels/Pages/GameSettingsViewModel.cs @@ -1,7 +1,7 @@ using CommunityToolkit.Mvvm.Input; -using Hollow.Controls; using Hollow.Enums; using Hollow.Services.ConfigurationService; +using Hollow.Views.Controls; namespace Hollow.ViewModels.Pages; diff --git a/Hollow/ViewModels/Pages/HomeViewModel.cs b/Hollow/ViewModels/Pages/HomeViewModel.cs index f5552f4..57605f9 100644 --- a/Hollow/ViewModels/Pages/HomeViewModel.cs +++ b/Hollow/ViewModels/Pages/HomeViewModel.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Hollow.Core.MiHoYoLauncher.Models; +using Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher; using Hollow.Helpers; using Hollow.Models.Pages.Home; using Hollow.Services.ConfigurationService; diff --git a/Hollow/ViewModels/Pages/SettingsViewModel.cs b/Hollow/ViewModels/Pages/SettingsViewModel.cs index ce88f41..2dfeb0b 100644 --- a/Hollow/ViewModels/Pages/SettingsViewModel.cs +++ b/Hollow/ViewModels/Pages/SettingsViewModel.cs @@ -7,13 +7,14 @@ using Avalonia.Markup.Xaml.MarkupExtensions; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Hollow.Controls; +using Hollow.Abstractions.Models; using Hollow.Helpers; using Hollow.Languages; using Hollow.Models; using Hollow.Services.ConfigurationService; using Hollow.Services.GameService; using Hollow.Services.NavigationService; +using Hollow.Views.Controls; using Hollow.Views.Pages; using NotificationType = Hollow.Enums.NotificationType; diff --git a/Hollow/ViewModels/Pages/SignalSearchViewModel.cs b/Hollow/ViewModels/Pages/SignalSearchViewModel.cs index 7e0c470..0068390 100644 --- a/Hollow/ViewModels/Pages/SignalSearchViewModel.cs +++ b/Hollow/ViewModels/Pages/SignalSearchViewModel.cs @@ -7,8 +7,9 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Hollow.Controls; -using Hollow.Core.Gacha.Uigf; +using Hollow.Abstractions.Models; +using Hollow.Abstractions.Models.HttpContrasts; +using Hollow.Abstractions.Models.HttpContrasts.Gacha.Uigf; using Hollow.Enums; using Hollow.Helpers; using Hollow.Languages; @@ -16,6 +17,7 @@ using Hollow.Models.Pages.SignalSearch; using Hollow.Services.GachaService; using Hollow.Services.NavigationService; +using Hollow.Views.Controls; using Hollow.Views.Dialogs; namespace Hollow.ViewModels.Pages; diff --git a/Hollow/Controls/Acrylic/Acrylic.axaml b/Hollow/Views/Controls/Acrylic/Acrylic.axaml similarity index 89% rename from Hollow/Controls/Acrylic/Acrylic.axaml rename to Hollow/Views/Controls/Acrylic/Acrylic.axaml index 3b88785..71595d0 100644 --- a/Hollow/Controls/Acrylic/Acrylic.axaml +++ b/Hollow/Views/Controls/Acrylic/Acrylic.axaml @@ -1,7 +1,7 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:acrylic="clr-namespace:Hollow.Views.Controls.Acrylic"> diff --git a/Hollow/Controls/Acrylic/Acrylic.cs b/Hollow/Views/Controls/Acrylic/Acrylic.cs similarity index 97% rename from Hollow/Controls/Acrylic/Acrylic.cs rename to Hollow/Views/Controls/Acrylic/Acrylic.cs index 0d73bcf..c75a8e5 100644 --- a/Hollow/Controls/Acrylic/Acrylic.cs +++ b/Hollow/Views/Controls/Acrylic/Acrylic.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Media; -namespace Hollow.Controls.Acrylic; +namespace Hollow.Views.Controls.Acrylic; public class Acrylic: ContentControl { diff --git a/Hollow/Controls/Acrylic/AcrylicRenderOperation.cs b/Hollow/Views/Controls/Acrylic/AcrylicRenderOperation.cs similarity index 99% rename from Hollow/Controls/Acrylic/AcrylicRenderOperation.cs rename to Hollow/Views/Controls/Acrylic/AcrylicRenderOperation.cs index 07f0690..c125d1f 100644 --- a/Hollow/Controls/Acrylic/AcrylicRenderOperation.cs +++ b/Hollow/Views/Controls/Acrylic/AcrylicRenderOperation.cs @@ -7,7 +7,7 @@ using Avalonia.Threading; using SkiaSharp; -namespace Hollow.Controls.Acrylic; +namespace Hollow.Views.Controls.Acrylic; public class AcrylicRenderOperation( Acrylic acrylic, diff --git a/Hollow/Controls/Coverages/LoadingCoverage.axaml b/Hollow/Views/Controls/Coverages/LoadingCoverage.axaml similarity index 85% rename from Hollow/Controls/Coverages/LoadingCoverage.axaml rename to Hollow/Views/Controls/Coverages/LoadingCoverage.axaml index 234393b..45f1bfe 100644 --- a/Hollow/Controls/Coverages/LoadingCoverage.axaml +++ b/Hollow/Views/Controls/Coverages/LoadingCoverage.axaml @@ -1,9 +1,9 @@  - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:coverages="clr-namespace:Hollow.Views.Controls.Coverages"> + @@ -37,6 +37,6 @@ + TargetType="coverages:LoadingCoverage" + x:Key="{x:Type coverages:LoadingCoverage}" /> \ No newline at end of file diff --git a/Hollow/Controls/Coverages/LoadingCoverage.axaml.cs b/Hollow/Views/Controls/Coverages/LoadingCoverage.axaml.cs similarity index 95% rename from Hollow/Controls/Coverages/LoadingCoverage.axaml.cs rename to Hollow/Views/Controls/Coverages/LoadingCoverage.axaml.cs index 71e4e38..7620bcd 100644 --- a/Hollow/Controls/Coverages/LoadingCoverage.axaml.cs +++ b/Hollow/Views/Controls/Coverages/LoadingCoverage.axaml.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; -namespace Hollow.Controls; +namespace Hollow.Views.Controls.Coverages; public class LoadingCoverage : UserControl { diff --git a/Hollow/Controls/Coverages/ProhibitedCoverage.axaml b/Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml similarity index 74% rename from Hollow/Controls/Coverages/ProhibitedCoverage.axaml rename to Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml index ba4e219..1db8bae 100644 --- a/Hollow/Controls/Coverages/ProhibitedCoverage.axaml +++ b/Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml @@ -1,9 +1,9 @@  - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:coverages="clr-namespace:Hollow.Views.Controls.Coverages"> + @@ -23,6 +23,6 @@ + TargetType="coverages:ProhibitedCoverage" + x:Key="{x:Type coverages:ProhibitedCoverage}" /> \ No newline at end of file diff --git a/Hollow/Controls/Coverages/ProhibitedCoverage.axaml.cs b/Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml.cs similarity index 89% rename from Hollow/Controls/Coverages/ProhibitedCoverage.axaml.cs rename to Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml.cs index 1661e85..aa5d9d4 100644 --- a/Hollow/Controls/Coverages/ProhibitedCoverage.axaml.cs +++ b/Hollow/Views/Controls/Coverages/ProhibitedCoverage.axaml.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; -namespace Hollow.Controls; +namespace Hollow.Views.Controls.Coverages; public class ProhibitedCoverage : UserControl { diff --git a/Hollow/Controls/HollowHost.axaml b/Hollow/Views/Controls/HollowHost.axaml similarity index 98% rename from Hollow/Controls/HollowHost.axaml rename to Hollow/Views/Controls/HollowHost.axaml index d0a83da..380edde 100644 --- a/Hollow/Controls/HollowHost.axaml +++ b/Hollow/Views/Controls/HollowHost.axaml @@ -1,7 +1,7 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Hollow.Views.Controls"> diff --git a/Hollow/Controls/HollowHost.axaml.cs b/Hollow/Views/Controls/HollowHost.axaml.cs similarity index 99% rename from Hollow/Controls/HollowHost.axaml.cs rename to Hollow/Views/Controls/HollowHost.axaml.cs index 49bd259..17ac64a 100644 --- a/Hollow/Controls/HollowHost.axaml.cs +++ b/Hollow/Views/Controls/HollowHost.axaml.cs @@ -11,11 +11,11 @@ using Avalonia.LogicalTree; using Avalonia.Rendering.Composition; using Avalonia.Threading; -using Hollow.Controls.Toast; using Hollow.Helpers; +using Hollow.Views.Controls.Toast; using NotificationType = Hollow.Enums.NotificationType; -namespace Hollow.Controls; +namespace Hollow.Views.Controls; public class HollowHost : ContentControl diff --git a/Hollow/Controls/Home/StartGameButton.axaml b/Hollow/Views/Controls/Home/StartGameButton.axaml similarity index 96% rename from Hollow/Controls/Home/StartGameButton.axaml rename to Hollow/Views/Controls/Home/StartGameButton.axaml index b89b5b5..baaf0a9 100644 --- a/Hollow/Controls/Home/StartGameButton.axaml +++ b/Hollow/Views/Controls/Home/StartGameButton.axaml @@ -1,17 +1,17 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:home="clr-namespace:Hollow.Views.Controls.Home"> - - + @@ -160,6 +160,6 @@ + TargetType="home:StartGameButton" + x:Key="{x:Type home:StartGameButton}" /> \ No newline at end of file diff --git a/Hollow/Controls/Home/StartGameButton.axaml.cs b/Hollow/Views/Controls/Home/StartGameButton.axaml.cs similarity index 97% rename from Hollow/Controls/Home/StartGameButton.axaml.cs rename to Hollow/Views/Controls/Home/StartGameButton.axaml.cs index 7254920..ac8eb45 100644 --- a/Hollow/Controls/Home/StartGameButton.axaml.cs +++ b/Hollow/Views/Controls/Home/StartGameButton.axaml.cs @@ -2,7 +2,7 @@ using Avalonia; using Avalonia.Controls; -namespace Hollow.Controls; +namespace Hollow.Views.Controls.Home; public class StartGameButton : ContentControl { diff --git a/Hollow/Controls/Home/StartGameNoticeButton.axaml b/Hollow/Views/Controls/Home/StartGameNoticeButton.axaml similarity index 90% rename from Hollow/Controls/Home/StartGameNoticeButton.axaml rename to Hollow/Views/Controls/Home/StartGameNoticeButton.axaml index c5cbeec..4e782e8 100644 --- a/Hollow/Controls/Home/StartGameNoticeButton.axaml +++ b/Hollow/Views/Controls/Home/StartGameNoticeButton.axaml @@ -1,15 +1,15 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:home="clr-namespace:Hollow.Views.Controls.Home"> - - + @@ -73,6 +73,6 @@ + TargetType="home:StartGameNoticeButton" + x:Key="{x:Type home:StartGameNoticeButton}" /> \ No newline at end of file diff --git a/Hollow/Controls/Home/StartGameNoticeButton.cs b/Hollow/Views/Controls/Home/StartGameNoticeButton.cs similarity index 96% rename from Hollow/Controls/Home/StartGameNoticeButton.cs rename to Hollow/Views/Controls/Home/StartGameNoticeButton.cs index 8c1289f..305abfb 100644 --- a/Hollow/Controls/Home/StartGameNoticeButton.cs +++ b/Hollow/Views/Controls/Home/StartGameNoticeButton.cs @@ -2,7 +2,7 @@ using Avalonia; using Avalonia.Controls; -namespace Hollow.Controls; +namespace Hollow.Views.Controls.Home; public class StartGameNoticeButton : ContentControl { diff --git a/Hollow/Controls/Screenshots/ScreenshotImage.axaml b/Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml similarity index 89% rename from Hollow/Controls/Screenshots/ScreenshotImage.axaml rename to Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml index abd5123..9321a32 100644 --- a/Hollow/Controls/Screenshots/ScreenshotImage.axaml +++ b/Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml @@ -2,7 +2,7 @@ d:DesignHeight="450" d:DesignWidth="800" mc:Ignorable="d" - x:Class="Hollow.Controls.Screenshots.ScreenshotImage" + x:Class="Hollow.Views.Controls.Screenshots.ScreenshotImage" xmlns="https://github.com/avaloniaui" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" diff --git a/Hollow/Controls/Screenshots/ScreenshotImage.axaml.cs b/Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml.cs similarity index 53% rename from Hollow/Controls/Screenshots/ScreenshotImage.axaml.cs rename to Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml.cs index f3c63b9..322d1e5 100644 --- a/Hollow/Controls/Screenshots/ScreenshotImage.axaml.cs +++ b/Hollow/Views/Controls/Screenshots/ScreenshotImage.axaml.cs @@ -1,8 +1,6 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Controls; -namespace Hollow.Controls.Screenshots; +namespace Hollow.Views.Controls.Screenshots; public partial class ScreenshotImage : UserControl { diff --git a/Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml b/Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml similarity index 98% rename from Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml rename to Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml index da82576..1c9d526 100644 --- a/Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml +++ b/Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml @@ -1,9 +1,9 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:signalSearch="clr-namespace:Hollow.Views.Controls.SignalSearch"> diff --git a/Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs b/Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs similarity index 98% rename from Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs rename to Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs index f1d0b7e..34f3e22 100644 --- a/Hollow/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs +++ b/Hollow/Views/Controls/SignalSearch/SignalSearchOverviewCard.axaml.cs @@ -3,7 +3,7 @@ using Avalonia.Controls; using Hollow.Models.Pages.SignalSearch; -namespace Hollow.Controls.SignalSearch; +namespace Hollow.Views.Controls.SignalSearch; public class SignalSearchOverviewCard : UserControl { diff --git a/Hollow/Controls/Toast/Toast.axaml b/Hollow/Views/Controls/Toast/Toast.axaml similarity index 97% rename from Hollow/Controls/Toast/Toast.axaml rename to Hollow/Views/Controls/Toast/Toast.axaml index 16037a9..7cff067 100644 --- a/Hollow/Controls/Toast/Toast.axaml +++ b/Hollow/Views/Controls/Toast/Toast.axaml @@ -1,8 +1,8 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:toast="clr-namespace:Hollow.Views.Controls.Toast"> /// NavigationCompleted dispatches after a navigated of the top level document completes rendering either successfully or not. @@ -103,11 +104,9 @@ internal interface IWebView bool Stop(); } -internal interface IWebViewAdapter : IWebView, IDisposable, IPlatformHandle +public interface IWebViewAdapter : IWebView, IDisposable, IPlatformHandle { - event EventHandler? Initialized; + Task SetParentAsync(IntPtr handle); - bool IsInitialized { get; } - - void SizeChanged(); + void HandleSizeChanged(Size newSize); } diff --git a/Hollow/Views/Controls/WebView/NativeWebView.cs b/Hollow/Views/Controls/WebView/NativeWebView.cs new file mode 100644 index 0000000..e272c43 --- /dev/null +++ b/Hollow/Views/Controls/WebView/NativeWebView.cs @@ -0,0 +1,124 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Platform; +using Hollow.Extensions; + +namespace Hollow.Views.Controls.WebView; + +public class NativeWebView : NativeControlHost, IWebView, IDisposable +{ + private static readonly Uri EmptyPageLink = new("about:blank"); + + private readonly IWebViewAdapter _webViewAdapter = App.GetService(); + + public event EventHandler? NavigationCompleted; + public event EventHandler? NavigationStarted; + public event EventHandler? DomContentLoaded; + + public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source)); + + public Uri? Source + { + get => GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + public bool CanGoBack => _webViewAdapter.CanGoBack; + + public bool CanGoForward => _webViewAdapter.CanGoForward; + + public bool GoBack() => _webViewAdapter.GoBack(); + + public bool GoForward() => _webViewAdapter.GoForward(); + + public NativeWebView() + { + _webViewAdapter.NavigationStarted += WebViewAdapterOnNavigationStarted; + _webViewAdapter.NavigationCompleted += WebViewAdapterOnNavigationCompleted; + _webViewAdapter.DomContentLoaded += WebViewAdapterOnDomContentLoaded; + } + + ~NativeWebView() + { + _webViewAdapter.NavigationStarted -= WebViewAdapterOnNavigationStarted; + _webViewAdapter.NavigationCompleted -= WebViewAdapterOnNavigationCompleted; + _webViewAdapter.DomContentLoaded -= WebViewAdapterOnDomContentLoaded; + } + + public Task InvokeScript(string scriptName) + { + return _webViewAdapter is null + ? throw new InvalidOperationException("Control was not initialized") + : _webViewAdapter.InvokeScript(scriptName); + } + + public void Navigate(Uri url) + { + (_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized")) + .Navigate(url); + } + + public void NavigateToString(string text) + { + (_webViewAdapter ?? throw new InvalidOperationException("Control was not initialized")) + .NavigateToString(text); + } + + public bool Refresh() => _webViewAdapter.Refresh(); + + public bool Stop() => _webViewAdapter.Stop(); + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) + { + _webViewAdapter.SetParentAsync(parent.Handle).WaitOnDispatcherFrame(); + return _webViewAdapter; + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + // 先设置父句柄为空,防止WebView被销毁 + _webViewAdapter.SetParentAsync(IntPtr.Zero).WaitOnDispatcherFrame(); + base.OnDetachedFromVisualTree(e); + } + + private void WebViewAdapterOnDomContentLoaded(object? sender, WebViewDomContentLoadedEventArgs e) + { + DomContentLoaded?.Invoke(this, e); + } + + private void WebViewAdapterOnNavigationStarted(object? sender, WebViewNavigationStartingEventArgs e) + { + NavigationStarted?.Invoke(this, e); + } + + private void WebViewAdapterOnNavigationCompleted(object? sender, WebViewNavigationCompletedEventArgs e) + { + SetCurrentValue(SourceProperty, e.Request); + NavigationCompleted?.Invoke(this, e); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == SourceProperty) + { + _webViewAdapter.Source = change.GetNewValue() ?? EmptyPageLink; + } + } + + protected override void OnSizeChanged(SizeChangedEventArgs e) + { + _webViewAdapter.HandleSizeChanged(e.NewSize); + + base.OnSizeChanged(e); + } + + public void Dispose() + { + _webViewAdapter.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/Hollow/Views/Dialogs/StandardDialog.axaml.cs b/Hollow/Views/Dialogs/StandardDialog.axaml.cs index 915084d..d69fefa 100644 --- a/Hollow/Views/Dialogs/StandardDialog.axaml.cs +++ b/Hollow/Views/Dialogs/StandardDialog.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; using Avalonia.Interactivity; -using Hollow.Controls; +using Hollow.Views.Controls; namespace Hollow.Views.Dialogs; diff --git a/Hollow/Views/MainWindow.axaml b/Hollow/Views/MainWindow.axaml index 3733073..b15eca7 100644 --- a/Hollow/Views/MainWindow.axaml +++ b/Hollow/Views/MainWindow.axaml @@ -5,23 +5,23 @@ ExtendClientAreaToDecorationsHint="True" FontFamily="avares://Hollow/Assets/Fonts/#MiSans" Height="700" - Icon="/Assets/Icons/small_icon.ico" + Icon="avares://Hollow/Assets/Icons/small_icon.png" Title="Hollow" Width="1200" mc:Ignorable="d" x:Class="Hollow.Views.MainWindow" x:DataType="vm:MainWindowViewModel" xmlns="https://github.com/avaloniaui" - xmlns:acrylic="clr-namespace:Hollow.Controls.Acrylic" xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" xmlns:avalonia="clr-namespace:FluentIcons.Avalonia;assembly=FluentIcons.Avalonia" - xmlns:controls="clr-namespace:Hollow.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:markupExtensions="clr-namespace:FluentIcons.Avalonia.MarkupExtensions;assembly=FluentIcons.Avalonia" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pages="clr-namespace:Hollow.Views.Pages" xmlns:vm="using:Hollow.ViewModels" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="clr-namespace:Hollow.Views.Controls" + xmlns:acrylic="clr-namespace:Hollow.Views.Controls.Acrylic"> @@ -50,13 +50,6 @@ - - - - - - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:webView="clr-namespace:Hollow.Views.Controls.WebView"> diff --git a/Hollow/Views/Pages/Announcements.axaml.cs b/Hollow/Views/Pages/Announcements.axaml.cs index 8eeb7ae..0d08281 100644 --- a/Hollow/Views/Pages/Announcements.axaml.cs +++ b/Hollow/Views/Pages/Announcements.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -using Hollow.Controls.WebView; using Hollow.ViewModels.Pages; +using Hollow.Views.Controls.WebView; namespace Hollow.Views.Pages; diff --git a/Hollow/Views/Pages/GameSettings.axaml b/Hollow/Views/Pages/GameSettings.axaml index 13449f6..9c40467 100644 --- a/Hollow/Views/Pages/GameSettings.axaml +++ b/Hollow/Views/Pages/GameSettings.axaml @@ -3,12 +3,12 @@ x:Class="Hollow.Views.Pages.GameSettings" x:DataType="pages:GameSettingsViewModel" xmlns="https://github.com/avaloniaui" - xmlns:controls="clr-namespace:Hollow.Controls" xmlns:languages="clr-namespace:Hollow.Languages" xmlns:pages="clr-namespace:Hollow.ViewModels.Pages" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:coverages="clr-namespace:Hollow.Views.Controls.Coverages"> - + diff --git a/Hollow/Views/Pages/Home.axaml b/Hollow/Views/Pages/Home.axaml index ce7ca9c..7a62558 100644 --- a/Hollow/Views/Pages/Home.axaml +++ b/Hollow/Views/Pages/Home.axaml @@ -3,15 +3,15 @@ x:DataType="pages:HomeViewModel" xmlns="https://github.com/avaloniaui" xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" - xmlns:controls="clr-namespace:Hollow.Controls" xmlns:languages="clr-namespace:Hollow.Languages" xmlns:markupExtensions="clr-namespace:FluentIcons.Avalonia.MarkupExtensions;assembly=FluentIcons.Avalonia" - xmlns:models="clr-namespace:Hollow.Core.MiHoYoLauncher.Models;assembly=Hollow.Core" xmlns:pages="clr-namespace:Hollow.ViewModels.Pages" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:miHoYoLauncher="clr-namespace:Hollow.Abstractions.Models.HttpContrasts.MiHoYoLauncher;assembly=Hollow.Abstractions" + xmlns:home="clr-namespace:Hollow.Views.Controls.Home"> - + - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:signalSearch="clr-namespace:Hollow.Views.Controls.SignalSearch" + xmlns:coverages="clr-namespace:Hollow.Views.Controls.Coverages"> @@ -344,7 +344,7 @@ -