diff --git a/Directory.Packages.props b/Directory.Packages.props
index 19f6fe46..e55bb4d6 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,6 +9,7 @@
+
diff --git a/Emerald/App.xaml.cs b/Emerald/App.xaml.cs
index 25e5d31b..4c4e74a9 100644
--- a/Emerald/App.xaml.cs
+++ b/Emerald/App.xaml.cs
@@ -12,7 +12,7 @@ public App()
this.InitializeComponent();
}
- protected Window? MainWindow { get; private set; }
+ public Window? MainWindow { get; private set; }
protected IHost? Host { get; private set; }
protected override void OnLaunched(LaunchActivatedEventArgs args)
@@ -31,6 +31,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
);
MainWindow = builder.Window;
+
#if DEBUG
MainWindow.EnableHotReload();
#endif
@@ -48,6 +49,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
// Place the frame in the current Window
MainWindow.Content = rootFrame;
}
+#if WINDOWS
+ var mica = Emerald.Uno.Helpers.WindowManager.IntializeWindow(MainWindow);
+#endif
if (rootFrame.Content == null)
{
@@ -59,4 +63,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
// Ensure the current window is active
MainWindow.Activate();
}
+
+ ///
+ /// Gets the current instance in use
+ ///
+ public new static App Current => (App)Application.Current;
}
diff --git a/Emerald/Assets/icon.png b/Emerald/Assets/icon.png
new file mode 100644
index 00000000..94000d94
Binary files /dev/null and b/Emerald/Assets/icon.png differ
diff --git a/Emerald/Emerald.csproj b/Emerald/Emerald.csproj
index 5e96e83c..77654f5f 100644
--- a/Emerald/Emerald.csproj
+++ b/Emerald/Emerald.csproj
@@ -41,7 +41,20 @@
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ MSBuild:Compile
+
+
diff --git a/Emerald/Helpers/Extensions.cs b/Emerald/Helpers/Extensions.cs
new file mode 100644
index 00000000..34d20225
--- /dev/null
+++ b/Emerald/Helpers/Extensions.cs
@@ -0,0 +1,155 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.Windows.ApplicationModel.Resources;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Windows.System.Diagnostics;
+
+namespace Emerald.Uno.Helpers;
+
+public static class Extensions
+{
+ private static readonly ConcurrentDictionary cachedResources = new();
+
+ //public static void ShowAt(this TeachingTip tip, FrameworkElement element, TeachingTipPlacementMode placement = TeachingTipPlacementMode.Auto, bool closeWhenClick = true, bool addToMainGrid = true)
+ //{
+ // if (addToMainGrid)
+ // (App.Current.MainWindow.Content as Grid).Children.Add(tip);
+
+ // tip.Target = element;
+ // tip.PreferredPlacement = placement;
+ // tip.IsOpen = true;
+ // if (closeWhenClick)
+ // {
+ // tip.ActionButtonClick += (_, _) => tip.IsOpen = false;
+ // tip.CloseButtonClick += (_, _) => tip.IsOpen = false;
+ // }
+ //}
+
+ public static int GetMemoryGB()
+ {
+ SystemMemoryUsageReport systemMemoryUsageReport = SystemDiagnosticInfo.GetForCurrentSystem().MemoryUsage.GetReport();
+
+ long memkb = Convert.ToInt64(systemMemoryUsageReport.TotalPhysicalSizeInBytes);
+ return Convert.ToInt32(memkb / Math.Pow(1024, 3));
+ }
+
+ public static string KiloFormat(this int num)
+ {
+ if (num >= 100000000)
+ return (num / 1000000).ToString("#,0M");
+
+ if (num >= 10000000)
+ return (num / 1000000).ToString("0.#") + "M";
+
+ if (num >= 100000)
+ return (num / 1000).ToString("#,0K");
+
+ if (num >= 1000)
+ return (num / 100).ToString("0.#") + "K";
+
+ return num.ToString("#,0");
+ }
+
+ //public static ContentDialog ToContentDialog(this UIElement content, string title, string closebtnText = null, ContentDialogButton defaultButton = ContentDialogButton.Close)
+ //{
+ // ContentDialog dialog = new()
+ // {
+ // XamlRoot = App.Current.MainWindow.Content.XamlRoot,
+ // Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
+ // Title = title,
+ // CloseButtonText = closebtnText,
+ // DefaultButton = defaultButton,
+ // Content = content,
+ // RequestedTheme = (ElementTheme)Settings.SettingsSystem.Settings.App.Appearance.Theme
+ // };
+ // return dialog;
+ //}
+
+ public static int Remove(this ObservableCollection coll, Func condition)
+ {
+ var itemsToRemove = coll.Where(condition).ToList();
+
+ foreach (var itemToRemove in itemsToRemove)
+ {
+ coll.Remove(itemToRemove);
+ }
+ return itemsToRemove.Count;
+ }
+
+ public static int Remove(this List coll, Func condition)
+ {
+ var itemsToRemove = coll.Where(condition).ToList();
+
+ foreach (var itemToRemove in itemsToRemove)
+ {
+ coll.Remove(itemToRemove);
+ }
+
+ return itemsToRemove.Count;
+ }
+ public static void AddRange(this ObservableCollection cll, IEnumerable items)
+ {
+ foreach (var item in items)
+ cll.Add(item);
+ }
+
+ public static string ToBinaryString(this string str)
+ {
+ var binary = "";
+ foreach (char ch in str)
+ {
+ binary += Convert.ToString((int)ch, 2);
+ }
+ return binary;
+ }
+
+ public static string ToMD5(this string s)
+ {
+ StringBuilder sb = new();
+ byte[] hashValue = MD5.HashData(Encoding.UTF8.GetBytes(s));
+
+ foreach (byte b in hashValue)
+ {
+ sb.Append($"{b:X2}");
+ }
+
+ return sb.ToString();
+ }
+
+ public static string Localize(this string resourceKey)
+ {
+ try
+ {
+ string s = Windows.ApplicationModel.Resources.ResourceLoader
+ .GetForViewIndependentUse()
+ .GetString(resourceKey);
+
+ return string.IsNullOrEmpty(s) ? resourceKey : s;
+ }
+ catch
+ {
+ return resourceKey;
+ }
+ }
+
+ //public static string Localize(this Core.Localized resourceKey) =>
+ // resourceKey.ToString().Localize();
+
+ //public static Models.Account ToAccount(this CmlLib.Core.Auth.MSession session, bool plusCount = true)
+ //{
+ // bool isOffline = session.AccessToken == "access_token";
+ // return new Models.Account(session.Username, isOffline ? null : session.AccessToken, isOffline ? null : session.UUID, plusCount ? MainWindow.HomePage.AccountsPage.AllCount++ : 0, false, session.ClientToken);
+ //}
+
+ //public static CmlLib.Core.Auth.MSession ToMSession(this Models.Account account)
+ //{
+ // bool isOffline = account.UUID == null;
+ // return new CmlLib.Core.Auth.MSession(account.UserName, isOffline ? "access_token" : account.AccessToken, isOffline ? Guid.NewGuid().ToString().Replace("-", "") : account.UUID) { ClientToken = account.ClientToken };
+ //}
+}
diff --git a/Emerald/Helpers/MarkupExtensions/ConditionString.cs b/Emerald/Helpers/MarkupExtensions/ConditionString.cs
new file mode 100644
index 00000000..b73a2c3c
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/ConditionString.cs
@@ -0,0 +1,21 @@
+using Microsoft.UI.Xaml.Markup;
+
+namespace Emerald.Uno.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(string))]
+public class ConditionString : MarkupExtension
+{
+ public string TrueString { get; set; }
+
+ public string FalseString { get; set; }
+
+ public bool Condition { get; set; }
+
+ public string Result
+ => Condition ? TrueString : FalseString;
+
+ public override string ToString()
+ => Result;
+
+ protected override object ProvideValue() => Result;
+}
diff --git a/Emerald/Helpers/MarkupExtensions/FontIcon.cs b/Emerald/Helpers/MarkupExtensions/FontIcon.cs
new file mode 100644
index 00000000..e9d03396
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/FontIcon.cs
@@ -0,0 +1,14 @@
+using Microsoft.UI.Xaml.Markup;
+
+namespace Emerald.Uno.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(Microsoft.UI.Xaml.Controls.FontIcon))]
+public sealed class FontIcon : MarkupExtension
+{
+ public string Glyph { get; set; }
+
+ public int FontSize { get; set; } = 16;
+
+ protected override object ProvideValue()
+ => new Microsoft.UI.Xaml.Controls.FontIcon() { Glyph = Glyph, FontSize = FontSize };
+}
diff --git a/Emerald/Helpers/MarkupExtensions/LocalizeString.cs b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs
new file mode 100644
index 00000000..31b5689c
--- /dev/null
+++ b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs
@@ -0,0 +1,12 @@
+using Microsoft.UI.Xaml.Markup;
+using CommunityToolkit.Mvvm;
+namespace Emerald.Uno.Helpers;
+
+[MarkupExtensionReturnType(ReturnType = typeof(string))]
+public sealed class Localize : MarkupExtension
+{
+ public string Name { get; set; }
+
+ protected override object ProvideValue()
+ => Name.Localize();
+}
diff --git a/Emerald/Helpers/WindowManager.cs b/Emerald/Helpers/WindowManager.cs
new file mode 100644
index 00000000..5e16c5ca
--- /dev/null
+++ b/Emerald/Helpers/WindowManager.cs
@@ -0,0 +1,176 @@
+using Microsoft.UI;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Composition.SystemBackdrops;
+using Microsoft.UI.Windowing;
+using Microsoft.UI.Xaml;
+using System;
+using System.Runtime.InteropServices;
+using Windows.ApplicationModel;
+using WinRT;
+using WinRT.Interop;
+
+namespace Emerald.Uno.Helpers
+{
+#if WINDOWS
+ public static class WindowManager
+ {
+ ///
+ /// Add mica and the icon to the
+ ///
+ public static MicaBackground IntializeWindow(Window window)
+ {
+ var s = new MicaBackground(window);
+ s.TrySetMicaBackdrop();
+ return s;
+ }
+
+ ///
+ /// Sets the customized titlebar if supported
+ ///
+ ///
+ public static void SetTitleBar(Window window, UIElement AppTitleBar)
+ {
+ FrameworkElement RootUI = (FrameworkElement)window.Content;
+ if (AppWindowTitleBar.IsCustomizationSupported())
+ {
+ var titlebar = window.AppWindow.TitleBar;
+ titlebar.ExtendsContentIntoTitleBar = true;
+
+ void SetColor(ElementTheme acualTheme)
+ {
+ titlebar.ButtonBackgroundColor = titlebar.ButtonInactiveBackgroundColor = titlebar.ButtonPressedBackgroundColor = Colors.Transparent;
+ switch (acualTheme)
+ {
+ case ElementTheme.Dark:
+ titlebar.ButtonHoverBackgroundColor = Colors.Black;
+ titlebar.ButtonForegroundColor = Colors.White;
+ titlebar.ButtonHoverForegroundColor = Colors.White;
+ titlebar.ButtonPressedForegroundColor = Colors.Silver;
+ break;
+ case ElementTheme.Light:
+ titlebar.ButtonHoverBackgroundColor = Colors.White;
+ titlebar.ButtonForegroundColor = Colors.Black;
+ titlebar.ButtonHoverForegroundColor = Colors.Black;
+ titlebar.ButtonPressedForegroundColor = Colors.DarkGray;
+ break;
+ }
+ }
+
+ RootUI.ActualThemeChanged += (s, _) => SetColor(s.ActualTheme);
+ window.SetTitleBar(AppTitleBar);
+ SetColor(RootUI.ActualTheme);
+ }
+ else
+ {
+ window.ExtendsContentIntoTitleBar = true;
+ window.SetTitleBar(AppTitleBar);
+ }
+ }
+ }
+
+ public class WindowsSystemDispatcherQueueHelper
+ {
+ private object? _dispatcherQueueController;
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DispatcherQueueOptions
+ {
+ internal int dwSize;
+ internal int threadType;
+ internal int apartmentType;
+ }
+
+ [DllImport("CoreMessaging.dll")]
+ private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object? dispatcherQueueController);
+
+ public void EnsureWindowsSystemDispatcherQueueController()
+ {
+ if (Windows.System.DispatcherQueue.GetForCurrentThread() != null)
+ {
+ // one already exists, so we'll just use it.
+ return;
+ }
+
+ if (_dispatcherQueueController == null)
+ {
+ DispatcherQueueOptions options;
+ options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
+ options.threadType = 2;
+ options.apartmentType = 2;
+
+ CreateDispatcherQueueController(options, ref _dispatcherQueueController);
+ }
+ }
+ }
+
+ public class MicaBackground
+ {
+ private readonly Window _window;
+ public readonly MicaController MicaController = new();
+ private SystemBackdropConfiguration _backdropConfiguration = new();
+ private readonly WindowsSystemDispatcherQueueHelper _dispatcherQueueHelper = new();
+
+ public MicaBackground(Window window)
+ {
+ _window = window;
+ }
+
+ public bool TrySetMicaBackdrop()
+ {
+ if (MicaController.IsSupported())
+ {
+ _dispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController();
+ _window.Activated += WindowOnActivated;
+ _window.Closed += WindowOnClosed;
+ ((FrameworkElement)_window.Content).ActualThemeChanged += MicaBackground_ActualThemeChanged;
+ _backdropConfiguration.IsInputActive = true;
+
+ _backdropConfiguration.Theme = _window.Content switch
+ {
+ FrameworkElement { ActualTheme: ElementTheme.Dark } => SystemBackdropTheme.Dark,
+ FrameworkElement { ActualTheme: ElementTheme.Light } => SystemBackdropTheme.Light,
+ FrameworkElement { ActualTheme: ElementTheme.Default } => SystemBackdropTheme.Default,
+ _ => throw new InvalidOperationException("Unknown theme")
+ };
+
+ MicaController.AddSystemBackdropTarget(_window.As());
+ MicaController.SetSystemBackdropConfiguration(_backdropConfiguration);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void MicaBackground_ActualThemeChanged(FrameworkElement sender, object args)
+ {
+ if (_backdropConfiguration != null)
+ {
+ SetConfigurationSourceTheme();
+ }
+ }
+
+ private void SetConfigurationSourceTheme()
+ {
+ switch (((FrameworkElement)_window.Content).ActualTheme)
+ {
+ case ElementTheme.Dark: _backdropConfiguration.Theme = SystemBackdropTheme.Dark; break;
+ case ElementTheme.Light: _backdropConfiguration.Theme = SystemBackdropTheme.Light; break;
+ case ElementTheme.Default: _backdropConfiguration.Theme = SystemBackdropTheme.Default; break;
+ }
+ }
+
+ private void WindowOnClosed(object sender, WindowEventArgs args)
+ {
+ MicaController.Dispose();
+ _window.Activated -= WindowOnActivated;
+ _backdropConfiguration = null!;
+ }
+
+ private void WindowOnActivated(object sender, WindowActivatedEventArgs args)
+ {
+ _backdropConfiguration.IsInputActive = args.WindowActivationState is not WindowActivationState.Deactivated;
+ }
+ }
+#endif
+}
diff --git a/Emerald/MainPage.xaml b/Emerald/MainPage.xaml
index 7145924d..68ff87d4 100644
--- a/Emerald/MainPage.xaml
+++ b/Emerald/MainPage.xaml
@@ -3,12 +3,169 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Emerald"
xmlns:utu="using:Uno.Toolkit.UI"
- Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
-
-
-
+ xmlns:models="using:Emerald.Uno.Models"
+ xmlns:uc="using:Emerald.Uno.UserControls"
+ xmlns:helpers="using:Emerald.Uno.Helpers">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+ 24
+ 2
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/MainPage.xaml.cs b/Emerald/MainPage.xaml.cs
index f7f1791c..b118f31e 100644
--- a/Emerald/MainPage.xaml.cs
+++ b/Emerald/MainPage.xaml.cs
@@ -5,5 +5,13 @@ public sealed partial class MainPage : Page
public MainPage()
{
this.InitializeComponent();
+ this.Loaded += MainPage_Loaded;
+ }
+
+ private void MainPage_Loaded(object sender, RoutedEventArgs e)
+ {
+#if WINDOWS
+ Emerald.Uno.Helpers.WindowManager.SetTitleBar(App.Current.MainWindow, AppTitleBar);
+#endif
}
}
diff --git a/Emerald/Models/Model.cs b/Emerald/Models/Model.cs
new file mode 100644
index 00000000..1374d30e
--- /dev/null
+++ b/Emerald/Models/Model.cs
@@ -0,0 +1,17 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System.ComponentModel;
+
+namespace Emerald.Uno.Models;
+
+public partial class Model : ObservableObject
+{
+ internal void Set(ref T obj, T value, string name = null)
+ {
+ SetProperty(ref obj, value, name);
+ }
+
+ public void InvokePropertyChanged(string name = null)
+ {
+ OnPropertyChanged(new PropertyChangedEventArgs(name));
+ }
+}
diff --git a/Emerald/Models/NavViewHeader.cs b/Emerald/Models/NavViewHeader.cs
new file mode 100644
index 00000000..97c3ab38
--- /dev/null
+++ b/Emerald/Models/NavViewHeader.cs
@@ -0,0 +1,23 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Emerald.Uno.Models;
+
+public partial class NavViewHeader : Model
+{
+ [ObservableProperty]
+ private string _HeaderText;
+
+ [ObservableProperty]
+ private string _CustomButtonText;
+
+ public Visibility CustomButtonVisibility { get => CustomButtonText == null ? Visibility.Collapsed : Visibility.Visible; }
+
+
+ [ObservableProperty]
+ private string _CustomContent;
+
+ public Visibility CustomContentVisibility { get => CustomContent == null ? Visibility.Collapsed : Visibility.Visible; }
+
+ [ObservableProperty]
+ private Thickness _HeaderMargin;
+}
diff --git a/Emerald/Models/SquareNavigationViewItem.cs b/Emerald/Models/SquareNavigationViewItem.cs
new file mode 100644
index 00000000..8e3510ed
--- /dev/null
+++ b/Emerald/Models/SquareNavigationViewItem.cs
@@ -0,0 +1,61 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Emerald.Uno.Models;
+
+public partial class SquareNavigationViewItem : Model
+{
+ public SquareNavigationViewItem()
+ {
+ PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons))
+ {
+ InvokePropertyChanged(null);
+ }
+ };
+ }
+ public SquareNavigationViewItem(string name, bool isSelected = false, ImageSource image = null, InfoBadge infoBadge = null)
+ {
+ Name = name;
+ IsSelected = isSelected;
+ Thumbnail = image;
+ InfoBadge = infoBadge;
+ PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons))
+ {
+ InvokePropertyChanged(null);
+ }
+ };
+ }
+
+
+ [ObservableProperty]
+ private string _Name;
+
+ [ObservableProperty]
+ private string _FontIconGlyph;
+
+ [ObservableProperty]
+ private string _SolidFontIconGlyph;
+
+ [ObservableProperty]
+ private bool _IsSelected;
+
+ [ObservableProperty]
+ private bool _IsEnabled = true;
+
+ [ObservableProperty]
+ private ImageSource _Thumbnail;
+
+ [ObservableProperty]
+ private InfoBadge _InfoBadge;
+
+ [ObservableProperty]
+ private bool _ShowFontIcons;
+
+ public Visibility FontIconVisibility => ShowFontIcons && !IsSelected ? Visibility.Visible : Visibility.Collapsed;
+ public Visibility SolidFontIconVisibility => ShowFontIcons && IsSelected ? Visibility.Visible : Visibility.Collapsed;
+
+ public bool ShowThumbnail => !ShowFontIcons;
+}
diff --git a/Emerald/UserControls/Titlebar.xaml b/Emerald/UserControls/Titlebar.xaml
new file mode 100644
index 00000000..2ecadaa5
--- /dev/null
+++ b/Emerald/UserControls/Titlebar.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Emerald/UserControls/Titlebar.xaml.cs b/Emerald/UserControls/Titlebar.xaml.cs
new file mode 100644
index 00000000..dafb733a
--- /dev/null
+++ b/Emerald/UserControls/Titlebar.xaml.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+
+// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
+
+namespace Emerald.Uno.UserControls;
+public sealed partial class TitleBar : UserControl
+{
+ public TitleBar()
+ {
+ this.InitializeComponent();
+ }
+}