Editor optimizations

This commit is contained in:
Burak Kaan Köse
2026-02-08 10:35:24 +01:00
parent ad9b94d407
commit 22c6452227
4 changed files with 63 additions and 56 deletions
@@ -133,6 +133,7 @@ public sealed partial class WebViewEditorControl : Control, IDisposable
private const string PART_WebView = "WebView";
private WebView2 _chromium = null!;
private bool _disposedValue;
private bool? _lastAppliedDarkTheme;
private readonly TaskCompletionSource<bool> _domLoadedTask = new();
public WebViewEditorControl()
@@ -153,8 +154,9 @@ public sealed partial class WebViewEditorControl : Control, IDisposable
private async Task InitializeComponent()
{
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation");
WebViewExtensions.EnsureWebView2Environment();
_chromium.CoreWebView2Initialized -= ChromiumInitialized;
_chromium.CoreWebView2Initialized += ChromiumInitialized;
await _chromium.EnsureCoreWebView2Async();
@@ -218,7 +220,7 @@ public sealed partial class WebViewEditorControl : Control, IDisposable
int composerFontSize = _preferencesService.ComposerFontSize;
var readerFont = _preferencesService.ReaderFont;
int readerFontSize = _preferencesService.ReaderFontSize;
return await _chromium.ExecuteScriptFunctionAsync("initializeJodit", false,
return await _chromium.ExecuteScriptFunctionAsync("initializeJodit",
JsonSerializer.Serialize(fonts, BasicTypesJsonContext.Default.ListString),
JsonSerializer.Serialize(composerFont, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(composerFontSize, BasicTypesJsonContext.Default.Int32),
@@ -233,16 +235,24 @@ public sealed partial class WebViewEditorControl : Control, IDisposable
_chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
_chromium.Source = new Uri("https://app.editor/editor.html");
_chromium.CoreWebView2.DOMContentLoaded -= DomLoaded;
_chromium.CoreWebView2.DOMContentLoaded += DomLoaded;
_chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived;
_chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived;
}
public async Task UpdateEditorThemeAsync()
public async Task UpdateEditorThemeAsync(bool force = false)
{
await _domLoadedTask.Task;
if (IsEditorDarkMode)
var isDark = IsEditorDarkMode;
if (!force && _lastAppliedDarkTheme == isDark) return;
_lastAppliedDarkTheme = isDark;
if (isDark)
{
_chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
await _chromium.ExecuteScriptFunctionSafeAsync("SetDarkEditor");
@@ -5,16 +5,34 @@ namespace Wino.Mail.WinUI.Extensions;
public static class WebViewExtensions
{
private static bool _environmentInitialized;
/// <summary>
/// Sets WebView2 environment variables once per process.
/// Must be called before any WebView2 is initialized.
/// </summary>
public static void EnsureWebView2Environment()
{
if (_environmentInitialized) return;
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS",
"--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
_environmentInitialized = true;
}
/// <summary>
/// Executes a script function in the WebView2 control.
/// </summary>
/// <param name="isChromiumDisposed">Weird parameter that needed in mailRendering page. TODO: should be reconsidered.</param>
/// <param name="parameters">Parameters should be serialized to json</param>
public static async Task<string> ExecuteScriptFunctionAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string functionName, bool isChromiumDisposed = false, params string[] parameters)
public static async Task<string> ExecuteScriptFunctionAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string functionName, params string[] parameters)
{
if (Chromium?.CoreWebView2 == null) return string.Empty;
string script = functionName + "(" + string.Join(", ", parameters) + ");";
return isChromiumDisposed ? string.Empty : await Chromium.ExecuteScriptAsync(script);
return await Chromium.ExecuteScriptAsync(script);
}
public static async Task<string> ExecuteScriptFunctionSafeAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string functionName, params string[] parameters)
@@ -24,14 +24,12 @@ public sealed partial class EventDetailsPage : EventDetailsPageAbstract,
{
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>()!;
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
private bool isChromiumDisposed = false;
public EventDetailsPage()
{
InitializeComponent();
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
WebViewExtensions.EnsureWebView2Environment();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
@@ -91,11 +89,11 @@ public sealed partial class EventDetailsPage : EventDetailsPageAbstract,
if (string.IsNullOrEmpty(description))
{
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed, JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML", JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
}
else
{
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed,
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML",
JsonSerializer.Serialize(description, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(true, BasicTypesJsonContext.Default.Boolean));
}
@@ -138,8 +136,6 @@ public sealed partial class EventDetailsPage : EventDetailsPageAbstract,
EventDetailsWebView.CoreWebView2.NewWindowRequested -= WindowRequested;
}
isChromiumDisposed = true;
EventDetailsWebView.Close();
}
@@ -150,34 +146,23 @@ public sealed partial class EventDetailsPage : EventDetailsPageAbstract,
if (ViewModel.IsDarkWebviewRenderer)
{
EventDetailsWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
await InvokeScriptSafeAsync("SetDarkEditor();");
await EventDetailsWebView.ExecuteScriptSafeAsync("SetDarkEditor();");
}
else
{
EventDetailsWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
await InvokeScriptSafeAsync("SetLightEditor();");
await EventDetailsWebView.ExecuteScriptSafeAsync("SetLightEditor();");
}
}
private async Task UpdateReaderFontPropertiesAsync()
{
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontSize", isChromiumDisposed, JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontSize", JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
var fontName = _preferencesService.ReaderFont;
fontName += ", sans-serif";
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontFamily", isChromiumDisposed, JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
}
private async Task<string> InvokeScriptSafeAsync(string function)
{
try
{
return await EventDetailsWebView.ExecuteScriptAsync(function);
}
catch (Exception) { }
return string.Empty;
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontFamily", JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
}
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
@@ -32,18 +32,16 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>()!;
private bool isRenderingInProgress = false;
private bool? _lastAppliedDarkTheme;
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
private bool isChromiumDisposed = false;
public WebView2 GetWebView() => Chromium;
public MailRenderingPage()
{
InitializeComponent();
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
WebViewExtensions.EnsureWebView2Environment();
ViewModel.DirectPrintFuncAsync = DirectPrintAsync;
@@ -81,17 +79,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
await UpdateEditorThemeAsync();
}
private async Task<string> InvokeScriptSafeAsync(string function)
{
try
{
return await Chromium.ExecuteScriptAsync(function);
}
catch (Exception) { }
return string.Empty;
}
private async Task RenderInternalAsync(string htmlBody)
{
isRenderingInProgress = true;
@@ -103,12 +90,12 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
if (string.IsNullOrEmpty(htmlBody))
{
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed, JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
}
else
{
var shouldLinkifyText = ViewModel.CurrentRenderModel?.MailRenderingOptions?.RenderPlaintextLinks ?? true;
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed,
await Chromium.ExecuteScriptFunctionAsync("RenderHTML",
JsonSerializer.Serialize(htmlBody, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(shouldLinkifyText, BasicTypesJsonContext.Default.Boolean));
}
@@ -131,14 +118,17 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
async void IRecipient<HtmlRenderingRequested>.Receive(HtmlRenderingRequested message)
{
// Ensure WebView2 is fully initialized before first render.
// OnNavigatedTo starts initialization fire-and-forget; this await
// guarantees the core is ready before we invoke any script.
await Chromium.EnsureCoreWebView2Async();
if (message == null || string.IsNullOrEmpty(message.HtmlBody))
{
await RenderInternalAsync(string.Empty);
return;
}
await Chromium.EnsureCoreWebView2Async();
await RenderInternalAsync(message.HtmlBody);
}
@@ -168,8 +158,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
Chromium.CoreWebView2.NewWindowRequested -= WindowRequested;
}
isChromiumDisposed = true;
Chromium.Close();
GC.Collect();
}
@@ -261,23 +249,29 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
{
await DOMLoadedTask.Task;
if (ViewModel.IsDarkWebviewRenderer)
var isDark = ViewModel.IsDarkWebviewRenderer;
if (_lastAppliedDarkTheme == isDark) return;
_lastAppliedDarkTheme = isDark;
if (isDark)
{
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
await InvokeScriptSafeAsync("SetDarkEditor();");
await Chromium.ExecuteScriptSafeAsync("SetDarkEditor();");
}
else
{
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
await InvokeScriptSafeAsync("SetLightEditor();");
await Chromium.ExecuteScriptSafeAsync("SetLightEditor();");
}
}
private async Task UpdateReaderFontPropertiesAsync()
{
await Chromium.ExecuteScriptFunctionAsync("ChangeFontSize", isChromiumDisposed, JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
await Chromium.ExecuteScriptFunctionAsync("ChangeFontSize", JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
// Prepare font family name with fallback to sans-serif by default.
var fontName = _preferencesService.ReaderFont;
@@ -285,7 +279,7 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
// If font family name is not supported by the browser, fallback to sans-serif.
fontName += ", sans-serif";
await Chromium.ExecuteScriptFunctionAsync("ChangeFontFamily", isChromiumDisposed, JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
await Chromium.ExecuteScriptFunctionAsync("ChangeFontFamily", JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
}
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)