Initial WinUI switch.

This commit is contained in:
Burak Kaan Köse
2025-09-29 11:16:14 +02:00
parent f9c53ca2c9
commit e67b893ae4
345 changed files with 22458 additions and 746 deletions
@@ -0,0 +1,36 @@
using System.Threading.Tasks;
namespace Wino.Activation;
public abstract class ActivationHandler
{
public abstract bool CanHandle(object args);
public abstract Task HandleAsync(object args);
}
// Extend this class to implement new ActivationHandlers
public abstract class ActivationHandler<T> : ActivationHandler
where T : class
{
// Override this method to add the activation logic in your activation handler
protected abstract Task HandleInternalAsync(T args);
public override async Task HandleAsync(object args)
{
await HandleInternalAsync(args as T);
}
public override bool CanHandle(object args)
{
// CanHandle checks the args is of type you have configured
return args is T && CanHandleInternal(args as T);
}
// You can override this method to add extra validation on activation args
// to determine if your ActivationHandler should handle this activation args
protected virtual bool CanHandleInternal(T args)
{
return true;
}
}
+35
View File
@@ -0,0 +1,35 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.UI.Xaml.Media"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Acrylic</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<!-- Acrylic Template -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor"
FallbackColor="#F9F9F9"
TintColor="#FCFCFC"
TintOpacity="0.75" />
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
<local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor"
FallbackColor="#2C2C2C"
TintColor="#2C2C2C"
TintOpacity="0.30" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+25
View File
@@ -0,0 +1,25 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Clouds</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///Wino.Core.UWP/BackgroundImages/Clouds.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+47
View File
@@ -0,0 +1,47 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Custom</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appdata:///local/CustomWallpaper.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush
x:Key="WinoApplicationBackgroundColor"
ImageSource="{StaticResource ThemeBackgroundImage}"
Stretch="UniformToFill" />
<!-- Navigation View Settings -->
<xaml:CornerRadius x:Key="NavigationViewContentGridCornerRadius">0,0,0,0</xaml:CornerRadius>
<xaml:CornerRadius x:Key="OverlayCornerRadius">0,1,0,0</xaml:CornerRadius>
<Thickness x:Key="NavigationViewContentMargin">0,0,0,0</Thickness>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
<SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewDefaultPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
<!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewDefaultPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+21
View File
@@ -0,0 +1,21 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Forest</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///Wino.Core.UWP/BackgroundImages/Forest.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+24
View File
@@ -0,0 +1,24 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Garden</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///Wino.Core.UWP/BackgroundImages/Garden.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush>
</ResourceDictionary>
<!-- N/A. Light theme only. -->
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+22
View File
@@ -0,0 +1,22 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Mica</x:String>
<x:Boolean x:Key="UseMica">True</x:Boolean>
<SolidColorBrush x:Key="WinoApplicationBackgroundColor">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<!-- Mica Template -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+23
View File
@@ -0,0 +1,23 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Nighty</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///Wino.Core.UWP/BackgroundImages/Nighty.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+23
View File
@@ -0,0 +1,23 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Microsoft.UI.Xaml">
<x:String x:Key="ThemeName">Snowflake</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///Wino.Core.UWP/BackgroundImages/Snowflake.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
+22
View File
@@ -0,0 +1,22 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">TestTheme.xaml</x:String>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Background Image -->
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/bg6.jpg</x:String>
<SolidColorBrush x:Key="ShellTitleBarBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ShellNavigationViewBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#fdcb6e</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Background Image -->
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/bg6.jpg</x:String>
<SolidColorBrush x:Key="ShellTitleBarBackgroundColorBrush">#A3000000</SolidColorBrush>
<SolidColorBrush x:Key="ShellNavigationViewBackgroundColorBrush">#A3000000</SolidColorBrush>
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+90
View File
@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Wino.Core.ViewModels;
using Wino.Messaging.Client.Shell;
namespace Wino.Core.UWP;
public partial class BasePage : Page, IRecipient<LanguageChanged>
{
public UIElement ShellContent
{
get { return (UIElement)GetValue(ShellContentProperty); }
set { SetValue(ShellContentProperty, value); }
}
public static readonly DependencyProperty ShellContentProperty = DependencyProperty.Register(nameof(ShellContent), typeof(UIElement), typeof(BasePage), new PropertyMetadata(null));
public void Receive(LanguageChanged message)
{
OnLanguageChanged();
}
public virtual void OnLanguageChanged() { }
}
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
{
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>();
protected BasePage()
{
ViewModel.Dispatcher = new WinUIDispatcher(DispatcherQueue);
Loaded += PageLoaded;
Unloaded += PageUnloaded;
}
private void PageUnloaded(object sender, RoutedEventArgs e)
{
Loaded -= PageLoaded;
Unloaded -= PageUnloaded;
}
private void PageLoaded(object sender, RoutedEventArgs e) => ViewModel.OnPageLoaded();
~BasePage()
{
Debug.WriteLine($"Disposed {GetType().Name}");
}
[RequiresDynamicCode("AOT")]
[RequiresUnreferencedCode("AOT")]
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var mode = GetNavigationMode(e.NavigationMode);
var parameter = e.Parameter;
WeakReferenceMessenger.Default.UnregisterAll(this);
WeakReferenceMessenger.Default.RegisterAll(this);
ViewModel.OnNavigatedTo(mode, parameter);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
var mode = GetNavigationMode(e.NavigationMode);
var parameter = e.Parameter;
WeakReferenceMessenger.Default.UnregisterAll(this);
ViewModel.OnNavigatedFrom(mode, parameter);
GC.Collect();
}
private Domain.Models.Navigation.NavigationMode GetNavigationMode(NavigationMode mode)
{
return (Domain.Models.Navigation.NavigationMode)mode;
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,75 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Messaging.UI;
namespace Wino.Core.UWP.Controls;
public sealed partial class AccountCreationDialogControl : UserControl, IRecipient<CopyAuthURLRequested>
{
private string copyClipboardURL;
public event EventHandler CancelClicked;
public AccountCreationDialogState State
{
get { return (AccountCreationDialogState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialogControl), new PropertyMetadata(AccountCreationDialogState.Idle, new PropertyChangedCallback(OnStateChanged)));
public AccountCreationDialogControl()
{
InitializeComponent();
}
private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is AccountCreationDialogControl dialog)
{
dialog.UpdateVisualStates();
}
}
private void UpdateVisualStates() => VisualStateManager.GoToState(this, State.ToString(), false);
public async void Receive(CopyAuthURLRequested message)
{
copyClipboardURL = message.AuthURL;
await Task.Delay(2000);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
AuthHelpDialogButton.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
});
}
private void ControlLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
WeakReferenceMessenger.Default.Register(this);
}
private void ControlUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
WeakReferenceMessenger.Default.UnregisterAll(this);
}
private async void CopyClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (string.IsNullOrEmpty(copyClipboardURL)) return;
var clipboardService = WinoApplication.Current.Services.GetService<IClipboardService>();
await clipboardService.CopyClipboardAsync(copyClipboardURL);
}
private void CancelButtonClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) => CancelClicked?.Invoke(this, null);
}
@@ -0,0 +1,105 @@
using System.Collections.Generic;
namespace Wino.Core.UWP.Controls;
public static class ControlConstants
{
public static Dictionary<WinoIconGlyph, string> WinoIconFontDictionary = new Dictionary<WinoIconGlyph, string>()
{
{ WinoIconGlyph.None, "\u0020" },
{ WinoIconGlyph.Archive, "\uE066" },
{ WinoIconGlyph.UnArchive, "\uE06C" },
{ WinoIconGlyph.Reply, "\uF176" },
{ WinoIconGlyph.ReplyAll, "\uF17A" },
{ WinoIconGlyph.Sync, "\uE895" },
{ WinoIconGlyph.Send, "\uEA8E" },
{ WinoIconGlyph.LightEditor, "\uE1F6" },
{ WinoIconGlyph.Delete, "\uEEA6" },
{ WinoIconGlyph.DarkEditor, "\uEE44" },
{ WinoIconGlyph.Draft, "\uF3BE" },
{ WinoIconGlyph.Flag, "\uF40C" },
{ WinoIconGlyph.ClearFlag, "\uF40F" },
{ WinoIconGlyph.Folder, "\uE643" },
{ WinoIconGlyph.Forward, "\uE7AA" },
{ WinoIconGlyph.Inbox, "\uF516" },
{ WinoIconGlyph.MarkRead, "\uF522" },
{ WinoIconGlyph.MarkUnread, "\uF529" },
{ WinoIconGlyph.MultiSelect, "\uE84D" },
{ WinoIconGlyph.Save, "\uEA43" },
{ WinoIconGlyph.CreateFolder, "\uE645" },
{ WinoIconGlyph.Pin, "\uF5FF" },
{ WinoIconGlyph.UnPin, "\uE985" },
{ WinoIconGlyph.Star, "\uE734" },
{ WinoIconGlyph.Ignore, "\uF5D0" },
{ WinoIconGlyph.Find, "\uEA7D" },
{ WinoIconGlyph.Zoom, "\uEE8E" },
{ WinoIconGlyph.SpecialFolderInbox, "\uF516" },
{ WinoIconGlyph.SpecialFolderStarred, "\uF70D" },
{ WinoIconGlyph.SpecialFolderImportant, "\uE2F4" },
{ WinoIconGlyph.SpecialFolderSent, "\uEA8E" },
{ WinoIconGlyph.SpecialFolderDraft, "\uF3BE" },
{ WinoIconGlyph.SpecialFolderArchive, "\uE066" },
{ WinoIconGlyph.SpecialFolderDeleted, "\uEEA6" },
{ WinoIconGlyph.SpecialFolderJunk, "\uF140" },
{ WinoIconGlyph.SpecialFolderChat, "\uE8BD" },
{ WinoIconGlyph.SpecialFolderCategory, "\uF599" },
{ WinoIconGlyph.SpecialFolderUnread, "\uF529" },
{ WinoIconGlyph.SpecialFolderForums, "\uF5B8" },
{ WinoIconGlyph.SpecialFolderUpdated, "\uF565" },
{ WinoIconGlyph.SpecialFolderPersonal, "\uE25A" },
{ WinoIconGlyph.SpecialFolderPromotions, "\uF7B6" },
{ WinoIconGlyph.SpecialFolderSocial, "\uEEEB" },
{ WinoIconGlyph.SpecialFolderOther, "\uE643" },
{ WinoIconGlyph.SpecialFolderMore, "\uF0F4" },
{ WinoIconGlyph.Microsoft, "\uE904" },
{ WinoIconGlyph.Google, "\uE905" },
{ WinoIconGlyph.NewMail, "\uF107" },
{ WinoIconGlyph.TurnOfNotifications, "\uF11D" },
{ WinoIconGlyph.Rename, "\uF668" },
{ WinoIconGlyph.EmptyFolder, "\uE47E" },
{ WinoIconGlyph.DontSync, "\uF195" },
{ WinoIconGlyph.Move, "\uE7B8" },
{ WinoIconGlyph.Mail, "\uF509" },
{ WinoIconGlyph.More, "\uE824" },
{ WinoIconGlyph.CustomServer, "\uF509" },
{ WinoIconGlyph.Print, "\uE922" },
{ WinoIconGlyph.Attachment, "\uE723" },
{ WinoIconGlyph.SortTextDesc, "\U000F3606" },
{ WinoIconGlyph.SortLinesDesc, "\U000F038A" },
{ WinoIconGlyph.Certificate, "\uEB95" },
{ WinoIconGlyph.OpenInNewWindow, "\uE8A7" },
{ WinoIconGlyph.Message, "\uE8BD" },
{ WinoIconGlyph.New, "\U000F002A" },
{ WinoIconGlyph.Blocked,"\uF140" },
{ WinoIconGlyph.IMAP, "\uE715" },
{ WinoIconGlyph.Calendar, "\uE912" },
{ WinoIconGlyph.CalendarToday, "\uE911" },
{ WinoIconGlyph.CalendarDay, "\uE913" },
{ WinoIconGlyph.CalendarWeek, "\uE914" },
{ WinoIconGlyph.CalendarMonth, "\uE91c" },
{ WinoIconGlyph.CalendarYear, "\uE917" },
{ WinoIconGlyph.WeatherBlow, "\uE907" },
{ WinoIconGlyph.WeatherCloudy, "\uE920" },
{ WinoIconGlyph.WeatherSunny, "\uE90e" },
{ WinoIconGlyph.WeatherRainy, "\uE908" },
{ WinoIconGlyph.WeatherSnowy, "\uE90a" },
{ WinoIconGlyph.WeatherSnowShowerAtNight, "\uE90c" },
{ WinoIconGlyph.WeatherThunderstorm, "\uE906" },
{ WinoIconGlyph.CalendarEventRepeat, "\uE915" },
{ WinoIconGlyph.CalendarEventMuiltiDay, "\uE91b" },
{ WinoIconGlyph.Reminder, "\uE918" },
{ WinoIconGlyph.CalendarAttendee, "\uE91a" },
{ WinoIconGlyph.CalendarSync, "\uE91d" },
{ WinoIconGlyph.CalendarError, "\uE916" },
{ WinoIconGlyph.CalendarAttendees, "\uE929" },
{ WinoIconGlyph.EventEditSeries, "\uE92A" },
{ WinoIconGlyph.EventTentative, "\uE928" },
{ WinoIconGlyph.EventAccept, "\uE925" },
{ WinoIconGlyph.EventRespond, "\uE924" },
{ WinoIconGlyph.EventReminder, "\uE923" },
{ WinoIconGlyph.EventJoinOnline, "\uE926" },
{ WinoIconGlyph.ViewMessageSource, "\uE943" },
{ WinoIconGlyph.Apple, "\uE92B" },
{ WinoIconGlyph.Yahoo, "\uE92C" }
};
}
@@ -0,0 +1,70 @@
using System;
namespace Wino.Core.UWP.Controls
{
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace CustomControls
{
public partial class CustomWrapPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
double currentRowWidth = 0;
double currentRowHeight = 0;
double totalHeight = 0;
foreach (UIElement child in Children)
{
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var childDesiredSize = child.DesiredSize;
if (currentRowWidth + childDesiredSize.Width > availableSize.Width)
{
// Wrap to the next row
totalHeight += currentRowHeight;
currentRowWidth = 0;
currentRowHeight = 0;
}
currentRowWidth += childDesiredSize.Width;
currentRowHeight = Math.Max(currentRowHeight, childDesiredSize.Height);
}
totalHeight += currentRowHeight;
return new Size(availableSize.Width, totalHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
double currentRowWidth = 0;
double currentRowHeight = 0;
double currentY = 0;
foreach (UIElement child in Children)
{
var childDesiredSize = child.DesiredSize;
if (currentRowWidth + childDesiredSize.Width > finalSize.Width)
{
currentY += currentRowHeight;
currentRowWidth = 0;
currentRowHeight = 0;
}
child.Arrange(new Rect(new Point(currentRowWidth, currentY), childDesiredSize));
currentRowWidth += childDesiredSize.Width;
currentRowHeight = Math.Max(currentRowHeight, childDesiredSize.Height);
}
return finalSize;
}
}
}
}
@@ -0,0 +1,86 @@
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Wino.Core.UWP.Controls;
public partial class EqualGridPanel : Panel
{
public int Rows
{
get { return (int)GetValue(RowsProperty); }
set { SetValue(RowsProperty, value); }
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register(
nameof(Rows),
typeof(int),
typeof(EqualGridPanel),
new PropertyMetadata(1, OnLayoutPropertyChanged));
public int Columns
{
get { return (int)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
nameof(Columns),
typeof(int),
typeof(EqualGridPanel),
new PropertyMetadata(1, OnLayoutPropertyChanged));
private static void OnLayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is EqualGridPanel panel)
{
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
}
protected override Size MeasureOverride(Size availableSize)
{
if (Rows <= 0 || Columns <= 0)
{
return new Size(0, 0);
}
double cellWidth = availableSize.Width / Columns;
double cellHeight = availableSize.Height / Rows;
foreach (UIElement child in Children)
{
child.Measure(new Size(cellWidth, cellHeight));
}
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (Rows <= 0 || Columns <= 0)
{
return new Size(0, 0);
}
double cellWidth = finalSize.Width / Columns;
double cellHeight = finalSize.Height / Rows;
for (int i = 0; i < Children.Count; i++)
{
int row = i / Columns;
int column = i % Columns;
double x = column * cellWidth;
double y = row * cellHeight;
Rect rect = new Rect(x, y, cellWidth, cellHeight);
Children[i].Arrange(rect);
}
return finalSize;
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,255 @@
using System.Windows.Input;
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Controls;
public sealed partial class WinoAppTitleBar : UserControl
{
public event TypedEventHandler<WinoAppTitleBar, RoutedEventArgs> BackButtonClicked;
public static readonly DependencyProperty IsRenderingPaneVisibleProperty = DependencyProperty.Register(nameof(IsRenderingPaneVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
public static readonly DependencyProperty IsReaderNarrowedProperty = DependencyProperty.Register(nameof(IsReaderNarrowed), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnIsReaderNarrowedChanged));
public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0d, OnDrawingPropertyChanged));
public static readonly DependencyProperty IsNavigationPaneOpenProperty = DependencyProperty.Register(nameof(IsNavigationPaneOpen), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
public static readonly DependencyProperty NavigationViewDisplayModeProperty = DependencyProperty.Register(nameof(NavigationViewDisplayMode), typeof(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode), typeof(WinoAppTitleBar), new PropertyMetadata(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact, OnDrawingPropertyChanged));
public static readonly DependencyProperty ShellFrameContentProperty = DependencyProperty.Register(nameof(ShellFrameContent), typeof(UIElement), typeof(WinoAppTitleBar), new PropertyMetadata(null, OnDrawingPropertyChanged));
public static readonly DependencyProperty SystemReservedProperty = DependencyProperty.Register(nameof(SystemReserved), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0, OnDrawingPropertyChanged));
public static readonly DependencyProperty CoreWindowTextProperty = DependencyProperty.Register(nameof(CoreWindowText), typeof(string), typeof(WinoAppTitleBar), new PropertyMetadata(string.Empty, OnDrawingPropertyChanged));
public static readonly DependencyProperty ReadingPaneLengthProperty = DependencyProperty.Register(nameof(ReadingPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(420d, OnDrawingPropertyChanged));
public static readonly DependencyProperty ConnectionStatusProperty = DependencyProperty.Register(nameof(ConnectionStatus), typeof(WinoServerConnectionStatus), typeof(WinoAppTitleBar), new PropertyMetadata(WinoServerConnectionStatus.None, new PropertyChangedCallback(OnConnectionStatusChanged)));
public static readonly DependencyProperty ReconnectCommandProperty = DependencyProperty.Register(nameof(ReconnectCommand), typeof(ICommand), typeof(WinoAppTitleBar), new PropertyMetadata(null));
public static readonly DependencyProperty ShrinkShellContentOnExpansionProperty = DependencyProperty.Register(nameof(ShrinkShellContentOnExpansion), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
public static readonly DependencyProperty IsDragAreaProperty = DependencyProperty.Register(nameof(IsDragArea), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, new PropertyChangedCallback(OnIsDragAreaChanged)));
public static readonly DependencyProperty IsShellFrameContentVisibleProperty = DependencyProperty.Register(nameof(IsShellFrameContentVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
public static readonly DependencyProperty IsMenuButtonVisibleProperty = DependencyProperty.Register(nameof(IsMenuButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
public bool IsShellFrameContentVisible
{
get { return (bool)GetValue(IsShellFrameContentVisibleProperty); }
set { SetValue(IsShellFrameContentVisibleProperty, value); }
}
public ICommand ReconnectCommand
{
get { return (ICommand)GetValue(ReconnectCommandProperty); }
set { SetValue(ReconnectCommandProperty, value); }
}
public WinoServerConnectionStatus ConnectionStatus
{
get { return (WinoServerConnectionStatus)GetValue(ConnectionStatusProperty); }
set { SetValue(ConnectionStatusProperty, value); }
}
public string CoreWindowText
{
get { return (string)GetValue(CoreWindowTextProperty); }
set { SetValue(CoreWindowTextProperty, value); }
}
public bool IsDragArea
{
get { return (bool)GetValue(IsDragAreaProperty); }
set { SetValue(IsDragAreaProperty, value); }
}
public double SystemReserved
{
get { return (double)GetValue(SystemReservedProperty); }
set { SetValue(SystemReservedProperty, value); }
}
public UIElement ShellFrameContent
{
get { return (UIElement)GetValue(ShellFrameContentProperty); }
set { SetValue(ShellFrameContentProperty, value); }
}
public Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayMode
{
get { return (Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode)GetValue(NavigationViewDisplayModeProperty); }
set { SetValue(NavigationViewDisplayModeProperty, value); }
}
public bool ShrinkShellContentOnExpansion
{
get { return (bool)GetValue(ShrinkShellContentOnExpansionProperty); }
set { SetValue(ShrinkShellContentOnExpansionProperty, value); }
}
public bool IsNavigationPaneOpen
{
get { return (bool)GetValue(IsNavigationPaneOpenProperty); }
set { SetValue(IsNavigationPaneOpenProperty, value); }
}
public double OpenPaneLength
{
get { return (double)GetValue(OpenPaneLengthProperty); }
set { SetValue(OpenPaneLengthProperty, value); }
}
public bool IsMenuButtonVisible
{
get { return (bool)GetValue(IsMenuButtonVisibleProperty); }
set { SetValue(IsMenuButtonVisibleProperty, value); }
}
public bool IsBackButtonVisible
{
get { return (bool)GetValue(IsBackButtonVisibleProperty); }
set { SetValue(IsBackButtonVisibleProperty, value); }
}
public bool IsReaderNarrowed
{
get { return (bool)GetValue(IsReaderNarrowedProperty); }
set { SetValue(IsReaderNarrowedProperty, value); }
}
public bool IsRenderingPaneVisible
{
get { return (bool)GetValue(IsRenderingPaneVisibleProperty); }
set { SetValue(IsRenderingPaneVisibleProperty, value); }
}
public double ReadingPaneLength
{
get { return (double)GetValue(ReadingPaneLengthProperty); }
set { SetValue(ReadingPaneLengthProperty, value); }
}
private static void OnIsReaderNarrowedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoAppTitleBar bar)
{
bar.DrawTitleBar();
}
}
private static void OnDrawingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoAppTitleBar bar)
{
bar.DrawTitleBar();
}
}
private static void OnConnectionStatusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoAppTitleBar bar)
{
bar.UpdateConnectionStatus();
}
}
private static void OnIsDragAreaChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoAppTitleBar bar)
{
bar.SetDragArea();
}
}
private void SetDragArea()
{
if (IsDragArea)
{
Window.Current.SetTitleBar(dragbar);
}
}
private void UpdateConnectionStatus()
{
}
private void DrawTitleBar()
{
UpdateLayout();
CoreWindowTitleTextBlock.Visibility = Visibility.Collapsed;
ShellContentContainer.Width = double.NaN;
ShellContentContainer.Margin = new Thickness(0, 0, 0, 0);
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
EmptySpaceWidth.Width = new GridLength(1, GridUnitType.Star);
// Menu is not visible.
if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal)
{
}
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact)
{
// Icons are visible.
if (!IsReaderNarrowed && ShrinkShellContentOnExpansion)
{
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Left;
ShellContentContainer.Width = ReadingPaneLength;
}
}
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded)
{
if (IsNavigationPaneOpen)
{
CoreWindowTitleTextBlock.Visibility = Visibility.Visible;
// LMargin = OpenPaneLength - LeftMenuStackPanel
ShellContentContainer.Margin = new Thickness(OpenPaneLength - LeftMenuStackPanel.ActualSize.X, 0, 0, 0);
if (!IsReaderNarrowed && ShrinkShellContentOnExpansion)
{
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Left;
ShellContentContainer.Width = ReadingPaneLength;
}
}
else
{
if (ShrinkShellContentOnExpansion)
{
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Pixel);
}
else
{
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Star);
}
}
}
}
public WinoAppTitleBar()
{
InitializeComponent();
}
private void BackClicked(object sender, RoutedEventArgs e)
{
BackButtonClicked?.Invoke(this, e);
}
private void PaneClicked(object sender, RoutedEventArgs e)
{
IsNavigationPaneOpen = !IsNavigationPaneOpen;
}
private void TitlebarSizeChanged(object sender, SizeChangedEventArgs e) => DrawTitleBar();
private void ReconnectClicked(object sender, RoutedEventArgs e)
{
// Close the popup for reconnect button.
ReconnectFlyout.Hide();
// Execute the reconnect command.
ReconnectCommand?.Execute(null);
}
}
+135
View File
@@ -0,0 +1,135 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Wino.Core.UWP.Controls;
public enum WinoIconGlyph
{
None,
NewMail,
Google,
Microsoft,
CustomServer,
Archive,
UnArchive,
Reply,
ReplyAll,
LightEditor,
DarkEditor,
Delete,
Move,
Mail,
Draft,
Flag,
ClearFlag,
Folder,
Forward,
Inbox,
MarkRead,
MarkUnread,
Send,
Save,
Sync,
MultiSelect,
Zoom,
Pin,
UnPin,
Ignore,
Star,
CreateFolder,
More,
Find,
SpecialFolderInbox,
SpecialFolderStarred,
SpecialFolderImportant,
SpecialFolderSent,
SpecialFolderDraft,
SpecialFolderArchive,
SpecialFolderDeleted,
SpecialFolderJunk,
SpecialFolderChat,
SpecialFolderCategory,
SpecialFolderUnread,
SpecialFolderForums,
SpecialFolderUpdated,
SpecialFolderPersonal,
SpecialFolderPromotions,
SpecialFolderSocial,
SpecialFolderOther,
SpecialFolderMore,
TurnOfNotifications,
EmptyFolder,
Rename,
DontSync,
Attachment,
SortTextDesc,
SortLinesDesc,
Certificate,
OpenInNewWindow,
Blocked,
Message,
New,
IMAP,
Print,
Calendar,
CalendarToday,
CalendarDay,
CalendarWeek,
CalendarWorkWeek,
CalendarMonth,
CalendarYear,
WeatherBlow,
WeatherCloudy,
WeatherSunny,
WeatherRainy,
WeatherSnowy,
WeatherSnowShowerAtNight,
WeatherThunderstorm,
CalendarEventRepeat,
CalendarEventMuiltiDay,
CalendarError,
Reminder,
CalendarAttendee,
CalendarAttendees,
CalendarSync,
EventRespond,
EventAccept,
EventTentative,
EventDecline,
EventReminder,
EventEditSeries,
EventJoinOnline,
ViewMessageSource,
Apple,
Yahoo
}
public partial class WinoFontIcon : FontIcon
{
public WinoIconGlyph Icon
{
get { return (WinoIconGlyph)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIcon), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
public WinoFontIcon()
{
FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("/Wino.Core.WinUI/Assets/WinoIcons.ttf#WinoIcons");
FontSize = 32;
}
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoFontIcon fontIcon)
{
fontIcon.UpdateGlyph();
}
}
private void UpdateGlyph()
{
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
}
}
@@ -0,0 +1,34 @@
using Microsoft.UI.Xaml;
using Wino.Core.UWP.Controls;
namespace Wino.Controls;
public partial class WinoFontIconSource : Microsoft.UI.Xaml.Controls.FontIconSource
{
public WinoIconGlyph Icon
{
get { return (WinoIconGlyph)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIconSource), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
public WinoFontIconSource()
{
FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("ms-appx:///Assets/WinoIcons.ttf#WinoIcons");
FontSize = 32;
}
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoFontIconSource fontIcon)
{
fontIcon.UpdateGlyph();
}
}
private void UpdateGlyph()
{
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
}
}
+90
View File
@@ -0,0 +1,90 @@
using System;
using System.Numerics;
using CommunityToolkit.WinUI.Animations;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Controls;
public partial class WinoInfoBar : InfoBar
{
public static readonly DependencyProperty AnimationTypeProperty = DependencyProperty.Register(nameof(AnimationType), typeof(InfoBarAnimationType), typeof(WinoInfoBar), new PropertyMetadata(InfoBarAnimationType.SlideFromRightToLeft));
public static readonly DependencyProperty DismissIntervalProperty = DependencyProperty.Register(nameof(DismissInterval), typeof(int), typeof(WinoInfoBar), new PropertyMetadata(5, new PropertyChangedCallback(OnDismissIntervalChanged)));
public InfoBarAnimationType AnimationType
{
get { return (InfoBarAnimationType)GetValue(AnimationTypeProperty); }
set { SetValue(AnimationTypeProperty, value); }
}
public int DismissInterval
{
get { return (int)GetValue(DismissIntervalProperty); }
set { SetValue(DismissIntervalProperty, value); }
}
private readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer();
public WinoInfoBar()
{
RegisterPropertyChangedCallback(IsOpenProperty, IsOpenChanged);
_dispatcherTimer.Interval = TimeSpan.FromSeconds(DismissInterval);
}
private static void OnDismissIntervalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoInfoBar bar)
{
bar.UpdateInterval(bar.DismissInterval);
}
}
private void UpdateInterval(int seconds) => _dispatcherTimer.Interval = TimeSpan.FromSeconds(seconds);
private async void IsOpenChanged(DependencyObject sender, DependencyProperty dp)
{
if (sender is WinoInfoBar control)
{
// Message shown.
if (!control.IsOpen) return;
Opacity = 1;
_dispatcherTimer.Stop();
_dispatcherTimer.Tick -= TimerTick;
_dispatcherTimer.Tick += TimerTick;
_dispatcherTimer.Start();
// Slide from right.
if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft)
{
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(150, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
}
else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop)
{
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(0, 50, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
}
}
}
private async void TimerTick(object sender, object e)
{
_dispatcherTimer.Stop();
_dispatcherTimer.Tick -= TimerTick;
if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft)
{
await AnimationBuilder.Create().Translation(new Vector3((float)ActualWidth, 0, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
}
else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop)
{
await AnimationBuilder.Create().Translation(new Vector3(0, (float)ActualHeight, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
}
IsOpen = false;
}
}
@@ -0,0 +1,46 @@
using System.Numerics;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
namespace Wino.Core.UWP.Controls;
public partial class WinoNavigationViewItem : NavigationViewItem
{
public bool IsDraggingItemOver
{
get { return (bool)GetValue(IsDraggingItemOverProperty); }
set { SetValue(IsDraggingItemOverProperty, value); }
}
public static readonly DependencyProperty IsDraggingItemOverProperty = DependencyProperty.Register(nameof(IsDraggingItemOver), typeof(bool), typeof(WinoNavigationViewItem), new PropertyMetadata(false, OnIsDraggingItemOverChanged));
private static void OnIsDraggingItemOverChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoNavigationViewItem control)
control.UpdateDragEnterState();
}
private void UpdateDragEnterState()
{
// TODO: Add animation. Maybe after overriding DragUI in shell?
//if (IsDraggingItemOver)
//{
// ScaleAnimation(new System.Numerics.Vector3(1.2f, 1.2f, 1.2f));
//}
//else
//{
// ScaleAnimation(new System.Numerics.Vector3(1f, 1f, 1f));
//}
}
private void ScaleAnimation(Vector3 vector)
{
if (Content is UIElement content)
{
var visual = ElementCompositionPreview.GetElementVisual(content);
visual.Scale = vector;
}
}
}
@@ -0,0 +1,26 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Wino.Converters;
public partial class GridLengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is double doubleValue)
{
return new GridLength(doubleValue);
}
return new GridLength(1, GridUnitType.Auto);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is GridLength gridLength)
{
return gridLength.Value;
}
return 0.0;
}
}
@@ -0,0 +1,81 @@
using System;
using System.Globalization;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
namespace Wino.Core.UWP.Converters;
public partial class HexToColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string hexColor && !string.IsNullOrEmpty(hexColor))
{
try
{
// Remove # if present
hexColor = hexColor.Replace("#", "");
// Parse hex to color
byte r = byte.Parse(hexColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
byte g = byte.Parse(hexColor.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
byte b = byte.Parse(hexColor.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
return new SolidColorBrush(Color.FromArgb(255, r, g, b));
}
catch
{
return GetDefaultBrush();
}
}
return GetDefaultBrush();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is string hexColor)
{
try
{
// Remove # if present
hexColor = hexColor.Replace("#", string.Empty);
// Parse ARGB values
byte a = 255; // Default to fully opaque
byte r = byte.Parse(hexColor.Substring(0, 2), NumberStyles.HexNumber);
byte g = byte.Parse(hexColor.Substring(2, 2), NumberStyles.HexNumber);
byte b = byte.Parse(hexColor.Substring(4, 2), NumberStyles.HexNumber);
// If 8-digit hex (with alpha), parse alpha value
if (hexColor.Length == 8)
{
a = byte.Parse(hexColor.Substring(6, 2), NumberStyles.HexNumber);
}
return Color.FromArgb(a, r, g, b);
}
catch
{
return DependencyProperty.UnsetValue;
}
}
return DependencyProperty.UnsetValue;
}
private SolidColorBrush GetDefaultBrush()
{
// Get current theme's foreground brush.
// Bug: This should be ThemeResource to react to theme changes, but it's not.
// So if user changes the dark/light theme, this won't update.
if (WinoApplication.Current.UnderlyingThemeService.IsUnderlyingThemeDark())
return new SolidColorBrush(Colors.White);
else
return new SolidColorBrush(Colors.Black);
}
}
@@ -0,0 +1,20 @@
using System;
using Microsoft.UI.Xaml.Data;
namespace Wino.Converters;
public partial class ReverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolval)
return !boolval;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,18 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Wino.Converters;
public partial class ReverseBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return ((bool)value) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
+115
View File
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
x:Class="Wino.Core.UWP.CoreGeneric"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:coreControls="using:Wino.Core.UWP.Controls"
xmlns:styles="using:Wino.Core.UWP.Styles">
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="Styles/Colors.xaml" />
<ResourceDictionary Source="Styles/ContentPresenters.xaml" />
<ResourceDictionary Source="Styles/Converters.xaml" />
<ResourceDictionary Source="Styles/FontIcons.xaml" />
<ResourceDictionary Source="Styles/WinoInfoBar.xaml" />
<ResourceDictionary Source="Styles/SharedStyles.xaml" />
<ResourceDictionary Source="Styles/IconTemplates.xaml" />
<styles:CustomMessageDialogStyles />
<styles:DataTemplates />
<ResourceDictionary>
<Style TargetType="ScrollViewer">
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
</Style>
<!-- Remove border/backgroud of command bar -->
<SolidColorBrush x:Key="CommandBarBackground" Color="Transparent" />
<SolidColorBrush x:Key="CommandBarBackgroundOpen" Color="Transparent" />
<SolidColorBrush x:Key="CommandBarBorderBrushOpen" Color="Transparent" />
<Thickness x:Key="CommandBarBorderThicknessOpen">0</Thickness>
<!-- Override AppBarToggleButton's checked background color. -->
<StaticResource x:Key="AppBarToggleButtonBackgroundChecked" ResourceKey="SystemAccentColor" />
<StaticResource x:Key="AppBarToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColor" />
<StaticResource x:Key="AppBarToggleButtonBackgroundCheckedPressed" ResourceKey="SystemAccentColor" />
<Thickness x:Key="ImapSetupDialogSubPagePadding">24,24,24,24</Thickness>
<!-- Border style for each page's root border for separation of zones. -->
<Style x:Key="PageRootBorderStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource WinoContentZoneBackgroud}" />
<Setter Property="BorderBrush" Value="{StaticResource CardStrokeColorDefaultBrush}" />
<Setter Property="CornerRadius" Value="7" />
<Setter Property="BorderThickness" Value="1" />
</Style>
<!-- Custom Grid style for info panels. -->
<Style x:Key="InformationAreaGridStyle" TargetType="Grid">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Padding" Value="16" />
</Style>
<!-- Default StackPanel animation. -->
<Style TargetType="StackPanel">
<Setter Property="ChildrenTransitions">
<Setter.Value>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
</Style>
<!-- Default Style for ContentDialog -->
<Style
x:Key="WinoDialogStyle"
BasedOn="{StaticResource DefaultContentDialogStyle}"
TargetType="ContentDialog" />
<!-- Wino Navigation View Item -->
<Style TargetType="coreControls:WinoNavigationViewItem">
<Setter Property="ContentTransitions">
<Setter.Value>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="PageStyle" TargetType="Page">
<Setter Property="Margin" Value="-1,0,0,0" />
<Setter Property="Padding" Value="12" />
<Setter Property="Background" Value="{ThemeResource AppBarBackgroundColor}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Padding="12">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Spacing between cards -->
<x:Double x:Key="SettingsCardSpacing">4</x:Double>
<!-- Style (inc. the correct spacing) of a section header -->
<Style
x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,30,0,6" />
</Style.Setters>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
+8
View File
@@ -0,0 +1,8 @@
using Microsoft.UI.Xaml;
namespace Wino.Core.UWP;
public partial class CoreGeneric : ResourceDictionary
{
public CoreGeneric() => InitializeComponent();
}
+49
View File
@@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel.AppService;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Services;
using Wino.Core.ViewModels;
using Wino.Services;
namespace Wino.Core.UWP;
public static class CoreUWPContainerSetup
{
public static void RegisterCoreUWPServices(this IServiceCollection services)
{
var serverConnectionManager = new WinoServerConnectionManager();
services.AddSingleton<IWinoServerConnectionManager>(serverConnectionManager);
services.AddSingleton<IWinoServerConnectionManager<AppServiceConnection>>(serverConnectionManager);
services.AddSingleton<IApplicationResourceManager<ResourceDictionary>, ApplicationResourceManager>();
services.AddSingleton<IUnderlyingThemeService, UnderlyingThemeService>();
services.AddSingleton<INativeAppService, NativeAppService>();
services.AddSingleton<IStoreManagementService, StoreManagementService>();
services.AddSingleton<IPreferencesService, PreferencesService>();
services.AddSingleton<IThemeService, ThemeService>();
services.AddSingleton<IStatePersistanceService, StatePersistenceService>();
services.AddSingleton<IThumbnailService, ThumbnailService>();
services.AddSingleton<IDialogServiceBase, DialogServiceBase>();
services.AddTransient<IConfigurationService, ConfigurationService>();
services.AddTransient<IFileService, FileService>();
services.AddTransient<IStoreRatingService, StoreRatingService>();
services.AddTransient<IKeyPressService, KeyPressService>();
services.AddTransient<INotificationBuilder, NotificationBuilder>();
services.AddTransient<IClipboardService, ClipboardService>();
services.AddTransient<IStartupBehaviorService, StartupBehaviorService>();
services.AddSingleton<IPrintService, PrintService>();
}
public static void RegisterCoreViewModels(this IServiceCollection services)
{
services.AddTransient(typeof(SettingsDialogViewModel));
services.AddTransient(typeof(PersonalizationPageViewModel));
services.AddTransient(typeof(SettingOptionsPageViewModel));
services.AddTransient(typeof(AboutPageViewModel));
services.AddTransient(typeof(SettingsPageViewModel));
services.AddTransient(typeof(ManageAccountsPagePageViewModel));
}
}
@@ -0,0 +1,20 @@
<ContentDialog
x:Class="Wino.Dialogs.AccountCreationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Core.UWP.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dialogs="using:Wino.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Closing="DialogClosing"
CornerRadius="8"
mc:Ignorable="d">
<!-- ContentDialogs do not support VSM. -->
<controls:AccountCreationDialogControl
VerticalAlignment="Center"
CancelClicked="CancelClicked"
State="{x:Bind State, Mode=OneWay}" />
</ContentDialog>
@@ -0,0 +1,70 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
namespace Wino.Dialogs;
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
{
private TaskCompletionSource<bool> dialogOpened = new TaskCompletionSource<bool>();
public CancellationTokenSource CancellationTokenSource { get; private set; }
public AccountCreationDialogState State
{
get { return (AccountCreationDialogState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle));
public AccountCreationDialog()
{
InitializeComponent();
}
// Prevent users from dismissing it by ESC key.
public void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
{
if (args.Result == ContentDialogResult.None)
{
args.Cancel = true;
}
}
public void Complete(bool cancel)
{
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
// Unregister from closing event.
Closing -= DialogClosing;
if (cancel && !CancellationTokenSource.IsCancellationRequested)
{
CancellationTokenSource.Cancel();
}
Hide();
}
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
{
CancellationTokenSource = cancellationTokenSource;
Opened += DialogOpened;
_ = ShowAsync();
await dialogOpened.Task;
}
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args)
{
Opened -= DialogOpened;
dialogOpened?.SetResult(true);
}
}
@@ -0,0 +1,19 @@
<ContentDialog
x:Class="Wino.Dialogs.AccountPickerDialog"
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:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{x:Bind domain:Translator.AccountPickerDialog_Title}"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ListView
DisplayMemberPath="Address"
IsItemClickEnabled="True"
ItemClick="AccountClicked"
ItemsSource="{x:Bind AvailableAccounts}"
SelectionMode="None" />
</ContentDialog>
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Dialogs;
public sealed partial class AccountPickerDialog : ContentDialog
{
public MailAccount PickedAccount { get; set; }
public List<MailAccount> AvailableAccounts { get; set; }
public AccountPickerDialog(List<MailAccount> availableAccounts)
{
AvailableAccounts = availableAccounts;
InitializeComponent();
}
private void AccountClicked(object sender, ItemClickEventArgs e)
{
PickedAccount = e.ClickedItem as MailAccount;
Hide();
}
}
@@ -0,0 +1,23 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Enums;
namespace Wino.Dialogs;
public partial class CustomMessageDialogInformationContainer : ObservableObject
{
[ObservableProperty]
public partial bool IsDontAskChecked { get; set; }
public CustomMessageDialogInformationContainer(string title, string description, WinoCustomMessageDialogIcon icon, bool isDontAskAgainEnabled)
{
Title = title;
Description = description;
Icon = icon;
IsDontAskAgainEnabled = isDontAskAgainEnabled;
}
public string Title { get; }
public string Description { get; }
public WinoCustomMessageDialogIcon Icon { get; }
public bool IsDontAskAgainEnabled { get; }
}
@@ -0,0 +1,85 @@
<ContentDialog
x:Class="Wino.Dialogs.CustomThemeBuilderDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:local="using:Wino.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Title="{x:Bind domain:Translator.CustomThemeBuilder_Title}"
DefaultButton="Primary"
FullSizeDesired="False"
IsPrimaryButtonEnabled="False"
PrimaryButtonClick="ApplyClicked"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_ApplyTheme}"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">600</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
<x:Double x:Key="ContentDialogMinHeight">200</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</ContentDialog.Resources>
<StackPanel Spacing="6">
<TextBlock x:Name="ErrorTextBlock" Foreground="Gold" />
<controls:SettingsCard Description="{x:Bind domain:Translator.CustomThemeBuilder_ThemeNameDescription}" Header="{x:Bind domain:Translator.CustomThemeBuilder_ThemeNameTitle}">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 10 0.625 C 10 0.45573 10.061849 0.309246 10.185547 0.185547 C 10.309244 0.06185 10.455729 0 10.625 0 L 15.625 0 C 15.79427 0 15.940754 0.06185 16.064453 0.185547 C 16.18815 0.309246 16.25 0.45573 16.25 0.625 C 16.25 0.794271 16.18815 0.940756 16.064453 1.064453 C 15.940754 1.188152 15.79427 1.25 15.625 1.25 L 13.75 1.25 L 13.75 18.75 L 15.625 18.75 C 15.79427 18.75 15.940754 18.81185 16.064453 18.935547 C 16.18815 19.059244 16.25 19.205729 16.25 19.375 C 16.25 19.544271 16.18815 19.690756 16.064453 19.814453 C 15.940754 19.93815 15.79427 20 15.625 20 L 10.625 20 C 10.455729 20 10.309244 19.93815 10.185547 19.814453 C 10.061849 19.690756 10 19.544271 10 19.375 C 10 19.205729 10.061849 19.059244 10.185547 18.935547 C 10.309244 18.81185 10.455729 18.75 10.625 18.75 L 12.5 18.75 L 12.5 1.25 L 10.625 1.25 C 10.455729 1.25 10.309244 1.188152 10.185547 1.064453 C 10.061849 0.940756 10 0.794271 10 0.625 Z M 0 6.25 C 0 5.735678 0.097656 5.250651 0.292969 4.794922 C 0.488281 4.339193 0.756836 3.94043 1.098633 3.598633 C 1.44043 3.256836 1.837565 2.988281 2.290039 2.792969 C 2.742513 2.597656 3.229167 2.5 3.75 2.5 L 11.25 2.5 L 11.25 3.75 L 3.75 3.75 C 3.404948 3.75 3.081055 3.815105 2.77832 3.945312 C 2.475586 4.075521 2.210286 4.254558 1.982422 4.482422 C 1.754557 4.710287 1.575521 4.975587 1.445312 5.27832 C 1.315104 5.581056 1.25 5.904949 1.25 6.25 L 1.25 13.75 C 1.25 14.095053 1.315104 14.418945 1.445312 14.72168 C 1.575521 15.024414 1.754557 15.289714 1.982422 15.517578 C 2.210286 15.745443 2.475586 15.924479 2.77832 16.054688 C 3.081055 16.184896 3.404948 16.25 3.75 16.25 L 11.25 16.25 L 11.25 17.5 L 3.75 17.5 C 3.229167 17.5 2.742513 17.402344 2.290039 17.207031 C 1.837565 17.011719 1.44043 16.743164 1.098633 16.401367 C 0.756836 16.05957 0.488281 15.662436 0.292969 15.209961 C 0.097656 14.757487 0 14.270834 0 13.75 Z M 15 3.75 L 15 2.5 L 16.25 2.5 C 16.764322 2.5 17.249348 2.597656 17.705078 2.792969 C 18.160807 2.988281 18.55957 3.256836 18.901367 3.598633 C 19.243164 3.94043 19.511719 4.339193 19.707031 4.794922 C 19.902344 5.250651 20 5.735678 20 6.25 L 20 13.75 C 20 14.270834 19.902344 14.757487 19.707031 15.209961 C 19.511719 15.662436 19.243164 16.05957 18.901367 16.401367 C 18.55957 16.743164 18.160807 17.011719 17.705078 17.207031 C 17.249348 17.402344 16.764322 17.5 16.25 17.5 L 15 17.5 L 15 16.25 L 16.25 16.25 C 16.595051 16.25 16.918945 16.184896 17.22168 16.054688 C 17.524414 15.924479 17.789713 15.745443 18.017578 15.517578 C 18.245441 15.289714 18.424479 15.024414 18.554688 14.72168 C 18.684895 14.418945 18.75 14.095053 18.75 13.75 L 18.75 6.25 C 18.75 5.904949 18.684895 5.581056 18.554688 5.27832 C 18.424479 4.975587 18.245441 4.710287 18.017578 4.482422 C 17.789713 4.254558 17.524414 4.075521 17.22168 3.945312 C 16.918945 3.815105 16.595051 3.75 16.25 3.75 Z M 7.441406 5.361328 C 7.389323 5.250651 7.312825 5.162761 7.211914 5.097656 C 7.111002 5.032553 6.998697 5.000001 6.875 5 C 6.751302 5.000001 6.638997 5.032553 6.538086 5.097656 C 6.437174 5.162761 6.360677 5.250651 6.308594 5.361328 L 2.871094 12.861328 C 2.799479 13.017578 2.792969 13.177084 2.851562 13.339844 C 2.910156 13.502604 3.017578 13.619792 3.173828 13.691406 C 3.330078 13.763021 3.489583 13.769531 3.652344 13.710938 C 3.815104 13.652344 3.932292 13.544922 4.003906 13.388672 L 4.84375 11.5625 L 8.896484 11.5625 L 8.90625 11.5625 L 9.746094 13.388672 C 9.817708 13.544922 9.934896 13.652344 10.097656 13.710938 C 10.260416 13.769531 10.419922 13.763021 10.576172 13.691406 C 10.732422 13.619792 10.839844 13.502604 10.898438 13.339844 C 10.957031 13.177084 10.950521 13.017578 10.878906 12.861328 Z M 5.410156 10.3125 L 6.875 7.128906 L 8.339844 10.3125 Z " />
</controls:SettingsCard.HeaderIcon>
<controls:SettingsCard.Content>
<TextBox x:Name="ThemeNameBox" />
</controls:SettingsCard.Content>
</controls:SettingsCard>
<controls:SettingsCard
x:Name="CustomWallpaperCard"
Description="{x:Bind domain:Translator.CustomThemeBuilder_WallpaperDescription}"
Header="{x:Bind domain:Translator.CustomThemeBuilder_WallpaperTitle}">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 4.921875 16.25 C 4.433594 16.25 3.966471 16.150717 3.520508 15.952148 C 3.074544 15.753581 2.683919 15.486654 2.348633 15.151367 C 2.013346 14.816081 1.746419 14.425456 1.547852 13.979492 C 1.349284 13.533529 1.25 13.066406 1.25 12.578125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 12.578125 1.25 C 13.066406 1.25 13.533528 1.349285 13.979492 1.547852 C 14.425455 1.74642 14.81608 2.013348 15.151367 2.348633 C 15.486653 2.68392 15.75358 3.074545 15.952148 3.520508 C 16.150715 3.966473 16.25 4.433594 16.25 4.921875 L 16.25 12.578125 C 16.25 13.066406 16.150715 13.533529 15.952148 13.979492 C 15.75358 14.425456 15.486653 14.816081 15.151367 15.151367 C 14.81608 15.486654 14.425455 15.753581 13.979492 15.952148 C 13.533528 16.150717 13.066406 16.25 12.578125 16.25 Z M 8.75 8.90625 C 9.082031 8.90625 9.401041 8.969727 9.707031 9.09668 C 10.013021 9.223633 10.283203 9.404297 10.517578 9.638672 L 14.658203 13.769531 C 14.768879 13.580729 14.853515 13.377279 14.912109 13.15918 C 14.970702 12.941081 14.999999 12.721354 15 12.5 L 15 4.951172 C 14.999999 4.625651 14.933268 4.314779 14.799805 4.018555 C 14.666341 3.722332 14.487305 3.461914 14.262695 3.237305 C 14.038086 3.012695 13.777669 2.83366 13.481445 2.700195 C 13.185221 2.566732 12.874348 2.5 12.548828 2.5 L 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 L 2.5 12.5 C 2.5 12.721354 2.529297 12.941081 2.587891 13.15918 C 2.646484 13.377279 2.73112 13.580729 2.841797 13.769531 L 6.982422 9.638672 C 7.216797 9.404297 7.486979 9.223633 7.792969 9.09668 C 8.098958 8.969727 8.417969 8.90625 8.75 8.90625 Z M 10.625 5.625 C 10.625 5.449219 10.657552 5.286459 10.722656 5.136719 C 10.78776 4.98698 10.877278 4.855145 10.991211 4.741211 C 11.105143 4.627279 11.236979 4.537761 11.386719 4.472656 C 11.536458 4.407553 11.699219 4.375 11.875 4.375 C 12.04427 4.375 12.205402 4.407553 12.358398 4.472656 C 12.511393 4.537761 12.644856 4.627279 12.758789 4.741211 C 12.872721 4.855145 12.962239 4.988607 13.027344 5.141602 C 13.092447 5.294597 13.124999 5.455729 13.125 5.625 C 13.124999 5.800781 13.092447 5.963542 13.027344 6.113281 C 12.962239 6.263021 12.872721 6.394857 12.758789 6.508789 C 12.644856 6.622722 12.513021 6.71224 12.363281 6.777344 C 12.213541 6.842449 12.050781 6.875001 11.875 6.875 C 11.699219 6.875001 11.53483 6.842449 11.381836 6.777344 C 11.228841 6.71224 11.097005 6.62435 10.986328 6.513672 C 10.87565 6.402996 10.78776 6.27116 10.722656 6.118164 C 10.657552 5.96517 10.625 5.800781 10.625 5.625 Z M 7.5 18.75 C 6.966146 18.75 6.455078 18.642578 5.966797 18.427734 C 5.478516 18.212891 5.058594 17.903646 4.707031 17.5 L 13.193359 17.5 C 13.785807 17.5 14.344075 17.382812 14.868164 17.148438 C 15.392252 16.914062 15.849609 16.59668 16.240234 16.196289 C 16.630859 15.795898 16.938477 15.332031 17.163086 14.804688 C 17.387695 14.277344 17.5 13.717448 17.5 13.125 L 17.5 4.707031 C 17.903645 5.058595 18.212891 5.478517 18.427734 5.966797 C 18.642578 6.455079 18.75 6.966146 18.75 7.5 L 18.75 13.125 C 18.75 13.89974 18.601887 14.628906 18.305664 15.3125 C 18.009439 15.996094 17.607422 16.591797 17.099609 17.099609 C 16.591797 17.607422 15.996094 18.009439 15.3125 18.305664 C 14.628906 18.601889 13.899739 18.75 13.125 18.75 Z M 12.5 15 C 12.721354 15 12.94108 14.970703 13.15918 14.912109 C 13.377278 14.853516 13.580729 14.768881 13.769531 14.658203 L 9.638672 10.517578 C 9.391275 10.270183 9.095052 10.146484 8.75 10.146484 C 8.404947 10.146484 8.108724 10.270183 7.861328 10.517578 L 3.730469 14.658203 C 3.919271 14.768881 4.122721 14.853516 4.34082 14.912109 C 4.558919 14.970703 4.778646 15 5 15 Z " />
</controls:SettingsCard.HeaderIcon>
<controls:SettingsCard.Content>
<Button Click="BrowseWallpaperClicked" Content="{x:Bind domain:Translator.Buttons_Browse}" />
</controls:SettingsCard.Content>
</controls:SettingsCard>
<controls:SettingsCard Description="{x:Bind domain:Translator.CustomThemeBuilder_AccentColorDescription}" Header="{x:Bind domain:Translator.CustomThemeBuilder_AccentColorTitle}">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 4.921875 16.25 C 4.433594 16.25 3.966471 16.150717 3.520508 15.952148 C 3.074544 15.753581 2.683919 15.486654 2.348633 15.151367 C 2.013346 14.816081 1.746419 14.425456 1.547852 13.979492 C 1.349284 13.533529 1.25 13.066406 1.25 12.578125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 12.578125 1.25 C 13.066406 1.25 13.533528 1.349285 13.979492 1.547852 C 14.425455 1.74642 14.81608 2.013348 15.151367 2.348633 C 15.486653 2.68392 15.75358 3.074545 15.952148 3.520508 C 16.150715 3.966473 16.25 4.433594 16.25 4.921875 L 16.25 12.578125 C 16.25 13.066406 16.150715 13.533529 15.952148 13.979492 C 15.75358 14.425456 15.486653 14.816081 15.151367 15.151367 C 14.81608 15.486654 14.425455 15.753581 13.979492 15.952148 C 13.533528 16.150717 13.066406 16.25 12.578125 16.25 Z M 8.75 8.90625 C 9.082031 8.90625 9.401041 8.969727 9.707031 9.09668 C 10.013021 9.223633 10.283203 9.404297 10.517578 9.638672 L 14.658203 13.769531 C 14.768879 13.580729 14.853515 13.377279 14.912109 13.15918 C 14.970702 12.941081 14.999999 12.721354 15 12.5 L 15 4.951172 C 14.999999 4.625651 14.933268 4.314779 14.799805 4.018555 C 14.666341 3.722332 14.487305 3.461914 14.262695 3.237305 C 14.038086 3.012695 13.777669 2.83366 13.481445 2.700195 C 13.185221 2.566732 12.874348 2.5 12.548828 2.5 L 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 L 2.5 12.5 C 2.5 12.721354 2.529297 12.941081 2.587891 13.15918 C 2.646484 13.377279 2.73112 13.580729 2.841797 13.769531 L 6.982422 9.638672 C 7.216797 9.404297 7.486979 9.223633 7.792969 9.09668 C 8.098958 8.969727 8.417969 8.90625 8.75 8.90625 Z M 10.625 5.625 C 10.625 5.449219 10.657552 5.286459 10.722656 5.136719 C 10.78776 4.98698 10.877278 4.855145 10.991211 4.741211 C 11.105143 4.627279 11.236979 4.537761 11.386719 4.472656 C 11.536458 4.407553 11.699219 4.375 11.875 4.375 C 12.04427 4.375 12.205402 4.407553 12.358398 4.472656 C 12.511393 4.537761 12.644856 4.627279 12.758789 4.741211 C 12.872721 4.855145 12.962239 4.988607 13.027344 5.141602 C 13.092447 5.294597 13.124999 5.455729 13.125 5.625 C 13.124999 5.800781 13.092447 5.963542 13.027344 6.113281 C 12.962239 6.263021 12.872721 6.394857 12.758789 6.508789 C 12.644856 6.622722 12.513021 6.71224 12.363281 6.777344 C 12.213541 6.842449 12.050781 6.875001 11.875 6.875 C 11.699219 6.875001 11.53483 6.842449 11.381836 6.777344 C 11.228841 6.71224 11.097005 6.62435 10.986328 6.513672 C 10.87565 6.402996 10.78776 6.27116 10.722656 6.118164 C 10.657552 5.96517 10.625 5.800781 10.625 5.625 Z M 7.5 18.75 C 6.966146 18.75 6.455078 18.642578 5.966797 18.427734 C 5.478516 18.212891 5.058594 17.903646 4.707031 17.5 L 13.193359 17.5 C 13.785807 17.5 14.344075 17.382812 14.868164 17.148438 C 15.392252 16.914062 15.849609 16.59668 16.240234 16.196289 C 16.630859 15.795898 16.938477 15.332031 17.163086 14.804688 C 17.387695 14.277344 17.5 13.717448 17.5 13.125 L 17.5 4.707031 C 17.903645 5.058595 18.212891 5.478517 18.427734 5.966797 C 18.642578 6.455079 18.75 6.966146 18.75 7.5 L 18.75 13.125 C 18.75 13.89974 18.601887 14.628906 18.305664 15.3125 C 18.009439 15.996094 17.607422 16.591797 17.099609 17.099609 C 16.591797 17.607422 15.996094 18.009439 15.3125 18.305664 C 14.628906 18.601889 13.899739 18.75 13.125 18.75 Z M 12.5 15 C 12.721354 15 12.94108 14.970703 13.15918 14.912109 C 13.377278 14.853516 13.580729 14.768881 13.769531 14.658203 L 9.638672 10.517578 C 9.391275 10.270183 9.095052 10.146484 8.75 10.146484 C 8.404947 10.146484 8.108724 10.270183 7.861328 10.517578 L 3.730469 14.658203 C 3.919271 14.768881 4.122721 14.853516 4.34082 14.912109 C 4.558919 14.970703 4.778646 15 5 15 Z " />
</controls:SettingsCard.HeaderIcon>
<controls:SettingsCard.Content>
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock Text="{x:Bind domain:Translator.CustomThemeBuilder_PickColor}" />
<Grid
x:Name="PreviewAccentColorGrid"
Width="20"
Height="20"
Background="{ThemeResource SystemAccentColor}" />
</StackPanel>
</Button.Content>
<Button.Flyout>
<Flyout Placement="Bottom">
<muxc:ColorPicker
ColorChanged="PickerColorChanged"
ColorSpectrumShape="Box"
IsAlphaEnabled="False"
IsAlphaSliderVisible="False"
IsAlphaTextInputVisible="False"
IsColorChannelTextInputVisible="False"
IsHexInputVisible="False" />
</Flyout>
</Button.Flyout>
</Button>
</controls:SettingsCard.Content>
</controls:SettingsCard>
</StackPanel>
</ContentDialog>
@@ -0,0 +1,64 @@
using System;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP;
namespace Wino.Dialogs;
public sealed partial class CustomThemeBuilderDialog : ContentDialog
{
public byte[] WallpaperData { get; private set; }
public string AccentColor { get; private set; }
private IThemeService _themeService;
public CustomThemeBuilderDialog()
{
InitializeComponent();
_themeService = WinoApplication.Current.Services.GetService<IThemeService>();
}
private async void ApplyClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
if (Array.Empty<byte>() == WallpaperData)
return;
var deferal = args.GetDeferral();
try
{
await _themeService.CreateNewCustomThemeAsync(ThemeNameBox.Text, AccentColor, WallpaperData);
}
catch (Exception exception)
{
ErrorTextBlock.Text = exception.Message;
}
finally
{
deferal.Complete();
}
}
private async void BrowseWallpaperClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
var dialogService = WinoApplication.Current.Services.GetService<IMailDialogService>();
var pickedFileData = await dialogService.PickWindowsFileContentAsync(".jpg", ".png");
if (pickedFileData == Array.Empty<byte>()) return;
IsPrimaryButtonEnabled = true;
WallpaperData = pickedFileData;
}
private void PickerColorChanged(Microsoft.UI.Xaml.Controls.ColorPicker sender, Microsoft.UI.Xaml.Controls.ColorChangedEventArgs args)
{
PreviewAccentColorGrid.Background = new SolidColorBrush(args.NewColor);
AccentColor = args.NewColor.ToHex();
}
}
@@ -0,0 +1,152 @@
<ContentDialog
x:Class="Wino.Core.UWP.Dialogs.NewAccountDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:accounts="using:Wino.Core.Domain.Models.Accounts"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:helpers="using:Wino.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Title="{x:Bind domain:Translator.NewAccountDialog_Title}"
HorizontalContentAlignment="Stretch"
DefaultButton="Primary"
IsPrimaryButtonEnabled="False"
Opened="DialogOpened"
PrimaryButtonClick="CreateClicked"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_CreateAccount}"
SecondaryButtonClick="CancelClicked"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<DataTemplate x:Key="NewMailProviderTemplate" x:DataType="accounts:ProviderDetail">
<Grid Margin="0,8" Padding="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Width="35"
Height="35"
Source="{x:Bind ProviderImage}" />
<StackPanel
Grid.Column="1"
Margin="12,0"
VerticalAlignment="Center"
Spacing="2">
<TextBlock FontWeight="Bold" Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Description}" />
</StackPanel>
</Grid>
</DataTemplate>
</ContentDialog.Resources>
<Grid MinWidth="400" RowSpacing="12">
<Grid Visibility="{x:Bind IsProviderSelectionVisible, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Account Name -->
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
x:Name="AccountNameTextbox"
Header="{x:Bind domain:Translator.NewAccountDialog_AccountName}"
PlaceholderText="{x:Bind domain:Translator.NewAccountDialog_AccountNamePlaceholder}"
TextChanged="InputChanged" />
<Button Grid.Column="1" VerticalAlignment="Bottom">
<Grid>
<Ellipse
x:Name="SelectedColorEllipse"
Width="16"
Height="16" />
<TextBlock x:Name="PickColorTextblock" Text="{x:Bind domain:Translator.CalendarDisplayOptions_Color}" />
</Grid>
<Button.Flyout>
<Flyout Placement="TopEdgeAlignedLeft">
<GridView
Width="150"
ItemTemplate="{StaticResource AccountColorTemplate}"
ItemsSource="{x:Bind AvailableColors, Mode=OneWay}"
SelectedItem="{x:Bind SelectedColor, Mode=TwoWay}" />
</Flyout>
</Button.Flyout>
</Button>
</Grid>
<ListView
Grid.Row="2"
Margin="0,12"
Padding="0"
ItemTemplate="{StaticResource NewMailProviderTemplate}"
ItemsSource="{x:Bind Providers}"
SelectedItem="{x:Bind SelectedMailProvider, Mode=TwoWay}"
SelectionMode="Single" />
</Grid>
<!-- Known special IMAP login details. -->
<Grid RowSpacing="12" Visibility="{x:Bind IsSpecialImapServerPartVisible, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Click="BackClicked">
<Button.Content>
<Viewbox Width="16">
<PathIcon Data="F1 M 20 9.375 C 20 9.544271 19.93815 9.690756 19.814453 9.814453 C 19.690754 9.938151 19.54427 10 19.375 10 L 2.138672 10 L 9.814453 17.685547 C 9.93815 17.809244 10 17.955729 10 18.125 C 10 18.294271 9.93815 18.440756 9.814453 18.564453 C 9.690755 18.68815 9.544271 18.75 9.375 18.75 C 9.205729 18.75 9.059244 18.68815 8.935547 18.564453 L 0.214844 9.84375 C 0.143229 9.772136 0.089518 9.700521 0.053711 9.628906 C 0.017904 9.557292 0 9.472656 0 9.375 C 0 9.277344 0.017904 9.192709 0.053711 9.121094 C 0.089518 9.049479 0.143229 8.977865 0.214844 8.90625 L 8.935547 0.185547 C 9.059244 0.06185 9.205729 0 9.375 0 C 9.544271 0 9.690755 0.06185 9.814453 0.185547 C 9.93815 0.309246 10 0.45573 10 0.625 C 10 0.794271 9.93815 0.940756 9.814453 1.064453 L 2.138672 8.75 L 19.375 8.75 C 19.54427 8.75 19.690754 8.81185 19.814453 8.935547 C 19.93815 9.059245 20 9.205729 20 9.375 Z " />
</Viewbox>
</Button.Content>
</Button>
<Image
Width="150"
Height="50"
HorizontalAlignment="Center"
Source="{x:Bind SelectedMailProvider.ProviderImage, Mode=OneWay}" />
<TextBox
x:Name="DisplayNameTextBox"
Grid.Row="1"
Header="Display Name"
PlaceholderText="eg. John Doe"
TextChanged="InputChanged" />
<TextBox
x:Name="SpecialImapAddress"
Grid.Row="2"
Header="E-mail Address"
PlaceholderText="eg. johndoe@testmail.com"
TextChanged="InputChanged" />
<PasswordBox
x:Name="AppSpecificPassword"
Grid.Row="3"
Header="App-Specific Password"
PasswordChanged="ImapPasswordChanged" />
<HyperlinkButton
Grid.Row="4"
HorizontalAlignment="Right"
Click="AppSpecificHelpButtonClicked"
Content="How do I get app-specific password?" />
</Grid>
</Grid>
</ContentDialog>
@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.ViewModels.Data;
using Wino.Helpers;
namespace Wino.Core.UWP.Dialogs;
public sealed partial class NewAccountDialog : ContentDialog
{
private Dictionary<SpecialImapProvider, string> helpingLinks = new Dictionary<SpecialImapProvider, string>()
{
{ SpecialImapProvider.iCloud, "https://support.apple.com/en-us/102654" },
{ SpecialImapProvider.Yahoo, "http://help.yahoo.com/kb/SLN15241.html" },
};
public static readonly DependencyProperty IsProviderSelectionVisibleProperty = DependencyProperty.Register(nameof(IsProviderSelectionVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(true));
public static readonly DependencyProperty IsSpecialImapServerPartVisibleProperty = DependencyProperty.Register(nameof(IsSpecialImapServerPartVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(false));
public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged)));
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(nameof(SelectedColor), typeof(AppColorViewModel), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedColorChanged)));
public AppColorViewModel SelectedColor
{
get { return (AppColorViewModel)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
/// <summary>
/// Gets or sets current selected mail provider in the dialog.
/// </summary>
public ProviderDetail SelectedMailProvider
{
get { return (ProviderDetail)GetValue(SelectedMailProviderProperty); }
set { SetValue(SelectedMailProviderProperty, value); }
}
public bool IsProviderSelectionVisible
{
get { return (bool)GetValue(IsProviderSelectionVisibleProperty); }
set { SetValue(IsProviderSelectionVisibleProperty, value); }
}
public bool IsSpecialImapServerPartVisible
{
get { return (bool)GetValue(IsSpecialImapServerPartVisibleProperty); }
set { SetValue(IsSpecialImapServerPartVisibleProperty, value); }
}
// List of available mail providers for now.
public List<IProviderDetail> Providers { get; set; }
public List<AppColorViewModel> AvailableColors { get; set; }
public AccountCreationDialogResult Result = null;
public NewAccountDialog()
{
InitializeComponent();
var themeService = WinoApplication.Current.ThemeService.GetAvailableAccountColors();
AvailableColors = themeService.Select(a => new AppColorViewModel(a)).ToList();
UpdateSelectedColor();
}
private static void OnSelectedProviderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is NewAccountDialog dialog)
dialog.Validate();
}
private static void OnSelectedColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is NewAccountDialog dialog)
dialog.UpdateSelectedColor();
}
private void UpdateSelectedColor()
{
PickColorTextblock.Visibility = SelectedColor == null ? Visibility.Visible : Visibility.Collapsed;
SelectedColorEllipse.Fill = SelectedColor == null ? null : XamlHelpers.GetSolidColorBrushFromHex(SelectedColor.Hex);
}
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
Hide();
}
private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
if (IsSpecialImapServerPartVisible)
{
// Special imap detail input.
var details = new SpecialImapProviderDetails(SpecialImapAddress.Text.Trim(), AppSpecificPassword.Password.Trim(), DisplayNameTextBox.Text.Trim(), SelectedMailProvider.SpecialImapProvider);
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), details, SelectedColor?.Hex ?? string.Empty);
Hide();
return;
}
Validate();
if (IsSecondaryButtonEnabled)
{
if (SelectedMailProvider.SpecialImapProvider != SpecialImapProvider.None)
{
// This step requires app-sepcific password login for some providers.
args.Cancel = true;
IsProviderSelectionVisible = false;
IsSpecialImapServerPartVisible = true;
Validate();
}
else
{
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), null, SelectedColor?.Hex ?? string.Empty);
Hide();
}
}
}
private void InputChanged(object sender, TextChangedEventArgs e) => Validate();
private void SenderNameChanged(object sender, TextChangedEventArgs e) => Validate();
private void Validate()
{
ValidateCreateButton();
ValidateNames();
}
// Returns whether we can create account or not.
private void ValidateCreateButton()
{
bool shouldEnable = SelectedMailProvider != null
&& SelectedMailProvider.IsSupported
&& !string.IsNullOrEmpty(AccountNameTextbox.Text)
&& (IsSpecialImapServerPartVisible ? (!string.IsNullOrEmpty(AppSpecificPassword.Password)
&& !string.IsNullOrEmpty(DisplayNameTextBox.Text)
&& EmailValidation.EmailValidator.Validate(SpecialImapAddress.Text)) : true);
IsPrimaryButtonEnabled = shouldEnable;
}
private void ValidateNames()
{
AccountNameTextbox.IsEnabled = SelectedMailProvider != null;
}
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => Validate();
private void BackClicked(object sender, RoutedEventArgs e)
{
IsSpecialImapServerPartVisible = false;
IsProviderSelectionVisible = true;
Validate();
}
private void ImapPasswordChanged(object sender, RoutedEventArgs e) => Validate();
private async void AppSpecificHelpButtonClicked(object sender, RoutedEventArgs e)
{
var helpUrl = helpingLinks[SelectedMailProvider.SpecialImapProvider];
await Launcher.LaunchUriAsync(new Uri(helpUrl));
}
}
@@ -0,0 +1,28 @@
<ContentDialog
x:Class="Wino.Dialogs.TextInputDialog"
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:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
DefaultButton="Primary"
PrimaryButtonClick="UpdateOrCreateClicked"
SecondaryButtonClick="CancelClicked"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">400</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">400</x:Double>
<x:Double x:Key="ContentDialogMinHeight">200</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</ContentDialog.Resources>
<StackPanel Spacing="12">
<TextBlock x:Name="DialogDescription" TextWrapping="Wrap" />
<TextBox x:Name="FolderTextBox" Text="{x:Bind CurrentInput, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ContentDialog>
@@ -0,0 +1,44 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Wino.Dialogs;
public sealed partial class TextInputDialog : ContentDialog
{
public bool? HasInput { get; set; }
public string CurrentInput
{
get { return (string)GetValue(CurrentInputProperty); }
set { SetValue(CurrentInputProperty, value); }
}
public static readonly DependencyProperty CurrentInputProperty = DependencyProperty.Register(nameof(CurrentInput), typeof(string), typeof(TextInputDialog), new PropertyMetadata(string.Empty));
public TextInputDialog()
{
InitializeComponent();
}
public void SetDescription(string description)
{
DialogDescription.Text = description;
}
public void SetPrimaryButtonText(string text)
{
PrimaryButtonText = text;
}
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
Hide();
}
private void UpdateOrCreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
HasInput = true;
Hide();
}
}
+19
View File
@@ -0,0 +1,19 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP;
public class WinUIDispatcher : IDispatcher
{
private readonly DispatcherQueue _coreDispatcher;
public WinUIDispatcher(DispatcherQueue coreDispatcher)
{
_coreDispatcher = coreDispatcher;
}
public Task ExecuteOnUIThread(Action action) => _coreDispatcher.EnqueueAsync(action, DispatcherQueuePriority.Normal);
}
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Animation;
namespace Wino.Extensions;
public static class AnimationExtensions
{
#region Composition
public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(this Compositor compositor, float? from, float to,
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
{
var animation = compositor.CreateScalarKeyFrameAnimation();
animation.Duration = TimeSpan.FromMilliseconds(duration);
if (!delay.Equals(0)) animation.DelayTime = TimeSpan.FromMilliseconds(delay);
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
animation.InsertKeyFrame(1.0f, to, easing);
animation.IterationBehavior = iterationBehavior;
return animation;
}
public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
{
var animation = compositor.CreateVector2KeyFrameAnimation();
animation.Duration = TimeSpan.FromMilliseconds(duration);
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
animation.InsertKeyFrame(1.0f, to, easing);
animation.IterationBehavior = iterationBehavior;
return animation;
}
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
{
var animation = compositor.CreateVector3KeyFrameAnimation();
animation.Duration = TimeSpan.FromMilliseconds(duration);
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
if (from.HasValue) animation.InsertKeyFrame(0.0f, new Vector3(from.Value, 1.0f), easing);
animation.InsertKeyFrame(1.0f, new Vector3(to, 1.0f), easing);
animation.IterationBehavior = iterationBehavior;
return animation;
}
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector3? from, Vector3 to,
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
{
var animation = compositor.CreateVector3KeyFrameAnimation();
animation.Duration = TimeSpan.FromMilliseconds(duration);
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
animation.InsertKeyFrame(1.0f, to, easing);
animation.IterationBehavior = iterationBehavior;
return animation;
}
#endregion
#region Xaml Storyboard
public static void Animate(this DependencyObject target, double? from, double to,
string propertyPath, int duration = 400, int startTime = 0,
EasingFunctionBase easing = null, Action completed = null, bool enableDependentAnimation = false)
{
if (easing == null)
{
easing = new ExponentialEase();
}
var db = new DoubleAnimation
{
EnableDependentAnimation = enableDependentAnimation,
To = to,
From = from,
EasingFunction = easing,
Duration = TimeSpan.FromMilliseconds(duration)
};
Storyboard.SetTarget(db, target);
Storyboard.SetTargetProperty(db, propertyPath);
var sb = new Storyboard
{
BeginTime = TimeSpan.FromMilliseconds(startTime)
};
if (completed != null)
{
sb.Completed += (s, e) =>
{
completed();
};
}
sb.Children.Add(db);
sb.Begin();
}
#endregion
}
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wino.Extensions;
public enum TransitionDirection
{
TopToBottom,
BottomToTop,
LeftToRight,
RightToLeft
}
public enum ClipAnimationDirection
{
Top,
Bottom,
Left,
Right
}
public enum AnimationAxis
{
X,
Y,
Z
}
public enum AnimationType
{
KeyFrame,
Expression
}
public enum FlickDirection
{
None,
Up,
Down,
Left,
Right
}
public enum ViewState
{
Empty,
Small,
Big,
Full
}
public enum Gesture
{
Initial,
Tap,
Swipe
}
[Flags]
public enum VisualPropertyType
{
None = 0,
Opacity = 1 << 0,
Offset = 1 << 1,
Scale = 1 << 2,
Size = 1 << 3,
RotationAngleInDegrees = 1 << 4,
All = ~0
}
@@ -0,0 +1,195 @@
using System;
using System.Numerics;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
namespace Wino.Extensions;
public static partial class CompositionExtensions
{
public static void EnableFluidVisibilityAnimation(this UIElement element, AnimationAxis? axis = null,
float showFromOffset = 0.0f, float hideToOffset = 0.0f, Vector3? centerPoint = null,
float showFromScale = 1.0f, float hideToScale = 1.0f, float showDuration = 800.0f, float hideDuration = 800.0f,
int showDelay = 0, int hideDelay = 0, bool animateOpacity = true)
{
var elementVisual = element.Visual();
var compositor = elementVisual.Compositor;
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
ScalarKeyFrameAnimation hideOpacityAnimation = null;
ScalarKeyFrameAnimation showOpacityAnimation = null;
ScalarKeyFrameAnimation hideOffsetAnimation = null;
ScalarKeyFrameAnimation showOffsetAnimation = null;
Vector2KeyFrameAnimation hideScaleAnimation = null;
Vector2KeyFrameAnimation showeScaleAnimation = null;
if (animateOpacity)
{
hideOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
hideOpacityAnimation.InsertKeyFrame(1.0f, 0.0f);
hideOpacityAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
hideOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
hideOpacityAnimation.Target = "Opacity";
}
if (!hideToOffset.Equals(0.0f))
{
hideOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
hideOffsetAnimation.InsertKeyFrame(1.0f, hideToOffset);
hideOffsetAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
hideOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
hideOffsetAnimation.Target = $"Translation.{axis}";
}
if (centerPoint.HasValue)
{
elementVisual.CenterPoint = centerPoint.Value;
}
if (!hideToScale.Equals(1.0f))
{
hideScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
hideScaleAnimation.InsertKeyFrame(1.0f, new Vector2(hideToScale));
hideScaleAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
hideScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
hideScaleAnimation.Target = "Scale.XY";
}
var hideAnimationGroup = compositor.CreateAnimationGroup();
if (hideOpacityAnimation != null)
{
hideAnimationGroup.Add(hideOpacityAnimation);
}
if (hideOffsetAnimation != null)
{
hideAnimationGroup.Add(hideOffsetAnimation);
}
if (hideScaleAnimation != null)
{
hideAnimationGroup.Add(hideScaleAnimation);
}
ElementCompositionPreview.SetImplicitHideAnimation(element, hideAnimationGroup);
if (animateOpacity)
{
showOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
showOpacityAnimation.InsertKeyFrame(1.0f, 1.0f);
showOpacityAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
showOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
showOpacityAnimation.Target = "Opacity";
}
if (!showFromOffset.Equals(0.0f))
{
showOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
showOffsetAnimation.InsertKeyFrame(0.0f, showFromOffset);
showOffsetAnimation.InsertKeyFrame(1.0f, 0.0f);
showOffsetAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
showOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
showOffsetAnimation.Target = $"Translation.{axis}";
}
if (!showFromScale.Equals(1.0f))
{
showeScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
showeScaleAnimation.InsertKeyFrame(0.0f, new Vector2(showFromScale));
showeScaleAnimation.InsertKeyFrame(1.0f, Vector2.One);
showeScaleAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
showeScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
showeScaleAnimation.Target = "Scale.XY";
}
var showAnimationGroup = compositor.CreateAnimationGroup();
if (showOpacityAnimation != null)
{
showAnimationGroup.Add(showOpacityAnimation);
}
if (showOffsetAnimation != null)
{
showAnimationGroup.Add(showOffsetAnimation);
}
if (showeScaleAnimation != null)
{
showAnimationGroup.Add(showeScaleAnimation);
}
ElementCompositionPreview.SetImplicitShowAnimation(element, showAnimationGroup);
}
public static void EnableImplicitAnimation(this UIElement element, VisualPropertyType typeToAnimate,
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
{
var visual = element.Visual();
var compositor = visual.Compositor;
var animationCollection = compositor.CreateImplicitAnimationCollection();
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
{
if (!typeToAnimate.HasFlag(type)) continue;
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
if (animation != null)
{
animationCollection[type.ToString()] = animation;
}
}
visual.ImplicitAnimations = animationCollection;
}
public static void EnableImplicitAnimation(this Visual visual, VisualPropertyType typeToAnimate,
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
{
var compositor = visual.Compositor;
var animationCollection = compositor.CreateImplicitAnimationCollection();
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
{
if (!typeToAnimate.HasFlag(type)) continue;
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
if (animation != null)
{
animationCollection[type.ToString()] = animation;
}
}
visual.ImplicitAnimations = animationCollection;
}
private static KeyFrameAnimation CreateAnimationByType(Compositor compositor, VisualPropertyType type,
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
{
KeyFrameAnimation animation;
switch (type)
{
case VisualPropertyType.Offset:
case VisualPropertyType.Scale:
animation = compositor.CreateVector3KeyFrameAnimation();
break;
case VisualPropertyType.Size:
animation = compositor.CreateVector2KeyFrameAnimation();
break;
case VisualPropertyType.Opacity:
case VisualPropertyType.RotationAngleInDegrees:
animation = compositor.CreateScalarKeyFrameAnimation();
break;
default:
return null;
}
animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue", easing);
animation.Duration = TimeSpan.FromMilliseconds(duration);
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
animation.Target = type.ToString();
return animation;
}
}
@@ -0,0 +1,33 @@
using Microsoft.UI.Xaml;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Extensions;
public static class ElementThemeExtensions
{
public static ApplicationElementTheme ToWinoElementTheme(this ElementTheme elementTheme)
{
switch (elementTheme)
{
case ElementTheme.Light:
return ApplicationElementTheme.Light;
case ElementTheme.Dark:
return ApplicationElementTheme.Dark;
}
return ApplicationElementTheme.Default;
}
public static ElementTheme ToWindowsElementTheme(this ApplicationElementTheme elementTheme)
{
switch (elementTheme)
{
case ApplicationElementTheme.Light:
return ElementTheme.Light;
case ApplicationElementTheme.Dark:
return ElementTheme.Dark;
}
return ElementTheme.Default;
}
}
@@ -0,0 +1,24 @@
using Windows.ApplicationModel;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Extensions;
public static class StartupTaskStateExtensions
{
public static StartupBehaviorResult AsStartupBehaviorResult(this StartupTaskState state)
{
switch (state)
{
case StartupTaskState.Disabled:
case StartupTaskState.DisabledByPolicy:
return StartupBehaviorResult.Disabled;
case StartupTaskState.DisabledByUser:
return StartupBehaviorResult.DisabledByUser;
case StartupTaskState.Enabled:
case StartupTaskState.EnabledByPolicy:
return StartupBehaviorResult.Enabled;
default:
return StartupBehaviorResult.Fatal;
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Wino.Core.Domain.Models.Common;
namespace Wino.Core.UWP.Extensions;
public static class StorageFileExtensions
{
public static async Task<SharedFile> ToSharedFileAsync(this Windows.Storage.StorageFile storageFile)
{
var content = await storageFile.ToByteArrayAsync();
return new SharedFile(storageFile.Path, content);
}
public static async Task<byte[]> ToByteArrayAsync(this StorageFile file)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
using (var stream = await file.OpenReadAsync())
using (var memoryStream = new MemoryStream())
{
await stream.AsStreamForRead().CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
}
@@ -0,0 +1,19 @@
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Extensions;
public static class UIExtensions
{
public static InfoBarSeverity AsMUXCInfoBarSeverity(this InfoBarMessageType messageType)
{
return messageType switch
{
InfoBarMessageType.Error => InfoBarSeverity.Error,
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
InfoBarMessageType.Information => InfoBarSeverity.Informational,
InfoBarMessageType.Success => InfoBarSeverity.Success,
_ => InfoBarSeverity.Informational
};
}
}
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Media;
using Windows.Foundation;
namespace Wino.Extensions;
public static class UtilExtensions
{
public static float ToFloat(this double value) => (float)value;
public static List<FrameworkElement> Children(this DependencyObject parent)
{
var list = new List<FrameworkElement>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is FrameworkElement)
{
list.Add(child as FrameworkElement);
}
list.AddRange(Children(child));
}
return list;
}
public static T GetChildByName<T>(this DependencyObject parent, string name)
{
var childControls = Children(parent);
var controls = childControls.OfType<FrameworkElement>();
if (controls == null)
{
return default(T);
}
var control = controls
.Where(x => x.Name.Equals(name))
.Cast<T>()
.First();
return control;
}
public static Visual Visual(this UIElement element) =>
ElementCompositionPreview.GetElementVisual(element);
public static void SetChildVisual(this UIElement element, Visual childVisual) =>
ElementCompositionPreview.SetElementChildVisual(element, childVisual);
public static Point RelativePosition(this UIElement element, UIElement other) =>
element.TransformToVisual(other).TransformPoint(new Point(0, 0));
public static bool IsFullyVisibile(this FrameworkElement element, FrameworkElement parent)
{
if (element == null || parent == null)
return false;
if (element.Visibility != Visibility.Visible)
return false;
var elementBounds = element.TransformToVisual(parent).TransformBounds(new Windows.Foundation.Rect(0, 0, element.ActualWidth, element.ActualHeight));
var containerBounds = new Windows.Foundation.Rect(0, 0, parent.ActualWidth, parent.ActualHeight);
var originalElementWidth = elementBounds.Width;
var originalElementHeight = elementBounds.Height;
elementBounds.Intersect(containerBounds);
var newElementWidth = elementBounds.Width;
var newElementHeight = elementBounds.Height;
return originalElementWidth.Equals(newElementWidth) && originalElementHeight.Equals(newElementHeight);
}
public static void ScrollToElement(this ScrollViewer scrollViewer, FrameworkElement element,
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null, bool bringToTopOrLeft = true, bool addMargin = true)
{
if (!bringToTopOrLeft && element.IsFullyVisibile(scrollViewer))
return;
var contentArea = (FrameworkElement)scrollViewer.Content;
var position = element.RelativePosition(contentArea);
if (isVerticalScrolling)
{
// Accomodate for additional header.
scrollViewer.ChangeView(null, Math.Max(0, position.Y - (addMargin ? 48 : 0)), zoomFactor, !smoothScrolling);
}
else
{
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
}
}
public static T[] GetValues<T>() where T : struct, Enum => Enum.GetValues<T>();
}
@@ -0,0 +1,45 @@
using System;
using System.Threading.Tasks;
namespace Wino.Core.UWP.Extensions;
public static class WebViewExtensions
{
/// <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)
{
string script = functionName + "(" + string.Join(", ", parameters) + ");";
return isChromiumDisposed ? string.Empty : await Chromium.ExecuteScriptAsync(script);
}
public static async Task<string> ExecuteScriptFunctionSafeAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string functionName, params string[] parameters)
{
if (Chromium == null) return string.Empty;
try
{
return await Chromium.ExecuteScriptFunctionAsync(functionName, parameters: parameters);
}
catch { }
return string.Empty;
}
public static async Task<string> ExecuteScriptSafeAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string script)
{
if (Chromium == null) return string.Empty;
try
{
return await Chromium.ExecuteScriptAsync(script);
}
catch { }
return string.Empty;
}
}
@@ -0,0 +1,53 @@
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
namespace Wino.Helpers;
public static class WinoVisualTreeHelper
{
public static T GetChildObject<T>(DependencyObject obj, string name) where T : FrameworkElement
{
DependencyObject child = null;
T grandChild = null;
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
{
return (T)child;
}
else
{
grandChild = GetChildObject<T>(child, name);
}
if (grandChild != null)
{
return grandChild;
}
}
return null;
}
public static IEnumerable<T> FindDescendants<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindDescendants<T>(child))
{
yield return childOfChild;
}
}
}
}
}
+366
View File
@@ -0,0 +1,366 @@
using System;
using System.Globalization;
using System.Linq;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
using Windows.UI.Text;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.UWP.Controls;
namespace Wino.Helpers;
public static class XamlHelpers
{
private const string TwentyFourHourTimeFormat = "HH:mm";
private const string TwelveHourTimeFormat = "hh:mm tt";
#region Converters
public static bool IsMultiple(int count) => count > 1;
public static bool ReverseIsMultiple(int count) => count < 1;
public static PopupPlacementMode GetPlaccementModeForCalendarType(CalendarDisplayType type)
{
return type switch
{
CalendarDisplayType.Week => PopupPlacementMode.Right,
_ => PopupPlacementMode.Bottom,
};
}
public static Visibility ReverseBoolToVisibilityConverter(bool value) => value ? Visibility.Collapsed : Visibility.Visible;
public static Visibility ReverseVisibilityConverter(Visibility visibility) => visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
public static bool ReverseBoolConverter(bool value) => !value;
public static bool ShouldDisplayPreview(string text) => text.Any(x => char.IsLetter(x));
public static bool CountToBooleanConverter(int value) => value > 0;
public static bool ObjectEquals(object obj1, object obj2) => object.Equals(obj1, obj2);
public static Visibility CountToVisibilityConverter(int value) => value > 0 ? Visibility.Visible : Visibility.Collapsed;
public static Visibility CountToVisibilityConverterWithThreshold(int value, int threshold) => value > threshold ? Visibility.Visible : Visibility.Collapsed;
public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType)
{
return messageType switch
{
InfoBarMessageType.Information => InfoBarSeverity.Informational,
InfoBarMessageType.Success => InfoBarSeverity.Success,
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
InfoBarMessageType.Error => InfoBarSeverity.Error,
_ => InfoBarSeverity.Informational,
};
}
public static SolidColorBrush GetReadableTextColor(string backgroundColor)
{
if (!backgroundColor.StartsWith("#")) throw new ArgumentException("Hex color must start with #.");
backgroundColor = backgroundColor.TrimStart('#');
if (backgroundColor.Length == 6)
{
var r = int.Parse(backgroundColor.Substring(0, 2), NumberStyles.HexNumber);
var g = int.Parse(backgroundColor.Substring(2, 2), NumberStyles.HexNumber);
var b = int.Parse(backgroundColor.Substring(4, 2), NumberStyles.HexNumber);
// Calculate relative luminance
double luminance = (0.2126 * GetLinearValue(r)) +
(0.7152 * GetLinearValue(g)) +
(0.0722 * GetLinearValue(b));
return luminance > 0.5 ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
}
else
{
throw new ArgumentException("Hex color must be 6 characters long (e.g., #RRGGBB).");
}
}
private static double GetLinearValue(int colorComponent)
{
double sRGB = colorComponent / 255.0;
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.Pow((sRGB + 0.055) / 1.055, 2.4);
}
public static Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayModeConverter(SplitViewDisplayMode splitViewDisplayMode)
{
return splitViewDisplayMode switch
{
SplitViewDisplayMode.CompactOverlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact,
SplitViewDisplayMode.CompactInline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
SplitViewDisplayMode.Overlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
SplitViewDisplayMode.Inline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
_ => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
};
}
public static string GetColorFromHex(Color color) => color.ToHex();
public static Color GetWindowsColorFromHex(string hex) => hex.ToColor();
public static SolidColorBrush GetSolidColorBrushFromHex(string colorHex) => string.IsNullOrEmpty(colorHex) ? new SolidColorBrush(Colors.Transparent) : new SolidColorBrush(colorHex.ToColor());
public static Visibility IsSelectionModeMultiple(ListViewSelectionMode mode) => mode == ListViewSelectionMode.Multiple ? Visibility.Visible : Visibility.Collapsed;
public static FontWeight GetFontWeightBySyncState(bool isSyncing) => isSyncing ? FontWeights.SemiBold : FontWeights.Normal;
public static FontWeight GetFontWeightByChildSelectedState(bool isChildSelected) => isChildSelected ? FontWeights.SemiBold : FontWeights.Normal;
public static Visibility StringToVisibilityConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Collapsed : Visibility.Visible;
public static Visibility StringToVisibilityReversedConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Visible : Visibility.Collapsed;
public static string GetMailItemDisplaySummaryForListing(bool isDraft, DateTime receivedDate, bool prefer24HourTime)
{
if (isDraft)
return Translator.Draft;
else
{
var localTime = receivedDate.ToLocalTime();
return prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat);
}
}
public static string GetCreationDateString(DateTime date, bool prefer24HourTime)
{
var localTime = date.ToLocalTime();
return $"{localTime.ToString("D", CultureInfo.DefaultThreadCurrentUICulture)} {(prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat))}";
}
public static string GetMailGroupDateString(object groupObject)
{
if (groupObject is string stringObject)
return stringObject;
object dateObject = null;
// From regular mail header template
if (groupObject is DateTime groupedDate)
dateObject = groupedDate;
else if (groupObject is IGrouping<object, IMailItem> groupKey)
{
// From semantic group header.
dateObject = groupKey.Key;
}
if (dateObject != null)
{
if (dateObject is DateTime dateTimeValue)
{
if (dateTimeValue == DateTime.Today)
return Translator.Today;
else if (dateTimeValue == DateTime.Today.AddDays(-1))
return Translator.Yesterday;
else
{
return dateTimeValue.ToString("D", CultureInfo.DefaultThreadCurrentUICulture);
}
}
else
return dateObject.ToString();
}
return Translator.UnknownDateHeader;
}
public static bool ConnectionStatusEquals(WinoServerConnectionStatus winoServerConnectionStatus, WinoServerConnectionStatus connectionStatus) => winoServerConnectionStatus == connectionStatus;
#endregion
#region Wino Font Icon Transformation
public static WinoIconGlyph GetWinoIconGlyph(FilterOptionType type) => type switch
{
FilterOptionType.All => WinoIconGlyph.Mail,
FilterOptionType.Unread => WinoIconGlyph.MarkUnread,
FilterOptionType.Flagged => WinoIconGlyph.Flag,
FilterOptionType.Mentions => WinoIconGlyph.NewMail,
FilterOptionType.Files => WinoIconGlyph.Attachment,
_ => WinoIconGlyph.None,
};
public static WinoIconGlyph GetWinoIconGlyph(SortingOptionType type) => type switch
{
SortingOptionType.Sender => WinoIconGlyph.SortTextDesc,
SortingOptionType.ReceiveDate => WinoIconGlyph.SortLinesDesc,
_ => WinoIconGlyph.None,
};
public static WinoIconGlyph GetWinoIconGlyph(MailOperation operation)
{
return operation switch
{
MailOperation.None => WinoIconGlyph.None,
MailOperation.Archive => WinoIconGlyph.Archive,
MailOperation.UnArchive => WinoIconGlyph.UnArchive,
MailOperation.SoftDelete => WinoIconGlyph.Delete,
MailOperation.HardDelete => WinoIconGlyph.Delete,
MailOperation.Move => WinoIconGlyph.Forward,
MailOperation.MoveToJunk => WinoIconGlyph.Blocked,
MailOperation.MoveToFocused => WinoIconGlyph.None,
MailOperation.MoveToOther => WinoIconGlyph.None,
MailOperation.AlwaysMoveToOther => WinoIconGlyph.None,
MailOperation.AlwaysMoveToFocused => WinoIconGlyph.None,
MailOperation.SetFlag => WinoIconGlyph.Flag,
MailOperation.ClearFlag => WinoIconGlyph.ClearFlag,
MailOperation.MarkAsRead => WinoIconGlyph.MarkRead,
MailOperation.MarkAsUnread => WinoIconGlyph.MarkUnread,
MailOperation.MarkAsNotJunk => WinoIconGlyph.Blocked,
MailOperation.Ignore => WinoIconGlyph.Ignore,
MailOperation.Reply => WinoIconGlyph.Reply,
MailOperation.ReplyAll => WinoIconGlyph.ReplyAll,
MailOperation.Zoom => WinoIconGlyph.Zoom,
MailOperation.SaveAs => WinoIconGlyph.Save,
MailOperation.Print => WinoIconGlyph.Print,
MailOperation.Find => WinoIconGlyph.Find,
MailOperation.Forward => WinoIconGlyph.Forward,
MailOperation.DarkEditor => WinoIconGlyph.DarkEditor,
MailOperation.LightEditor => WinoIconGlyph.LightEditor,
MailOperation.ViewMessageSource => WinoIconGlyph.ViewMessageSource,
_ => WinoIconGlyph.None,
};
}
public static WinoIconGlyph GetPathGeometry(FolderOperation operation)
{
return operation switch
{
FolderOperation.None => WinoIconGlyph.None,
FolderOperation.Pin => WinoIconGlyph.Pin,
FolderOperation.Unpin => WinoIconGlyph.UnPin,
FolderOperation.MarkAllAsRead => WinoIconGlyph.MarkRead,
FolderOperation.DontSync => WinoIconGlyph.DontSync,
FolderOperation.Empty => WinoIconGlyph.EmptyFolder,
FolderOperation.Rename => WinoIconGlyph.Rename,
FolderOperation.Delete => WinoIconGlyph.Delete,
FolderOperation.Move => WinoIconGlyph.Forward,
FolderOperation.TurnOffNotifications => WinoIconGlyph.TurnOfNotifications,
FolderOperation.CreateSubFolder => WinoIconGlyph.CreateFolder,
_ => WinoIconGlyph.None,
};
}
public static WinoIconGlyph GetSpecialFolderPathIconGeometry(SpecialFolderType specialFolderType)
{
return specialFolderType switch
{
SpecialFolderType.Inbox => WinoIconGlyph.SpecialFolderInbox,
SpecialFolderType.Starred => WinoIconGlyph.SpecialFolderStarred,
SpecialFolderType.Important => WinoIconGlyph.SpecialFolderImportant,
SpecialFolderType.Sent => WinoIconGlyph.SpecialFolderSent,
SpecialFolderType.Draft => WinoIconGlyph.SpecialFolderDraft,
SpecialFolderType.Archive => WinoIconGlyph.SpecialFolderArchive,
SpecialFolderType.Deleted => WinoIconGlyph.SpecialFolderDeleted,
SpecialFolderType.Junk => WinoIconGlyph.SpecialFolderJunk,
SpecialFolderType.Chat => WinoIconGlyph.SpecialFolderChat,
SpecialFolderType.Category => WinoIconGlyph.SpecialFolderCategory,
SpecialFolderType.Unread => WinoIconGlyph.SpecialFolderUnread,
SpecialFolderType.Forums => WinoIconGlyph.SpecialFolderForums,
SpecialFolderType.Updates => WinoIconGlyph.SpecialFolderUpdated,
SpecialFolderType.Personal => WinoIconGlyph.SpecialFolderPersonal,
SpecialFolderType.Promotions => WinoIconGlyph.SpecialFolderPromotions,
SpecialFolderType.Social => WinoIconGlyph.SpecialFolderSocial,
SpecialFolderType.Other => WinoIconGlyph.SpecialFolderOther,
SpecialFolderType.More => WinoIconGlyph.SpecialFolderMore,
_ => WinoIconGlyph.None,
};
}
public static WinoIconGlyph GetProviderIcon(MailProviderType providerType, SpecialImapProvider specialImapProvider)
{
if (specialImapProvider == SpecialImapProvider.None)
{
return providerType switch
{
MailProviderType.Outlook => WinoIconGlyph.Microsoft,
MailProviderType.Gmail => WinoIconGlyph.Google,
MailProviderType.IMAP4 => WinoIconGlyph.IMAP,
_ => WinoIconGlyph.None,
};
}
else
{
return specialImapProvider switch
{
SpecialImapProvider.iCloud => WinoIconGlyph.Apple,
SpecialImapProvider.Yahoo => WinoIconGlyph.Yahoo,
_ => WinoIconGlyph.None,
};
}
}
public static WinoIconGlyph GetProviderIcon(MailAccount account)
=> GetProviderIcon(account.ProviderType, account.SpecialImapProvider);
public static Geometry GetPathGeometry(string pathMarkup)
{
string xaml =
"<Path " +
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
"<Path.Data>" + pathMarkup + "</Path.Data></Path>";
var path = XamlReader.Load(xaml) as Microsoft.UI.Xaml.Shapes.Path;
Geometry geometry = path.Data;
path.Data = null;
return geometry;
}
#endregion
#region Internationalization
public static string GetOperationString(MailOperation operation)
{
return operation switch
{
MailOperation.None => "unknown",
MailOperation.Archive => Translator.MailOperation_Archive,
MailOperation.UnArchive => Translator.MailOperation_Unarchive,
MailOperation.SoftDelete => Translator.MailOperation_Delete,
MailOperation.HardDelete => Translator.MailOperation_Delete,
MailOperation.Move => Translator.MailOperation_Move,
MailOperation.MoveToJunk => Translator.MailOperation_MoveJunk,
MailOperation.MoveToFocused => Translator.MailOperation_MoveFocused,
MailOperation.MoveToOther => Translator.MailOperation_MoveOther,
MailOperation.AlwaysMoveToOther => Translator.MailOperation_AlwaysMoveOther,
MailOperation.AlwaysMoveToFocused => Translator.MailOperation_AlwaysMoveFocused,
MailOperation.SetFlag => Translator.MailOperation_SetFlag,
MailOperation.ClearFlag => Translator.MailOperation_ClearFlag,
MailOperation.MarkAsRead => Translator.MailOperation_MarkAsRead,
MailOperation.MarkAsUnread => Translator.MailOperation_MarkAsUnread,
MailOperation.MarkAsNotJunk => Translator.MailOperation_MarkNotJunk,
MailOperation.Seperator => string.Empty,
MailOperation.Ignore => Translator.MailOperation_Ignore,
MailOperation.Reply => Translator.MailOperation_Reply,
MailOperation.ReplyAll => Translator.MailOperation_ReplyAll,
MailOperation.Zoom => Translator.MailOperation_Zoom,
MailOperation.SaveAs => Translator.MailOperation_SaveAs,
MailOperation.Find => Translator.MailOperation_Find,
MailOperation.Forward => Translator.MailOperation_Forward,
MailOperation.DarkEditor => string.Empty,
MailOperation.LightEditor => string.Empty,
MailOperation.Print => Translator.MailOperation_Print,
MailOperation.ViewMessageSource => Translator.MailOperation_ViewMessageSource,
MailOperation.Navigate => Translator.MailOperation_Navigate,
_ => "unknown",
};
}
public static string GetOperationString(FolderOperation operation)
{
return operation switch
{
FolderOperation.None => string.Empty,
FolderOperation.Pin => Translator.FolderOperation_Pin,
FolderOperation.Unpin => Translator.FolderOperation_Unpin,
FolderOperation.MarkAllAsRead => Translator.FolderOperation_MarkAllAsRead,
FolderOperation.DontSync => Translator.FolderOperation_DontSync,
FolderOperation.Empty => Translator.FolderOperation_Empty,
FolderOperation.Rename => Translator.FolderOperation_Rename,
FolderOperation.Delete => Translator.FolderOperation_Delete,
FolderOperation.Move => Translator.FolderOperation_Move,
FolderOperation.CreateSubFolder => Translator.FolderOperation_CreateSubFolder,
_ => string.Empty,
};
}
#endregion
}
@@ -0,0 +1,11 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Wino.Core.WinUI.Interfaces;
public interface IWinoShellWindow
{
void HandleAppActivation(LaunchActivatedEventArgs args);
Microsoft.UI.Xaml.Controls.TitleBar GetTitleBar();
Frame GetMainFrame();
}
@@ -0,0 +1,30 @@
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Personalization;
using Wino.Services;
namespace Wino.Core.UWP.Models.Personalization;
/// <summary>
/// Custom themes that are generated by users.
/// </summary>
public class CustomAppTheme : AppThemeBase
{
public CustomAppTheme(CustomThemeMetadata metadata) : base(metadata.Name, metadata.Id)
{
AccentColor = metadata.AccentColorHex;
}
public override AppThemeType AppThemeType => AppThemeType.Custom;
public override string GetBackgroundPreviewImagePath()
=> $"ms-appdata:///local/{ThemeService.CustomThemeFolderName}/{Id}_preview.jpg";
public override async Task<string> GetThemeResourceDictionaryContentAsync()
{
var customAppThemeFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Wino.Core.UWP/AppThemes/Custom.xaml"));
return await FileIO.ReadTextAsync(customAppThemeFile);
}
}
@@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Personalization;
namespace Wino.Core.UWP.Models.Personalization;
/// <summary>
/// Forest, Nighty, Clouds etc. applies to pre-defined themes in Wino.
/// </summary>
public class PreDefinedAppTheme : AppThemeBase
{
public PreDefinedAppTheme(string themeName,
Guid id,
string accentColor = "",
ApplicationElementTheme forcedElementTheme = ApplicationElementTheme.Default) : base(themeName, id)
{
AccentColor = accentColor;
ForceElementTheme = forcedElementTheme;
}
public override AppThemeType AppThemeType => AppThemeType.PreDefined;
public override string GetBackgroundPreviewImagePath()
=> $"ms-appx:///Wino.Core.UWP/BackgroundImages/{ThemeName}.jpg";
public override async Task<string> GetThemeResourceDictionaryContentAsync()
{
var xamlDictionaryFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Wino.Core.UWP/AppThemes/{ThemeName}.xaml"));
return await FileIO.ReadTextAsync(xamlDictionaryFile);
}
}
@@ -0,0 +1,12 @@
using System;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Models.Personalization;
// Mica - Acrylic.
public class SystemAppTheme : PreDefinedAppTheme
{
public SystemAppTheme(string themeName, Guid id) : base(themeName, id, "") { }
public override AppThemeType AppThemeType => AppThemeType.System;
}
@@ -0,0 +1,24 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.UWP.Models.Personalization;
namespace Wino.Core.UWP.Selectors;
public partial class AppThemePreviewTemplateSelector : DataTemplateSelector
{
public DataTemplate SystemThemeTemplate { get; set; }
public DataTemplate PreDefinedThemeTemplate { get; set; }
public DataTemplate CustomAppTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is SystemAppTheme)
return SystemThemeTemplate;
else if (item is PreDefinedAppTheme)
return PreDefinedThemeTemplate;
else if (item is CustomAppTheme)
return CustomAppTemplate;
return base.SelectTemplateCore(item);
}
}
@@ -0,0 +1,37 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Selectors;
public partial class CustomWinoMessageDialogIconSelector : DataTemplateSelector
{
public DataTemplate InfoIconTemplate { get; set; }
public DataTemplate WarningIconTemplate { get; set; }
public DataTemplate QuestionIconTemplate { get; set; }
public DataTemplate ErrorIconTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item == null) return null;
if (item is WinoCustomMessageDialogIcon icon)
{
switch (icon)
{
case WinoCustomMessageDialogIcon.Information:
return InfoIconTemplate;
case WinoCustomMessageDialogIcon.Warning:
return WarningIconTemplate;
case WinoCustomMessageDialogIcon.Error:
return ErrorIconTemplate;
case WinoCustomMessageDialogIcon.Question:
return QuestionIconTemplate;
default:
throw new Exception("Unknown custom message dialog icon.");
}
}
return base.SelectTemplateCore(item, container);
}
}
@@ -0,0 +1,51 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Core.UWP.Selectors;
public partial class FileAttachmentTypeSelector : DataTemplateSelector
{
public DataTemplate None { get; set; }
public DataTemplate Executable { get; set; }
public DataTemplate Image { get; set; }
public DataTemplate Audio { get; set; }
public DataTemplate Video { get; set; }
public DataTemplate PDF { get; set; }
public DataTemplate HTML { get; set; }
public DataTemplate RarArchive { get; set; }
public DataTemplate Archive { get; set; }
public DataTemplate Other { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item == null)
return None;
var type = (MailAttachmentType)item;
switch (type)
{
case MailAttachmentType.None:
return None;
case MailAttachmentType.Executable:
return Executable;
case MailAttachmentType.Image:
return Image;
case MailAttachmentType.Audio:
return Audio;
case MailAttachmentType.Video:
return Video;
case MailAttachmentType.PDF:
return PDF;
case MailAttachmentType.HTML:
return HTML;
case MailAttachmentType.RarArchive:
return RarArchive;
case MailAttachmentType.Archive:
return Archive;
default:
return Other;
}
}
}
@@ -0,0 +1,58 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.MenuItems;
namespace Wino.Core.UWP.Selectors;
public partial class NavigationMenuTemplateSelector : DataTemplateSelector
{
public DataTemplate MenuItemTemplate { get; set; }
public DataTemplate AccountManagementTemplate { get; set; }
public DataTemplate ClickableAccountMenuTemplate { get; set; }
public DataTemplate MergedAccountTemplate { get; set; }
public DataTemplate MergedAccountFolderTemplate { get; set; }
public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; }
public DataTemplate FolderMenuTemplate { get; set; }
public DataTemplate SettingsItemTemplate { get; set; }
public DataTemplate MoreItemsFolderTemplate { get; set; }
public DataTemplate RatingItemTemplate { get; set; }
public DataTemplate CreateNewFolderTemplate { get; set; }
public DataTemplate SeperatorTemplate { get; set; }
public DataTemplate NewMailTemplate { get; set; }
public DataTemplate CategoryItemsTemplate { get; set; }
public DataTemplate FixAuthenticationIssueTemplate { get; set; }
public DataTemplate FixMissingFolderConfigTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is NewMailMenuItem)
return NewMailTemplate;
else if (item is SettingsItem)
return SettingsItemTemplate;
else if (item is SeperatorItem)
return SeperatorTemplate;
else if (item is AccountMenuItem accountMenuItem)
// Merged inbox account menu items must be nested.
return ClickableAccountMenuTemplate;
else if (item is ManageAccountsMenuItem)
return AccountManagementTemplate;
else if (item is RateMenuItem)
return RatingItemTemplate;
else if (item is MergedAccountMenuItem)
return MergedAccountTemplate;
else if (item is MergedAccountMoreFolderMenuItem)
return MergedAccountMoreExpansionItemTemplate;
else if (item is MergedAccountFolderMenuItem)
return MergedAccountFolderTemplate;
else if (item is FolderMenuItem)
return FolderMenuTemplate;
else if (item is FixAccountIssuesMenuItem fixAccountIssuesMenuItem)
return fixAccountIssuesMenuItem.Account.AttentionReason == Domain.Enums.AccountAttentionReason.MissingSystemFolderConfiguration
? FixMissingFolderConfigTemplate : FixAuthenticationIssueTemplate;
else
{
var type = item.GetType();
return null;
}
}
}
@@ -0,0 +1,26 @@
using System.Linq;
using Microsoft.UI.Xaml;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP;
namespace Wino.Services;
public class ApplicationResourceManager : IApplicationResourceManager<ResourceDictionary>
{
public void AddResource(ResourceDictionary resource)
=> WinoApplication.Current.Resources.MergedDictionaries.Add(resource);
public void RemoveResource(ResourceDictionary resource)
=> WinoApplication.Current.Resources.MergedDictionaries.Remove(resource);
public bool ContainsResourceKey(string resourceKey)
=> WinoApplication.Current.Resources.ContainsKey(resourceKey);
public ResourceDictionary GetLastResource()
=> WinoApplication.Current.Resources.MergedDictionaries.LastOrDefault();
public void ReplaceResource(string resourceKey, object resource)
=> WinoApplication.Current.Resources[resourceKey] = resource;
public TReturnType GetResource<TReturnType>(string resourceKey)
=> (TReturnType)WinoApplication.Current.Resources[resourceKey];
}
@@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP.Services;
public class ClipboardService : IClipboardService
{
public Task CopyClipboardAsync(string text)
{
var package = new DataPackage();
package.SetText(text);
Clipboard.SetContent(package);
return Task.CompletedTask;
}
}
@@ -0,0 +1,51 @@
using System;
using System.ComponentModel;
using System.Globalization;
using Windows.Foundation.Collections;
using Windows.Storage;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP.Services;
public class ConfigurationService : IConfigurationService
{
public T Get<T>(string key, T defaultValue = default)
=> GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue);
public T GetRoaming<T>(string key, T defaultValue = default)
=> GetInternal(key, ApplicationData.Current.RoamingSettings.Values, defaultValue);
public void Set(string key, object value)
=> SetInternal(key, value, ApplicationData.Current.LocalSettings.Values);
public void SetRoaming(string key, object value)
=> SetInternal(key, value, ApplicationData.Current.RoamingSettings.Values);
private static T GetInternal<T>(string key, IPropertySet collection, T defaultValue = default)
{
if (collection.TryGetValue(key, out object value))
{
var stringValue = value?.ToString();
if (typeof(T).IsEnum)
return (T)Enum.Parse(typeof(T), stringValue);
if ((typeof(T) == typeof(Guid?) || typeof(T) == typeof(Guid)) && Guid.TryParse(stringValue, out Guid guidResult))
{
return (T)(object)guidResult;
}
if (typeof(T) == typeof(TimeSpan))
{
return (T)(object)TimeSpan.Parse(stringValue);
}
return (T)Convert.ChangeType(stringValue, typeof(T));
}
return defaultValue;
}
private static void SetInternal(string key, object value, IPropertySet collection)
=> collection[key] = value?.ToString();
}
@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Serilog;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Common;
using Wino.Core.UWP.Dialogs;
using Wino.Core.UWP.Extensions;
using Wino.Dialogs;
using Wino.Messaging.Client.Shell;
namespace Wino.Core.UWP.Services;
public class DialogServiceBase : IDialogServiceBase
{
private SemaphoreSlim _presentationSemaphore = new SemaphoreSlim(1);
protected IThemeService ThemeService { get; }
protected IConfigurationService ConfigurationService { get; }
protected IApplicationResourceManager<ResourceDictionary> ApplicationResourceManager { get; }
public DialogServiceBase(IThemeService themeService, IConfigurationService configurationService, IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
{
ThemeService = themeService;
ConfigurationService = configurationService;
ApplicationResourceManager = applicationResourceManager;
}
public async Task<string> PickFilePathAsync(string saveFileName)
{
var picker = new FolderPicker()
{
SuggestedStartLocation = PickerLocationId.Desktop
};
picker.FileTypeFilter.Add("*");
var folder = await picker.PickSingleFolderAsync();
if (folder == null) return string.Empty;
StorageApplicationPermissions.FutureAccessList.Add(folder);
return folder.Path;
//var picker = new FileSavePicker
//{
// SuggestedStartLocation = PickerLocationId.Desktop,
// SuggestedFileName = saveFileName
//};
//picker.FileTypeChoices.Add(Translator.FilteringOption_All, [".*"]);
//var file = await picker.PickSaveFileAsync();
//if (file == null) return string.Empty;
//StorageApplicationPermissions.FutureAccessList.Add(file);
//return file.Path;
}
public async Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters)
{
var returnList = new List<SharedFile>();
var picker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.Desktop
};
foreach (var filter in typeFilters)
{
picker.FileTypeFilter.Add(filter.ToString());
}
var files = await picker.PickMultipleFilesAsync();
if (files == null) return returnList;
foreach (var file in files)
{
StorageApplicationPermissions.FutureAccessList.Add(file);
var sharedFile = await file.ToSharedFileAsync();
returnList.Add(sharedFile);
}
return returnList;
}
private async Task<StorageFile> PickFileAsync(params object[] typeFilters)
{
var picker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail
};
foreach (var filter in typeFilters)
{
picker.FileTypeFilter.Add(filter.ToString());
}
var file = await picker.PickSingleFileAsync();
if (file == null) return null;
StorageApplicationPermissions.FutureAccessList.Add(file);
return file;
}
public virtual IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult)
{
return new AccountCreationDialog
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
}
public async Task<byte[]> PickWindowsFileContentAsync(params object[] typeFilters)
{
var file = await PickFileAsync(typeFilters);
if (file == null) return [];
return await file.ToByteArrayAsync();
}
public Task ShowMessageAsync(string message, string title, WinoCustomMessageDialogIcon icon = WinoCustomMessageDialogIcon.Information)
=> ShowWinoCustomMessageDialogAsync(title, message, Translator.Buttons_Close, icon);
public Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle)
=> ShowWinoCustomMessageDialogAsync(title, question, confirmationButtonTitle, WinoCustomMessageDialogIcon.Question, Translator.Buttons_Cancel, string.Empty);
public async Task<bool> ShowWinoCustomMessageDialogAsync(string title,
string description,
string approveButtonText,
WinoCustomMessageDialogIcon? icon,
string cancelButtonText = "",
string dontAskAgainConfigurationKey = "")
{
// This config key has been marked as don't ask again already.
// Return immidiate result without presenting the dialog.
bool isDontAskEnabled = !string.IsNullOrEmpty(dontAskAgainConfigurationKey);
if (isDontAskEnabled && ConfigurationService.Get(dontAskAgainConfigurationKey, false)) return false;
var informationContainer = new CustomMessageDialogInformationContainer(title, description, icon.Value, isDontAskEnabled);
var dialog = new ContentDialog
{
Style = ApplicationResourceManager.GetResource<Style>("WinoDialogStyle"),
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
DefaultButton = ContentDialogButton.Primary,
PrimaryButtonText = approveButtonText,
ContentTemplate = ApplicationResourceManager.GetResource<DataTemplate>("CustomWinoContentDialogContentTemplate"),
Content = informationContainer
};
if (!string.IsNullOrEmpty(cancelButtonText))
{
dialog.SecondaryButtonText = cancelButtonText;
}
var dialogResult = await HandleDialogPresentationAsync(dialog);
// Mark this key to not ask again if user checked the checkbox.
if (informationContainer.IsDontAskChecked)
{
ConfigurationService.Set(dontAskAgainConfigurationKey, true);
}
return dialogResult == ContentDialogResult.Primary;
}
/// <summary>
/// Waits for PopupRoot to be available before presenting the dialog and returns the result after presentation.
/// </summary>
/// <param name="dialog">Dialog to present and wait for closing.</param>
/// <returns>Dialog result from WinRT.</returns>
public async Task<ContentDialogResult> HandleDialogPresentationAsync(ContentDialog dialog)
{
await _presentationSemaphore.WaitAsync();
try
{
return await dialog.ShowAsync();
}
catch (Exception ex)
{
Log.Error(ex, $"Handling dialog service failed. Dialog was {dialog.GetType().Name}");
}
finally
{
_presentationSemaphore.Release();
}
return ContentDialogResult.None;
}
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType)
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message));
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action)
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message, actionButtonText, action));
public void ShowNotSupportedMessage()
=> InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle,
Translator.Info_UnsupportedFunctionalityDescription,
InfoBarMessageType.Error);
public async Task<string> ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription, string primaryButtonText)
{
var inputDialog = new TextInputDialog()
{
CurrentInput = currentInput,
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
Title = dialogTitle
};
inputDialog.SetDescription(dialogDescription);
inputDialog.SetPrimaryButtonText(primaryButtonText);
await HandleDialogPresentationAsync(inputDialog);
if (inputDialog.HasInput.GetValueOrDefault() && !currentInput.Equals(inputDialog.CurrentInput))
return inputDialog.CurrentInput;
return string.Empty;
}
public async Task<string> PickWindowsFolderAsync()
{
var picker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add("*");
var pickedFolder = await picker.PickSingleFolderAsync();
if (pickedFolder != null)
{
Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("FolderPickerToken", pickedFolder);
return pickedFolder.Path;
}
return string.Empty;
}
public async Task<bool> ShowCustomThemeBuilderDialogAsync()
{
var themeBuilderDialog = new CustomThemeBuilderDialog()
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
var dialogResult = await HandleDialogPresentationAsync(themeBuilderDialog);
return dialogResult == ContentDialogResult.Primary;
}
public async Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders)
{
var dialog = new NewAccountDialog
{
Providers = availableProviders,
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
await HandleDialogPresentationAsync(dialog);
return dialog.Result;
}
}
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Windows.Storage;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP.Services;
public class FileService : IFileService
{
public async Task<string> CopyFileAsync(string sourceFilePath, string destinationFolderPath)
{
var fileName = Path.GetFileName(sourceFilePath);
var sourceFileHandle = await StorageFile.GetFileFromPathAsync(sourceFilePath);
var destinationFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderPath);
var copiedFile = await sourceFileHandle.CopyAsync(destinationFolder, fileName, NameCollisionOption.GenerateUniqueName);
return copiedFile.Path;
}
public async Task<string> GetFileContentByApplicationUriAsync(string resourcePath)
{
var releaseNoteFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(resourcePath));
return await FileIO.ReadTextAsync(releaseNoteFile);
}
public async Task<Stream> GetFileStreamAsync(string folderPath, string fileName)
{
var folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
var createdFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
return await createdFile.OpenStreamForWriteAsync();
}
public async Task<bool> SaveLogsToFolderAsync(string logsFolder, string destinationFolder)
{
var logFiles = Directory.GetFiles(logsFolder, "*.log");
if (logFiles.Length == 0) return false;
using var fileStream = await GetFileStreamAsync(destinationFolder, Constants.LogArchiveFileName);
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true);
foreach (var logFile in logFiles)
{
using FileStream logFileStream = File.Open(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var zipArchiveEntry = archive.CreateEntry(Path.GetFileName(logFile), CompressionLevel.Fastest);
using var zipStream = zipArchiveEntry.Open();
await logFileStream.CopyToAsync(zipStream);
}
return true;
}
}
@@ -0,0 +1,15 @@
using Windows.System;
using Windows.UI.Core;
using Microsoft.UI.Xaml;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP.Services;
public class KeyPressService : IKeyPressService
{
public bool IsCtrlKeyPressed()
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false;
public bool IsShiftKeyPressed()
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false;
}
@@ -0,0 +1,107 @@
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Foundation.Metadata;
using Windows.Storage;
using Windows.System;
using Windows.UI.Shell;
using Wino.Core.Domain.Interfaces;
#if WINDOWS_UWP
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
#endif
namespace Wino.Services;
public class NativeAppService : INativeAppService
{
private string _mimeMessagesFolder;
private string _editorBundlePath;
public Func<IntPtr> GetCoreWindowHwnd { get; set; }
public string GetWebAuthenticationBrokerUri()
{
#if WINDOWS_UWP
return WebAuthenticationBroker.GetCurrentApplicationCallbackUri().AbsoluteUri;
#endif
return string.Empty;
}
public async Task<string> GetMimeMessageStoragePath()
{
if (!string.IsNullOrEmpty(_mimeMessagesFolder))
return _mimeMessagesFolder;
var localFolder = ApplicationData.Current.LocalFolder;
var mimeFolder = await localFolder.CreateFolderAsync("Mime", CreationCollisionOption.OpenIfExists);
_mimeMessagesFolder = mimeFolder.Path;
return _mimeMessagesFolder;
}
public async Task<string> GetEditorBundlePathAsync()
{
if (string.IsNullOrEmpty(_editorBundlePath))
{
var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/editor.html"))
.AsTask()
.ConfigureAwait(false);
_editorBundlePath = editorFileFromBundle.Path;
}
return _editorBundlePath;
}
[Obsolete("This should be removed. There should be no functionality.")]
public bool IsAppRunning()
{
#if WINDOWS_UWP
return (Window.Current?.Content as Frame)?.Content != null;
#endif
return true;
}
public async Task LaunchFileAsync(string filePath)
{
var file = await StorageFile.GetFileFromPathAsync(filePath);
await Launcher.LaunchFileAsync(file);
}
public Task<bool> LaunchUriAsync(Uri uri) => Launcher.LaunchUriAsync(uri).AsTask();
public string GetFullAppVersion()
{
Package package = Package.Current;
PackageId packageId = package.Id;
PackageVersion version = packageId.Version;
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
}
public async Task PinAppToTaskbarAsync()
{
// If Start screen manager API's aren't present
if (!ApiInformation.IsTypePresent("Windows.UI.Shell.TaskbarManager")) return;
// Get the taskbar manager
var taskbarManager = TaskbarManager.GetDefault();
// If Taskbar doesn't allow pinning, don't show the tip
if (!taskbarManager.IsPinningAllowed) return;
// If already pinned, don't show the tip
if (await taskbarManager.IsCurrentAppPinnedAsync()) return;
await taskbarManager.RequestPinCurrentAppAsync();
}
}
@@ -0,0 +1,27 @@
using System;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Wino.Core.Domain.Models.Navigation;
namespace Wino.Core.UWP.Services;
public class NavigationServiceBase
{
public NavigationTransitionInfo GetNavigationTransitionInfo(NavigationTransitionType transition)
{
return transition switch
{
NavigationTransitionType.DrillIn => new DrillInNavigationTransitionInfo(),
NavigationTransitionType.Entrance => new EntranceNavigationTransitionInfo(),
_ => new SuppressNavigationTransitionInfo(),
};
}
public Type GetCurrentFrameType(ref Frame _frame)
{
if (_frame != null && _frame.Content != null)
return _frame.Content.GetType();
return null;
}
}
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Toolkit.Uwp.Notifications;
using Serilog;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Messaging.UI;
namespace Wino.Core.UWP.Services;
// TODO: Refactor this thing. It's garbage.
public class NotificationBuilder : INotificationBuilder
{
private readonly IUnderlyingThemeService _underlyingThemeService;
private readonly IAccountService _accountService;
private readonly IFolderService _folderService;
private readonly IMailService _mailService;
private readonly IThumbnailService _thumbnailService;
public NotificationBuilder(IUnderlyingThemeService underlyingThemeService,
IAccountService accountService,
IFolderService folderService,
IMailService mailService,
IThumbnailService thumbnailService)
{
_underlyingThemeService = underlyingThemeService;
_accountService = accountService;
_folderService = folderService;
_mailService = mailService;
_thumbnailService = thumbnailService;
WeakReferenceMessenger.Default.Register<MailReadStatusChanged>(this, (r, msg) =>
{
RemoveNotification(msg.UniqueId);
});
}
public async Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable<IMailItem> downloadedMailItems)
{
var mailCount = downloadedMailItems.Count();
try
{
// If there are more than 3 mails, just display 1 general toast.
if (mailCount > 3)
{
var builder = new ToastContentBuilder();
builder.SetToastScenario(ToastScenario.Default);
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
builder.AddButton(GetDismissButton());
builder.AddAudio(new ToastAudio()
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
builder.Show();
}
else
{
var validItems = new List<IMailItem>();
// Fetch mails again to fill up assigned folder data and latest statuses.
// They've been marked as read by executing synchronizer tasks until inital sync finishes.
foreach (var item in downloadedMailItems)
{
var mailItem = await _mailService.GetSingleMailItemAsync(item.UniqueId);
if (mailItem != null && mailItem.AssignedFolder != null)
{
validItems.Add(mailItem);
}
}
foreach (var mailItem in validItems)
{
if (mailItem.IsRead)
{
// Remove the notification for a specific mail if it exists
ToastNotificationManager.History.Remove(mailItem.UniqueId.ToString());
continue;
}
var builder = new ToastContentBuilder();
builder.SetToastScenario(ToastScenario.Default);
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
if (!string.IsNullOrEmpty(avatarThumbnail))
{
var tempFile = await Windows.Storage.ApplicationData.Current.TemporaryFolder.CreateFileAsync($"{Guid.NewGuid()}.png", Windows.Storage.CreationCollisionOption.ReplaceExisting);
await using (var stream = await tempFile.OpenStreamForWriteAsync())
{
var bytes = Convert.FromBase64String(avatarThumbnail);
await stream.WriteAsync(bytes);
}
builder.AddAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), hintCrop: ToastGenericAppLogoCrop.Default);
}
// Override system notification timetamp with received date of the mail.
// It may create confusion for some users, but still it's the truth...
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
builder.AddText(mailItem.FromName);
builder.AddText(mailItem.Subject);
builder.AddText(mailItem.PreviewText);
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate);
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
builder.AddAudio(new ToastAudio()
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
// Use UniqueId as tag to allow removal
builder.Show(toast => toast.Tag = mailItem.UniqueId.ToString());
}
await UpdateTaskbarIconBadgeAsync();
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to create notifications.");
}
}
private ToastButton GetDismissButton()
=> new ToastButton()
.SetDismissActivation()
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/dismiss.png"));
private static ToastButton GetArchiveButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_Archive)
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/archive.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.Archive)
.SetBackgroundActivation();
private ToastButton GetDeleteButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_Delete)
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/delete.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
.SetBackgroundActivation();
private static ToastButton GetMarkAsReadButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_MarkAsRead)
.SetImageUri(new System.Uri("ms-appx:///Assets/NotificationIcons/markread.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
.SetBackgroundActivation();
public async Task UpdateTaskbarIconBadgeAsync()
{
int totalUnreadCount = 0;
try
{
var badgeUpdater = BadgeUpdateManager.CreateBadgeUpdaterForApplication();
var accounts = await _accountService.GetAccountsAsync();
foreach (var account in accounts)
{
if (!account.Preferences.IsTaskbarBadgeEnabled) continue;
var accountInbox = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Inbox);
if (accountInbox == null)
continue;
var inboxUnreadCount = await _folderService.GetFolderNotificationBadgeAsync(accountInbox.Id);
totalUnreadCount += inboxUnreadCount;
}
if (totalUnreadCount > 0)
{
// Get the blank badge XML payload for a badge number
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
// Set the value of the badge in the XML to our number
XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
// Create the badge notification
BadgeNotification badge = new BadgeNotification(badgeXml);
// And update the badge
badgeUpdater.Update(badge);
}
else
badgeUpdater.Clear();
}
catch (Exception ex)
{
Log.Error(ex, "Error while updating taskbar badge.");
}
}
public async Task CreateTestNotificationAsync(string title, string message)
{
// with args test.
await CreateNotificationsAsync(Guid.Parse("28c3c39b-7147-4de3-b209-949bd19eede6"), new List<IMailItem>()
{
new MailCopy()
{
Subject = "test subject",
PreviewText = "preview html",
CreationDate = DateTime.UtcNow,
FromAddress = "bkaankose@outlook.com",
Id = "AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AnMdP0zg8wkS_Ib2Eeh80LAAGq91I3QAA",
}
});
//var builder = new ToastContentBuilder();
//builder.SetToastScenario(ToastScenario.Default);
//builder.AddText(title);
//builder.AddText(message);
//builder.Show();
//await Task.CompletedTask;
}
public void RemoveNotification(Guid mailUniqueId)
{
try
{
ToastNotificationManager.History.Remove(mailUniqueId.ToString());
}
catch (Exception ex)
{
Log.Error(ex, $"Failed to remove notification for mail {mailUniqueId}");
}
}
}
@@ -0,0 +1,334 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Reader;
using Wino.Core.Domain.Translations;
using Wino.Services;
namespace Wino.Core.UWP.Services;
public class PreferencesService(IConfigurationService configurationService) : ObservableObject, IPreferencesService
{
private readonly IConfigurationService _configurationService = configurationService;
public event EventHandler<string> PreferenceChanged;
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
PreferenceChanged?.Invoke(this, e.PropertyName);
}
private void SaveProperty(string propertyName, object value) => _configurationService.Set(propertyName, value);
private void SetPropertyAndSave(string propertyName, object value)
{
_configurationService.Set(propertyName, value);
OnPropertyChanged(propertyName);
Debug.WriteLine($"PreferencesService -> {propertyName}:{value?.ToString()}");
}
public MailRenderingOptions GetRenderingOptions()
=> new MailRenderingOptions()
{
LoadImages = RenderImages,
LoadStyles = RenderStyles,
RenderPlaintextLinks = RenderPlaintextLinks
};
public MailListDisplayMode MailItemDisplayMode
{
get => _configurationService.Get(nameof(MailItemDisplayMode), MailListDisplayMode.Spacious);
set => SetPropertyAndSave(nameof(MailItemDisplayMode), value);
}
public bool IsSemanticZoomEnabled
{
get => _configurationService.Get(nameof(IsSemanticZoomEnabled), true);
set => SetPropertyAndSave(nameof(IsSemanticZoomEnabled), value);
}
public bool IsHardDeleteProtectionEnabled
{
get => _configurationService.Get(nameof(IsHardDeleteProtectionEnabled), true);
set => SetPropertyAndSave(nameof(IsHardDeleteProtectionEnabled), value);
}
public bool IsThreadingEnabled
{
get => _configurationService.Get(nameof(IsThreadingEnabled), true);
set => SetPropertyAndSave(nameof(IsThreadingEnabled), value);
}
public bool IsMailListActionBarEnabled
{
get => _configurationService.Get(nameof(IsMailListActionBarEnabled), false);
set => SetPropertyAndSave(nameof(IsMailListActionBarEnabled), value);
}
public bool IsShowActionLabelsEnabled
{
get => _configurationService.Get(nameof(IsShowActionLabelsEnabled), true);
set => SetPropertyAndSave(nameof(IsShowActionLabelsEnabled), value);
}
public bool IsShowSenderPicturesEnabled
{
get => _configurationService.Get(nameof(IsShowSenderPicturesEnabled), true);
set => SetPropertyAndSave(nameof(IsShowSenderPicturesEnabled), value);
}
public bool IsShowPreviewEnabled
{
get => _configurationService.Get(nameof(IsShowPreviewEnabled), true);
set => SetPropertyAndSave(nameof(IsShowPreviewEnabled), value);
}
public bool RenderStyles
{
get => _configurationService.Get(nameof(RenderStyles), true);
set => SetPropertyAndSave(nameof(RenderStyles), value);
}
public bool RenderPlaintextLinks
{
get => _configurationService.Get(nameof(RenderPlaintextLinks), true);
set => SetPropertyAndSave(nameof(RenderPlaintextLinks), value);
}
public bool RenderImages
{
get => _configurationService.Get(nameof(RenderImages), true);
set => SetPropertyAndSave(nameof(RenderImages), value);
}
public bool Prefer24HourTimeFormat
{
get => _configurationService.Get(nameof(Prefer24HourTimeFormat), false);
set => SetPropertyAndSave(nameof(Prefer24HourTimeFormat), value);
}
public MailMarkAsOption MarkAsPreference
{
get => _configurationService.Get(nameof(MarkAsPreference), MailMarkAsOption.WhenSelected);
set => SetPropertyAndSave(nameof(MarkAsPreference), value);
}
public int MarkAsDelay
{
get => _configurationService.Get(nameof(MarkAsDelay), 5);
set => SetPropertyAndSave(nameof(MarkAsDelay), value);
}
public MailOperation RightSwipeOperation
{
get => _configurationService.Get(nameof(RightSwipeOperation), MailOperation.MarkAsRead);
set => SetPropertyAndSave(nameof(RightSwipeOperation), value);
}
public MailOperation LeftSwipeOperation
{
get => _configurationService.Get(nameof(LeftSwipeOperation), MailOperation.SoftDelete);
set => SetPropertyAndSave(nameof(LeftSwipeOperation), value);
}
public bool IsHoverActionsEnabled
{
get => _configurationService.Get(nameof(IsHoverActionsEnabled), true);
set => SetPropertyAndSave(nameof(IsHoverActionsEnabled), value);
}
public MailOperation LeftHoverAction
{
get => _configurationService.Get(nameof(LeftHoverAction), MailOperation.Archive);
set => SetPropertyAndSave(nameof(LeftHoverAction), value);
}
public MailOperation CenterHoverAction
{
get => _configurationService.Get(nameof(CenterHoverAction), MailOperation.SoftDelete);
set => SetPropertyAndSave(nameof(CenterHoverAction), value);
}
public MailOperation RightHoverAction
{
get => _configurationService.Get(nameof(RightHoverAction), MailOperation.SetFlag);
set => SetPropertyAndSave(nameof(RightHoverAction), value);
}
public bool IsLoggingEnabled
{
get => _configurationService.Get(nameof(IsLoggingEnabled), true);
set => SetPropertyAndSave(nameof(IsLoggingEnabled), value);
}
public bool IsMailkitProtocolLoggerEnabled
{
get => _configurationService.Get(nameof(IsMailkitProtocolLoggerEnabled), false);
set => SetPropertyAndSave(nameof(IsMailkitProtocolLoggerEnabled), value);
}
public bool IsGravatarEnabled
{
get => _configurationService.Get(nameof(IsGravatarEnabled), true);
set => SetPropertyAndSave(nameof(IsGravatarEnabled), value);
}
public bool IsFaviconEnabled
{
get => _configurationService.Get(nameof(IsFaviconEnabled), true);
set => SetPropertyAndSave(nameof(IsFaviconEnabled), value);
}
public Guid? StartupEntityId
{
get => _configurationService.Get<Guid?>(nameof(StartupEntityId), null);
set => SaveProperty(propertyName: nameof(StartupEntityId), value);
}
public AppLanguage CurrentLanguage
{
get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage);
set => SaveProperty(propertyName: nameof(CurrentLanguage), value);
}
public string ReaderFont
{
get => _configurationService.Get(nameof(ReaderFont), "Calibri");
set => SaveProperty(propertyName: nameof(ReaderFont), value);
}
public int ReaderFontSize
{
get => _configurationService.Get(nameof(ReaderFontSize), 14);
set => SaveProperty(propertyName: nameof(ReaderFontSize), value);
}
public string ComposerFont
{
get => _configurationService.Get(nameof(ComposerFont), "Calibri");
set => SaveProperty(propertyName: nameof(ComposerFont), value);
}
public int ComposerFontSize
{
get => _configurationService.Get(nameof(ComposerFontSize), 14);
set => SaveProperty(propertyName: nameof(ComposerFontSize), value);
}
public bool IsNavigationPaneOpened
{
get => _configurationService.Get(nameof(IsNavigationPaneOpened), true);
set => SaveProperty(propertyName: nameof(IsNavigationPaneOpened), value);
}
public bool AutoSelectNextItem
{
get => _configurationService.Get(nameof(AutoSelectNextItem), true);
set => SaveProperty(propertyName: nameof(AutoSelectNextItem), value);
}
public ServerBackgroundMode ServerTerminationBehavior
{
get => _configurationService.Get(nameof(ServerTerminationBehavior), ServerBackgroundMode.MinimizedTray);
set => SaveProperty(propertyName: nameof(ServerTerminationBehavior), value);
}
public string DiagnosticId
{
get => _configurationService.Get(nameof(DiagnosticId), Guid.NewGuid().ToString());
set => SaveProperty(propertyName: nameof(DiagnosticId), value);
}
public SearchMode DefaultSearchMode
{
get => _configurationService.Get(nameof(DefaultSearchMode), SearchMode.Local);
set => SaveProperty(propertyName: nameof(DefaultSearchMode), value);
}
public DayOfWeek FirstDayOfWeek
{
get => _configurationService.Get(nameof(FirstDayOfWeek), DayOfWeek.Monday);
set => SaveProperty(propertyName: nameof(FirstDayOfWeek), value);
}
public double HourHeight
{
get => _configurationService.Get(nameof(HourHeight), 60.0);
set => SaveProperty(propertyName: nameof(HourHeight), value);
}
public TimeSpan WorkingHourStart
{
get => _configurationService.Get(nameof(WorkingHourStart), new TimeSpan(8, 0, 0));
set => SaveProperty(propertyName: nameof(WorkingHourStart), value);
}
public TimeSpan WorkingHourEnd
{
get => _configurationService.Get(nameof(WorkingHourEnd), new TimeSpan(17, 0, 0));
set => SaveProperty(propertyName: nameof(WorkingHourEnd), value);
}
public DayOfWeek WorkingDayStart
{
get => _configurationService.Get(nameof(WorkingDayStart), DayOfWeek.Monday);
set => SaveProperty(propertyName: nameof(WorkingDayStart), value);
}
public DayOfWeek WorkingDayEnd
{
get => _configurationService.Get(nameof(WorkingDayEnd), DayOfWeek.Friday);
set => SaveProperty(propertyName: nameof(WorkingDayEnd), value);
}
public int EmailSyncIntervalMinutes
{
get => _configurationService.Get(nameof(EmailSyncIntervalMinutes), 3);
set => SetPropertyAndSave(nameof(EmailSyncIntervalMinutes), value);
}
public CalendarSettings GetCurrentCalendarSettings()
{
var workingDays = GetDaysBetween(WorkingDayStart, WorkingDayEnd);
return new CalendarSettings(FirstDayOfWeek,
workingDays,
WorkingHourStart,
WorkingHourEnd,
HourHeight,
Prefer24HourTimeFormat ? DayHeaderDisplayType.TwentyFourHour : DayHeaderDisplayType.TwelveHour,
new CultureInfo(WinoTranslationDictionary.GetLanguageFileNameRelativePath(CurrentLanguage)));
}
private List<DayOfWeek> GetDaysBetween(DayOfWeek startDay, DayOfWeek endDay)
{
var daysOfWeek = new List<DayOfWeek>();
int currentDay = (int)startDay;
int endDayInt = (int)endDay;
// If endDay is before startDay in the week, wrap around
if (endDayInt < currentDay)
{
endDayInt += 7;
}
// Collect days from startDay to endDay
while (currentDay <= endDayInt)
{
daysOfWeek.Add((DayOfWeek)(currentDay % 7));
currentDay++;
}
return daysOfWeek;
}
}
+265
View File
@@ -0,0 +1,265 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Printing;
using Windows.Data.Pdf;
using Windows.Graphics.Printing;
using Windows.Graphics.Printing.OptionDetails;
using Windows.Storage.Streams;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Printing;
namespace Wino.Core.UWP.Services;
/// <summary>
/// Printer service that uses WinRT APIs to print PDF files.
/// Used modified version of the code here:
/// https://github.com/microsoft/Win2D-Samples/blob/reunion_master/ExampleGallery/PrintingExample.xaml.cs
/// HTML file is saved as PDF to temporary location.
/// Then PDF is loaded as PdfDocument and printed using CanvasBitmap for each page.
/// </summary>
public class PrintService : IPrintService
{
private TaskCompletionSource<PrintingResult> _taskCompletionSource;
private CanvasPrintDocument printDocument;
private PrintTask printTask;
private PdfDocument pdfDocument;
private List<CanvasBitmap> bitmaps = new();
private Vector2 largestBitmap;
private Vector2 pageSize;
private Vector2 imagePadding = new Vector2(64, 64);
private Vector2 cellSize;
private int bitmapCount;
private int columns;
private int rows;
private int bitmapsPerPage;
private int pageCount = -1;
private PrintInformation _currentPrintInformation;
public async Task<PrintingResult> PrintPdfFileAsync(string pdfFilePath, string printTitle)
{
if (_taskCompletionSource != null)
{
_taskCompletionSource.TrySetResult(PrintingResult.Abandoned);
_taskCompletionSource = new TaskCompletionSource<PrintingResult>();
}
// Load the PDF file
var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(pdfFilePath);
pdfDocument = await PdfDocument.LoadFromFileAsync(file);
_taskCompletionSource ??= new TaskCompletionSource<PrintingResult>();
_currentPrintInformation = new PrintInformation(pdfFilePath, printTitle);
printDocument = new CanvasPrintDocument();
printDocument.PrintTaskOptionsChanged += OnDocumentTaskOptionsChanged;
printDocument.Preview += OnDocumentPreview;
printDocument.Print += OnDocumentPrint;
var printManager = PrintManager.GetForCurrentView();
printManager.PrintTaskRequested += PrintingExample_PrintTaskRequested;
try
{
await PrintManager.ShowPrintUIAsync();
var result = await _taskCompletionSource.Task;
return result;
}
finally
{
// Dispose everything.
UnregisterPrintManager(printManager);
ClearBitmaps();
UnregisterTask();
DisposePDFDocument();
_taskCompletionSource = null;
}
}
private void DisposePDFDocument()
{
if (pdfDocument != null)
{
pdfDocument = null;
}
}
private void UnregisterTask()
{
if (printTask != null)
{
printTask.Completed -= TaskCompleted;
printTask = null;
}
}
private void UnregisterPrintManager(PrintManager manager)
{
manager.PrintTaskRequested -= PrintingExample_PrintTaskRequested;
}
private void ClearBitmaps()
{
foreach (var bitmap in bitmaps)
{
bitmap.Dispose();
}
bitmaps.Clear();
}
private void PrintingExample_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
if (_currentPrintInformation == null) return;
printTask = args.Request.CreatePrintTask(_currentPrintInformation.PDFTitle, (createPrintTaskArgs) =>
{
createPrintTaskArgs.SetSource(printDocument);
});
printTask.Completed += TaskCompleted;
}
private void TaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
=> _taskCompletionSource?.TrySetResult((PrintingResult)args.Completion);
private async void OnDocumentTaskOptionsChanged(CanvasPrintDocument sender, CanvasPrintTaskOptionsChangedEventArgs args)
{
var deferral = args.GetDeferral();
try
{
await LoadPDFPageBitmapsAsync(sender);
var pageDesc = args.PrintTaskOptions.GetPageDescription(1);
var newPageSize = pageDesc.PageSize.ToVector2();
if (pageSize == newPageSize && pageCount != -1)
{
// We've already figured out the pages and the page size hasn't changed, so there's nothing left for us to do here.
return;
}
pageSize = newPageSize;
sender.InvalidatePreview();
// Figure out the bitmap index at the top of the current preview page. We'll request that the preview defaults to showing
// the page that still has this bitmap on it in the new layout.
int indexOnCurrentPage = 0;
if (pageCount != -1)
{
indexOnCurrentPage = (int)(args.CurrentPreviewPageNumber - 1) * bitmapsPerPage;
}
// Calculate the new layout
var printablePageSize = pageSize * 0.9f;
cellSize = largestBitmap + imagePadding;
var cellsPerPage = printablePageSize / cellSize;
columns = Math.Max(1, (int)Math.Floor(cellsPerPage.X));
rows = Math.Max(1, (int)Math.Floor(cellsPerPage.Y));
bitmapsPerPage = columns * rows;
// Calculate the page count
bitmapCount = bitmaps.Count;
pageCount = (int)Math.Ceiling(bitmapCount / (double)bitmapsPerPage);
sender.SetPageCount((uint)pageCount);
// Set the preview page to the one that has the item that was currently displayed in the last preview
args.NewPreviewPageNumber = (uint)(indexOnCurrentPage / bitmapsPerPage) + 1;
}
finally
{
deferral.Complete();
}
}
private async Task LoadPDFPageBitmapsAsync(CanvasPrintDocument sender)
{
ClearBitmaps();
bitmaps ??= new List<CanvasBitmap>();
for (int i = 0; i < pdfDocument.PageCount; i++)
{
var page = pdfDocument.GetPage((uint)i);
var stream = new InMemoryRandomAccessStream();
await page.RenderToStreamAsync(stream);
var bitmap = await CanvasBitmap.LoadAsync(sender, stream);
bitmaps.Add(bitmap);
}
largestBitmap = Vector2.Zero;
foreach (var bitmap in bitmaps)
{
largestBitmap.X = Math.Max(largestBitmap.X, (float)bitmap.Size.Width);
largestBitmap.Y = Math.Max(largestBitmap.Y, (float)bitmap.Size.Height);
}
}
private void OnDocumentPreview(CanvasPrintDocument sender, CanvasPreviewEventArgs args)
{
var ds = args.DrawingSession;
var pageNumber = args.PageNumber;
DrawPdfPage(sender, ds, pageNumber);
}
private void OnDocumentPrint(CanvasPrintDocument sender, CanvasPrintEventArgs args)
{
var detailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(args.PrintTaskOptions);
int pageCountToPrint = (int)pdfDocument.PageCount;
for (uint i = 1; i <= pageCountToPrint; ++i)
{
using var ds = args.CreateDrawingSession();
var imageableRect = args.PrintTaskOptions.GetPageDescription(i).ImageableRect;
DrawPdfPage(sender, ds, i);
}
}
private void DrawPdfPage(CanvasPrintDocument sender, CanvasDrawingSession ds, uint pageNumber)
{
if (bitmaps?.Count == 0) return;
var cellAcross = new Vector2(cellSize.X, 0);
var cellDown = new Vector2(0, cellSize.Y);
var totalSize = cellAcross * columns + cellDown * rows;
Vector2 topLeft = (pageSize - totalSize) / 2;
int bitmapIndex = ((int)pageNumber - 1) * bitmapsPerPage;
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < columns; ++column)
{
var cellTopLeft = topLeft + cellAcross * column + cellDown * row;
var bitmapInfo = bitmaps[bitmapIndex % bitmaps.Count];
var bitmapPos = cellTopLeft + (cellSize - bitmapInfo.Size.ToVector2()) / 2;
ds.DrawImage(bitmapInfo, bitmapPos);
bitmapIndex++;
}
}
}
}
@@ -0,0 +1,55 @@
using System;
using System.Threading.Tasks;
using Serilog;
using Windows.ApplicationModel;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Extensions;
namespace Wino.Core.UWP.Services;
public class StartupBehaviorService : IStartupBehaviorService
{
private const string WinoServerTaskId = "WinoServer";
public async Task<StartupBehaviorResult> ToggleStartupBehavior(bool isEnabled)
{
try
{
var task = await StartupTask.GetAsync(WinoServerTaskId);
if (isEnabled)
{
await task.RequestEnableAsync();
}
else
{
task.Disable();
}
}
catch (Exception)
{
Log.Error("Error toggling startup behavior");
}
return await GetCurrentStartupBehaviorAsync();
}
public async Task<StartupBehaviorResult> GetCurrentStartupBehaviorAsync()
{
try
{
var task = await StartupTask.GetAsync(WinoServerTaskId);
return task.State.AsStartupBehaviorResult();
}
catch (Exception ex)
{
Log.Error(ex, "Error getting startup behavior");
return StartupBehaviorResult.Fatal;
}
}
}
@@ -0,0 +1,138 @@
using System;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP;
namespace Wino.Services;
public class StatePersistenceService : ObservableObject, IStatePersistanceService
{
public event EventHandler<string> StatePropertyChanged;
private const string OpenPaneLengthKey = nameof(OpenPaneLengthKey);
private const string MailListPaneLengthKey = nameof(MailListPaneLengthKey);
private readonly IConfigurationService _configurationService;
public StatePersistenceService(IConfigurationService configurationService)
{
_configurationService = configurationService;
_openPaneLength = _configurationService.Get(OpenPaneLengthKey, 320d);
_mailListPaneLength = _configurationService.Get(MailListPaneLengthKey, 420d);
_calendarDisplayType = _configurationService.Get(nameof(CalendarDisplayType), CalendarDisplayType.Week);
_dayDisplayCount = _configurationService.Get(nameof(DayDisplayCount), 1);
PropertyChanged += ServicePropertyChanged;
}
private void ServicePropertyChanged(object sender, PropertyChangedEventArgs e) => StatePropertyChanged?.Invoke(this, e.PropertyName);
public bool IsBackButtonVisible => IsReadingMail && IsReaderNarrowed;
private bool isReadingMail;
public bool IsReadingMail
{
get => isReadingMail;
set
{
if (SetProperty(ref isReadingMail, value))
{
OnPropertyChanged(nameof(IsBackButtonVisible));
}
}
}
private bool shouldShiftMailRenderingDesign;
public bool ShouldShiftMailRenderingDesign
{
get { return shouldShiftMailRenderingDesign; }
set { shouldShiftMailRenderingDesign = value; }
}
private bool isReaderNarrowed;
public bool IsReaderNarrowed
{
get => isReaderNarrowed;
set
{
if (SetProperty(ref isReaderNarrowed, value))
{
OnPropertyChanged(nameof(IsBackButtonVisible));
}
}
}
private string coreWindowTitle;
public string CoreWindowTitle
{
get => coreWindowTitle;
set
{
if (SetProperty(ref coreWindowTitle, value))
{
UpdateAppCoreWindowTitle();
}
}
}
private double _openPaneLength;
public double OpenPaneLength
{
get => _openPaneLength;
set
{
if (SetProperty(ref _openPaneLength, value))
{
_configurationService.Set(OpenPaneLengthKey, value);
}
}
}
private double _mailListPaneLength;
public double MailListPaneLength
{
get => _mailListPaneLength;
set
{
if (SetProperty(ref _mailListPaneLength, value))
{
_configurationService.Set(MailListPaneLengthKey, value);
}
}
}
private CalendarDisplayType _calendarDisplayType;
public CalendarDisplayType CalendarDisplayType
{
get => _calendarDisplayType;
set
{
if (SetProperty(ref _calendarDisplayType, value))
{
_configurationService.Set(nameof(CalendarDisplayType), value);
}
}
}
private int _dayDisplayCount;
public int DayDisplayCount
{
get => _dayDisplayCount;
set
{
if (SetProperty(ref _dayDisplayCount, value))
{
_configurationService.Set(nameof(DayDisplayCount), value);
}
}
}
private void UpdateAppCoreWindowTitle() => WinoApplication.MainWindow.Title = CoreWindowTitle;
}
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Services.Store;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Store;
namespace Wino.Core.UWP.Services;
public class StoreManagementService : IStoreManagementService
{
private StoreContext CurrentContext { get; }
private readonly Dictionary<StoreProductType, string> productIds = new Dictionary<StoreProductType, string>()
{
{ StoreProductType.UnlimitedAccounts, "UnlimitedAccounts" }
};
private readonly Dictionary<StoreProductType, string> skuIds = new Dictionary<StoreProductType, string>()
{
{ StoreProductType.UnlimitedAccounts, "9P02MXZ42GSM" }
};
public StoreManagementService()
{
CurrentContext = StoreContext.GetDefault();
}
public async Task<bool> HasProductAsync(StoreProductType productType)
{
var productKey = productIds[productType];
var appLicense = await CurrentContext.GetAppLicenseAsync();
if (appLicense == null)
return false;
// Access the valid licenses for durable add-ons for this app.
foreach (KeyValuePair<string, StoreLicense> item in appLicense.AddOnLicenses)
{
StoreLicense addOnLicense = item.Value;
if (addOnLicense.InAppOfferToken == productKey)
{
return addOnLicense.IsActive;
}
}
return false;
}
public async Task<Domain.Enums.StorePurchaseResult> PurchaseAsync(StoreProductType productType)
{
if (await HasProductAsync(productType))
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
else
{
var productKey = skuIds[productType];
var result = await CurrentContext.RequestPurchaseAsync(productKey);
switch (result.Status)
{
case StorePurchaseStatus.Succeeded:
return Domain.Enums.StorePurchaseResult.Succeeded;
case StorePurchaseStatus.AlreadyPurchased:
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
default:
return Domain.Enums.StorePurchaseResult.NotPurchased;
}
}
}
}
@@ -0,0 +1,132 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Services.Store;
using Windows.System;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.UWP.Services;
public class StoreRatingService : IStoreRatingService
{
private const string RatedStorageKey = nameof(RatedStorageKey);
private const string LatestAskedKey = nameof(LatestAskedKey);
private readonly IConfigurationService _configurationService;
private readonly IMailDialogService _dialogService;
public StoreRatingService(IConfigurationService configurationService, IMailDialogService dialogService)
{
_configurationService = configurationService;
_dialogService = dialogService;
}
private bool IsAskingThresholdExceeded()
{
var latestAskedDate = _configurationService.Get(LatestAskedKey, DateTime.MinValue);
// Never asked before.
// Set the threshold and wait for the next trigger.
if (latestAskedDate == DateTime.MinValue)
{
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
}
else if (DateTime.UtcNow >= latestAskedDate.AddMinutes(30))
{
return true;
}
return false;
}
public async Task PromptRatingDialogAsync()
{
// Annoying.
if (Debugger.IsAttached) return;
// Swallow all exceptions. App should not crash in any errors.
try
{
bool isRated = _configurationService.GetRoaming(RatedStorageKey, false);
if (isRated) return;
if (!isRated)
{
if (!IsAskingThresholdExceeded()) return;
var isRateWinoApproved = await _dialogService.ShowWinoCustomMessageDialogAsync(Translator.StoreRatingDialog_Title,
Translator.StoreRatingDialog_MessageFirstLine,
Translator.Buttons_RateWino,
Domain.Enums.WinoCustomMessageDialogIcon.Question,
Translator.Buttons_No,
RatedStorageKey);
if (isRateWinoApproved)
{
// In case of failure of this call, we will navigate users to Store page directly.
try
{
await ShowPortableRatingDialogAsync();
}
catch (Exception)
{
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
}
}
}
}
catch (Exception) { }
finally
{
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
}
}
private async Task ShowPortableRatingDialogAsync()
{
var _storeContext = StoreContext.GetDefault();
StoreRateAndReviewResult result = await _storeContext.RequestRateAndReviewAppAsync();
// Check status
switch (result.Status)
{
case StoreRateAndReviewStatus.Succeeded:
if (result.WasUpdated)
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewUpdatedMessage, Domain.Enums.InfoBarMessageType.Success);
else
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewNewMessage, Domain.Enums.InfoBarMessageType.Success);
_configurationService.Set(RatedStorageKey, true);
break;
case StoreRateAndReviewStatus.CanceledByUser:
break;
case StoreRateAndReviewStatus.NetworkError:
_dialogService.InfoBarMessage(Translator.Info_ReviewNetworkErrorTitle, Translator.Info_ReviewNetworkErrorMessage, Domain.Enums.InfoBarMessageType.Warning);
break;
default:
_dialogService.InfoBarMessage(Translator.Info_ReviewUnknownErrorTitle, string.Format(Translator.Info_ReviewUnknownErrorMessage, result.ExtendedError.Message), Domain.Enums.InfoBarMessageType.Warning);
break;
}
}
public async Task LaunchStorePageForReviewAsync()
{
try
{
await CoreApplication.GetCurrentView()?.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
// TODO: Get it from package info.
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
});
}
catch (Exception) { }
}
}
+517
View File
@@ -0,0 +1,517 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Windows.Storage;
using Windows.UI.ViewManagement;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Personalization;
using Wino.Core.UWP;
using Wino.Core.UWP.Extensions;
using Wino.Core.UWP.Models.Personalization;
using Wino.Core.UWP.Services;
using Wino.Messaging.Client.Shell;
namespace Wino.Services;
/// <summary>
/// Class providing functionality around switching and restoring theme settings
/// </summary>
public class ThemeService : IThemeService
{
public const string CustomThemeFolderName = "CustomThemes";
private static string _micaThemeId = "a160b1b0-2ab8-4e97-a803-f4050f036e25";
private static string _acrylicThemeId = "fc08e58c-36fd-46e2-a562-26cf277f1467";
private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0";
private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd";
private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a";
private static string _snowflakeThemeId = "e143ddde-2e28-4846-9d98-dad63d6505f1";
private static string _gardenThemeId = "698e4466-f88c-4799-9c61-f0ea1308ed49";
private Frame mainApplicationFrame = null;
public event EventHandler<ApplicationElementTheme> ElementThemeChanged;
public event EventHandler<string> AccentColorChanged;
private const string AccentColorKey = nameof(AccentColorKey);
private const string CurrentApplicationThemeKey = nameof(CurrentApplicationThemeKey);
// Custom theme
public const string CustomThemeAccentColorKey = nameof(CustomThemeAccentColorKey);
// Keep reference so it does not get optimized/garbage collected
private readonly UISettings uiSettings = new UISettings();
private readonly IConfigurationService _configurationService;
private readonly IUnderlyingThemeService _underlyingThemeService;
private readonly IApplicationResourceManager<ResourceDictionary> _applicationResourceManager;
private List<AppThemeBase> preDefinedThemes { get; set; } = new List<AppThemeBase>()
{
new SystemAppTheme("Mica", Guid.Parse(_micaThemeId)),
new SystemAppTheme("Acrylic", Guid.Parse(_acrylicThemeId)),
new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark),
new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark),
new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light),
new PreDefinedAppTheme("Snowflake", Guid.Parse(_snowflakeThemeId), "#4a69bd", ApplicationElementTheme.Light),
new PreDefinedAppTheme("Garden", Guid.Parse(_gardenThemeId), "#05c46b", ApplicationElementTheme.Light),
};
public ThemeService(IConfigurationService configurationService,
IUnderlyingThemeService underlyingThemeService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
{
_configurationService = configurationService;
_underlyingThemeService = underlyingThemeService;
_applicationResourceManager = applicationResourceManager;
}
/// <summary>
/// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element.
/// </summary>
public ApplicationElementTheme RootTheme
{
get
{
if (mainApplicationFrame == null) return ApplicationElementTheme.Default;
return mainApplicationFrame.RequestedTheme.ToWinoElementTheme();
}
set
{
if (mainApplicationFrame == null)
return;
mainApplicationFrame.RequestedTheme = value.ToWindowsElementTheme();
_configurationService.Set(UnderlyingThemeService.SelectedAppThemeKey, value);
UpdateSystemCaptionButtonColors();
// PopupRoot usually needs to react to changes.
NotifyThemeUpdate();
}
}
private Guid currentApplicationThemeId;
public Guid CurrentApplicationThemeId
{
get { return currentApplicationThemeId; }
set
{
currentApplicationThemeId = value;
_configurationService.Set(CurrentApplicationThemeKey, value);
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () =>
{
await ApplyCustomThemeAsync(false);
});
}
}
private string accentColor;
public string AccentColor
{
get { return accentColor; }
set
{
accentColor = value;
UpdateAccentColor(value);
_configurationService.Set(AccentColorKey, value);
AccentColorChanged?.Invoke(this, value);
}
}
public bool IsCustomTheme
{
get
{
return currentApplicationThemeId != Guid.Parse(_micaThemeId) &&
currentApplicationThemeId != Guid.Parse(_acrylicThemeId);
}
}
public async Task InitializeAsync()
{
// Already initialized. There is no need.
if (mainApplicationFrame != null)
return;
// Save reference as this might be null when the user is in another app
mainApplicationFrame = WinoApplication.MainWindow.Content as Frame;
if (mainApplicationFrame == null) return;
RootTheme = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
AccentColor = _configurationService.Get(AccentColorKey, string.Empty);
// Set the current theme id. Default to Mica.
currentApplicationThemeId = _configurationService.Get(CurrentApplicationThemeKey, Guid.Parse(_micaThemeId));
await ApplyCustomThemeAsync(true);
// Registering to color changes, thus we notice when user changes theme system wide
uiSettings.ColorValuesChanged -= UISettingsColorChanged;
uiSettings.ColorValuesChanged += UISettingsColorChanged;
}
private void NotifyThemeUpdate()
{
if (mainApplicationFrame == null || mainApplicationFrame.Dispatcher == null) return;
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
ElementThemeChanged?.Invoke(this, RootTheme);
WeakReferenceMessenger.Default.Send(new ApplicationThemeChanged(_underlyingThemeService.IsUnderlyingThemeDark()));
});
}
private void UISettingsColorChanged(UISettings sender, object args)
{
// Make sure we have a reference to our window so we dispatch a UI change
if (mainApplicationFrame != null)
{
// Dispatch on UI thread so that we have a current appbar to access and change
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
UpdateSystemCaptionButtonColors();
var accentColor = sender.GetColorValue(UIColorType.Accent);
//AccentColorChangedBySystem?.Invoke(this, accentColor.ToHex());
});
}
NotifyThemeUpdate();
}
public void UpdateSystemCaptionButtonColors()
{
if (mainApplicationFrame == null) return;
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
if (titleBar == null) return;
if (_underlyingThemeService.IsUnderlyingThemeDark())
{
titleBar.ButtonForegroundColor = Colors.White;
}
else
{
titleBar.ButtonForegroundColor = Colors.Black;
}
});
}
public void UpdateAccentColor(string hex)
{
// Change accent color if specified.
if (!string.IsNullOrEmpty(hex))
{
var color = CommunityToolkit.WinUI.Helpers.ColorHelper.ToColor(hex);
var brush = new SolidColorBrush(color);
if (_applicationResourceManager.ContainsResourceKey("SystemAccentColor"))
_applicationResourceManager.ReplaceResource("SystemAccentColor", color);
if (_applicationResourceManager.ContainsResourceKey("NavigationViewSelectionIndicatorForeground"))
_applicationResourceManager.ReplaceResource("NavigationViewSelectionIndicatorForeground", brush);
if (_applicationResourceManager.ContainsResourceKey("SystemControlBackgroundAccentBrush"))
_applicationResourceManager.ReplaceResource("SystemControlBackgroundAccentBrush", brush);
if (_applicationResourceManager.ContainsResourceKey("SystemColorControlAccentBrush"))
_applicationResourceManager.ReplaceResource("SystemColorControlAccentBrush", brush);
RefreshThemeResource();
}
}
private void RefreshThemeResource()
{
if (mainApplicationFrame == null) return;
if (mainApplicationFrame.RequestedTheme == ElementTheme.Dark)
{
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
}
else if (mainApplicationFrame.RequestedTheme == ElementTheme.Light)
{
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
}
else
{
var isUnderlyingDark = _underlyingThemeService.IsUnderlyingThemeDark();
mainApplicationFrame.RequestedTheme = isUnderlyingDark ? ElementTheme.Light : ElementTheme.Dark;
mainApplicationFrame.RequestedTheme = ElementTheme.Default;
}
}
public async Task ApplyCustomThemeAsync(bool isInitializing)
{
AppThemeBase applyingTheme = null;
var controlThemeList = new List<AppThemeBase>(preDefinedThemes);
// Don't search for custom themes if applying theme is already in pre-defined templates.
// This is important for startup performance because we won't be loading the custom themes on launch.
bool isApplyingPreDefinedTheme = preDefinedThemes.Exists(a => a.Id == currentApplicationThemeId);
if (isApplyingPreDefinedTheme)
{
applyingTheme = preDefinedThemes.Find(a => a.Id == currentApplicationThemeId);
}
else
{
// User applied custom theme. Load custom themes and find it there.
// Fallback to Mica if nothing found.
var customThemes = await GetCurrentCustomThemesAsync();
controlThemeList.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
applyingTheme = controlThemeList.Find(a => a.Id == currentApplicationThemeId) ?? preDefinedThemes.First(a => a.Id == Guid.Parse(_micaThemeId));
}
try
{
var existingThemeDictionary = _applicationResourceManager.GetLastResource();
if (existingThemeDictionary != null && existingThemeDictionary.TryGetValue("ThemeName", out object themeNameString))
{
var themeName = themeNameString.ToString();
// Applying different theme.
if (themeName != applyingTheme.ThemeName)
{
var resourceDictionaryContent = await applyingTheme.GetThemeResourceDictionaryContentAsync();
var resourceDictionary = XamlReader.Load(resourceDictionaryContent) as ResourceDictionary;
// Custom themes require special attention for background image because
// they share the same base theme resource dictionary.
if (applyingTheme is CustomAppTheme)
{
resourceDictionary["ThemeBackgroundImage"] = $"ms-appdata:///local/{CustomThemeFolderName}/{applyingTheme.Id}.jpg";
}
_applicationResourceManager.RemoveResource(existingThemeDictionary);
_applicationResourceManager.AddResource(resourceDictionary);
bool isSystemTheme = applyingTheme is SystemAppTheme || applyingTheme is CustomAppTheme;
if (isSystemTheme)
{
// For system themes, set the RootElement theme from saved values.
// Potential bug: When we set it to system default, theme is not applied when system and
// app element theme is different :)
var savedElement = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
RootTheme = savedElement;
// Quickly switch theme to apply theme resource changes.
RefreshThemeResource();
}
else
RootTheme = applyingTheme.ForceElementTheme;
// Theme has accent color. Override.
if (!isInitializing)
{
AccentColor = applyingTheme.AccentColor;
}
}
else
UpdateSystemCaptionButtonColors();
}
}
catch (Exception ex)
{
Debug.WriteLine($"Apply theme failed -> {ex.Message}");
}
}
public async Task<List<AppThemeBase>> GetAvailableThemesAsync()
{
var availableThemes = new List<AppThemeBase>(preDefinedThemes);
var customThemes = await GetCurrentCustomThemesAsync();
availableThemes.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
return availableThemes;
}
public async Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData)
{
if (wallpaperData == null || wallpaperData.Length == 0)
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingWallpaper);
if (string.IsNullOrEmpty(themeName))
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingName);
var themes = await GetCurrentCustomThemesAsync();
if (themes.Exists(a => a.Name == themeName))
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeExists);
var newTheme = new CustomThemeMetadata()
{
Id = Guid.NewGuid(),
Name = themeName,
AccentColorHex = accentColor
};
// Save wallpaper.
// Filename would be the same as metadata id, in jpg format.
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
var wallpaperFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.jpg", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(wallpaperFile, wallpaperData);
// Generate thumbnail for settings page.
var thumbnail = await wallpaperFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView);
var thumbnailFile = await themeFolder.CreateFileAsync($"{newTheme.Id}_preview.jpg", CreationCollisionOption.ReplaceExisting);
using (var readerStream = thumbnail.AsStreamForRead())
{
byte[] bytes = new byte[readerStream.Length];
await readerStream.ReadExactlyAsync(bytes);
var buffer = bytes.AsBuffer();
await FileIO.WriteBufferAsync(thumbnailFile, buffer);
}
// Save metadata.
var metadataFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.json", CreationCollisionOption.ReplaceExisting);
var serialized = JsonSerializer.Serialize(newTheme, DomainModelsJsonContext.Default.CustomThemeMetadata);
await FileIO.WriteTextAsync(metadataFile, serialized);
return newTheme;
}
public async Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync()
{
var results = new List<CustomThemeMetadata>();
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
var allFiles = await themeFolder.GetFilesAsync();
var themeMetadatas = allFiles.Where(a => a.FileType == ".json");
foreach (var theme in themeMetadatas)
{
var metadata = await GetCustomMetadataAsync(theme).ConfigureAwait(false);
if (metadata == null) continue;
results.Add(metadata);
}
return results;
}
private async Task<CustomThemeMetadata> GetCustomMetadataAsync(IStorageFile file)
{
var fileContent = await FileIO.ReadTextAsync(file);
return JsonSerializer.Deserialize(fileContent, DomainModelsJsonContext.Default.CustomThemeMetadata);
}
public string GetSystemAccentColorHex()
=> uiSettings.GetColorValue(UIColorType.Accent).ToHex();
public List<string> GetAvailableAccountColors()
{
return new List<string>()
{
"#e74c3c",
"#c0392b",
"#e53935",
"#d81b60",
// Pinks
"#e91e63",
"#ec407a",
"#ff4081",
// Purples
"#9b59b6",
"#8e44ad",
"#673ab7",
// Blues
"#3498db",
"#2980b9",
"#2196f3",
"#03a9f4",
"#00bcd4",
// Teals
"#009688",
"#1abc9c",
"#16a085",
// Greens
"#2ecc71",
"#27ae60",
"#4caf50",
"#8bc34a",
// Yellows & Oranges
"#f1c40f",
"#f39c12",
"#ff9800",
"#ff5722",
// Browns
"#795548",
"#a0522d",
// Grays
"#9e9e9e",
"#607d8b",
"#34495e",
"#2c3e50",
};
}
}

Some files were not shown because too many files have changed in this diff Show More