Procházet zdrojové kódy

Merge pull request #758 from PixiEditor/linux

Linux improvements
Krzysztof Krysiński před 6 měsíci
rodič
revize
0bfb762ead

+ 12 - 0
src/PixiEditor.AnimationRenderer.FFmpeg/PixiEditor.AnimationRenderer.FFmpeg.csproj

@@ -42,5 +42,17 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
+  
+  <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
+    <Content Include="ThirdParty/Linux/ffmpeg/**">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  
+  <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">
+    <Content Include="ThirdParty/Linux/ffmpeg/**">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
 
 </Project>

binární
src/PixiEditor.AnimationRenderer.FFmpeg/ThirdParty/Linux/ffmpeg/ffmpeg


+ 0 - 45
src/PixiEditor.Desktop/Info.plist

@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-    <dict>
-        <key>CFBundleName</key>
-        <string>PixiEditor</string>
-        <key>CFBundleDisplayName</key>
-        <string>PixiEditor</string>
-        <key>CFBundleIdentifier</key>
-        <string>com.pixieditor.macos</string>
-        <key>CFBundleVersion</key>
-        <string>{version-string}</string>
-        <key>CFBundlePackageType</key>
-        <string>APPL</string>
-        <key>CFBundleExecutable</key>
-        <string>PixiEditor</string>
-        <key>CFBundleIconFile</key>
-        <string>PixiEditor.icns</string>
-        <key>CFBundleShortVersionString</key>
-        <string>{version-string}</string>
-        <key>CFBundleSignature</key>
-        <string>????</string>
-        <key>NSPrincipalClass</key>
-        <string>NSApplication</string>
-        <key>NSHighResolutionCapable</key>
-        <key>CFBundleDocumentTypes</key>
-        <array>
-            <dict>
-                <key>CFBundleTypeName</key>
-                <string>Pixi Document</string>
-                <key>CFBundleTypeExtensions</key>
-                <array>
-                    <string>pixi</string>
-                </array>
-                <key>CFBundleTypeIconFile</key>
-                <string>PixiEditor.icns</string>
-                <key>CFBundleTypeRole</key>
-                <string>Editor</string>
-                <key>LSHandlerRank</key>
-                <string>Owner</string>
-            </dict>
-        </array>
-        <true/>
-    </dict>
-</plist>

binární
src/PixiEditor.Desktop/PixiEditor.icns


+ 17 - 0
src/PixiEditor.Linux/LinuxOperatingSystem.cs

@@ -86,4 +86,21 @@ public sealed class LinuxOperatingSystem : IOperatingSystem
 
         public override string ToString() => $"{Name} {Version}";
     }
+
+    public string GetActiveDesktopEnvironment()
+    {
+        var desktopSession = Environment.GetEnvironmentVariable("DESKTOP_SESSION");
+        if (desktopSession != null)
+        {
+            return desktopSession;
+        }
+
+        var desktopSessionFile = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
+        if (desktopSessionFile != null)
+        {
+            return desktopSessionFile;
+        }
+
+        return "Unknown";
+    }
 }

+ 19 - 22
src/PixiEditor.UpdateModule/UpdateDownloader.cs

@@ -20,17 +20,15 @@ public static class UpdateDownloader
             throw new FileNotFoundException("No matching update for your system found.");
         }
 
-        using (HttpClient client = new HttpClient())
+        using HttpClient client = new HttpClient();
+        client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
+        client.DefaultRequestHeaders.Add("Accept", "application/octet-stream");
+        var response = await client.GetAsync(matchingAsset.Url);
+        if (response.StatusCode == HttpStatusCode.OK)
         {
-            client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
-            client.DefaultRequestHeaders.Add("Accept", "application/octet-stream");
-            var response = await client.GetAsync(matchingAsset.Url);
-            if (response.StatusCode == HttpStatusCode.OK)
-            {
-                byte[] bytes = await response.Content.ReadAsByteArrayAsync();
-                CreateTempDirectory();
-                File.WriteAllBytes(Path.Join(DownloadLocation, $"update-{release.TagName}.zip"), bytes);
-            }
+            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
+            CreateTempDirectory();
+            await File.WriteAllBytesAsync(Path.Join(DownloadLocation, $"update-{release.TagName}.zip"), bytes);
         }
     }
 
@@ -43,17 +41,15 @@ public static class UpdateDownloader
             throw new FileNotFoundException("No matching update for your system found.");
         }
 
-        using (HttpClient client = new HttpClient())
+        using HttpClient client = new HttpClient();
+        client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
+        client.DefaultRequestHeaders.Add("Accept", "application/octet-stream");
+        var response = await client.GetAsync(matchingAsset.Url);
+        if (response.StatusCode == HttpStatusCode.OK)
         {
-            client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
-            client.DefaultRequestHeaders.Add("Accept", "application/octet-stream");
-            var response = await client.GetAsync(matchingAsset.Url);
-            if (response.StatusCode == HttpStatusCode.OK)
-            {
-                byte[] bytes = await response.Content.ReadAsByteArrayAsync();
-                CreateTempDirectory();
-                File.WriteAllBytes(Path.Join(DownloadLocation, $"update-{info.TagName}.exe"), bytes);
-            }
+            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
+            CreateTempDirectory();
+            await File.WriteAllBytesAsync(Path.Join(DownloadLocation, $"update-{info.TagName}.exe"), bytes);
         }
     }
 
@@ -67,8 +63,9 @@ public static class UpdateDownloader
 
     private static Asset? GetMatchingAsset(ReleaseInfo release, string assetType = "zip")
     {
-        string arch = IntPtr.Size == 8 ? "x64" : "x86";
+        string arch = "x64";
+        string os = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsLinux() ? "linux" : "mac";
         return release.Assets.FirstOrDefault(x => x.ContentType.Contains(assetType)
-                                         && x.Name.Contains(arch));
+                                         && x.Name.Contains(arch) && x.Name.Contains(os));
     }
 }

+ 80 - 44
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -1,17 +1,16 @@
-using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics;
 using System.Globalization;
-using System.IO;
 using System.IO.Compression;
-using System.Linq;
 using System.Reflection;
 using System.Text;
 using Newtonsoft.Json;
-using PixiEditor.Models.Preferences;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Helpers;
+#if LINUX
+using PixiEditor.Linux;
+#endif
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands;
 using PixiEditor.OperatingSystem;
@@ -34,7 +33,7 @@ internal class CrashReport : IDisposable
 
         apiReport.Version = VersionHelpers.GetCurrentAssemblyVersion();
         apiReport.BuildId = VersionHelpers.GetBuildId();
-        
+
         apiReport.ReportTime = currentTime.UtcDateTime;
         apiReport.ProcessStart = processStartTime.ToUniversalTime();
         apiReport.IsCrash = nonCrashInfo == null;
@@ -51,6 +50,13 @@ internal class CrashReport : IDisposable
 
             apiReport.SystemInformation["PlatformId"] = os.AnalyticsId;
             apiReport.SystemInformation["PlatformName"] = os.AnalyticsName;
+            apiReport.SystemInformation["OSVersion"] = Environment.OSVersion.VersionString;
+#if LINUX
+            if (os is LinuxOperatingSystem linux)
+            {
+                apiReport.SystemInformation["DesktopEnvironment"] = linux.GetActiveDesktopEnvironment();
+            }
+#endif
         }
         catch (Exception e)
         {
@@ -62,7 +68,7 @@ internal class CrashReport : IDisposable
             var sessionId = AnalyticsPeriodicReporter.Instance?.SessionId;
 
             if (sessionId == Guid.Empty) sessionId = null;
-            
+
             apiReport.SessionId = sessionId;
         }
         catch (Exception e)
@@ -71,13 +77,19 @@ internal class CrashReport : IDisposable
         }
 
         builder
-            .AppendLine($"PixiEditor {VersionHelpers.GetCurrentAssemblyVersionString(moreSpecific: true)} x{IntPtr.Size * 8} crashed on {currentTime:yyyy.MM.dd} at {currentTime:HH:mm:ss} {currentTime:zzz}")
-            .AppendLine($"Application started {GetFormatted(() => processStartTime, "yyyy.MM.dd HH:hh:ss")}, {GetFormatted(() => currentTime - processStartTime, @"d\ hh\:mm\.ss")} ago")
+            .AppendLine(
+                $"PixiEditor {VersionHelpers.GetCurrentAssemblyVersionString(moreSpecific: true)} x{IntPtr.Size * 8} crashed on {currentTime:yyyy.MM.dd} at {currentTime:HH:mm:ss} {currentTime:zzz}")
+            .AppendLine(
+                $"Application started {GetFormatted(() => processStartTime, "yyyy.MM.dd HH:hh:ss")}, {GetFormatted(() => currentTime - processStartTime, @"d\ hh\:mm\.ss")} ago")
             .AppendLine($"Report: {Guid.NewGuid()}\n")
             .AppendLine("-----System Information----")
-            .AppendLine("General:")
-            .AppendLine($"  OS: {Environment.OSVersion.VersionString}")
-            .AppendLine();
+            .AppendLine("General:");
+        foreach (var sysInfo in apiReport.SystemInformation)
+        {
+            builder.AppendLine($"  {sysInfo.Key}: {sysInfo.Value}");
+        }
+
+        builder.AppendLine();
 
         CrashHelper helper = new();
 
@@ -91,7 +103,8 @@ internal class CrashReport : IDisposable
         }
         catch (Exception cemLogException)
         {
-            builder.AppendLine($"Error ({cemLogException.GetType().FullName}: {cemLogException.Message}) while gathering command log, skipping...");
+            builder.AppendLine(
+                $"Error ({cemLogException.GetType().FullName}: {cemLogException.Message}) while gathering command log, skipping...");
         }
 
         builder.AppendLine("\n-----------State-----------");
@@ -102,8 +115,10 @@ internal class CrashReport : IDisposable
         }
         catch (Exception stateException)
         {
-            exception = new AggregateException(exception, new CrashInfoCollectionException("state information", stateException));
-            builder.AppendLine($"Error ({stateException.GetType().FullName}: {stateException.Message}) while gathering state (Must be bug in GetPreferenceFormatted, GetFormatted or StringBuilder.AppendLine as these should not throw), skipping...");
+            exception = new AggregateException(exception,
+                new CrashInfoCollectionException("state information", stateException));
+            builder.AppendLine(
+                $"Error ({stateException.GetType().FullName}: {stateException.Message}) while gathering state (Must be bug in GetPreferenceFormatted, GetFormatted or StringBuilder.AppendLine as these should not throw), skipping...");
         }
 
         apiReport.Exception = new ExceptionDetails(exception);
@@ -142,7 +157,8 @@ internal class CrashReport : IDisposable
         }
         catch (Exception cpuE)
         {
-            builder.AppendLine($"Error ({cpuE.GetType().FullName}: {cpuE.Message}) while gathering CPU information, skipping...");
+            builder.AppendLine(
+                $"Error ({cpuE.GetType().FullName}: {cpuE.Message}) while gathering CPU information, skipping...");
         }
 
         try
@@ -151,19 +167,21 @@ internal class CrashReport : IDisposable
         }
         catch (Exception gpuE)
         {
-            builder.AppendLine($"Error ({gpuE.GetType().FullName}: {gpuE.Message}) while gathering GPU information, skipping...");
+            builder.AppendLine(
+                $"Error ({gpuE.GetType().FullName}: {gpuE.Message}) while gathering GPU information, skipping...");
         }
 
-        
+
         try
         {
             helper.GetMemoryInformation(builder, apiReport);
         }
         catch (Exception memE)
         {
-            builder.AppendLine($"Error ({memE.GetType().FullName}: {memE.Message}) while gathering memory information, skipping...");
+            builder.AppendLine(
+                $"Error ({memE.GetType().FullName}: {memE.Message}) while gathering memory information, skipping...");
         }
-}
+    }
 
     private static void AppendStateInfo(StringBuilder builder, ApiCrashReport apiReport)
     {
@@ -186,17 +204,20 @@ internal class CrashReport : IDisposable
             .AppendLine($"  MainWindow Size: {GetFormatted(() => MainWindow.Current?.Bounds)}")
             .AppendLine($"  MainWindow State: {GetFormatted(() => MainWindow.Current?.WindowState)}")
             .AppendLine("\nViewModels:")
-            .AppendLine($"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive)}")
-            .AppendLine($"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
+            .AppendLine(
+                $"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive)}")
+            .AppendLine(
+                $"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
             .AppendLine($"  Primary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.PrimaryColor)}")
-            .AppendLine($"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
+            .AppendLine(
+                $"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
             .Append("\nActive Document: ");
 
         apiReport.StateInformation["Environment"] = new
         {
             ThreadCount = GetOrExceptionMessage(() => Process.GetCurrentProcess().Threads.Count)
         };
-        
+
         apiReport.StateInformation["Culture"] = new
         {
             SelectedLanguage = GetPreferenceFormatted("LanguageCode", true, "system"),
@@ -221,10 +242,16 @@ internal class CrashReport : IDisposable
 
         apiReport.StateInformation["ViewModels"] = new
         {
-            HasActiveUpdateableChange = GetOrExceptionMessage(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive),
-            CurrentTool = GetOrExceptionMessage(() => ViewModelMain.Current?.ToolsSubViewModel?.ActiveTool?.ToolName),
-            PrimaryColor = GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.PrimaryColor.ToString()),
-            SecondaryColor = GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.SecondaryColor.ToString())
+            HasActiveUpdateableChange =
+                GetOrExceptionMessage(() =>
+                    ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument
+                        ?.BlockingUpdateableChangeActive),
+            CurrentTool =
+                GetOrExceptionMessage(() => ViewModelMain.Current?.ToolsSubViewModel?.ActiveTool?.ToolName),
+            PrimaryColor =
+                GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.PrimaryColor.ToString()),
+            SecondaryColor = GetOrExceptionMessage(() =>
+                ViewModelMain.Current?.ColorsSubViewModel?.SecondaryColor.ToString())
         };
 
         apiReport.StateInformation["ActiveDocument"] = new { };
@@ -285,9 +312,9 @@ internal class CrashReport : IDisposable
             .AppendLine($"  Updateable Change Active: {FormatObject(document.BlockingUpdateableChangeActive)}")
             .AppendLine($"  Transform: {FormatObject(document.TransformViewModel)}");
     }
-    
+
     private static object GetPreferenceOrExceptionMessage<T>(string name, bool roaming, T defaultValue)
-    {        
+    {
         try
         {
             var preferences = IPreferences.Current;
@@ -307,7 +334,8 @@ internal class CrashReport : IDisposable
         }
     }
 
-    private static string GetPreferenceFormatted<T>(string name, bool roaming, T defaultValue = default, string? format = null)
+    private static string GetPreferenceFormatted<T>(string name, bool roaming, T defaultValue = default,
+        string? format = null)
     {
         try
         {
@@ -378,11 +406,11 @@ internal class CrashReport : IDisposable
         string FormatLocalizedString(LocalizedString localizedS)
         {
             return localizedS.Parameters != null
-                ? $"{localizedS.Key} @({string.Join(", ", localizedS.Parameters.Select(x => FormatObject(x, format)))})" 
+                ? $"{localizedS.Key} @({string.Join(", ", localizedS.Parameters.Select(x => FormatObject(x, format)))})"
                 : localizedS.Key;
         }
     }
-    
+
     public static CrashReport Parse(string path)
     {
         CrashReport report = new();
@@ -398,7 +426,7 @@ internal class CrashReport : IDisposable
     public string FilePath { get; set; }
 
     public string ReportText { get; set; }
-    
+
     public string ApiReportJson { get; set; }
 
     private ZipArchive ZipFile { get; set; }
@@ -431,15 +459,16 @@ internal class CrashReport : IDisposable
         {
             recoveredDocuments.AddRange(
                 ZipFile.Entries
-                    .Where(x => 
-                        x.FullName.StartsWith("Documents") && 
+                    .Where(x =>
+                        x.FullName.StartsWith("Documents") &&
                         x.FullName.EndsWith(".pixi"))
                     .Select(entry => new RecoveredPixi(null, entry)));
 
             return recoveredDocuments;
         }
 
-        recoveredDocuments.AddRange(sessionInfo.OpenedDocuments.Select(path => new RecoveredPixi(path.OriginalPath, ZipFile.GetEntry($"Documents/{path.ZipName}"))));
+        recoveredDocuments.AddRange(sessionInfo.OpenedDocuments.Select(path =>
+            new RecoveredPixi(path.OriginalPath, ZipFile.GetEntry($"Documents/{path.ZipName}"))));
 
         return recoveredDocuments;
 
@@ -475,8 +504,9 @@ internal class CrashReport : IDisposable
         string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) +
                           IOperatingSystem.Current.ExecutableExtension;
 #if DEBUG
-        if(!File.Exists(fileName))
-            fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + $".Desktop{IOperatingSystem.Current.ExecutableExtension}";
+        if (!File.Exists(fileName))
+            fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) +
+                       $".Desktop{IOperatingSystem.Current.ExecutableExtension}";
 #endif
 
         IOperatingSystem.Current.ProcessUtility.ShellExecute(fileName, $"--crash \"{Path.GetFullPath(FilePath)}\"");
@@ -518,10 +548,13 @@ internal class CrashReport : IDisposable
         {
             try
             {
-                string fileName = string.IsNullOrWhiteSpace(document.FullFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.FullFilePath);
-                string nameInZip = $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
-                    .Replace(':', '_')
-                    .Replace('/', '_');
+                string fileName = string.IsNullOrWhiteSpace(document.FullFilePath)
+                    ? "Unsaved"
+                    : Path.GetFileNameWithoutExtension(document.FullFilePath);
+                string nameInZip =
+                    $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
+                        .Replace(':', '_')
+                        .Replace('/', '_');
 
                 byte[] serialized = PixiParser.V5.Serialize(document.ToSerializable());
 
@@ -531,6 +564,7 @@ internal class CrashReport : IDisposable
                 originalPaths.Add(new CrashedFileInfo(nameInZip, document.FullFilePath));
             }
             catch { }
+
             counter++;
         }
 
@@ -539,7 +573,9 @@ internal class CrashReport : IDisposable
             using Stream jsonStream = archive.CreateEntry("DocumentInfo.json").Open();
             using StreamWriter writer = new StreamWriter(jsonStream);
 
-            string originalPathsJson = JsonConvert.SerializeObject(new CrashedSessionInfo(AnalyticsPeriodicReporter.Instance?.SessionId ?? Guid.Empty, originalPaths), Formatting.Indented);
+            string originalPathsJson = JsonConvert.SerializeObject(
+                new CrashedSessionInfo(AnalyticsPeriodicReporter.Instance?.SessionId ?? Guid.Empty, originalPaths),
+                Formatting.Indented);
             writer.Write(originalPathsJson);
         }
     }

+ 6 - 1
src/PixiEditor/ViewModels/SubViewModels/UpdateViewModel.cs

@@ -257,7 +257,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
     [Conditional("UPDATE")]
     private async void ConditionalUPDATE()
     {
-        if (PixiEditorSettings.Update.CheckUpdatesOnStartup.Value)
+        if (PixiEditorSettings.Update.CheckUpdatesOnStartup.Value && OsSupported())
         {
             try
             {
@@ -281,6 +281,11 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         }
     }
     
+    private bool OsSupported()
+    {
+        return IOperatingSystem.Current.IsWindows;
+    }
+    
     private bool UpdateInfoExists()
     {
         return File.Exists(Path.Join(Paths.TempFilesPath, "updateInfo.txt"));

+ 12 - 10
src/PixiEditor/Views/Input/NumberInput.cs

@@ -242,7 +242,7 @@ internal partial class NumberInput : TextBox
 
         behavior.Bind(TextBoxFocusBehavior.ConfirmOnEnterProperty, confirmOnEnterBinding);
     }
-    
+
     private static double CoerceValue(AvaloniaObject o, double value)
     {
         double min = (double)o.GetValue(MinProperty);
@@ -251,14 +251,14 @@ internal partial class NumberInput : TextBox
 
         return Math.Round(Math.Clamp(value, min, max), decimals);
     }
-    
+
     private static int CoerceDecimals(AvaloniaObject o, int value)
     {
         if (value < 0)
         {
             value = 0;
         }
-        
+
         return value;
     }
 
@@ -350,23 +350,25 @@ internal partial class NumberInput : TextBox
         }
 
         e.Handled = true;
-        double requiredBuildup = 1;
-        
-        if(Decimals == 0 && e.KeyModifiers.HasFlag(KeyModifiers.Control))
+        double requiredBuildup = 1f;
+
+        if (Decimals == 0 && e.KeyModifiers.HasFlag(KeyModifiers.Control))
         {
             requiredBuildup = 2;
         }
-        
+
+        scrollBuildup += e.Delta.Y;
+
         if (Math.Abs(scrollBuildup) < requiredBuildup)
         {
-            scrollBuildup += e.Delta.Y;
             return;
         }
 
         double step = Math.Sign(e.Delta.Y);
 
         double newValue = Value;
-        if (e.KeyModifiers.HasFlag(KeyModifiers.Shift) && Min - double.NegativeInfinity > 0.1f && Max - double.PositiveInfinity > 0.1f)
+        if (e.KeyModifiers.HasFlag(KeyModifiers.Shift) && Min - double.NegativeInfinity > 0.1f &&
+            Max - double.PositiveInfinity > 0.1f)
         {
             double multiplier = (Max - Min) * 0.1f;
             newValue += step * multiplier;
@@ -381,7 +383,7 @@ internal partial class NumberInput : TextBox
         }
 
         Value = (float)Math.Round(Math.Clamp(newValue, Min, Max), Decimals);
-        
+
         scrollBuildup = 0;
         OnScrollAction?.Invoke();
     }