Implemented initial version for popping out window for rendering and compose pages
This commit is contained in:
@@ -23,6 +23,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
private const string MailOperationTemplateKey = "OperationCommandBarMailOperationTemplate";
|
||||
private const string FolderOperationTemplateKey = "OperationCommandBarFolderOperationTemplate";
|
||||
private const string AIActionsTemplateKey = "OperationCommandBarAIActionsTemplate";
|
||||
private const string PopOutTemplateKey = "OperationCommandBarThemeToggleTemplate";
|
||||
private const string ThemeToggleTemplateKey = "OperationCommandBarThemeToggleTemplate";
|
||||
private const string SeparatorTemplateKey = "OperationCommandBarSeparatorTemplate";
|
||||
|
||||
@@ -47,7 +48,11 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
[GeneratedDependencyProperty]
|
||||
public partial bool IsEditorThemeToggleVisible { get; set; }
|
||||
|
||||
[GeneratedDependencyProperty]
|
||||
public partial bool IsPopOutButtonVisible { get; set; }
|
||||
|
||||
public event EventHandler<bool>? AIActionsEnabledChanged;
|
||||
public event EventHandler? PopOutClicked;
|
||||
|
||||
public OperationCommandBar()
|
||||
{
|
||||
@@ -58,7 +63,6 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Auto;
|
||||
|
||||
Loaded += OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
DynamicOverflowItemsChanging += OperationCommandBar_DynamicOverflowItemsChanging;
|
||||
}
|
||||
|
||||
@@ -100,14 +104,14 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
RefreshCommands();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
partial void OnIsPopOutButtonVisibleChanged(bool newValue)
|
||||
{
|
||||
RefreshCommands();
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ClearGeneratedCommands();
|
||||
RefreshCommands();
|
||||
}
|
||||
|
||||
private void OperationCommandBar_DynamicOverflowItemsChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
|
||||
@@ -216,6 +220,11 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
PrimaryCommands.Add(CreateAIActionsToggleButton());
|
||||
}
|
||||
|
||||
if (IsPopOutButtonVisible)
|
||||
{
|
||||
PrimaryCommands.Add(CreatePopOutButton());
|
||||
}
|
||||
|
||||
if (IsEditorThemeToggleVisible)
|
||||
{
|
||||
PrimaryCommands.Add(CreateThemeToggleButton());
|
||||
@@ -266,6 +275,7 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
case AppBarButton button:
|
||||
button.Click -= OperationButton_Click;
|
||||
button.Click -= ThemeButton_Click;
|
||||
button.Click -= PopOutButton_Click;
|
||||
break;
|
||||
case AppBarToggleButton toggleButton:
|
||||
toggleButton.ClearValue(AppBarToggleButton.IsCheckedProperty);
|
||||
@@ -356,6 +366,16 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
return button;
|
||||
}
|
||||
|
||||
private AppBarButton CreatePopOutButton()
|
||||
{
|
||||
var button = (AppBarButton)LoadCommandBarElementTemplate(
|
||||
PopOutTemplateKey,
|
||||
new OperationCommandBarThemeItemViewModel(Translator.Buttons_PopOut, WinoIconGlyph.OpenInNewWindow));
|
||||
|
||||
button.Click += PopOutButton_Click;
|
||||
return button;
|
||||
}
|
||||
|
||||
private void OperationButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is AppBarButton button && button.Tag is IMenuOperation operation)
|
||||
@@ -369,6 +389,11 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
IsEditorThemeDark = !IsEditorThemeDark;
|
||||
}
|
||||
|
||||
private void PopOutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PopOutClicked?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private object? FindTemplateResource(string key)
|
||||
{
|
||||
if (TryGetResourceRecursive(Resources, key, out var resource))
|
||||
@@ -430,6 +455,11 @@ public sealed partial class OperationCommandBar : CommandBar
|
||||
: CommandBarOverflowButtonVisibility.Auto;
|
||||
}
|
||||
|
||||
public void InvalidateCommands()
|
||||
{
|
||||
RefreshCommands();
|
||||
}
|
||||
|
||||
private sealed class SeparatorCommandBarItemViewModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winuiex:WindowEx
|
||||
x:Class="Wino.Mail.WinUI.HostedContentPopoutWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Padding="4,32,4,4"
|
||||
Background="{ThemeResource WinoApplicationBackgroundColor}">
|
||||
<Grid x:Name="ContentHost">
|
||||
<Grid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
<PopupThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Grid.ChildrenTransitions>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Wino.Mail.WinUI;
|
||||
|
||||
public sealed partial class HostedContentPopoutWindow : WindowEx
|
||||
{
|
||||
private readonly Action _closedCallback;
|
||||
|
||||
public HostedPopoutDescriptor Descriptor { get; }
|
||||
|
||||
public HostedContentPopoutWindow(HostedPopoutDescriptor descriptor, Action closedCallback)
|
||||
{
|
||||
Descriptor = descriptor;
|
||||
_closedCallback = closedCallback;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Title = descriptor.Title;
|
||||
Width = descriptor.Width;
|
||||
Height = descriptor.Height;
|
||||
MinWidth = descriptor.MinWidth;
|
||||
MinHeight = descriptor.MinHeight;
|
||||
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
this.SetIcon("Assets/Wino_Icon.ico");
|
||||
this.CenterOnScreen();
|
||||
|
||||
Closed += OnClosed;
|
||||
}
|
||||
|
||||
public void SetHostedContent(FrameworkElement content)
|
||||
{
|
||||
ContentHost.Children.Clear();
|
||||
ContentHost.Children.Add(content);
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
Closed -= OnClosed;
|
||||
_closedCallback();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Wino.Mail.WinUI;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
|
||||
namespace Wino.Mail.WinUI.Interfaces;
|
||||
|
||||
public interface IHostedPopoutSource
|
||||
{
|
||||
bool CanPopOutCurrentContent();
|
||||
FrameworkElement? GetCurrentHostedContent();
|
||||
HostedPopoutDescriptor CreatePopoutDescriptor(IPopoutClient client);
|
||||
FrameworkElement DetachHostedContent();
|
||||
void OnHostedContentPoppedOut(FrameworkElement content, HostedContentPopoutWindow window, HostedPopoutDescriptor descriptor);
|
||||
void OnHostedPopoutClosed(FrameworkElement content, HostedPopoutDescriptor descriptor);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
|
||||
namespace Wino.Mail.WinUI.Interfaces;
|
||||
|
||||
public interface IPopoutClient
|
||||
{
|
||||
bool SupportsPopOut { get; }
|
||||
event EventHandler<PopOutRequestedEventArgs> PopOutRequested;
|
||||
event EventHandler<PopoutHostActionRequestedEventArgs> HostActionRequested;
|
||||
HostedPopoutDescriptor GetPopoutDescriptor();
|
||||
void OnPopoutStateChanged(bool isPoppedOut);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Wino.Mail.WinUI.Models;
|
||||
|
||||
public sealed record HostedPopoutDescriptor(
|
||||
string WindowName,
|
||||
string Title,
|
||||
double Width,
|
||||
double Height,
|
||||
double MinWidth,
|
||||
double MinHeight,
|
||||
string ContentKind);
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Mail.WinUI.Models;
|
||||
|
||||
public sealed class PopOutRequestedEventArgs : EventArgs
|
||||
{
|
||||
public static PopOutRequestedEventArgs Default { get; } = new();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Mail.WinUI.Models;
|
||||
|
||||
public enum PopoutHostActionKind
|
||||
{
|
||||
CloseHostedInstance,
|
||||
PopOutNextNavigation
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Mail.WinUI.Models;
|
||||
|
||||
public sealed class PopoutHostActionRequestedEventArgs : EventArgs
|
||||
{
|
||||
public PopoutHostActionRequestedEventArgs(PopoutHostActionKind actionKind, Type? targetPageType = null, Guid? targetMailUniqueId = null)
|
||||
{
|
||||
ActionKind = actionKind;
|
||||
TargetPageType = targetPageType;
|
||||
TargetMailUniqueId = targetMailUniqueId;
|
||||
}
|
||||
|
||||
public PopoutHostActionKind ActionKind { get; }
|
||||
public Type? TargetPageType { get; }
|
||||
public Guid? TargetMailUniqueId { get; }
|
||||
}
|
||||
@@ -3,5 +3,6 @@ namespace Wino.Mail.WinUI.Models;
|
||||
public enum WinoWindowKind
|
||||
{
|
||||
Shell,
|
||||
Welcome
|
||||
Welcome,
|
||||
HostedPopout
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Mail.WinUI.Interfaces;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
|
||||
namespace Wino.Mail.WinUI.Services;
|
||||
|
||||
public static class HostedContentPopoutCoordinator
|
||||
{
|
||||
public static async Task<bool> PopOutCurrentContentAsync(IHostedPopoutSource source)
|
||||
{
|
||||
if (!source.CanPopOutCurrentContent())
|
||||
return false;
|
||||
|
||||
var content = source.GetCurrentHostedContent();
|
||||
if (content is not FrameworkElement frameworkElement || frameworkElement is not IPopoutClient client || !client.SupportsPopOut)
|
||||
return false;
|
||||
|
||||
var descriptor = source.CreatePopoutDescriptor(client);
|
||||
var windowManager = WinoApplication.Current.Services.GetRequiredService<IWinoWindowManager>();
|
||||
|
||||
if (windowManager.GetWindow(WinoWindowKind.HostedPopout, descriptor.WindowName) is HostedContentPopoutWindow existingWindow)
|
||||
{
|
||||
windowManager.ActivateWindow(existingWindow);
|
||||
return false;
|
||||
}
|
||||
|
||||
var detachedContent = source.DetachHostedContent();
|
||||
if (detachedContent is IPopoutClient detachedClient)
|
||||
{
|
||||
detachedClient.OnPopoutStateChanged(true);
|
||||
}
|
||||
|
||||
HostedContentPopoutWindow? popoutWindow = null;
|
||||
|
||||
popoutWindow = (HostedContentPopoutWindow)windowManager.CreateWindow(
|
||||
WinoWindowKind.HostedPopout,
|
||||
() => new HostedContentPopoutWindow(descriptor, () =>
|
||||
{
|
||||
source.OnHostedPopoutClosed(detachedContent, descriptor);
|
||||
}),
|
||||
descriptor.WindowName);
|
||||
|
||||
popoutWindow.SetHostedContent(detachedContent);
|
||||
source.OnHostedContentPoppedOut(detachedContent, popoutWindow, descriptor);
|
||||
windowManager.ActivateWindow(popoutWindow);
|
||||
|
||||
var themeService = WinoApplication.Current.Services.GetService<INewThemeService>();
|
||||
if (themeService != null)
|
||||
{
|
||||
await themeService.ApplyThemeToActiveWindowAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -381,7 +381,10 @@ public class NavigationService : NavigationServiceBase, INavigationService
|
||||
&& parameter is MailItemViewModel mailItemViewModel
|
||||
&& page != WinoPage.ComposePage)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ReaderItemRefreshRequestedEvent(mailItemViewModel));
|
||||
if (listingFrame.Content is MailRenderingPage renderingPage)
|
||||
{
|
||||
_ = renderingPage.RefreshMailItemAsync(mailItemViewModel);
|
||||
}
|
||||
}
|
||||
else if (listingFrame.Content != null
|
||||
&& listingFrame.Content.GetType() == GetPageType(WinoPage.ComposePage)
|
||||
@@ -390,7 +393,10 @@ public class NavigationService : NavigationServiceBase, INavigationService
|
||||
{
|
||||
// ComposePage is already active and we're switching to another draft.
|
||||
// Reuse existing ComposePage and WebView2 instead of navigating.
|
||||
WeakReferenceMessenger.Default.Send(new ReaderItemRefreshRequestedEvent(composeDraftViewModel));
|
||||
if (listingFrame.Content is ComposePage composePage)
|
||||
{
|
||||
_ = composePage.RefreshDraftAsync(composeDraftViewModel);
|
||||
}
|
||||
}
|
||||
else if (listingFrame.Content != null
|
||||
&& listingFrame.Content.GetType() == GetPageType(WinoPage.IdlePage)
|
||||
|
||||
@@ -172,6 +172,14 @@
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarButton
|
||||
Click="PopOutButton_Click"
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_PopOut}"
|
||||
Visibility="{x:Bind GetPopOutButtonVisibility(), Mode=OneWay}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="OpenInNewWindow" />
|
||||
</AppBarButton.Icon>
|
||||
</AppBarButton>
|
||||
<AppBarButton Click="ToggleEditorThemeClicked" ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(WebViewEditor.IsEditorDarkMode), Mode=OneWay}">
|
||||
<AppBarButton.Icon>
|
||||
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(WebViewEditor.IsEditorDarkMode), Mode=OneWay}" />
|
||||
|
||||
@@ -25,6 +25,8 @@ using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Messages;
|
||||
using Wino.Mail.WinUI.Controls;
|
||||
using Wino.Mail.WinUI.Extensions;
|
||||
using Wino.Mail.WinUI.Interfaces;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
using Wino.Messaging.Client.Mails;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
using Wino.Views.Abstract;
|
||||
@@ -33,13 +35,19 @@ namespace Wino.Views.Mail;
|
||||
|
||||
public sealed partial class ComposePage : ComposePageAbstract,
|
||||
IAiHtmlActionHost,
|
||||
IRecipient<CreateNewComposeMailRequested>,
|
||||
IRecipient<ApplicationThemeChanged>,
|
||||
IRecipient<ReaderItemRefreshRequestedEvent>
|
||||
IPopoutClient,
|
||||
IRecipient<ApplicationThemeChanged>
|
||||
{
|
||||
private bool _isPoppedOut;
|
||||
|
||||
public bool SupportsPopOut => !_isPoppedOut;
|
||||
public event EventHandler<PopOutRequestedEventArgs>? PopOutRequested;
|
||||
public event EventHandler<PopoutHostActionRequestedEventArgs>? HostActionRequested;
|
||||
|
||||
public WebView2 GetWebView() => WebViewEditor.GetUnderlyingWebView();
|
||||
|
||||
public Visibility GetAiActionsToggleVisibility(bool isHidden) => isHidden ? Visibility.Collapsed : Visibility.Visible;
|
||||
public Visibility GetPopOutButtonVisibility() => SupportsPopOut ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public Visibility GetAiActionsPanelVisibility(bool? isChecked, bool isHidden)
|
||||
=> !isHidden && isChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
@@ -49,6 +57,28 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
public ComposePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
ViewModel.CloseRequested += ViewModel_CloseRequested;
|
||||
}
|
||||
|
||||
public HostedPopoutDescriptor GetPopoutDescriptor()
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(ViewModel.Subject) ? Translator.Draft : ViewModel.Subject;
|
||||
var draftId = ViewModel.CurrentMailDraftItem?.MailCopy?.UniqueId.ToString("N") ?? title;
|
||||
|
||||
return new HostedPopoutDescriptor(
|
||||
$"compose-{draftId}",
|
||||
title,
|
||||
1180,
|
||||
860,
|
||||
760,
|
||||
600,
|
||||
nameof(ComposePage));
|
||||
}
|
||||
|
||||
public void OnPopoutStateChanged(bool isPoppedOut)
|
||||
{
|
||||
_isPoppedOut = isPoppedOut;
|
||||
Bindings.Update();
|
||||
}
|
||||
|
||||
public WinoIconGlyph GetEditorThemeIcon(bool isDarkMode) => isDarkMode ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;
|
||||
@@ -277,11 +307,7 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
_disposables.Add(WebViewEditor);
|
||||
|
||||
ViewModel.GetHTMLBodyFunction = WebViewEditor.GetHtmlBodyAsync;
|
||||
}
|
||||
|
||||
async void IRecipient<CreateNewComposeMailRequested>.Receive(CreateNewComposeMailRequested message)
|
||||
{
|
||||
await WebViewEditor.RenderHtmlAsync(message.RenderModel.RenderHtml);
|
||||
ViewModel.RenderHtmlBodyAsyncFunc = WebViewEditor.RenderHtmlAsync;
|
||||
}
|
||||
|
||||
private void ShowCCBCCClicked(object sender, RoutedEventArgs e)
|
||||
@@ -289,6 +315,16 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
ViewModel.IsCCBCCVisible = true;
|
||||
}
|
||||
|
||||
private void PopOutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PopOutRequested?.Invoke(this, PopOutRequestedEventArgs.Default);
|
||||
}
|
||||
|
||||
private void ViewModel_CloseRequested(object? sender, EventArgs e)
|
||||
{
|
||||
HostActionRequested?.Invoke(this, new PopoutHostActionRequestedEventArgs(PopoutHostActionKind.CloseHostedInstance));
|
||||
}
|
||||
|
||||
private async void ComposeAiActionsToggleButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await ComposeAiActionsPanel.RefreshAvailabilityAsync();
|
||||
@@ -333,12 +369,13 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
WebViewEditor.IsEditorDarkMode = message.IsUnderlyingThemeDark;
|
||||
}
|
||||
|
||||
void IRecipient<ReaderItemRefreshRequestedEvent>.Receive(ReaderItemRefreshRequestedEvent message)
|
||||
public async Task RefreshDraftAsync(MailItemViewModel draftMailItemViewModel)
|
||||
{
|
||||
if (message.MailItemViewModel == null || !message.MailItemViewModel.IsDraft) return;
|
||||
if (draftMailItemViewModel == null || !draftMailItemViewModel.IsDraft) return;
|
||||
|
||||
// Reset the initial focus flag so ToBox gets focus for the new draft.
|
||||
isInitialFocusHandled = false;
|
||||
await ViewModel.RefreshDraftAsync(draftMailItemViewModel);
|
||||
}
|
||||
|
||||
private void ImportanceClicked(object sender, RoutedEventArgs e)
|
||||
@@ -423,6 +460,7 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
FocusManager.GotFocus -= GlobalFocusManagerGotFocus;
|
||||
ComposeAiActionsPanel.CancelPendingOperation();
|
||||
await ViewModel.UpdateMimeChangesAsync();
|
||||
ViewModel.RenderHtmlBodyAsyncFunc = null;
|
||||
|
||||
DisposeDisposables();
|
||||
}
|
||||
@@ -496,18 +534,14 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<CreateNewComposeMailRequested>(this);
|
||||
WeakReferenceMessenger.Default.Register<ApplicationThemeChanged>(this);
|
||||
WeakReferenceMessenger.Default.Register<ReaderItemRefreshRequestedEvent>(this);
|
||||
}
|
||||
|
||||
protected override void UnregisterRecipients()
|
||||
{
|
||||
base.UnregisterRecipients();
|
||||
|
||||
WeakReferenceMessenger.Default.Unregister<CreateNewComposeMailRequested>(this);
|
||||
WeakReferenceMessenger.Default.Unregister<ApplicationThemeChanged>(this);
|
||||
WeakReferenceMessenger.Default.Unregister<ReaderItemRefreshRequestedEvent>(this);
|
||||
}
|
||||
|
||||
// TODO: Save mime on closing the app.
|
||||
|
||||
@@ -176,12 +176,11 @@
|
||||
<coreControls:OperationCommandBar
|
||||
HorizontalAlignment="Left"
|
||||
DefaultLabelPosition="Collapsed"
|
||||
IsEnabled="{x:Bind helpers:XamlHelpers.CountToBooleanConverter(ViewModel.MailCollection.SelectedItemsCount), Mode=OneWay}"
|
||||
IsAIActionsPaneToggleVisible="False"
|
||||
IsEnabled="{x:Bind helpers:XamlHelpers.CountToBooleanConverter(ViewModel.MailCollection.SelectedItemsCount), Mode=OneWay}"
|
||||
ItemInvokedCommand="{x:Bind ViewModel.ExecuteTopBarActionCommand}"
|
||||
MenuItems="{x:Bind ViewModel.ActionItems, Mode=OneWay}"
|
||||
OverflowButtonVisibility="Auto">
|
||||
</coreControls:OperationCommandBar>
|
||||
OverflowButtonVisibility="Auto" />
|
||||
</Grid>
|
||||
|
||||
<!-- Pivot + Sync + Multi Select -->
|
||||
@@ -359,7 +358,7 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="-6"
|
||||
CanDragItems="True"
|
||||
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
||||
IsItemClickEnabled="True"
|
||||
@@ -377,7 +376,7 @@
|
||||
</listview:WinoListView.ItemContainerTransitions>
|
||||
<listview:WinoListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" GroupPadding="10,10,10,0" />
|
||||
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" GroupPadding="4,0,4,4" />
|
||||
</ItemsPanelTemplate>
|
||||
</listview:WinoListView.ItemsPanel>
|
||||
<listview:WinoListView.Resources>
|
||||
@@ -488,7 +487,7 @@
|
||||
<Grid
|
||||
x:Name="RenderingGrid"
|
||||
Grid.Column="1"
|
||||
Margin="7,0,0,0">
|
||||
Margin="0,3,0,0">
|
||||
<!-- Mail Rendering Frame -->
|
||||
<Frame x:Name="RenderingFrame" IsNavigationStackEnabled="False" />
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@ using Wino.Mail.ViewModels.Messages;
|
||||
using Wino.Mail.WinUI;
|
||||
using Wino.Mail.WinUI.Controls.ListView;
|
||||
using Wino.Mail.WinUI.Extensions;
|
||||
using Wino.Mail.WinUI.Helpers;
|
||||
using Wino.Mail.WinUI.Interfaces;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
using Wino.Mail.WinUI.Services;
|
||||
using Wino.MenuFlyouts.Context;
|
||||
using Wino.Messaging.Client.Mails;
|
||||
using Wino.Views.Abstract;
|
||||
@@ -48,12 +50,16 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
IRecipient<ActiveMailItemChangedEvent>,
|
||||
IRecipient<SelectMailItemContainerEvent>,
|
||||
IRecipient<DisposeRenderingFrameRequested>,
|
||||
IHostedPopoutSource,
|
||||
ITitleBarSearchHost
|
||||
{
|
||||
private const double RENDERING_COLUMN_MIN_WIDTH = 375;
|
||||
private const int SELECTION_SETTLE_DELAY_MS = 120;
|
||||
private const int RENDERING_FRAME_RELEASE_DELAY_MS = 2000;
|
||||
private int _idleNavigationRequestVersion = 0;
|
||||
private IPopoutClient? _activePopoutClient;
|
||||
private readonly Dictionary<FrameworkElement, HostedContentPopoutWindow> _hostedPopoutWindows = [];
|
||||
private PendingHostedPopoutNavigation? _pendingHostedPopoutNavigation;
|
||||
|
||||
private IStatePersistanceService StatePersistenceService { get; } = WinoApplication.Current.Services.GetService<IStatePersistanceService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||
private IKeyPressService KeyPressService { get; } = WinoApplication.Current.Services.GetService<IKeyPressService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||
@@ -69,6 +75,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
public MailListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
RenderingFrame.Navigated += RenderingFrame_Navigated;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
@@ -101,6 +108,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
base.OnNavigatedFrom(e);
|
||||
|
||||
InvalidatePendingIdleNavigation();
|
||||
DetachPopoutClient();
|
||||
|
||||
this.Bindings.StopTracking();
|
||||
|
||||
@@ -325,7 +333,10 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
PrepareRenderingPageWebViewTransition();
|
||||
|
||||
// Dispose existing HTML content from rendering page webview.
|
||||
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
|
||||
if (RenderingFrame.Content is MailRenderingPage renderingPage)
|
||||
{
|
||||
_ = renderingPage.ClearRenderedContentAsync();
|
||||
}
|
||||
}
|
||||
else if (IsComposingPageActive())
|
||||
{
|
||||
@@ -359,6 +370,55 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
private bool IsRenderingPageActive() => RenderingFrame.Content is MailRenderingPage;
|
||||
private bool IsComposingPageActive() => RenderingFrame.Content is ComposePage;
|
||||
|
||||
private void RenderingFrame_Navigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
AttachPopoutClient(RenderingFrame.Content as IPopoutClient);
|
||||
|
||||
if (_pendingHostedPopoutNavigation != null
|
||||
&& TryGetPendingHostedPopoutTarget(RenderingFrame.Content, _pendingHostedPopoutNavigation, out var hostedContent))
|
||||
{
|
||||
_ = ContinuePendingHostedPopoutNavigationAsync(hostedContent, _pendingHostedPopoutNavigation);
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachPopoutClient(IPopoutClient? client)
|
||||
{
|
||||
if (ReferenceEquals(_activePopoutClient, client))
|
||||
return;
|
||||
|
||||
DetachPopoutClient();
|
||||
|
||||
_activePopoutClient = client;
|
||||
if (_activePopoutClient != null)
|
||||
{
|
||||
_activePopoutClient.PopOutRequested += ActivePopoutClient_PopOutRequested;
|
||||
_activePopoutClient.HostActionRequested += ActivePopoutClient_HostActionRequested;
|
||||
}
|
||||
}
|
||||
|
||||
private void DetachPopoutClient()
|
||||
{
|
||||
if (_activePopoutClient != null)
|
||||
{
|
||||
_activePopoutClient.PopOutRequested -= ActivePopoutClient_PopOutRequested;
|
||||
_activePopoutClient.HostActionRequested -= ActivePopoutClient_HostActionRequested;
|
||||
_activePopoutClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ActivePopoutClient_PopOutRequested(object? sender, PopOutRequestedEventArgs e)
|
||||
{
|
||||
await HostedContentPopoutCoordinator.PopOutCurrentContentAsync(this);
|
||||
}
|
||||
|
||||
private void ActivePopoutClient_HostActionRequested(object? sender, PopoutHostActionRequestedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement content)
|
||||
{
|
||||
HandleHostedClientAction(content, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidatePendingIdleNavigation()
|
||||
{
|
||||
unchecked
|
||||
@@ -378,7 +438,10 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
|
||||
if (IsRenderingPageActive())
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
|
||||
if (RenderingFrame.Content is MailRenderingPage renderingPage)
|
||||
{
|
||||
_ = renderingPage.ClearRenderedContentAsync();
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(RENDERING_FRAME_RELEASE_DELAY_MS);
|
||||
@@ -926,4 +989,125 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool CanPopOutCurrentContent()
|
||||
{
|
||||
return RenderingFrame.Content is FrameworkElement
|
||||
&& RenderingFrame.Content is IPopoutClient client
|
||||
&& client.SupportsPopOut;
|
||||
}
|
||||
|
||||
public FrameworkElement? GetCurrentHostedContent()
|
||||
{
|
||||
return RenderingFrame.Content as FrameworkElement;
|
||||
}
|
||||
|
||||
public HostedPopoutDescriptor CreatePopoutDescriptor(IPopoutClient client)
|
||||
{
|
||||
return client.GetPopoutDescriptor();
|
||||
}
|
||||
|
||||
public FrameworkElement DetachHostedContent()
|
||||
{
|
||||
if (RenderingFrame.Content is not FrameworkElement content)
|
||||
throw new InvalidOperationException("RenderingFrame does not host detachable content.");
|
||||
|
||||
InvalidatePendingIdleNavigation();
|
||||
DetachPopoutClient();
|
||||
RenderingFrame.Content = null;
|
||||
ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.None);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public void OnHostedContentPoppedOut(FrameworkElement content, HostedContentPopoutWindow window, HostedPopoutDescriptor descriptor)
|
||||
{
|
||||
if (content is IPopoutClient client)
|
||||
{
|
||||
client.HostActionRequested -= ActivePopoutClient_HostActionRequested;
|
||||
client.HostActionRequested += ActivePopoutClient_HostActionRequested;
|
||||
}
|
||||
|
||||
_hostedPopoutWindows[content] = window;
|
||||
_ = ViewModel.MailCollection.UnselectAllAsync();
|
||||
UpdateAdaptiveness();
|
||||
}
|
||||
|
||||
public void OnHostedPopoutClosed(FrameworkElement content, HostedPopoutDescriptor descriptor)
|
||||
{
|
||||
if (_hostedPopoutWindows.Remove(content) && content is IPopoutClient hostedClient)
|
||||
{
|
||||
hostedClient.HostActionRequested -= ActivePopoutClient_HostActionRequested;
|
||||
}
|
||||
|
||||
if (_pendingHostedPopoutNavigation?.SourceContent == content)
|
||||
{
|
||||
_pendingHostedPopoutNavigation = null;
|
||||
}
|
||||
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (content is IPopoutClient client)
|
||||
{
|
||||
client.OnPopoutStateChanged(false);
|
||||
}
|
||||
|
||||
WindowCleanupHelper.CleanupObject(content);
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleHostedClientAction(FrameworkElement content, PopoutHostActionRequestedEventArgs args)
|
||||
{
|
||||
if (!_hostedPopoutWindows.TryGetValue(content, out var hostedWindow))
|
||||
return;
|
||||
|
||||
switch (args.ActionKind)
|
||||
{
|
||||
case PopoutHostActionKind.CloseHostedInstance:
|
||||
hostedWindow.Close();
|
||||
break;
|
||||
case PopoutHostActionKind.PopOutNextNavigation when args.TargetPageType != null:
|
||||
_pendingHostedPopoutNavigation = new PendingHostedPopoutNavigation(content, hostedWindow, args.TargetPageType, args.TargetMailUniqueId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetPendingHostedPopoutTarget(object? currentContent, PendingHostedPopoutNavigation pendingHostedNavigation, out FrameworkElement hostedContent)
|
||||
{
|
||||
hostedContent = null!;
|
||||
|
||||
if (currentContent is not FrameworkElement currentFrameworkElement || currentFrameworkElement.GetType() != pendingHostedNavigation.TargetPageType)
|
||||
return false;
|
||||
|
||||
if (pendingHostedNavigation.TargetMailUniqueId.HasValue
|
||||
&& currentFrameworkElement is ComposePage composePage
|
||||
&& composePage.ViewModel.CurrentMailDraftItem?.MailCopy?.UniqueId != pendingHostedNavigation.TargetMailUniqueId.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hostedContent = currentFrameworkElement;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ContinuePendingHostedPopoutNavigationAsync(FrameworkElement content, PendingHostedPopoutNavigation pendingHostedNavigation)
|
||||
{
|
||||
if (!ReferenceEquals(_pendingHostedPopoutNavigation, pendingHostedNavigation))
|
||||
return;
|
||||
|
||||
_pendingHostedPopoutNavigation = null;
|
||||
|
||||
var didPopOut = await HostedContentPopoutCoordinator.PopOutCurrentContentAsync(this);
|
||||
|
||||
if (didPopOut)
|
||||
{
|
||||
pendingHostedNavigation.SourceWindow.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record PendingHostedPopoutNavigation(
|
||||
FrameworkElement SourceContent,
|
||||
HostedContentPopoutWindow SourceWindow,
|
||||
Type TargetPageType,
|
||||
Guid? TargetMailUniqueId);
|
||||
}
|
||||
|
||||
@@ -272,8 +272,10 @@
|
||||
IsAIActionsPaneToggleVisible="{x:Bind GetAiActionsToggleVisible(ViewModel.PreferencesService.IsAiActionsPanelHidden), Mode=OneWay}"
|
||||
IsEditorThemeDark="{x:Bind ViewModel.IsDarkWebviewRenderer, Mode=TwoWay}"
|
||||
IsEditorThemeToggleVisible="True"
|
||||
IsPopOutButtonVisible="{x:Bind SupportsPopOut, Mode=OneWay}"
|
||||
ItemInvokedCommand="{x:Bind ViewModel.OperationClickedCommand}"
|
||||
MenuItems="{x:Bind ViewModel.MenuItems, Mode=OneWay}">
|
||||
MenuItems="{x:Bind ViewModel.MenuItems, Mode=OneWay}"
|
||||
PopOutClicked="RendererCommandBar_PopOutClicked">
|
||||
<coreControls:OperationCommandBar.Content>
|
||||
<Grid Padding="0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
||||
@@ -16,9 +16,12 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Printing;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Models;
|
||||
using Wino.Mail.WinUI;
|
||||
using Wino.Mail.WinUI.Controls;
|
||||
using Wino.Mail.WinUI.Extensions;
|
||||
using Wino.Mail.WinUI.Interfaces;
|
||||
using Wino.Mail.WinUI.Models;
|
||||
using Wino.Messaging.Client.Mails;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
using Wino.Views.Abstract;
|
||||
@@ -27,8 +30,7 @@ namespace Wino.Views.Mail;
|
||||
|
||||
public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
IAiHtmlActionHost,
|
||||
IRecipient<HtmlRenderingRequested>,
|
||||
IRecipient<CancelRenderingContentRequested>,
|
||||
IPopoutClient,
|
||||
IRecipient<ApplicationThemeChanged>
|
||||
{
|
||||
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>()!;
|
||||
@@ -39,6 +41,11 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
private bool? _lastAppliedDarkTheme;
|
||||
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
|
||||
private string _currentRenderedHtml = string.Empty;
|
||||
private bool _isPoppedOut;
|
||||
|
||||
public bool SupportsPopOut => !_isPoppedOut;
|
||||
public event EventHandler<PopOutRequestedEventArgs>? PopOutRequested;
|
||||
public event EventHandler<PopoutHostActionRequestedEventArgs>? HostActionRequested;
|
||||
|
||||
public WebView2 GetWebView() => Chromium;
|
||||
public bool GetAiActionsToggleVisible(bool isHidden) => !isHidden;
|
||||
@@ -57,9 +64,34 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
{
|
||||
return Chromium.CoreWebView2.PrintToPdfAsync(path, null).AsTask();
|
||||
});
|
||||
ViewModel.RenderHtmlAsyncFunc = RenderInternalAsync;
|
||||
ViewModel.ClearRenderedHtmlAsyncFunc = ClearRenderedContentAsync;
|
||||
ViewModel.CloseRequested += ViewModel_CloseRequested;
|
||||
ViewModel.ComposeRequested += ViewModel_ComposeRequested;
|
||||
|
||||
}
|
||||
|
||||
public HostedPopoutDescriptor GetPopoutDescriptor()
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(ViewModel.Subject) ? Translator.MailItemNoSubject : ViewModel.Subject;
|
||||
var uniquePart = ViewModel.CurrentMailFileId?.ToString("N") ?? title;
|
||||
return new HostedPopoutDescriptor(
|
||||
$"mail-rendering-{uniquePart}",
|
||||
title,
|
||||
1080,
|
||||
780,
|
||||
640,
|
||||
480,
|
||||
nameof(MailRenderingPage));
|
||||
}
|
||||
|
||||
public void OnPopoutStateChanged(bool isPoppedOut)
|
||||
{
|
||||
_isPoppedOut = isPoppedOut;
|
||||
Bindings.Update();
|
||||
RendererCommandBar.InvalidateCommands();
|
||||
}
|
||||
|
||||
private async Task<PrintingResult> DirectPrintAsync(WebView2PrintSettingsModel settings)
|
||||
{
|
||||
if (Chromium.CoreWebView2 == null) return PrintingResult.Failed;
|
||||
@@ -132,20 +164,14 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
|
||||
private void DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
|
||||
|
||||
async void IRecipient<HtmlRenderingRequested>.Receive(HtmlRenderingRequested message)
|
||||
public async Task ClearRenderedContentAsync()
|
||||
{
|
||||
// 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 EnsureChromiumInitializedAsync();
|
||||
|
||||
if (message == null || string.IsNullOrEmpty(message.HtmlBody))
|
||||
if (!isRenderingInProgress)
|
||||
{
|
||||
await RenderInternalAsync(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
await RenderInternalAsync(message.HtmlBody);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
@@ -157,8 +183,11 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
|
||||
ViewModel.SaveHTMLasPDFFunc = null;
|
||||
ViewModel.DirectPrintFuncAsync = null;
|
||||
ViewModel.RenderHtmlAsyncFunc = null;
|
||||
ViewModel.ClearRenderedHtmlAsyncFunc = null;
|
||||
_currentRenderedHtml = string.Empty;
|
||||
RendererCommandBar.AIActionsEnabledChanged -= RendererCommandBar_AIActionsEnabledChanged;
|
||||
RendererCommandBar.PopOutClicked -= RendererCommandBar_PopOutClicked;
|
||||
RendererCommandBar.IsAIActionsEnabled = false;
|
||||
ReaderAiActionsPanel.CancelPendingOperation();
|
||||
|
||||
@@ -178,6 +207,11 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
public Task RefreshMailItemAsync(MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
return ViewModel.RefreshMailItemAsync(mailItemViewModel);
|
||||
}
|
||||
|
||||
private async void RendererCommandBar_AIActionsEnabledChanged(object? sender, bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
@@ -275,11 +309,13 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
// Initialize WebView2 wiring before base navigation invokes ViewModel rendering.
|
||||
// Base.OnNavigatedTo triggers VM.OnNavigatedTo, which can send HtmlRenderingRequested.
|
||||
DOMLoadedTask = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
ViewModel.RenderHtmlAsyncFunc = RenderInternalAsync;
|
||||
ViewModel.ClearRenderedHtmlAsyncFunc = ClearRenderedContentAsync;
|
||||
RendererCommandBar.AIActionsEnabledChanged -= RendererCommandBar_AIActionsEnabledChanged;
|
||||
RendererCommandBar.AIActionsEnabledChanged += RendererCommandBar_AIActionsEnabledChanged;
|
||||
RendererCommandBar.PopOutClicked -= RendererCommandBar_PopOutClicked;
|
||||
RendererCommandBar.PopOutClicked += RendererCommandBar_PopOutClicked;
|
||||
RendererCommandBar.IsAIActionsEnabled = false;
|
||||
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
|
||||
Chromium.CoreWebView2Initialized += CoreWebViewInitialized;
|
||||
@@ -316,17 +352,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
Chromium.Source = new Uri("https://wino.mail/reader.html");
|
||||
}
|
||||
|
||||
|
||||
async void IRecipient<CancelRenderingContentRequested>.Receive(CancelRenderingContentRequested message)
|
||||
{
|
||||
await EnsureChromiumInitializedAsync();
|
||||
|
||||
if (!isRenderingInProgress)
|
||||
{
|
||||
await RenderInternalAsync(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private async void WebViewNavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
// This is our reader.
|
||||
@@ -397,7 +422,8 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
|
||||
private void InternetAddressClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is HyperlinkButton hyperlinkButton)
|
||||
// TODO: Popped out windows don't have xaml root assigned properly, therefore ShowAt will fail.
|
||||
if (sender is HyperlinkButton hyperlinkButton && !_isPoppedOut)
|
||||
{
|
||||
hyperlinkButton.ContextFlyout.ShowAt(hyperlinkButton);
|
||||
}
|
||||
@@ -411,6 +437,21 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
}
|
||||
}
|
||||
|
||||
private void RendererCommandBar_PopOutClicked(object? sender, EventArgs e)
|
||||
{
|
||||
PopOutRequested?.Invoke(this, PopOutRequestedEventArgs.Default);
|
||||
}
|
||||
|
||||
private void ViewModel_CloseRequested(object? sender, EventArgs e)
|
||||
{
|
||||
HostActionRequested?.Invoke(this, new PopoutHostActionRequestedEventArgs(PopoutHostActionKind.CloseHostedInstance));
|
||||
}
|
||||
|
||||
private void ViewModel_ComposeRequested(object? sender, ComposeDraftRequestedEventArgs e)
|
||||
{
|
||||
HostActionRequested?.Invoke(this, new PopoutHostActionRequestedEventArgs(PopoutHostActionKind.PopOutNextNavigation, typeof(ComposePage), e.DraftUniqueId));
|
||||
}
|
||||
|
||||
private void OpenAttachment_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
|
||||
@@ -431,8 +472,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<HtmlRenderingRequested>(this);
|
||||
WeakReferenceMessenger.Default.Register<CancelRenderingContentRequested>(this);
|
||||
WeakReferenceMessenger.Default.Register<ApplicationThemeChanged>(this);
|
||||
}
|
||||
|
||||
@@ -440,8 +479,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
|
||||
{
|
||||
base.UnregisterRecipients();
|
||||
|
||||
WeakReferenceMessenger.Default.Unregister<HtmlRenderingRequested>(this);
|
||||
WeakReferenceMessenger.Default.Unregister<CancelRenderingContentRequested>(this);
|
||||
WeakReferenceMessenger.Default.Unregister<ApplicationThemeChanged>(this);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user