From 07debda5e41e4153e6b788e37439b3995741f54d Mon Sep 17 00:00:00 2001 From: ema Date: Mon, 8 Sep 2025 01:14:01 +0800 Subject: [PATCH] feat: improve UI/UX of font loading --- .../ObservableFileStream.cs | 26 +++++++++++++++++++ .../QuickLook.Plugin.FontViewer/Plugin.cs | 8 +++++- .../WebfontPanel.cs | 18 ++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 QuickLook.Plugin/QuickLook.Plugin.FontViewer/ObservableFileStream.cs diff --git a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/ObservableFileStream.cs b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/ObservableFileStream.cs new file mode 100644 index 0000000..f74acf9 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/ObservableFileStream.cs @@ -0,0 +1,26 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace QuickLook.Plugin.FontViewer; + +public class ObservableFileStream(string path, FileMode mode, FileAccess access, FileShare share) : FileStream(path, mode, access, share) +{ + public bool IsEndOfStream { get; protected set; } = false; + + public override int Read(byte[] array, int offset, int count) + { + int result = base.Read(array, offset, count); + if (result == 0) + IsEndOfStream = true; + return result; + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + int result = await base.ReadAsync(buffer, offset, count, cancellationToken); + if (result == 0) + IsEndOfStream = true; + return result; + } +} diff --git a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/Plugin.cs index 68d62d8..f324234 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/Plugin.cs @@ -19,6 +19,7 @@ using QuickLook.Common.Plugin; using System; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Windows; namespace QuickLook.Plugin.FontViewer; @@ -52,7 +53,12 @@ public class Plugin : IViewer context.ViewerContent = _panel; context.Title = Path.GetFileName(path); - context.IsBusy = false; + + _ = Task.Run(() => + { + _ = _panel.WaitForFontSent(); + context.IsBusy = false; + }); } public void Cleanup() diff --git a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs index de3c425..af593d3 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs @@ -26,6 +26,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Threading; namespace QuickLook.Plugin.FontViewer; @@ -34,6 +35,7 @@ public class WebfontPanel : WebpagePanel protected const string _resourcePrefix = "QuickLook.Plugin.FontViewer.Resources."; protected internal static readonly Dictionary _resources = []; protected byte[] _homePage; + protected ObservableFileStream _fontStream = null; static WebfontPanel() { @@ -153,10 +155,11 @@ public class WebfontPanel : WebpagePanel if (File.Exists(localPath)) { - var fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); + var fileStream = new ObservableFileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); var response = _webView.CoreWebView2.Environment.CreateWebResourceResponse( fileStream, 200, "OK", MimeTypes.GetContentType()); args.Response = response; + _fontStream = fileStream; // Only the font request will set this } } } @@ -168,6 +171,19 @@ public class WebfontPanel : WebpagePanel } } + public bool WaitForFontSent() + { + bool timeout = SpinWait.SpinUntil( + () => _fontStream is not null && _fontStream.IsEndOfStream, + TimeSpan.FromSeconds(1.5d) // The prediction is MAX 100MB + ); + + // Only when the `IsEndOfStream` is true + // Delay 15ms per MB for webview2 to render the font + if (timeout) Thread.Sleep(15 * (int)(_fontStream.Position / 1_048_576)); + return timeout; + } + public static bool ContainsKey(string key) { return _resources.ContainsKey(key);