Browse Source

Rider like updating

Krzysztof Krysiński 4 months ago
parent
commit
f40fc5c18e

+ 2 - 2
src/PixiEditor.Linux/LinuxProcessUtility.cs

@@ -7,12 +7,12 @@ namespace PixiEditor.Linux;
 
 public class LinuxProcessUtility : IProcessUtility
 {
-    public Process RunAsAdmin(string path)
+    public Process RunAsAdmin(string path, string args)
     {
         throw new NotImplementedException("Running as admin is not supported on Linux");
     }
 
-    public Process RunAsAdmin(string path, bool createWindow)
+    public Process RunAsAdmin(string path, string args, bool createWindow)
     {
         throw new NotImplementedException("Running as admin is not supported on Linux");
     }

+ 4 - 3
src/PixiEditor.MacOs/MacOsProcessUtility.cs

@@ -5,17 +5,18 @@ namespace PixiEditor.MacOs;
 
 internal class MacOsProcessUtility : IProcessUtility
 {
-    public Process RunAsAdmin(string path)
+    public Process RunAsAdmin(string path, string args)
     {
-        return RunAsAdmin(path, true);
+        return RunAsAdmin(path, args, true);
     }
 
-    public Process RunAsAdmin(string path, bool createWindow)
+    public Process RunAsAdmin(string path, string args, bool createWindow)
     {
         ProcessStartInfo startInfo = new ProcessStartInfo
         {
             FileName = path,
             Verb = "runas",
+            Arguments = args,
             UseShellExecute = createWindow,
             CreateNoWindow = !createWindow,
             RedirectStandardOutput = !createWindow,

+ 2 - 2
src/PixiEditor.OperatingSystem/IProcessUtility.cs

@@ -4,8 +4,8 @@ namespace PixiEditor.OperatingSystem;
 
 public interface IProcessUtility
 {
-    public Process RunAsAdmin(string path);
-    public Process RunAsAdmin(string path, bool createWindow);
+    public Process RunAsAdmin(string path, string? args);
+    public Process RunAsAdmin(string path, string? args, bool createWindow);
     public bool IsRunningAsAdministrator();
     public Process ShellExecute(string toExecute);
     public Process ShellExecute(string toExecute, string args);

+ 102 - 81
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -1,6 +1,6 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:system="clr-namespace:System;assembly=System.Runtime">
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:system="clr-namespace:System;assembly=System.Runtime">
 
     <ResourceDictionary.ThemeDictionaries>
         <ResourceDictionary x:Key="Dark">
@@ -14,6 +14,7 @@
             <Color x:Key="AccentHighColor">#c0334e</Color>
             <Color x:Key="ThemeAccent2Color">#5fad65</Color>
             <Color x:Key="ThemeAccent2HighColor">#3dd276</Color>
+            <Color x:Key="ThemeAccent3Color">DodgerBlue</Color>
 
             <Color x:Key="ThemeForegroundColor">#FFFFFF</Color>
             <Color x:Key="ThemeForegroundSecondaryColor">#B3FFFFFF</Color>
@@ -34,15 +35,15 @@
             <Color x:Key="GlyphColor">#444</Color>
             <Color x:Key="GlyphBackground">White</Color>
             <Color x:Key="ThumbColor">#606060</Color>
-            
+
             <Color x:Key="SelectionFillColor">#510051ff</Color>
 
             <Color x:Key="NotificationCardBackgroundColor">#303030</Color>
-            
+
             <!--                          -->
             <!--     <| Node Colors |>    -->
             <!--                          -->
-            
+
             <!-- Sockets -->
             <Color x:Key="DefaultSocketColor">#FF00FF</Color>
             <Color x:Key="ImageSocketColor">#99c47a</Color>
@@ -63,14 +64,14 @@
             <Color x:Key="TextDataSocketColor">#f2f2f2</Color>
             <Color x:Key="Matrix3X3SocketColor">#ffea4f</Color>
             <GradientStops x:Key="ShapeDataSocketGradient">
-                <GradientStop Offset="0" Color="{StaticResource EllipseDataSocketColor}"/>
-                <GradientStop Offset="0.33" Color="{StaticResource EllipseDataSocketColor}"/>
-                <GradientStop Offset="0.33" Color="{StaticResource TextDataSocketColor}"/>
-                <GradientStop Offset="0.66" Color="{StaticResource TextDataSocketColor}"/>
-                <GradientStop Offset="0.66" Color="{StaticResource PointsDataSocketColor}"/>
-                <GradientStop Offset="1" Color="{StaticResource PointsDataSocketColor}"/>
+                <GradientStop Offset="0" Color="{StaticResource EllipseDataSocketColor}" />
+                <GradientStop Offset="0.33" Color="{StaticResource EllipseDataSocketColor}" />
+                <GradientStop Offset="0.33" Color="{StaticResource TextDataSocketColor}" />
+                <GradientStop Offset="0.66" Color="{StaticResource TextDataSocketColor}" />
+                <GradientStop Offset="0.66" Color="{StaticResource PointsDataSocketColor}" />
+                <GradientStop Offset="1" Color="{StaticResource PointsDataSocketColor}" />
             </GradientStops>
-            
+
             <!-- Zones & Frames -->
             <Color x:Key="PixiEditorModifyImageBorderColor">#68abdf</Color>
             <Color x:Key="PixiEditorModifyImageNodeBackgroundColor">#4068abdf</Color>
@@ -80,7 +81,7 @@
 
             <Color x:Key="NodeFrameBorderColor">#101010</Color>
             <Color x:Key="NodeFrameBackgroundColor">#40101010</Color>
-            
+
             <!-- Categories -->
             <Color x:Key="ImageCategoryBackgroundColor">#5B7348</Color>
             <Color x:Key="StructureCategoryBackgroundColor">#735C39</Color>
@@ -91,7 +92,7 @@
             <Color x:Key="AnimationCategoryBackgroundColor">#4D4466</Color>
             <Color x:Key="EffectsCategoryBackgroundColor">#e36262</Color>
             <Color x:Key="MatrixCategoryBackgroundColor">#9169ff</Color>
-            
+
             <Color x:Key="HorizontalSnapAxisColor">#B00022</Color>
             <Color x:Key="VerticalSnapAxisColor">#5fad65</Color>
             <Color x:Key="SnapPointPreviewColor">#68abdf</Color>
@@ -104,16 +105,19 @@
             <SolidColorBrush x:Key="ThemeForegroundLowBrush" Color="gray"></SolidColorBrush>
 
             <SolidColorBrush x:Key="ThemeBackgroundBrush" Color="{StaticResource ThemeBackgroundColor}" />
-            <SolidColorBrush x:Key="ThemeBackgroundTranslucentBrush" Color="{StaticResource ThemeBackgroundTranslucentColor}"/>
+            <SolidColorBrush x:Key="ThemeBackgroundTranslucentBrush"
+                             Color="{StaticResource ThemeBackgroundTranslucentColor}" />
             <SolidColorBrush x:Key="ThemeBackgroundBrush1" Color="{StaticResource ThemeBackgroundColor1}" />
             <SolidColorBrush x:Key="ThemeBackgroundBrush2" Color="{StaticResource ThemeBackgroundColor2}" />
             <SolidColorBrush x:Key="ThemeAccentLowBrush" Color="{StaticResource AccentLowColor}" />
             <SolidColorBrush x:Key="ThemeAccentBrush" Color="{StaticResource AccentColor}" />
             <SolidColorBrush x:Key="ThemeAccentHighBrush" Color="{StaticResource AccentHighColor}" />
             <SolidColorBrush x:Key="ThemeAccent2Brush" Color="{StaticResource ThemeAccent2Color}" />
+            <SolidColorBrush x:Key="ThemeAccent3Brush" Color="{StaticResource ThemeAccent3Color}" />
 
             <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />
-            <SolidColorBrush x:Key="ThemeForegroundSecondaryBrush" Color="{StaticResource ThemeForegroundSecondaryColor}" />
+            <SolidColorBrush x:Key="ThemeForegroundSecondaryBrush"
+                             Color="{StaticResource ThemeForegroundSecondaryColor}" />
 
             <SolidColorBrush x:Key="ThemeControlLowBrush" Color="{StaticResource ThemeControlLowColor}" />
             <SolidColorBrush x:Key="ThemeControlMidBrush" Color="{StaticResource ThemeControlMidColor}" />
@@ -125,76 +129,92 @@
             <SolidColorBrush x:Key="ThemeBorderMidBrush" Color="{StaticResource ThemeBorderMidColor}" />
             <SolidColorBrush x:Key="ThemeBorderHighBrush" Color="{StaticResource ThemeBorderHighColor}" />
 
-            <SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="{StaticResource NotificationCardBackgroundColor}" />
+            <SolidColorBrush x:Key="NotificationCardBackgroundBrush"
+                             Color="{StaticResource NotificationCardBackgroundColor}" />
 
             <SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}" />
             <SolidColorBrush x:Key="ErrorOnDarkBrush" Color="{StaticResource ErrorOnDarkColor}" />
-            <SolidColorBrush x:Key="GlyphBrush" Color="{StaticResource GlyphColor}"/>
-            <SolidColorBrush x:Key="ThumbBrush" Color="{StaticResource ThumbColor}"/>
-            
-            <SolidColorBrush x:Key="SelectionFillBrush" Color="{StaticResource SelectionFillColor}"/>
-            
+            <SolidColorBrush x:Key="GlyphBrush" Color="{StaticResource GlyphColor}" />
+            <SolidColorBrush x:Key="ThumbBrush" Color="{StaticResource ThumbColor}" />
+
+            <SolidColorBrush x:Key="SelectionFillBrush" Color="{StaticResource SelectionFillColor}" />
+
             <!--                           -->
             <!--     <| Node Brushes |>    -->
             <!--                           -->
-            
+
             <!-- Sockets -->
-            <SolidColorBrush x:Key="DefaultSocketBrush" Color="{StaticResource DefaultSocketColor}"/>
-            <SolidColorBrush x:Key="TextureSocketBrush" Color="{StaticResource ImageSocketColor}"/>
-            <SolidColorBrush x:Key="PainterSocketBrush" Color="{StaticResource PainterSocketColor}"/>
-            <SolidColorBrush x:Key="FilterSocketBrush" Color="{StaticResource FilterSocketColor}"/>
-            <SolidColorBrush x:Key="TextureSamplerSocketBrush" Color="{StaticResource ImageSocketColor}"/>
-            <SolidColorBrush x:Key="BooleanSocketBrush" Color="{StaticResource BoolSocketColor}"/>
-            <SolidColorBrush x:Key="SingleSocketBrush" Color="{StaticResource FloatSocketColor}"/>
-            <SolidColorBrush x:Key="DoubleSocketBrush" Color="{StaticResource DoubleSocketColor}"/>
-            <SolidColorBrush x:Key="Float1SocketBrush" Color="{StaticResource DoubleSocketColor}"/>
-            <SolidColorBrush x:Key="ColorSocketBrush" Color="{StaticResource ColorSocketColor}"/>
-            <SolidColorBrush x:Key="PaintableSocketBrush" Color="{StaticResource PaintableSocketColor}"/>
-            <SolidColorBrush x:Key="Half4SocketBrush" Color="{StaticResource ColorSocketColor}"/>
-            <SolidColorBrush x:Key="VecDSocketBrush" Color="{StaticResource VecDSocketColor}"/>
-            <SolidColorBrush x:Key="Vec3DSocketBrush" Color="{StaticResource Vec3DSocketColor}"/>
-            <SolidColorBrush x:Key="Float2SocketBrush" Color="{StaticResource VecDSocketColor}"/>
-            <SolidColorBrush x:Key="VecISocketBrush" Color="{StaticResource VecISocketColor}"/>
-            <SolidColorBrush x:Key="Int2SocketBrush" Color="{StaticResource VecISocketColor}"/>
-            <SolidColorBrush x:Key="Int32SocketBrush" Color="{StaticResource IntSocketColor}"/>
-            <SolidColorBrush x:Key="Int1SocketBrush" Color="{StaticResource IntSocketColor}"/>
-            <SolidColorBrush x:Key="StringSocketBrush" Color="{StaticResource StringSocketColor}"/>
-            <SolidColorBrush x:Key="Matrix3X3SocketBrush" Color="{StaticResource Matrix3X3SocketColor}"/>
-
-            <ConicGradientBrush x:Key="ShapeVectorDataSocketBrush" GradientStops="{StaticResource ShapeDataSocketGradient}"/>
-            <SolidColorBrush x:Key="EllipseVectorDataSocketBrush" Color="{StaticResource EllipseDataSocketColor}"/>
-            <SolidColorBrush x:Key="PointsVectorDataSocketBrush" Color="{StaticResource PointsDataSocketColor}"/>
-            <SolidColorBrush x:Key="TextVectorDataSocketBrush" Color="{StaticResource TextDataSocketColor}"/>
-            
+            <SolidColorBrush x:Key="DefaultSocketBrush" Color="{StaticResource DefaultSocketColor}" />
+            <SolidColorBrush x:Key="TextureSocketBrush" Color="{StaticResource ImageSocketColor}" />
+            <SolidColorBrush x:Key="PainterSocketBrush" Color="{StaticResource PainterSocketColor}" />
+            <SolidColorBrush x:Key="FilterSocketBrush" Color="{StaticResource FilterSocketColor}" />
+            <SolidColorBrush x:Key="TextureSamplerSocketBrush" Color="{StaticResource ImageSocketColor}" />
+            <SolidColorBrush x:Key="BooleanSocketBrush" Color="{StaticResource BoolSocketColor}" />
+            <SolidColorBrush x:Key="SingleSocketBrush" Color="{StaticResource FloatSocketColor}" />
+            <SolidColorBrush x:Key="DoubleSocketBrush" Color="{StaticResource DoubleSocketColor}" />
+            <SolidColorBrush x:Key="Float1SocketBrush" Color="{StaticResource DoubleSocketColor}" />
+            <SolidColorBrush x:Key="ColorSocketBrush" Color="{StaticResource ColorSocketColor}" />
+            <SolidColorBrush x:Key="PaintableSocketBrush" Color="{StaticResource PaintableSocketColor}" />
+            <SolidColorBrush x:Key="Half4SocketBrush" Color="{StaticResource ColorSocketColor}" />
+            <SolidColorBrush x:Key="VecDSocketBrush" Color="{StaticResource VecDSocketColor}" />
+            <SolidColorBrush x:Key="Vec3DSocketBrush" Color="{StaticResource Vec3DSocketColor}" />
+            <SolidColorBrush x:Key="Float2SocketBrush" Color="{StaticResource VecDSocketColor}" />
+            <SolidColorBrush x:Key="VecISocketBrush" Color="{StaticResource VecISocketColor}" />
+            <SolidColorBrush x:Key="Int2SocketBrush" Color="{StaticResource VecISocketColor}" />
+            <SolidColorBrush x:Key="Int32SocketBrush" Color="{StaticResource IntSocketColor}" />
+            <SolidColorBrush x:Key="Int1SocketBrush" Color="{StaticResource IntSocketColor}" />
+            <SolidColorBrush x:Key="StringSocketBrush" Color="{StaticResource StringSocketColor}" />
+            <SolidColorBrush x:Key="Matrix3X3SocketBrush" Color="{StaticResource Matrix3X3SocketColor}" />
+
+            <ConicGradientBrush x:Key="ShapeVectorDataSocketBrush"
+                                GradientStops="{StaticResource ShapeDataSocketGradient}" />
+            <SolidColorBrush x:Key="EllipseVectorDataSocketBrush" Color="{StaticResource EllipseDataSocketColor}" />
+            <SolidColorBrush x:Key="PointsVectorDataSocketBrush" Color="{StaticResource PointsDataSocketColor}" />
+            <SolidColorBrush x:Key="TextVectorDataSocketBrush" Color="{StaticResource TextDataSocketColor}" />
+
             <!-- Zones & Frames -->
-            <SolidColorBrush x:Key="PixiEditorModifyImageLeftBorderBrush" Color="{StaticResource PixiEditorModifyImageBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorModifyImageRightBorderBrush" Color="{StaticResource PixiEditorModifyImageBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorModifyImageZoneBorderBrush" Color="{StaticResource PixiEditorModifyImageBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorModifyImageZoneBackgroundBrush" Color="{StaticResource PixiEditorModifyImageNodeBackgroundColor}"/>
-            
-            <SolidColorBrush x:Key="PixiEditorColorEvaluatorLeftBorderBrush" Color="{StaticResource PixiEditorColorEvaluatorBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorColorEvaluatorRightBorderBrush" Color="{StaticResource PixiEditorColorEvaluatorBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorColorEvaluatorZoneBorderBrush" Color="{StaticResource PixiEditorColorEvaluatorBorderColor}"/>
-            <SolidColorBrush x:Key="PixiEditorColorEvaluatorZoneBackgroundBrush" Color="{StaticResource PixiEditorColorEvaluatorNodeBackgroundColor}"/>
-
-            <SolidColorBrush x:Key="NodeFrameBorderBrush" Color="{StaticResource NodeFrameBorderColor}"/>
-            <SolidColorBrush x:Key="NodeFrameBackgroundBrush" Color="{StaticResource NodeFrameBackgroundColor}"/>
-            
+            <SolidColorBrush x:Key="PixiEditorModifyImageLeftBorderBrush"
+                             Color="{StaticResource PixiEditorModifyImageBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorModifyImageRightBorderBrush"
+                             Color="{StaticResource PixiEditorModifyImageBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorModifyImageZoneBorderBrush"
+                             Color="{StaticResource PixiEditorModifyImageBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorModifyImageZoneBackgroundBrush"
+                             Color="{StaticResource PixiEditorModifyImageNodeBackgroundColor}" />
+
+            <SolidColorBrush x:Key="PixiEditorColorEvaluatorLeftBorderBrush"
+                             Color="{StaticResource PixiEditorColorEvaluatorBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorColorEvaluatorRightBorderBrush"
+                             Color="{StaticResource PixiEditorColorEvaluatorBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorColorEvaluatorZoneBorderBrush"
+                             Color="{StaticResource PixiEditorColorEvaluatorBorderColor}" />
+            <SolidColorBrush x:Key="PixiEditorColorEvaluatorZoneBackgroundBrush"
+                             Color="{StaticResource PixiEditorColorEvaluatorNodeBackgroundColor}" />
+
+            <SolidColorBrush x:Key="NodeFrameBorderBrush" Color="{StaticResource NodeFrameBorderColor}" />
+            <SolidColorBrush x:Key="NodeFrameBackgroundBrush" Color="{StaticResource NodeFrameBackgroundColor}" />
+
             <!-- Categories -->
             <SolidColorBrush x:Key="ImageCategoryBackgroundBrush" Color="{StaticResource ImageCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="StructureCategoryBackgroundBrush" Color="{StaticResource StructureCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="FiltersCategoryBackgroundBrush" Color="{StaticResource FiltersCategoryBackgroundColor}" />
+            <SolidColorBrush x:Key="StructureCategoryBackgroundBrush"
+                             Color="{StaticResource StructureCategoryBackgroundColor}" />
+            <SolidColorBrush x:Key="FiltersCategoryBackgroundBrush"
+                             Color="{StaticResource FiltersCategoryBackgroundColor}" />
             <SolidColorBrush x:Key="ShapeCategoryBackgroundBrush" Color="{StaticResource ShapeCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="NumbersCategoryBackgroundBrush" Color="{StaticResource NumbersCategoryBackgroundColor}" />
+            <SolidColorBrush x:Key="NumbersCategoryBackgroundBrush"
+                             Color="{StaticResource NumbersCategoryBackgroundColor}" />
             <SolidColorBrush x:Key="ColorCategoryBackgroundBrush" Color="{StaticResource ColorCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="AnimationCategoryBackgroundBrush" Color="{StaticResource AnimationCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="EffectsCategoryBackgroundBrush" Color="{StaticResource EffectsCategoryBackgroundColor}" />
-            <SolidColorBrush x:Key="MatrixCategoryBackgroundBrush" Color="{StaticResource MatrixCategoryBackgroundColor}" />
-
-            <SolidColorBrush x:Key="HorizontalSnapAxisBrush" Color="{StaticResource HorizontalSnapAxisColor}"/>
-            <SolidColorBrush x:Key="VerticalSnapAxisBrush" Color="{StaticResource VerticalSnapAxisColor}"/>
-            <SolidColorBrush x:Key="SnapPointPreviewBrush" Color="{StaticResource SnapPointPreviewColor}"/>
-            
+            <SolidColorBrush x:Key="AnimationCategoryBackgroundBrush"
+                             Color="{StaticResource AnimationCategoryBackgroundColor}" />
+            <SolidColorBrush x:Key="EffectsCategoryBackgroundBrush"
+                             Color="{StaticResource EffectsCategoryBackgroundColor}" />
+            <SolidColorBrush x:Key="MatrixCategoryBackgroundBrush"
+                             Color="{StaticResource MatrixCategoryBackgroundColor}" />
+
+            <SolidColorBrush x:Key="HorizontalSnapAxisBrush" Color="{StaticResource HorizontalSnapAxisColor}" />
+            <SolidColorBrush x:Key="VerticalSnapAxisBrush" Color="{StaticResource VerticalSnapAxisColor}" />
+            <SolidColorBrush x:Key="SnapPointPreviewBrush" Color="{StaticResource SnapPointPreviewColor}" />
+
             <CornerRadius x:Key="ControlCornerRadius">5</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadiusTop">5, 5, 0, 0</CornerRadius>
             <system:Double x:Key="ControlCornerRadiusValue">5</system:Double>
@@ -202,14 +222,15 @@
             <system:Double x:Key="ScrollBarThickness">12</system:Double>
             <system:Double x:Key="ScrollBarThumbThickness">8</system:Double>
 
-            <SolidColorBrush x:Key="DockApplicationAccentBrushLow" Color="{DynamicResource AccentLowColor}"/>
-            <SolidColorBrush x:Key="DockApplicationAccentBrushMed" Color="{DynamicResource AccentColor}"/>
-            <SolidColorBrush x:Key="DockApplicationAccentBrushHigh" Color="{DynamicResource AccentHighColor}"/>
-            <SolidColorBrush x:Key="DockApplicationAccentForegroundBrush" Color="{DynamicResource ThemeForegroundColor}"/>
-            <SolidColorBrush x:Key="DockApplicationAccentBrushIndicator" Color="{DynamicResource AccentColor}"/>
+            <SolidColorBrush x:Key="DockApplicationAccentBrushLow" Color="{DynamicResource AccentLowColor}" />
+            <SolidColorBrush x:Key="DockApplicationAccentBrushMed" Color="{DynamicResource AccentColor}" />
+            <SolidColorBrush x:Key="DockApplicationAccentBrushHigh" Color="{DynamicResource AccentHighColor}" />
+            <SolidColorBrush x:Key="DockApplicationAccentForegroundBrush"
+                             Color="{DynamicResource ThemeForegroundColor}" />
+            <SolidColorBrush x:Key="DockApplicationAccentBrushIndicator" Color="{DynamicResource AccentColor}" />
 
-            <SolidColorBrush x:Key="UnsavedDotBrush" Color="{DynamicResource UnsavedDotColor}"/>
-            <SolidColorBrush x:Key="AutosaveDotBrush" Color="{DynamicResource AutosaveDotColor}"/>
+            <SolidColorBrush x:Key="UnsavedDotBrush" Color="{DynamicResource UnsavedDotColor}" />
+            <SolidColorBrush x:Key="AutosaveDotBrush" Color="{DynamicResource AutosaveDotColor}" />
 
             <SolidColorBrush x:Key="DockThemeBorderLowBrush" Color="{DynamicResource ThemeBorderMidColor}" />
             <SolidColorBrush x:Key="DockThemeBackgroundBrush" Color="{DynamicResource ThemeBackgroundColor}" />

+ 20 - 6
src/PixiEditor.UpdateInstaller.Exe/Program.cs

@@ -4,6 +4,17 @@ using PixiEditor.UpdateInstaller.ViewModels;
 
 UpdateController controller = new UpdateController();
 StringBuilder log = new StringBuilder();
+bool startAfterUpdate = false;
+
+foreach (string arg in args)
+{
+    if (arg == "--startOnSuccess")
+    {
+        startAfterUpdate = true;
+        log.AppendLine($"{DateTime.Now}: Found --startOnSuccess argument, will start PixiEditor after update.");
+        break;
+    }
+}
 
 try
 {
@@ -24,13 +35,16 @@ finally
     }
     catch
     {
-       // probably permissions or disk full, the best we can do is to ignore this 
+        // probably permissions or disk full, the best we can do is to ignore this
     }
-    
-    var files = Directory.GetFiles(controller.UpdateDirectory, "PixiEditor.exe");
-    if (files.Length > 0)
+
+    if (startAfterUpdate)
     {
-        string pixiEditorExecutablePath = files[0];
-        Process.Start(pixiEditorExecutablePath);
+        var files = Directory.GetFiles(controller.UpdateDirectory, "PixiEditor.exe");
+        if (files.Length > 0)
+        {
+            string pixiEditorExecutablePath = files[0];
+            Process.Start(pixiEditorExecutablePath);
+        }
     }
 }

+ 13 - 3
src/PixiEditor.UpdateModule/UpdateInstaller.cs

@@ -24,7 +24,7 @@ public class UpdateInstaller
 
     public void Install(StringBuilder log)
     {
-        var processes = Process.GetProcessesByName("PixiEditor");
+        var processes = Process.GetProcessesByName("PixiEditor.Desktop");
         log.AppendLine($"Found {processes.Length} PixiEditor processes running.");
         if (processes.Length > 0)
         {
@@ -40,8 +40,18 @@ public class UpdateInstaller
         log.AppendLine("Files extracted");
         string dirWithFiles = Directory.GetDirectories(UpdateFilesPath)[0];
         log.AppendLine($"Copying files from {dirWithFiles} to {TargetDirectory}");
-        
-        CopyFilesToDestination(dirWithFiles, log);
+
+        try
+        {
+            CopyFilesToDestination(dirWithFiles, log);
+        }
+        catch (IOException ex)
+        {
+            log.AppendLine($"Error copying files: {ex.Message}. Retrying in 1 second...");
+            System.Threading.Thread.Sleep(1000);
+            CopyFilesToDestination(dirWithFiles, log);
+        }
+
         log.AppendLine("Files copied");
         log.AppendLine("Deleting archive and update files");
         

+ 4 - 3
src/PixiEditor.Windows/WindowsProcessUtility.cs

@@ -7,17 +7,18 @@ namespace PixiEditor.Windows;
 
 public class WindowsProcessUtility : IProcessUtility
 {
-    public Process RunAsAdmin(string path)
+    public Process RunAsAdmin(string path, string args)
     {
-        return RunAsAdmin(path, true);
+        return RunAsAdmin(path, args, true);
     }
 
-    public Process RunAsAdmin(string path, bool createWindow)
+    public Process RunAsAdmin(string path, string args, bool createWindow)
     {
         ProcessStartInfo startInfo = new ProcessStartInfo
         {
             FileName = path,
             Verb = "runas",
+            Arguments = args,
             UseShellExecute = createWindow,
             CreateNoWindow = !createWindow,
             RedirectStandardOutput = !createWindow,

+ 9 - 4
src/PixiEditor/Data/Localization/Languages/en.json

@@ -55,9 +55,7 @@
   "APPLY_TRANSFORM": "Apply transform",
   "INCREASE_TOOL_SIZE": "Increase tool size",
   "DECREASE_TOOL_SIZE": "Decrease tool size",
-  "TO_INSTALL_UPDATE": "to install update {0}",
-  "DOWNLOADING_UPDATE": "Downloading update...",
-  "UPDATE_READY": "Update is ready to be installed. Do you want to install it now?",
+ "UPDATE_READY": "Update is ready to be installed. Do you want to install it now?",
   "NEW_UPDATE": "New update",
   "COULD_NOT_UPDATE_WITHOUT_ADMIN": "Couldn't update without admin privileges. Please run PixiEditor as administrator.",
   "INSUFFICIENT_PERMISSIONS": "Insufficient permissions",
@@ -992,5 +990,12 @@
   "LANGUAGE_INFO": "All translations are community-driven. Join our Discord server for more information.",
   "UP_TO_DATE_UNKNOWN": "Couldn't check for updates",
   "UP_TO_DATE": "PixiEditor is up to date",
-  "UPDATE_AVAILABLE": "Update {0} is available"
+  "UPDATE_AVAILABLE": "Update {0} is available",
+  "CHECKING_UPDATES": "Checking for updates...",
+  "UPDATE_FAILED_DOWNLOAD": "Failed to download the update",
+  "UPDATE_READY_TO_INSTALL": "Update is ready. Switch to {0}?",
+  "SWITCH_TO_NEW_VERSION": "Switch",
+  "DOWNLOAD_UPDATE": "Download",
+  "DOWNLOADING_UPDATE": "Downloading update...",
+  "CHECKING_FOR_UPDATES": "Checking for updates..."
 }

+ 4 - 4
src/PixiEditor/Helpers/ProcessHelper.cs

@@ -5,9 +5,9 @@ namespace PixiEditor.Helpers;
 
 internal static class ProcessHelper
 {
-    public static Process RunAsAdmin(string path)
+    public static Process RunAsAdmin(string path, string? args = null)
     {
-        return IOperatingSystem.Current.ProcessUtility.RunAsAdmin(path);
+        return IOperatingSystem.Current.ProcessUtility.RunAsAdmin(path, args);
     }
 
     public static bool IsRunningAsAdministrator()
@@ -15,8 +15,8 @@ internal static class ProcessHelper
         return IOperatingSystem.Current.ProcessUtility.IsRunningAsAdministrator();
     }
 
-    public static void RunAsAdmin(string updaterPath, bool showWindow)
+    public static void RunAsAdmin(string path, string? args, bool showWindow)
     {
-        IOperatingSystem.Current.ProcessUtility.RunAsAdmin(updaterPath, showWindow);
+        IOperatingSystem.Current.ProcessUtility.RunAsAdmin(path, args, showWindow);
     }
 }

+ 57 - 105
src/PixiEditor/ViewModels/SubViewModels/UpdateViewModel.cs

@@ -7,6 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Threading;
 using CommunityToolkit.Mvvm.Input;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Extensions.Common.Localization;
@@ -41,7 +42,6 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             OnPropertyChanged(nameof(UpdateReadyToInstall));
             OnPropertyChanged(nameof(IsDownloading));
             OnPropertyChanged(nameof(IsUpToDate));
-            OnPropertyChanged(nameof(IsFailed));
             OnPropertyChanged(nameof(UpdateStateString));
         }
     }
@@ -61,19 +61,30 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 
     public string UpdateStateString
     {
-        get => _updateState switch
+        get
         {
-            UpdateState.UnableToCheck => new LocalizedString("UP_TO_DATE_UNKNOWN"),
-            UpdateState.Checking => new LocalizedString("CHECKING_FOR_UPDATES"),
-            UpdateState.FailedDownload => new LocalizedString("UPDATE_FAILED_DOWNLOAD"),
-            UpdateState.ReadyToInstall => new LocalizedString("UPDATE_READY_TO_INSTALL"),
-            UpdateState.Downloading => new LocalizedString("DOWNLOADING_UPDATE"),
-            UpdateState.UpdateAvailable => new LocalizedString("UPDATE_AVAILABLE",
-                UpdateChecker.LatestReleaseInfo.TagName),
-            UpdateState.UpToDate => new LocalizedString("UP_TO_DATE"),
-            UpdateState.Failed => new LocalizedString("UPDATE_FAILED"),
-            _ => new LocalizedString("UP_TO_DATE_UNKNOWN")
-        };
+            if (!SelfUpdatingAvailable)
+                return string.Empty;
+            switch (_updateState)
+            {
+                case UpdateState.UnableToCheck:
+                    return new LocalizedString("UP_TO_DATE_UNKNOWN");
+                case UpdateState.Checking:
+                    return new LocalizedString("CHECKING_FOR_UPDATES");
+                case UpdateState.FailedDownload:
+                    return new LocalizedString("UPDATE_FAILED_DOWNLOAD");
+                case UpdateState.ReadyToInstall:
+                    return new LocalizedString("UPDATE_READY_TO_INSTALL", UpdateChecker.LatestReleaseInfo.TagName);
+                case UpdateState.Downloading:
+                    return new LocalizedString("DOWNLOADING_UPDATE");
+                case UpdateState.UpdateAvailable:
+                    return new LocalizedString("UPDATE_AVAILABLE", UpdateChecker.LatestReleaseInfo.TagName);
+                case UpdateState.UpToDate:
+                    return new LocalizedString("UP_TO_DATE");
+                default:
+                    return new LocalizedString("UP_TO_DATE_UNKNOWN");
+            }
+        }
     }
 
     public bool IsUpdateAvailable
@@ -96,10 +107,12 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         get => _updateState == UpdateState.UpToDate;
     }
 
-    public bool IsFailed
-    {
-        get => _updateState == UpdateState.Failed;
-    }
+    public bool SelfUpdatingAvailable =>
+#if UPDATE
+        PixiEditorSettings.Update.CheckUpdatesOnStartup.Value && OsSupported();
+#else
+        false;
+#endif
 
     public AsyncRelayCommand DownloadCommand => new AsyncRelayCommand(Download);
     public RelayCommand InstallCommand => new RelayCommand(Install);
@@ -108,6 +121,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         : base(owner)
     {
         Owner.OnStartupEvent += Owner_OnStartupEvent;
+        Owner.OnClose += Owner_OnClose;
         PixiEditorSettings.Update.UpdateChannel.ValueChanged += (_, value) =>
         {
             string prevChannel = UpdateChecker.Channel.ApiUrl;
@@ -116,6 +130,9 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             {
                 ConditionalUPDATE();
             }
+
+            OnPropertyChanged(nameof(UpdateStateString));
+            OnPropertyChanged(nameof(SelfUpdatingAvailable));
         };
         InitUpdateChecker();
     }
@@ -138,35 +155,38 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         UpdateState = updateAvailable ? UpdateState.UpdateAvailable : UpdateState.UpToDate;
     }
 
+    private void Owner_OnClose()
+    {
+        if (UpdateState == UpdateState.ReadyToInstall)
+        {
+            Install(false);
+        }
+    }
+
     public async Task Download()
     {
         bool updateCompatible = await UpdateChecker.IsUpdateCompatible();
-        bool autoUpdateFailed = CheckAutoupdateFailed();
         bool updateFileDoesNotExists = !AutoUpdateFileExists();
         bool updateExeDoesNotExists = !UpdateInstallerFileExists();
 
-        if(!updateExeDoesNotExists || !updateFileDoesNotExists)
+        if (!updateExeDoesNotExists || !updateFileDoesNotExists)
         {
             UpdateState = UpdateState.ReadyToInstall;
             return;
         }
 
-        if ((updateFileDoesNotExists && updateExeDoesNotExists) || autoUpdateFailed)
+        if ((updateFileDoesNotExists && updateExeDoesNotExists))
         {
             try
             {
                 UpdateState = UpdateState.Downloading;
-                if (updateCompatible && !autoUpdateFailed)
+                if (updateCompatible)
                 {
                     await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
                 }
                 else
                 {
                     await UpdateDownloader.DownloadInstaller(UpdateChecker.LatestReleaseInfo);
-                    if (autoUpdateFailed)
-                    {
-                        RemoveZipIfExists();
-                    }
                 }
 
                 UpdateState = UpdateState.ReadyToInstall;
@@ -200,7 +220,13 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         return File.Exists(path);
     }
 
+
     private void Install()
+    {
+        Install(true);
+    }
+
+    private void Install(bool startAfterUpdate)
     {
 #if RELEASE || DEVRELEASE
         string dir = AppDomain.CurrentDomain.BaseDirectory;
@@ -226,32 +252,14 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         if (!updateFileExists && !updateExeExists)
         {
             EnsureUpdateFilesDeleted();
+            UpdateState = UpdateState.Checking;
+            Dispatcher.UIThread.InvokeAsync(async () => await CheckForUpdate());
             return;
         }
 
-        /*if (!UpdateInfoExists())
-        {
-            CreateUpdateInfo(UpdateChecker.LatestReleaseInfo.TagName);
-            return;
-        }
-
-        string[] info = IncrementUpdateInfo();
-
-        if (!UpdateInfoValid(UpdateChecker.LatestReleaseInfo.TagName, info))
-        {
-            File.Delete(Path.Join(Paths.TempFilesPath, "updateInfo.txt"));
-            CreateUpdateInfo(UpdateChecker.LatestReleaseInfo.TagName);
-            return;
-        }
-
-        if (!CanInstallUpdate(UpdateChecker.LatestReleaseInfo.TagName, info) && !updateExeExists)
-        {
-            return;
-        }*/
-
         if (updateFileExists && File.Exists(updaterPath))
         {
-            InstallHeadless(updaterPath);
+            InstallHeadless(updaterPath, startAfterUpdate);
         }
         else if (updateExeExists)
         {
@@ -260,11 +268,11 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
 #endif
     }
 
-    private static void InstallHeadless(string updaterPath)
+    private static void InstallHeadless(string updaterPath, bool startAfterUpdate)
     {
         try
         {
-            ProcessHelper.RunAsAdmin(updaterPath, false);
+            ProcessHelper.RunAsAdmin(updaterPath, startAfterUpdate ? "--startOnSuccess" : null, false);
             Shutdown();
         }
         catch (Win32Exception)
@@ -294,7 +302,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
     {
         try
         {
-            IOperatingSystem.Current.ProcessUtility.RunAsAdmin(updateExeFile);
+            IOperatingSystem.Current.ProcessUtility.RunAsAdmin(updateExeFile, null);
             Shutdown();
         }
         catch (Win32Exception)
@@ -369,30 +377,6 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         return IOperatingSystem.Current.IsWindows;
     }
 
-    private bool UpdateInfoExists()
-    {
-        return File.Exists(Path.Join(Paths.TempFilesPath, "updateInfo.txt"));
-    }
-
-    private void CreateUpdateInfo(string targetVersion)
-    {
-        StringBuilder sb = new StringBuilder();
-        sb.AppendLine(targetVersion);
-        sb.AppendLine("0");
-        File.WriteAllText(Path.Join(Paths.TempFilesPath, "updateInfo.txt"), sb.ToString());
-    }
-
-    private string[] IncrementUpdateInfo()
-    {
-        string[] lines = File.ReadAllLines(Path.Join(Paths.TempFilesPath, "updateInfo.txt"));
-        int.TryParse(lines[1], out int count);
-        count++;
-        lines[1] = count.ToString();
-        File.WriteAllLines(Path.Join(Paths.TempFilesPath, "updateInfo.txt"), lines);
-
-        return lines;
-    }
-
     private void EnsureUpdateFilesDeleted()
     {
         string path = Path.Combine(Paths.TempFilesPath, "updateInfo.txt");
@@ -402,37 +386,6 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    private bool CanInstallUpdate(string forVersion, string[] lines)
-    {
-        if (lines.Length != 2) return false;
-
-        if (lines[0] != forVersion) return false;
-
-        return int.TryParse(lines[1], out int count) && count < MaxRetryCount;
-    }
-
-    private bool UpdateInfoValid(string forVersion, string[] lines)
-    {
-        if (lines.Length != 2) return false;
-
-        if (lines[0] != forVersion) return false;
-
-        return int.TryParse(lines[1], out _);
-    }
-
-    private bool CheckAutoupdateFailed()
-    {
-        string path = Path.Combine(Paths.TempFilesPath, "updateInfo.txt");
-        if (!File.Exists(path)) return false;
-
-        string[] lines = File.ReadAllLines(path);
-        if (lines.Length != 2) return false;
-
-        if (!int.TryParse(lines[1], out int count)) return false;
-
-        return count >= MaxRetryCount;
-    }
-
     private void RemoveZipIfExists()
     {
         string zipPath = Path.Join(UpdateDownloader.DownloadLocation,
@@ -476,5 +429,4 @@ public enum UpdateState
     FailedDownload,
     ReadyToInstall,
     UpToDate,
-    Failed,
 }

+ 3 - 1
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -33,7 +33,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
     public static ViewModelMain Current { get; set; }
     public IServiceProvider Services { get; private set; }
 
-    public Action CloseAction { get; set; }
+    public event Action OnClose;
     public event Action OnStartupEvent;
     public FileViewModel FileSubViewModel { get; set; }
     public UpdateViewModel UpdateSubViewModel { get; set; }
@@ -197,6 +197,8 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
             {
                 await analytics.StopAsync();
             }
+
+            OnClose?.Invoke();
         }
     }
 

+ 71 - 29
src/PixiEditor/Views/Main/MainTitleBar.axaml

@@ -29,9 +29,9 @@
                 </Border>
             </dialogs:DialogTitleBar.AdditionalElement>
         </dialogs:DialogTitleBar>
-        <ToggleButton Name="LogoButton" Padding="0" BorderThickness="0" DockPanel.Dock="Left"
-                      HorizontalAlignment="Left" Width="20" Height="20">
-            <ToggleButton.Margin>
+        <Panel DockPanel.Dock="Left"
+               HorizontalAlignment="Left" Width="20" Height="20">
+            <Panel.Margin>
                 <OnPlatform>
                     <OnPlatform.macOS>
                         <Thickness>75, 0, 0, 0</Thickness>
@@ -40,32 +40,74 @@
                         <Thickness>10, 0, 0, 0</Thickness>
                     </OnPlatform.Default>
                 </OnPlatform>
-            </ToggleButton.Margin>
-            <ToggleButton.Background>
-                <VisualBrush>
-                    <VisualBrush.Visual>
-                        <Svg Path="/Images/PixiEditorLogo.svg" />
-                    </VisualBrush.Visual>
-                </VisualBrush>
-            </ToggleButton.Background>
-            <ToggleButton.Flyout>
-                <Flyout Placement="BottomEdgeAlignedLeft">
-                    <Flyout.Content>
-                        <StackPanel Margin="5">
-                            <TextBlock Text="PixiEditor" FontSize="20" />
-                            <TextBlock Text="{Binding UpdateViewModel.VersionText}" />
-                            <TextBlock Text="{Binding UpdateViewModel.UpdateStateString}" />
-                            <Button ui:Translator.Key="DOWNLOAD_UPDATE"
-                                    Command="{Binding UpdateViewModel.DownloadCommand}"
-                                    IsVisible="{Binding UpdateViewModel.IsUpdateAvailable}" />
-                            <Button ui:Translator.Key="SWITCH_TO_NEW_VERSION"
-                                    Command="{Binding UpdateViewModel.InstallCommand}"
-                                    IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}" />
-                        </StackPanel>
-                    </Flyout.Content>
-                </Flyout>
-            </ToggleButton.Flyout>
-        </ToggleButton>
+            </Panel.Margin>
+            <ToggleButton Name="LogoButton" Padding="0" BorderThickness="0">
+                <ToggleButton.Background>
+                    <VisualBrush>
+                        <VisualBrush.Visual>
+                            <Svg Path="/Images/PixiEditorLogo.svg" />
+                        </VisualBrush.Visual>
+                    </VisualBrush>
+                </ToggleButton.Background>
+                <ToggleButton.Styles>
+                    <Style Selector="FlyoutPresenter.arrow">
+                        <Setter Property="Cursor" Value="Arrow" />
+                    </Style>
+                </ToggleButton.Styles>
+                <ToggleButton.Flyout>
+                    <Flyout Placement="BottomEdgeAlignedLeft" FlyoutPresenterClasses="arrow">
+                        <Flyout.Content>
+                            <StackPanel Spacing="5" Margin="5">
+                                <TextBlock Text="PixiEditor" Classes="h3" />
+                                <TextBlock Text="{Binding UpdateViewModel.VersionText}" />
+                                <TextBlock IsVisible="{Binding UpdateViewModel.SelfUpdatingAvailable}"
+                                           Text="{Binding UpdateViewModel.UpdateStateString}" />
+                                <Button ui:Translator.Key="DOWNLOAD_UPDATE"
+                                        Background="{DynamicResource ThemeAccentBrush}"
+                                        Command="{Binding UpdateViewModel.DownloadCommand}"
+                                        IsVisible="{Binding UpdateViewModel.IsUpdateAvailable}" />
+                                <Button ui:Translator.Key="SWITCH_TO_NEW_VERSION"
+                                        Background="{DynamicResource ThemeAccentBrush}"
+                                        Command="{Binding UpdateViewModel.InstallCommand}"
+                                        IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}" />
+                            </StackPanel>
+                        </Flyout.Content>
+                    </Flyout>
+                </ToggleButton.Flyout>
+            </ToggleButton>
+            <Panel ClipToBounds="False" Margin="10, 10, 0, 0"
+                   IsVisible="{Binding UpdateViewModel.SelfUpdatingAvailable}"
+                   IsHitTestVisible="False" VerticalAlignment="Bottom" HorizontalAlignment="Right">
+                <TextBlock Text="!" IsVisible="{Binding UpdateViewModel.IsUpdateAvailable}"
+                           ClipToBounds="False"
+                           FontWeight="SemiBold"
+                           Foreground="Yellow" FontSize="18" />
+                <TextBlock Text="{DynamicResource icon-settings}"
+                           ClipToBounds="False"
+                            Width="16" Height="16"
+                           IsVisible="{Binding UpdateViewModel.IsDownloading}"
+                           Classes="pixi-icon" Foreground="DarkGray" FontSize="16">
+                    <TextBlock.Styles>
+                        <Style Selector="TextBlock">
+                            <Style.Animations>
+                                <Animation Duration="0:0:5" IterationCount="Infinite">
+                                    <KeyFrame Cue="0%">
+                                        <Setter Property="RotateTransform.Angle" Value="0.0" />
+                                    </KeyFrame>
+                                    <KeyFrame Cue="100%">
+                                        <Setter Property="RotateTransform.Angle" Value="360" />
+                                    </KeyFrame>
+                                </Animation>
+                            </Style.Animations>
+                        </Style>
+                    </TextBlock.Styles>
+                </TextBlock>
+                <Ellipse
+                    ClipToBounds="False"
+                    Fill="{DynamicResource ThemeAccent3Brush}" IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}"
+                    Width="8" Height="8" />
+            </Panel>
+        </Panel>
 
         <StackPanel Orientation="Horizontal">
             <StackPanel.Margin>