Explorar o código

Architecture update

Ruben hai 1 ano
pai
achega
b2e13e31e3
Modificáronse 51 ficheiros con 523 adicións e 476 borrados
  1. 6 0
      src/Directory.Build.props
  2. 4 2
      src/PicView.Avalonia.MacOS/App.axaml
  3. 28 0
      src/PicView.Avalonia.MacOS/App.axaml.cs
  4. 25 0
      src/PicView.Avalonia.MacOS/PicView.Avalonia.MacOS.csproj
  5. 28 0
      src/PicView.Avalonia.MacOS/Program.cs
  6. 8 3
      src/PicView.Avalonia.MacOS/Views/MainWindow.axaml
  7. 11 0
      src/PicView.Avalonia.MacOS/Views/MainWindow.axaml.cs
  8. 1 1
      src/PicView.Avalonia.MacOS/app.manifest
  9. 1 0
      src/PicView.Avalonia.MacOS/readme.md
  10. 10 0
      src/PicView.Avalonia.Win32/App.axaml
  11. 28 0
      src/PicView.Avalonia.Win32/App.axaml.cs
  12. 29 0
      src/PicView.Avalonia.Win32/PicView.Avalonia.Win32.csproj
  13. 12 5
      src/PicView.Avalonia.Win32/Program.cs
  14. 16 0
      src/PicView.Avalonia.Win32/Views/MainWindow.axaml
  15. 11 0
      src/PicView.Avalonia.Win32/Views/MainWindow.axaml.cs
  16. 18 0
      src/PicView.Avalonia.Win32/app.manifest
  17. 1 0
      src/PicView.Avalonia.Win32/readme.md
  18. 0 37
      src/PicView.Avalonia/App.axaml.cs
  19. BIN=BIN
      src/PicView.Avalonia/Assets/avalonia-logo.ico
  20. BIN=BIN
      src/PicView.Avalonia/Assets/icon.ico
  21. BIN=BIN
      src/PicView.Avalonia/Assets/icon.png
  22. 0 16
      src/PicView.Avalonia/Models/LoadSettings.cs
  23. 0 84
      src/PicView.Avalonia/Models/SkiaExtensions.cs
  24. 7 56
      src/PicView.Avalonia/PicView.Avalonia.csproj
  25. 6 0
      src/PicView.Avalonia/ViewModels/MainViewModel.cs
  26. 7 0
      src/PicView.Avalonia/ViewModels/ViewModelBase.cs
  27. 0 23
      src/PicView.Avalonia/Views/MacMainWindow.axaml
  28. 0 13
      src/PicView.Avalonia/Views/MacMainWindow.axaml.cs
  29. 16 0
      src/PicView.Avalonia/Views/MainView.axaml
  30. 11 0
      src/PicView.Avalonia/Views/MainView.axaml.cs
  31. 0 24
      src/PicView.Avalonia/Views/MainWindow.axaml
  32. 0 52
      src/PicView.Avalonia/Views/MainWindow.axaml.cs
  33. 0 11
      src/PicView.Avalonia/Views/UserControls/BottomMenu.axaml.cs
  34. 1 0
      src/PicView.Avalonia/readme.md
  35. 11 0
      src/PicView.Core/PicView.Core.csproj
  36. 7 0
      src/PicView.MacOS/Class1.cs
  37. 9 0
      src/PicView.MacOS/PicView.MacOS.csproj
  38. 1 1
      src/PicView.WPF/PicGallery/GalleryLoad.cs
  39. 1 0
      src/PicView.WPF/PicView.WPF.csproj
  40. 0 24
      src/PicView.WPF/SystemIntegration/LockScreenImage.cs
  41. 0 6
      src/PicView.WPF/SystemIntegration/NativeMethods.cs
  42. 9 88
      src/PicView.WPF/SystemIntegration/Wallpaper.cs
  43. 2 1
      src/PicView.WPF/UILogic/Loading/LoadContextMenus.cs
  44. 14 14
      src/PicView.WPF/UILogic/Loading/StartLoading.cs
  45. 2 1
      src/PicView.WPF/Views/UserControls/Gallery/PicGalleryItem.xaml.cs
  46. 6 5
      src/PicView.WPF/Views/Windows/EffectsWindows.xaml.cs
  47. 31 0
      src/PicView.Windows/Lockscreen/LockscreenHelper.cs
  48. 24 0
      src/PicView.Windows/PicView.Windows.csproj
  49. 91 0
      src/PicView.Windows/Wallpaper/WallpaperHelper.cs
  50. 29 5
      src/PicView.sln
  51. 1 4
      src/XamlAnimatedGif/Decoding/GifDecoderException.cs

+ 6 - 0
src/Directory.Build.props

@@ -0,0 +1,6 @@
+<Project>
+  <PropertyGroup>
+    <Nullable>enable</Nullable>
+    <AvaloniaVersion>11.0.2</AvaloniaVersion>
+  </PropertyGroup>
+</Project>

+ 4 - 2
src/PicView.Avalonia/App.axaml → src/PicView.Avalonia.MacOS/App.axaml

@@ -1,9 +1,11 @@
 <Application
 <Application
-    x:Class="PicView.Avalonia.App"
+    x:Class="PicView.Avalonia.MacOS.App"
     xmlns="https://github.com/avaloniaui"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     RequestedThemeVariant="Default">
     RequestedThemeVariant="Default">
+    <!--  "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options.  -->
+
     <Application.Styles>
     <Application.Styles>
-        <SimpleTheme />
+        <FluentTheme />
     </Application.Styles>
     </Application.Styles>
 </Application>
 </Application>

+ 28 - 0
src/PicView.Avalonia.MacOS/App.axaml.cs

@@ -0,0 +1,28 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using PicView.Avalonia.ViewModels;
+using MainWindow = PicView.Avalonia.MacOS.Views.MainWindow;
+
+namespace PicView.Avalonia.MacOS;
+
+public partial class App : Application
+{
+    public override void Initialize()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    public override void OnFrameworkInitializationCompleted()
+    {
+        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            desktop.MainWindow = new MainWindow
+            {
+                DataContext = new MainViewModel()
+            };
+        }
+
+        base.OnFrameworkInitializationCompleted();
+    }
+}

+ 25 - 0
src/PicView.Avalonia.MacOS/PicView.Avalonia.MacOS.csproj

@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
+    One for Windows with net7.0-windows TFM, one for MacOS with net7.0-macos and one with net7.0 TFM for Linux.-->
+    <TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\PicView.Avalonia\PicView.Avalonia.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="App.axaml.cs">
+      <DependentUpon>App.axaml</DependentUpon>
+    </Compile>
+  </ItemGroup>
+</Project>

+ 28 - 0
src/PicView.Avalonia.MacOS/Program.cs

@@ -0,0 +1,28 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+
+namespace PicView.Avalonia.MacOS;
+
+internal class Program
+{
+    // Initialization code. Don't use any Avalonia, third-party APIs or any
+    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+    // yet and stuff might break.
+    [STAThread]
+    public static void Main(string[] args) => BuildAvaloniaApp()
+        .StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose);
+
+    // Avalonia configuration, don't remove; also used by visual designer.
+    public static AppBuilder BuildAvaloniaApp()
+    {
+        return AppBuilder.Configure<App>()
+#if DEBUG
+            .LogToTrace()
+#endif
+            .UseReactiveUI()
+            .UsePlatformDetect()
+            .UseSkia();
+    }
+}

+ 8 - 3
src/PicView.Avalonia/Views/UserControls/BottomMenu.axaml → src/PicView.Avalonia.MacOS/Views/MainWindow.axaml

@@ -1,9 +1,14 @@
-<UserControl
-    x:Class="PicView.Avalonia.Views.UserControls.BottomMenu"
+<Window
+    x:Class="PicView.Avalonia.MacOS.Views.MainWindow"
     xmlns="https://github.com/avaloniaui"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:views="clr-namespace:PicView.Avalonia.Views;assembly=PicView.Avalonia"
+    xmlns:vm="using:PicView.Avalonia.ViewModels"
+    Title="PicView MacOS"
     d:DesignHeight="450"
     d:DesignHeight="450"
     d:DesignWidth="800"
     d:DesignWidth="800"
-    mc:Ignorable="d" />
+    mc:Ignorable="d">
+    <views:MainView />
+</Window>

+ 11 - 0
src/PicView.Avalonia.MacOS/Views/MainWindow.axaml.cs

@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace PicView.Avalonia.MacOS.Views;
+
+public partial class MainWindow : Window
+{
+    public MainWindow()
+    {
+        InitializeComponent();
+    }
+}

+ 1 - 1
src/PicView.Avalonia/app.manifest → src/PicView.Avalonia.MacOS/app.manifest

@@ -3,7 +3,7 @@
   <!-- This manifest is used on Windows only.
   <!-- This manifest is used on Windows only.
        Don't remove it as it might cause problems with window transparency and embeded controls.
        Don't remove it as it might cause problems with window transparency and embeded controls.
        For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
        For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
-  <assemblyIdentity version="1.0.0.0" name="PicView.Avalonia.Desktop"/>
+  <assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>
 
 
   <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
   <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
     <application>
     <application>

+ 1 - 0
src/PicView.Avalonia.MacOS/readme.md

@@ -0,0 +1 @@
+This project is for containing the views specific for the MacOS platform.

+ 10 - 0
src/PicView.Avalonia.Win32/App.axaml

@@ -0,0 +1,10 @@
+<Application xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="PicView.Avalonia.Win32.App"
+             RequestedThemeVariant="Default">
+             <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
+
+    <Application.Styles>
+        <FluentTheme />
+    </Application.Styles>
+</Application>

+ 28 - 0
src/PicView.Avalonia.Win32/App.axaml.cs

@@ -0,0 +1,28 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using PicView.Avalonia.ViewModels;
+using MainWindow = PicView.Avalonia.Win32.Views.MainWindow;
+
+namespace PicView.Avalonia.Win32;
+
+public partial class App : Application
+{
+    public override void Initialize()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    public override void OnFrameworkInitializationCompleted()
+    {
+        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            desktop.MainWindow = new MainWindow
+            {
+                DataContext = new MainViewModel()
+            };
+        }
+
+        base.OnFrameworkInitializationCompleted();
+    }
+}

+ 29 - 0
src/PicView.Avalonia.Win32/PicView.Avalonia.Win32.csproj

@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
+    One for Windows with net7.0-windows TFM, one for MacOS with net7.0-macos and one with net7.0 TFM for Linux.-->
+    <TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\PicView.Avalonia\PicView.Avalonia.csproj" />
+    <ProjectReference Include="..\PicView.Windows\PicView.Windows.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="App.axaml.cs">
+      <DependentUpon>%(Filename)</DependentUpon>
+    </Compile>
+    <Compile Update="Views\MainWindow.axaml.cs">
+      <DependentUpon>%(Filename)</DependentUpon>
+    </Compile>
+  </ItemGroup>
+</Project>

+ 12 - 5
src/PicView.Avalonia/Program.cs → src/PicView.Avalonia.Win32/Program.cs

@@ -1,6 +1,9 @@
-using Avalonia;
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.ReactiveUI;
 
 
-namespace PicView.Avalonia;
+namespace PicView.Avalonia.Win32;
 
 
 internal class Program
 internal class Program
 {
 {
@@ -9,13 +12,17 @@ internal class Program
     // yet and stuff might break.
     // yet and stuff might break.
     [STAThread]
     [STAThread]
     public static void Main(string[] args) => BuildAvaloniaApp()
     public static void Main(string[] args) => BuildAvaloniaApp()
-        .StartWithClassicDesktopLifetime(args);
+        .StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose);
 
 
     // Avalonia configuration, don't remove; also used by visual designer.
     // Avalonia configuration, don't remove; also used by visual designer.
     public static AppBuilder BuildAvaloniaApp()
     public static AppBuilder BuildAvaloniaApp()
-        => AppBuilder.Configure<App>()
+    {
+        return AppBuilder.Configure<App>()
 #if DEBUG
 #if DEBUG
             .LogToTrace()
             .LogToTrace()
 #endif
 #endif
-            .UsePlatformDetect();
+            .UseReactiveUI()
+            .UseWin32()
+            .UseSkia();
+    }
 }
 }

+ 16 - 0
src/PicView.Avalonia.Win32/Views/MainWindow.axaml

@@ -0,0 +1,16 @@
+<Window
+    x:Class="PicView.Avalonia.Win32.Views.MainWindow"
+    xmlns="https://github.com/avaloniaui"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:views="clr-namespace:PicView.Avalonia.Views"
+    xmlns:views1="clr-namespace:PicView.Avalonia.Win32.Views"
+    xmlns:views2="clr-namespace:PicView.Avalonia.Views;assembly=PicView.Avalonia"
+    xmlns:vm="using:PicView.Avalonia.ViewModels"
+    Title="PicView.Avalonia"
+    d:DesignHeight="450"
+    d:DesignWidth="800"
+    mc:Ignorable="d">
+    <views2:MainView />
+</Window>

+ 11 - 0
src/PicView.Avalonia.Win32/Views/MainWindow.axaml.cs

@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace PicView.Avalonia.Win32.Views;
+
+public partial class MainWindow : Window
+{
+    public MainWindow()
+    {
+        InitializeComponent();
+    }
+}

+ 18 - 0
src/PicView.Avalonia.Win32/app.manifest

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <!-- This manifest is used on Windows only.
+       Don't remove it as it might cause problems with window transparency and embeded controls.
+       For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
+  <assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on
+           and is designed to work with. Uncomment the appropriate elements
+           and Windows will automatically select the most compatible environment. -->
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+</assembly>

+ 1 - 0
src/PicView.Avalonia.Win32/readme.md

@@ -0,0 +1 @@
+This project is for containing the views specific for the Windows platform.

+ 0 - 37
src/PicView.Avalonia/App.axaml.cs

@@ -1,37 +0,0 @@
-using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Markup.Xaml;
-using PicView.Avalonia.Views;
-using PicView.Core.Config;
-using System.Runtime;
-using System.Runtime.InteropServices;
-
-namespace PicView.Avalonia;
-
-public partial class App : Application
-{
-    public override void Initialize()
-    {
-        ProfileOptimization.SetProfileRoot(AppDomain.CurrentDomain.BaseDirectory);
-        ProfileOptimization.StartProfile("ProfileOptimization");
-        AvaloniaXamlLoader.Load(this);
-    }
-
-    public override void OnFrameworkInitializationCompleted()
-    {
-        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-        {
-            // Run MacOS specific style
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
-            {
-                desktop.MainWindow = new MacMainWindow();
-            }
-            else
-            {
-                desktop.MainWindow = new MainWindow();
-            }
-        }
-
-        base.OnFrameworkInitializationCompleted();
-    }
-}

BIN=BIN
src/PicView.Avalonia/Assets/avalonia-logo.ico


BIN=BIN
src/PicView.Avalonia/Assets/icon.ico


BIN=BIN
src/PicView.Avalonia/Assets/icon.png


+ 0 - 16
src/PicView.Avalonia/Models/LoadSettings.cs

@@ -1,16 +0,0 @@
-using PicView.Core.Config;
-using PicView.Core.Localization;
-
-namespace PicView.Avalonia.Models;
-
-internal static class LoadSettings
-{
-    internal static async Task StartLoadingAsync()
-    {
-        await Task.Run(async () =>
-        {
-            await SettingsHelper.LoadSettingsAsync().ConfigureAwait(false);
-            await TranslationHelper.LoadLanguage(SettingsHelper.Settings.UIProperties.UserLanguage).ConfigureAwait(false);
-        }).ConfigureAwait(false);
-    }
-}

+ 0 - 84
src/PicView.Avalonia/Models/SkiaExtensions.cs

@@ -1,84 +0,0 @@
-using Avalonia;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Skia;
-using SkiaSharp;
-
-namespace PicView.Avalonia.Models;
-
-internal static class SkiaExtensions
-{
-    private record SKBitmapDrawOperation : ICustomDrawOperation
-    {
-        public Rect Bounds { get; set; }
-
-        public SKBitmap? Bitmap { get; init; }
-
-        public void Dispose()
-        {
-            Bitmap?.Dispose();
-        }
-
-        public bool Equals(ICustomDrawOperation? other) => false;
-
-        public bool HitTest(Point p) => Bounds.Contains(p);
-
-        public void Render(ImmediateDrawingContext context)
-        {
-            if (Bitmap is null || context.PlatformImpl.GetFeature<ISkiaSharpApiLeaseFeature>() is not { } leaseFeature)
-            {
-                return;
-            }
-
-            var lease = leaseFeature.Lease();
-            using (lease)
-            {
-                lease.SkCanvas.DrawBitmap(Bitmap, SKRect.Create((float)Bounds.X, (float)Bounds.Y, (float)Bounds.Width, (float)Bounds.Height));
-            }
-        }
-    }
-
-    private class AvaloniaImage : IImage, IDisposable
-    {
-        private readonly SKBitmap? _source;
-        private SKBitmapDrawOperation? _drawImageOperation;
-
-        public AvaloniaImage(SKBitmap? source)
-        {
-            _source = source;
-            if (source?.Info.Size is { } size)
-            {
-                Size = new Size(size.Width, size.Height);
-            }
-        }
-
-        public Size Size { get; }
-
-        public void Dispose()
-        {
-            _source?.Dispose();
-            _drawImageOperation?.Dispose();
-        }
-
-        public void Draw(DrawingContext context, Rect sourceRect, Rect destRect)
-        {
-            _drawImageOperation ??= new SKBitmapDrawOperation()
-            {
-                Bitmap = _source,
-            }; ;
-            _drawImageOperation.Bounds = sourceRect;
-            context.Custom(_drawImageOperation);
-        }
-    }
-
-    public static SKBitmap? ToSKBitmap(this Stream? stream)
-    {
-        return stream == null ? null : SKBitmap.Decode(stream);
-    }
-
-    public static IImage? ToAvaloniaImage(this SKBitmap? bitmap)
-    {
-        return bitmap is not null ? new AvaloniaImage(bitmap) : default(IImage?);
-    }
-}

+ 7 - 56
src/PicView.Avalonia/PicView.Avalonia.csproj

@@ -1,71 +1,22 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <OutputType>WinExe</OutputType>
     <TargetFramework>net8.0</TargetFramework>
     <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
-    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
-    <ApplicationManifest>app.manifest</ApplicationManifest>
+    <LangVersion>latest</LangVersion>
     <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
     <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
-    <ApplicationIcon>Assets\icon.ico</ApplicationIcon>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <Copyright>Ruben Hyldgaard Negendahl</Copyright>
-    <PackageProjectUrl>picview.org</PackageProjectUrl>
-    <RepositoryUrl>https://github.com/Ruben2776/PicView/</RepositoryUrl>
-    <PublishAot>False</PublishAot>
-    <PublishTrimmed>False</PublishTrimmed>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <DebugType>none</DebugType>
   </PropertyGroup>
   </PropertyGroup>
 
 
+  
   <ItemGroup>
   <ItemGroup>
     <AvaloniaResource Include="Assets\**" />
     <AvaloniaResource Include="Assets\**" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <AvaloniaResource Remove="Assets\icon.ico" />
-    <AvaloniaResource Remove="Assets\icon.png" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Remove="Assets\icon.ico" />
-    <None Remove="Assets\icon.png" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Content Include="Assets\icon.ico" />
-  </ItemGroup>
-
-
-  <ItemGroup>
-    <PackageReference Include="Avalonia" Version="11.0.6" />
-    <PackageReference Include="Avalonia.Desktop" Version="11.0.6" />
+    <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
+    <PackageReference Include="Avalonia.ReactiveUI" Version="$(AvaloniaVersion)" />
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
-    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.6" />
-    <PackageReference Include="Avalonia.Themes.Simple" Version="11.0.6" />
-    <PackageReference Include="Magick.NET-Q8-OpenMP-x64" Version="13.5.0" />
-  </ItemGroup>
-
-
-  <ItemGroup>
-    <ProjectReference Include="..\PicView.Core\PicView.Core.csproj" />
-  </ItemGroup>
-
-
-  <ItemGroup>
-    <Resource Include="Assets\icon.png" />
-    <Resource Include="Assets\icon.ico" />
-  </ItemGroup>
-
-
-  <ItemGroup>
-    <Compile Update="Views\MacMainWindow.axaml.cs">
-      <DependentUpon>MacMainWindow.axaml</DependentUpon>
-    </Compile>
-  </ItemGroup>
-
-
-  <ItemGroup>
-    <Using Include="System.Diagnostics" />
+    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 6 - 0
src/PicView.Avalonia/ViewModels/MainViewModel.cs

@@ -0,0 +1,6 @@
+namespace PicView.Avalonia.ViewModels;
+
+public class MainViewModel : ViewModelBase
+{
+    public string Greeting => "Welcome to Avalonia!";
+}

+ 7 - 0
src/PicView.Avalonia/ViewModels/ViewModelBase.cs

@@ -0,0 +1,7 @@
+using ReactiveUI;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class ViewModelBase : ReactiveObject
+{
+}

+ 0 - 23
src/PicView.Avalonia/Views/MacMainWindow.axaml

@@ -1,23 +0,0 @@
-<Window
-    x:Class="PicView.Avalonia.Views.MacMainWindow"
-    xmlns="https://github.com/avaloniaui"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-    Title="Loading..."
-    MinWidth="462"
-    MinHeight="400"
-    d:DesignHeight="450"
-    d:DesignWidth="800"
-    Background="Transparent"
-    ExtendClientAreaChromeHints="PreferSystemChrome"
-    ExtendClientAreaTitleBarHeightHint="-1"
-    ExtendClientAreaToDecorationsHint="True"
-    SizeToContent="WidthAndHeight"
-    TransparencyLevelHint="AcrylicBlur"
-    WindowStartupLocation="CenterScreen"
-    mc:Ignorable="d">
-    <Border>
-        <DockPanel />
-    </Border>
-</Window>

+ 0 - 13
src/PicView.Avalonia/Views/MacMainWindow.axaml.cs

@@ -1,13 +0,0 @@
-using Avalonia.Controls;
-using System;
-
-namespace PicView.Avalonia.Views;
-
-public partial class MacMainWindow : Window
-{
-    public MacMainWindow()
-    {
-        InitializeComponent();
-        Models.LoadSettings.StartLoadingAsync();
-    }
-}

+ 16 - 0
src/PicView.Avalonia/Views/MainView.axaml

@@ -0,0 +1,16 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:vm="clr-namespace:PicView.Avalonia.ViewModels"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PicView.Avalonia.Views.MainView"
+             x:DataType="vm:MainViewModel">
+  <Design.DataContext>
+    <!-- This only sets the DataContext for the previewer in an IDE,
+         to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
+    <vm:MainViewModel />
+  </Design.DataContext>
+
+  <TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+</UserControl>

+ 11 - 0
src/PicView.Avalonia/Views/MainView.axaml.cs

@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace PicView.Avalonia.Views;
+
+public partial class MainView : UserControl
+{
+    public MainView()
+    {
+        InitializeComponent();
+    }
+}

+ 0 - 24
src/PicView.Avalonia/Views/MainWindow.axaml

@@ -1,24 +0,0 @@
-<Window
-    x:Class="PicView.Avalonia.Views.MainWindow"
-    xmlns="https://github.com/avaloniaui"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-    Title="Loading..."
-    MinWidth="462"
-    MinHeight="400"
-    d:DesignHeight="450"
-    d:DesignWidth="800"
-    Background="Transparent"
-    ExtendClientAreaChromeHints="PreferSystemChrome"
-    ExtendClientAreaTitleBarHeightHint="-1"
-    ExtendClientAreaToDecorationsHint="True"
-    SizeToContent="WidthAndHeight"
-    TransparencyLevelHint="AcrylicBlur"
-    WindowStartupLocation="CenterScreen"
-    mc:Ignorable="d">
-    <Grid>
-
-        <Image x:Name="MainImage" Stretch="Uniform" />
-    </Grid>
-</Window>

+ 0 - 52
src/PicView.Avalonia/Views/MainWindow.axaml.cs

@@ -1,52 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Threading;
-using PicView.Avalonia.Models;
-using PicView.Core.Calculations;
-using PicView.Core.Config;
-
-namespace PicView.Avalonia.Views;
-
-public partial class MainWindow : Window
-{
-    public MainWindow()
-    {
-        InitializeComponent();
-
-        Loaded += async delegate
-        {
-            await LoadSettings.StartLoadingAsync().ConfigureAwait(false);
-            var args = Environment.GetCommandLineArgs();
-            if (args.Length > 1)
-            {
-                var skBitmap = await Core.ImageDecoding.ImageDecoder.GetSKBitmapAsync(new FileInfo(args[1])).ConfigureAwait(false);
-                var avaloniaPic = skBitmap.ToAvaloniaImage();
-                await Dispatcher.UIThread.InvokeAsync(() =>
-                {
-                    var size = ImageSizeCalculationHelper.GetImageSize(
-                        width: avaloniaPic.Size.Width,
-                        height: avaloniaPic.Size.Height,
-                        monitorWidth: 2560,
-                        monitorHeight: 1440,
-                        rotationAngle: 0,
-                        stretch: SettingsHelper.Settings.ImageScaling.StretchImage,
-                        padding: 0,
-                        dpiScaling: 1,
-                        fullscreen: SettingsHelper.Settings.ImageScaling.StretchImage,
-                        uiTopSize: 0,
-                        uiBottomSize: 0,
-                        galleryHeight: SettingsHelper.Settings.Gallery.IsBottomGalleryShown
-                            ? SettingsHelper.Settings.Gallery.BottomGalleryItemSize
-                            : 0,
-                        autoFit: SettingsHelper.Settings.WindowProperties.AutoFit,
-                        containerWidth: Width,
-                        containerHeight: Height,
-                        SettingsHelper.Settings.Zoom.ScrollEnabled
-                    );
-                    MainImage.Source = avaloniaPic;
-                    MainImage.Width = size.Width;
-                    MainImage.Height = size.Height;
-                });
-            }
-        };
-    }
-}

+ 0 - 11
src/PicView.Avalonia/Views/UserControls/BottomMenu.axaml.cs

@@ -1,11 +0,0 @@
-using Avalonia.Controls;
-
-namespace PicView.Avalonia.Views.UserControls;
-
-public partial class BottomMenu : UserControl
-{
-    public BottomMenu()
-    {
-        InitializeComponent();
-    }
-}

+ 1 - 0
src/PicView.Avalonia/readme.md

@@ -0,0 +1 @@
+Shared project for UI components and UI related logic in Avalonia, that is not platform specific. 

+ 11 - 0
src/PicView.Core/PicView.Core.csproj

@@ -4,6 +4,17 @@
     <TargetFramework>net8.0</TargetFramework>
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
+    <PlatformTarget>x64</PlatformTarget>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <IsAotCompatible>False</IsAotCompatible>
+    <IsTrimmable>False</IsTrimmable>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <IsAotCompatible>False</IsAotCompatible>
+    <IsTrimmable>False</IsTrimmable>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 7 - 0
src/PicView.MacOS/Class1.cs

@@ -0,0 +1,7 @@
+namespace PicView.MacOS
+{
+    public class Class1
+    {
+
+    }
+}

+ 9 - 0
src/PicView.MacOS/PicView.MacOS.csproj

@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>

+ 1 - 1
src/PicView.WPF/PicGallery/GalleryLoad.cs

@@ -121,7 +121,7 @@ internal static class GalleryLoad
             ParallelOptions options = new()
             ParallelOptions options = new()
             {
             {
                 // Don't slow the system down too much
                 // Don't slow the system down too much
-                MaxDegreeOfParallelism = 4
+                MaxDegreeOfParallelism = Math.Max(Environment.ProcessorCount - 2, 4)
             };
             };
             ParallelOptions secondOptions = new()
             ParallelOptions secondOptions = new()
             {
             {

+ 1 - 0
src/PicView.WPF/PicView.WPF.csproj

@@ -292,6 +292,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\PicView.Core\PicView.Core.csproj" />
     <ProjectReference Include="..\PicView.Core\PicView.Core.csproj" />
+    <ProjectReference Include="..\PicView.Windows\PicView.Windows.csproj" />
     <ProjectReference Include="..\XamlAnimatedGif\XamlAnimatedGif.csproj" />
     <ProjectReference Include="..\XamlAnimatedGif\XamlAnimatedGif.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>

+ 0 - 24
src/PicView.WPF/SystemIntegration/LockScreenImage.cs

@@ -124,28 +124,4 @@ public static class LockScreenHelper
 
 
         return true;
         return true;
     }
     }
-
-    [DllImport("kernel32.dll", SetLastError = true)]
-    public static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr); //If on 64 bit, C# will replace "System32" with "SysWOW64". This disables that.
-
-    public static bool SetLockScreenImage(string path)
-    {
-        const string registryKey =
-            @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP";
-        var ptr = new IntPtr();
-        Wow64DisableWow64FsRedirection(ref ptr);
-
-        try
-        {
-            Registry.SetValue(registryKey, "LockScreenImageStatus", 1, RegistryValueKind.DWord);
-            Registry.SetValue(registryKey, "LockScreenImagePath", path, RegistryValueKind.String);
-            Registry.SetValue(registryKey, "LockScreenImageUrl", path, RegistryValueKind.String);
-        }
-        catch
-        {
-            return false;
-        }
-
-        return true;
-    }
 }
 }

+ 0 - 6
src/PicView.WPF/SystemIntegration/NativeMethods.cs

@@ -37,12 +37,6 @@ internal static partial class NativeMethods
     [return: MarshalAs(UnmanagedType.Bool)]
     [return: MarshalAs(UnmanagedType.Bool)]
     internal static partial bool SetCursorPos(int x, int y);
     internal static partial bool SetCursorPos(int x, int y);
 
 
-    // Used to check for wallpaper support
-    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
-    [return: MarshalAs(UnmanagedType.Bool)]
-    internal static extern bool SystemParametersInfo(uint uiAction, uint uiParam,
-        string pvParam, uint fWinIni);
-
     #region Disable Screensaver and Power options
     #region Disable Screensaver and Power options
 
 
     internal const uint ES_CONTINUOUS = 0x80000000;
     internal const uint ES_CONTINUOUS = 0x80000000;

+ 9 - 88
src/PicView.WPF/SystemIntegration/Wallpaper.cs

@@ -1,31 +1,20 @@
-using System.ComponentModel;
-using System.IO;
-using System.Windows;
-using System.Windows.Input;
-using System.Windows.Media.Imaging;
-using Microsoft.Win32;
-using PicView.Core.FileHandling;
+using PicView.Core.FileHandling;
+using PicView.Windows.Wallpaper;
 using PicView.WPF.ChangeImage;
 using PicView.WPF.ChangeImage;
 using PicView.WPF.ChangeTitlebar;
 using PicView.WPF.ChangeTitlebar;
-using PicView.WPF.FileHandling;
 using PicView.WPF.ImageHandling;
 using PicView.WPF.ImageHandling;
 using PicView.WPF.UILogic;
 using PicView.WPF.UILogic;
+using System.IO;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
 using Rotation = PicView.WPF.UILogic.TransformImage.Rotation;
 using Rotation = PicView.WPF.UILogic.TransformImage.Rotation;
 
 
 namespace PicView.WPF.SystemIntegration;
 namespace PicView.WPF.SystemIntegration;
 
 
 public static class Wallpaper // Taken from a Microsoft sample...
 public static class Wallpaper // Taken from a Microsoft sample...
 {
 {
-    public enum WallpaperStyle
-    {
-        Tile,
-        Center,
-        Stretch,
-        Fit,
-        Fill
-    }
-
-    internal static async Task SetWallpaperAsync(WallpaperStyle style, string? path = null)
+    internal static async Task SetWallpaperAsync(WallpaperHelper.WallpaperStyle style, string? path = null)
     {
     {
         var url = string.Empty;
         var url = string.Empty;
         await ConfigureWindows.GetMainWindow.Dispatcher.InvokeAsync(() =>
         await ConfigureWindows.GetMainWindow.Dispatcher.InvokeAsync(() =>
@@ -83,11 +72,11 @@ public static class Wallpaper // Taken from a Microsoft sample...
 
 
             await SaveImages.SaveImageAsync(rotationAngle, isFlipped, bitmapSource, imagePath, destinationPath,
             await SaveImages.SaveImageAsync(rotationAngle, isFlipped, bitmapSource, imagePath, destinationPath,
                 null, effectApplied).ConfigureAwait(false);
                 null, effectApplied).ConfigureAwait(false);
-            SetDesktopWallpaper(destinationPath, style);
+            WallpaperHelper.SetDesktopWallpaper(destinationPath, style);
         }
         }
         else
         else
         {
         {
-            SetDesktopWallpaper(Navigation.Pics[Navigation.FolderIndex], style);
+            WallpaperHelper.SetDesktopWallpaper(Navigation.Pics[Navigation.FolderIndex], style);
         }
         }
 
 
         await ConfigureWindows.GetMainWindow.Dispatcher.InvokeAsync(() =>
         await ConfigureWindows.GetMainWindow.Dispatcher.InvokeAsync(() =>
@@ -106,72 +95,4 @@ public static class Wallpaper // Taken from a Microsoft sample...
             Application.Current.MainWindow!.Cursor = Cursors.Arrow;
             Application.Current.MainWindow!.Cursor = Cursors.Arrow;
         });
         });
     }
     }
-
-    /// <summary>
-    /// Set the desktop wallpaper.
-    /// </summary>
-    /// <param name="path">Path of the wallpaper</param>
-    /// <param name="style">Wallpaper style</param>
-    private static void SetDesktopWallpaper(string path, WallpaperStyle style)
-    {
-        // Set the wallpaper style and tile.
-        // Two registry values are set in the Control Panel\Desktop key.
-        // TileWallpaper
-        //  0: The wallpaper picture should not be tiled
-        //  1: The wallpaper picture should be tiled
-        // WallpaperStyle
-        //  0:  The image is centered if TileWallpaper=0 or tiled if TileWallpaper=1
-        //  2:  The image is stretched to fill the screen
-        //  6:  The image is resized to fit the screen while maintaining the aspect
-        //      ratio. (Windows 7 and later)
-        //  10: The image is resized and cropped to fill the screen while
-        //      maintaining the aspect ratio. (Windows 7 and later)
-#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
-        var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
-#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
-
-        switch (style)
-        {
-            case WallpaperStyle.Tile:
-                key.SetValue(@"WallpaperStyle", "0");
-                key.SetValue(@"TileWallpaper", "1");
-                break;
-
-            case WallpaperStyle.Center:
-                key.SetValue(@"WallpaperStyle", "0");
-                key.SetValue(@"TileWallpaper", "0");
-                break;
-
-            case WallpaperStyle.Stretch:
-                key.SetValue(@"WallpaperStyle", "2");
-                key.SetValue(@"TileWallpaper", "0");
-                break;
-
-            case WallpaperStyle.Fit: // (Windows 7 and later)
-                key.SetValue(@"WallpaperStyle", "6");
-                key.SetValue(@"TileWallpaper", "0");
-                break;
-
-            default:
-            case WallpaperStyle.Fill: // (Windows 7 and later)
-                key.SetValue(@"WallpaperStyle", "10");
-                key.SetValue(@"TileWallpaper", "0");
-                break;
-        }
-
-        key.Close();
-
-        // Set the desktop wallpaper by calling the Win32 API SystemParametersInfo
-        // with the SPI_SETDESKWALLPAPER desktop parameter. The changes should
-        // persist, and also be immediately visible.
-        if (!NativeMethods.SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, path,
-                SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE))
-        {
-            throw new Win32Exception();
-        }
-    }
-
-    private const uint SPI_SETDESKWALLPAPER = 20;
-    private const uint SPIF_UPDATEINIFILE = 0x01;
-    private const uint SPIF_SENDWININICHANGE = 0x02;
 }
 }

+ 2 - 1
src/PicView.WPF/UILogic/Loading/LoadContextMenus.cs

@@ -12,6 +12,7 @@ using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Input;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
 using PicView.Core.Navigation;
 using PicView.Core.Navigation;
+using PicView.Windows.Wallpaper;
 using static PicView.WPF.ChangeImage.Navigation;
 using static PicView.WPF.ChangeImage.Navigation;
 using static PicView.WPF.FileHandling.OpenSave;
 using static PicView.WPF.FileHandling.OpenSave;
 using static PicView.WPF.UILogic.ConfigureWindows;
 using static PicView.WPF.UILogic.ConfigureWindows;
@@ -282,7 +283,7 @@ internal static class LoadContextMenus
         SetWallpaperCm.Click += async (_, _) =>
         SetWallpaperCm.Click += async (_, _) =>
         {
         {
             MainContextMenu.IsOpen = false;
             MainContextMenu.IsOpen = false;
-            await Wallpaper.SetWallpaperAsync(Wallpaper.WallpaperStyle.Fill).ConfigureAwait(false);
+            await Wallpaper.SetWallpaperAsync(WallpaperHelper.WallpaperStyle.Fill).ConfigureAwait(false);
         };
         };
         SetWallpaperCm.Header = TranslationHelper.GetTranslation("SetAsWallpaper");
         SetWallpaperCm.Header = TranslationHelper.GetTranslation("SetAsWallpaper");
         var SetLockCm = (MenuItem)SetAsCm.Items[1];
         var SetLockCm = (MenuItem)SetAsCm.Items[1];

+ 14 - 14
src/PicView.WPF/UILogic/Loading/StartLoading.cs

@@ -41,18 +41,6 @@ internal static class StartLoading
         }
         }
 
 
         var args = Environment.GetCommandLineArgs();
         var args = Environment.GetCommandLineArgs();
-        if (args.Length > 1)
-        {
-            // Apply lockscreen and close
-            var arg = args[1];
-            if (arg.StartsWith("lockscreen"))
-            {
-                var path = arg[(arg.LastIndexOf(',') + 1)..];
-                path = Path.GetFullPath(path);
-                LockScreenHelper.SetLockScreenImage(path);
-                Environment.Exit(0);
-            }
-        }
 
 
         if (SettingsHelper.Settings.UIProperties.OpenInSameWindow)
         if (SettingsHelper.Settings.UIProperties.OpenInSameWindow)
         {
         {
@@ -77,6 +65,19 @@ internal static class StartLoading
             }
             }
         }
         }
 
 
+        if (args.Length > 1)
+        {
+            // Apply lockscreen and close
+            var arg = args[1];
+            if (arg.StartsWith("lockscreen"))
+            {
+                var path = arg[(arg.LastIndexOf(',') + 1)..];
+                path = Path.GetFullPath(path);
+                Windows.Lockscreen.LockscreenHelper.SetLockScreenImage(path);
+                Environment.Exit(0);
+            }
+        }
+
         MainWindow? mainWindow = null;
         MainWindow? mainWindow = null;
         var language = SettingsHelper.Settings.UIProperties.UserLanguage;
         var language = SettingsHelper.Settings.UIProperties.UserLanguage;
         await Core.Localization.TranslationHelper.LoadLanguage(language).ConfigureAwait(false);
         await Core.Localization.TranslationHelper.LoadLanguage(language).ConfigureAwait(false);
@@ -182,6 +183,7 @@ internal static class StartLoading
         try
         try
         {
         {
             await mainWindow.Dispatcher.InvokeAsync(startupWindow.Close);
             await mainWindow.Dispatcher.InvokeAsync(startupWindow.Close);
+            ConfigColors.UpdateColor(); // Try catch to prevent task canceled exception
         }
         }
         catch (Exception e)
         catch (Exception e)
         {
         {
@@ -189,8 +191,6 @@ internal static class StartLoading
             Trace.WriteLine($"{nameof(LoadedEvent)}: {nameof(startupWindow)} exception,\n{e.Message}");
             Trace.WriteLine($"{nameof(LoadedEvent)}: {nameof(startupWindow)} exception,\n{e.Message}");
 #endif
 #endif
         }
         }
-
-        ConfigColors.UpdateColor();
     }
     }
 
 
     internal static void AddDictionaries()
     internal static void AddDictionaries()

+ 2 - 1
src/PicView.WPF/Views/UserControls/Gallery/PicGalleryItem.xaml.cs

@@ -12,6 +12,7 @@ using PicView.Core.Localization;
 using PicView.WPF.SystemIntegration;
 using PicView.WPF.SystemIntegration;
 using static PicView.WPF.PicGallery.GalleryNavigation;
 using static PicView.WPF.PicGallery.GalleryNavigation;
 using PicView.Core.FileHandling;
 using PicView.Core.FileHandling;
+using PicView.Windows.Wallpaper;
 
 
 namespace PicView.WPF.Views.UserControls.Gallery;
 namespace PicView.WPF.Views.UserControls.Gallery;
 
 
@@ -141,7 +142,7 @@ public partial class PicGalleryItem
             }
             }
         };
         };
         setAsWallpaperMenu.Click += async (_, _) =>
         setAsWallpaperMenu.Click += async (_, _) =>
-            await Wallpaper.SetWallpaperAsync(Wallpaper.WallpaperStyle.Fill, FilePath)
+            await Wallpaper.SetWallpaperAsync(WallpaperHelper.WallpaperStyle.Fill, FilePath)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
         cm.Items.Add(setAsWallpaperMenu);
         cm.Items.Add(setAsWallpaperMenu);
 
 

+ 6 - 5
src/PicView.WPF/Views/Windows/EffectsWindows.xaml.cs

@@ -6,6 +6,7 @@ using PicView.WPF.ConfigureSettings;
 using PicView.WPF.FileHandling;
 using PicView.WPF.FileHandling;
 using PicView.Core.Config;
 using PicView.Core.Config;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using PicView.Windows.Wallpaper;
 using PicView.WPF.Shortcuts;
 using PicView.WPF.Shortcuts;
 using PicView.WPF.SystemIntegration;
 using PicView.WPF.SystemIntegration;
 using PicView.WPF.UILogic;
 using PicView.WPF.UILogic;
@@ -177,25 +178,25 @@ public partial class EffectsWindow
         SetButtonIconMouseOverAnimations(SetAsWallpaperButton, SetAsWallpaperButtonBrush, SetAsWallpaperText);
         SetButtonIconMouseOverAnimations(SetAsWallpaperButton, SetAsWallpaperButtonBrush, SetAsWallpaperText);
         SetAsWallpaperButton.Click += async delegate
         SetAsWallpaperButton.Click += async delegate
         {
         {
-            var x = WallpaperStyle.Fill;
+            var x = WallpaperHelper.WallpaperStyle.Fill;
             if (Fit.IsSelected)
             if (Fit.IsSelected)
             {
             {
-                x = WallpaperStyle.Fit;
+                x = WallpaperHelper.WallpaperStyle.Fit;
             }
             }
 
 
             if (Center.IsSelected)
             if (Center.IsSelected)
             {
             {
-                x = WallpaperStyle.Center;
+                x = WallpaperHelper.WallpaperStyle.Center;
             }
             }
 
 
             if (Tile.IsSelected)
             if (Tile.IsSelected)
             {
             {
-                x = WallpaperStyle.Tile;
+                x = WallpaperHelper.WallpaperStyle.Tile;
             }
             }
 
 
             if (Fit.IsSelected)
             if (Fit.IsSelected)
             {
             {
-                x = WallpaperStyle.Fit;
+                x = WallpaperHelper.WallpaperStyle.Fit;
             }
             }
 
 
             await SetWallpaperAsync(x).ConfigureAwait(false);
             await SetWallpaperAsync(x).ConfigureAwait(false);

+ 31 - 0
src/PicView.Windows/Lockscreen/LockscreenHelper.cs

@@ -0,0 +1,31 @@
+using Microsoft.Win32;
+using System.Runtime.InteropServices;
+
+namespace PicView.Windows.Lockscreen;
+
+public static class LockscreenHelper
+{
+    [DllImport("kernel32.dll", SetLastError = true)]
+    public static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr); //If on 64 bit, C# will replace "System32" with "SysWOW64". This disables that.
+
+    public static bool SetLockScreenImage(string path)
+    {
+        const string registryKey =
+            @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP";
+        var ptr = new IntPtr();
+        Wow64DisableWow64FsRedirection(ref ptr);
+
+        try
+        {
+            Registry.SetValue(registryKey, "LockScreenImageStatus", 1, RegistryValueKind.DWord);
+            Registry.SetValue(registryKey, "LockScreenImagePath", path, RegistryValueKind.String);
+            Registry.SetValue(registryKey, "LockScreenImageUrl", path, RegistryValueKind.String);
+        }
+        catch
+        {
+            return false;
+        }
+
+        return true;
+    }
+}

+ 24 - 0
src/PicView.Windows/PicView.Windows.csproj

@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <PlatformTarget>x64</PlatformTarget>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <IsTrimmable>True</IsTrimmable>
+    <IsAotCompatible>True</IsAotCompatible>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <IsTrimmable>True</IsTrimmable>
+    <IsAotCompatible>True</IsAotCompatible>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Folder Include="Copy\" />
+  </ItemGroup>
+
+</Project>

+ 91 - 0
src/PicView.Windows/Wallpaper/WallpaperHelper.cs

@@ -0,0 +1,91 @@
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using Microsoft.Win32;
+
+namespace PicView.Windows.Wallpaper;
+
+public static class WallpaperHelper
+{
+    public enum WallpaperStyle
+    {
+        Tile,
+        Center,
+        Stretch,
+        Fit,
+        Fill
+    }
+
+    // Used to check for wallpaper support
+    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Bool)]
+    internal static extern bool SystemParametersInfo(uint uiAction, uint uiParam,
+        string pvParam, uint fWinIni);
+
+    /// <summary>
+    /// Set the desktop wallpaper.
+    /// </summary>
+    /// <param name="path">Path of the wallpaper</param>
+    /// <param name="style">Wallpaper style</param>
+    public static void SetDesktopWallpaper(string path, WallpaperStyle style)
+    {
+        // Set the wallpaper style and tile.
+        // Two registry values are set in the Control Panel\Desktop key.
+        // TileWallpaper
+        //  0: The wallpaper picture should not be tiled
+        //  1: The wallpaper picture should be tiled
+        // WallpaperStyle
+        //  0:  The image is centered if TileWallpaper=0 or tiled if TileWallpaper=1
+        //  2:  The image is stretched to fill the screen
+        //  6:  The image is resized to fit the screen while maintaining the aspect
+        //      ratio. (Windows 7 and later)
+        //  10: The image is resized and cropped to fill the screen while
+        //      maintaining the aspect ratio. (Windows 7 and later)
+#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
+        var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);
+#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
+
+        switch (style)
+        {
+            case WallpaperStyle.Tile:
+                key.SetValue(@"WallpaperStyle", "0");
+                key.SetValue(@"TileWallpaper", "1");
+                break;
+
+            case WallpaperStyle.Center:
+                key.SetValue(@"WallpaperStyle", "0");
+                key.SetValue(@"TileWallpaper", "0");
+                break;
+
+            case WallpaperStyle.Stretch:
+                key.SetValue(@"WallpaperStyle", "2");
+                key.SetValue(@"TileWallpaper", "0");
+                break;
+
+            case WallpaperStyle.Fit: // (Windows 7 and later)
+                key.SetValue(@"WallpaperStyle", "6");
+                key.SetValue(@"TileWallpaper", "0");
+                break;
+
+            default:
+            case WallpaperStyle.Fill: // (Windows 7 and later)
+                key.SetValue(@"WallpaperStyle", "10");
+                key.SetValue(@"TileWallpaper", "0");
+                break;
+        }
+
+        key.Close();
+
+        // Set the desktop wallpaper by calling the Win32 API SystemParametersInfo
+        // with the SPI_SETDESKWALLPAPER desktop parameter. The changes should
+        // persist, and also be immediately visible.
+        if (!SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, path,
+                SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE))
+        {
+            throw new Win32Exception();
+        }
+    }
+
+    private const uint SPI_SETDESKWALLPAPER = 20;
+    private const uint SPIF_UPDATEINIFILE = 0x01;
+    private const uint SPIF_SENDWININICHANGE = 0x02;
+}

+ 29 - 5
src/PicView.sln

@@ -9,7 +9,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XamlAnimatedGif", "XamlAnim
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Core", "PicView.Core\PicView.Core.csproj", "{17C701BC-1F25-4995-A7E5-1360EBC5904B}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Core", "PicView.Core\PicView.Core.csproj", "{17C701BC-1F25-4995-A7E5-1360EBC5904B}"
 EndProject
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Avalonia", "PicView.Avalonia\PicView.Avalonia.csproj", "{E36E03B2-6CAB-407F-AD72-411F43CFC5A8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Avalonia", "PicView.Avalonia\PicView.Avalonia.csproj", "{83A0AA39-97B2-453B-BDAB-3DB4D8684D3B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Avalonia.Win32", "PicView.Avalonia.Win32\PicView.Avalonia.Win32.csproj", "{CDE55370-A53E-43C1-9FAB-B2973462A3C6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PicView.Avalonia.MacOS", "PicView.Avalonia.MacOS\PicView.Avalonia.MacOS.csproj", "{79402A1F-0EDE-49C8-970A-E7AC0065FFB4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PicView.Windows", "PicView.Windows\PicView.Windows.csproj", "{D63E2F56-02CC-4E23-AF78-F5218E83B2E6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PicView.MacOS", "PicView.MacOS\PicView.MacOS.csproj", "{FEAC727E-1102-4F11-A747-5A5789FBBF81}"
 EndProject
 EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -29,10 +37,26 @@ Global
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Release|Any CPU.Build.0 = Release|Any CPU
 		{17C701BC-1F25-4995-A7E5-1360EBC5904B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{E36E03B2-6CAB-407F-AD72-411F43CFC5A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{E36E03B2-6CAB-407F-AD72-411F43CFC5A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{E36E03B2-6CAB-407F-AD72-411F43CFC5A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{E36E03B2-6CAB-407F-AD72-411F43CFC5A8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{83A0AA39-97B2-453B-BDAB-3DB4D8684D3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{83A0AA39-97B2-453B-BDAB-3DB4D8684D3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{83A0AA39-97B2-453B-BDAB-3DB4D8684D3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{83A0AA39-97B2-453B-BDAB-3DB4D8684D3B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CDE55370-A53E-43C1-9FAB-B2973462A3C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CDE55370-A53E-43C1-9FAB-B2973462A3C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CDE55370-A53E-43C1-9FAB-B2973462A3C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CDE55370-A53E-43C1-9FAB-B2973462A3C6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{79402A1F-0EDE-49C8-970A-E7AC0065FFB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{79402A1F-0EDE-49C8-970A-E7AC0065FFB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{79402A1F-0EDE-49C8-970A-E7AC0065FFB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{79402A1F-0EDE-49C8-970A-E7AC0065FFB4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D63E2F56-02CC-4E23-AF78-F5218E83B2E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D63E2F56-02CC-4E23-AF78-F5218E83B2E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D63E2F56-02CC-4E23-AF78-F5218E83B2E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D63E2F56-02CC-4E23-AF78-F5218E83B2E6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FEAC727E-1102-4F11-A747-5A5789FBBF81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FEAC727E-1102-4F11-A747-5A5789FBBF81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FEAC727E-1102-4F11-A747-5A5789FBBF81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FEAC727E-1102-4F11-A747-5A5789FBBF81}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

+ 1 - 4
src/XamlAnimatedGif/Decoding/GifDecoderException.cs

@@ -13,10 +13,7 @@ public abstract class GifDecoderException : Exception
     {
     {
     }
     }
 
 
-    protected GifDecoderException(
-        SerializationInfo info,
-        StreamingContext context)
-        : base(info, context)
+    protected GifDecoderException(SerializationInfo info, StreamingContext context)
     {
     {
     }
     }
 }
 }