Browse Source

Improved save error messages

Krzysztof Krysiński 4 months ago
parent
commit
91776bb21d

+ 7 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -981,5 +981,11 @@
   "CLAMP_TILE_MODE": "Clamp",
   "REPEAT_TILE_MODE": "Repeat",
   "MIRROR_TILE_MODE": "Mirror",
-  "DECAL_TILE_MODE": "Decal"
+  "DECAL_TILE_MODE": "Decal",
+  "ERR_UNKNOWN_FILE_FORMAT": "Unknown file format",
+  "ERR_EXPORT_SIZE_INVALID": "Invalid export size. Values must be greater than 0.",
+  "ERR_UNKNOWN_IMG_FORMAT": "Unknown image format '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Failed generating sprite sheet",
+  "ERR_NO_RENDERER": "Animation renderer not found.",
+  "ERR_RENDERING_FAILED": "Rendering failed"
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/Autosave/AutosaverSaveBackupJob.cs

@@ -56,7 +56,7 @@ internal class AutosaverSaveBackupJob(DocumentViewModel documentToSave, int back
             ExportConfig config = new ExportConfig(documentToSave.SizeBindable);
             var result = Exporter.TrySave(documentToSave, filePath, config, null);
 
-            if (result == SaveResult.Success)
+            if (result.ResultType == SaveResultType.Success)
             {
                 documentToSave.MarkAsAutosaved();
                 documentToSave.AutosaveViewModel.AddAutosaveHistoryEntry(AutosaveHistoryType.Periodic,

+ 18 - 18
src/PixiEditor/Models/Files/ImageFileType.cs

@@ -28,7 +28,7 @@ internal abstract class ImageFileType : IoFileType
             job?.Report(0, new LocalizedString("GENERATING_SPRITE_SHEET"));
             finalSurface = GenerateSpriteSheet(document, exportConfig, job);
             if (finalSurface == null)
-                return SaveResult.UnknownError;
+                return new SaveResult(SaveResultType.CustomError, "ERR_FAILED_GENERATE_SPRITE_SHEET");
         }
         else
         {
@@ -37,12 +37,12 @@ internal abstract class ImageFileType : IoFileType
             var exportSize = exportConfig.ExportSize;
             if (exportSize.X <= 0 || exportSize.Y <= 0)
             {
-                return SaveResult.UnknownError; // TODO: Add InvalidParameters error type
+                return new SaveResult(SaveResultType.CustomError, "ERR_EXPORT_SIZE_INVALID");
             }
 
             var maybeBitmap = document.TryRenderWholeImage(0, exportSize);
             if (maybeBitmap.IsT0)
-                return SaveResult.ConcurrencyError;
+                return new SaveResult(SaveResultType.ConcurrencyError);
 
             finalSurface = maybeBitmap.AsT1;
         }
@@ -51,7 +51,7 @@ internal abstract class ImageFileType : IoFileType
 
         if (mappedFormat == EncodedImageFormat.Unknown)
         {
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_UNKNOWN_IMG_FORMAT", EncodedImageFormat));
         }
 
         UniversalFileEncoder encoder = new(mappedFormat);
@@ -72,7 +72,7 @@ internal abstract class ImageFileType : IoFileType
             job?.Report(0, new LocalizedString("GENERATING_SPRITE_SHEET"));
             finalSurface = GenerateSpriteSheet(document, config, job);
             if (finalSurface == null)
-                return SaveResult.UnknownError;
+                return new SaveResult(SaveResultType.CustomError, "ERR_FAILED_GENERATE_SPRITE_SHEET");
         }
         else
         {
@@ -81,12 +81,12 @@ internal abstract class ImageFileType : IoFileType
             var exportSize = config.ExportSize;
             if (exportSize.X <= 0 || exportSize.Y <= 0)
             {
-                return SaveResult.UnknownError; // TODO: Add InvalidParameters error type
+                return new SaveResult(SaveResultType.CustomError, "ERR_EXPORT_SIZE_INVALID");
             }
 
             var maybeBitmap = document.TryRenderWholeImage(0, exportSize);
             if (maybeBitmap.IsT0)
-                return SaveResult.ConcurrencyError;
+                return new SaveResult(SaveResultType.ConcurrencyError);
 
             finalSurface = maybeBitmap.AsT1;
         }
@@ -95,7 +95,7 @@ internal abstract class ImageFileType : IoFileType
 
         if (mappedFormat == EncodedImageFormat.Unknown)
         {
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_UNKNOWN_IMG_FORMAT", EncodedImageFormat));
         }
 
         UniversalFileEncoder encoder = new(mappedFormat);
@@ -160,22 +160,22 @@ internal abstract class ImageFileType : IoFileType
         }
         catch (SecurityException)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (UnauthorizedAccessException)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (IOException)
         {
-            return SaveResult.IoError;
+            return new SaveResult(SaveResultType.IoError);
         }
         catch
         {
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
 
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 
     /// <summary>
@@ -193,21 +193,21 @@ internal abstract class ImageFileType : IoFileType
         }
         catch (SecurityException)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (UnauthorizedAccessException)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (IOException)
         {
-            return SaveResult.IoError;
+            return new SaveResult(SaveResultType.IoError);
         }
         catch
         {
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
 
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 }

+ 8 - 8
src/PixiEditor/Models/Files/PixiFileType.cs

@@ -29,19 +29,19 @@ internal class PixiFileType : IoFileType
         }
         catch (UnauthorizedAccessException e)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (IOException)
         {
-            return SaveResult.IoError;
+            return new SaveResult(SaveResultType.IoError);
         }
         catch (Exception e)
         {
             CrashHelper.SendExceptionInfo(e);
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
 
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 
     public override SaveResult TrySave(string pathWithExtension, DocumentViewModel document, ExportConfig config,
@@ -55,18 +55,18 @@ internal class PixiFileType : IoFileType
         }
         catch (UnauthorizedAccessException e)
         {
-            return SaveResult.SecurityError;
+            return new SaveResult(SaveResultType.SecurityError);
         }
         catch (IOException)
         {
-            return SaveResult.IoError;
+            return new SaveResult(SaveResultType.IoError);
         }
         catch (Exception e)
         {
             CrashHelper.SendExceptionInfo(e);
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
 
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 }

+ 2 - 2
src/PixiEditor/Models/Files/SvgFileType.cs

@@ -35,7 +35,7 @@ internal class SvgFileType : IoFileType
         await writer.WriteAsync(xml);
 
         job?.Report(1, string.Empty);
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 
     public override SaveResult TrySave(string pathWithExtension, DocumentViewModel document, ExportConfig config,
@@ -55,6 +55,6 @@ internal class SvgFileType : IoFileType
         writer.Write(xml);
 
         job?.Report(1, string.Empty);
-        return SaveResult.Success;
+        return new SaveResult(SaveResultType.Success);
     }
 }

+ 4 - 4
src/PixiEditor/Models/Files/VideoFileType.cs

@@ -14,7 +14,7 @@ internal abstract class VideoFileType : IoFileType
         ExportConfig config, ExportJob? job)
     {
         if (config.AnimationRenderer is null)
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_NO_RENDERER"));
 
         List<Image> frames = new();
 
@@ -51,14 +51,14 @@ internal abstract class VideoFileType : IoFileType
             frame.Dispose();
         }
 
-        return result ? SaveResult.Success : SaveResult.UnknownError;
+        return result ? new SaveResult(SaveResultType.Success) : new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_RENDERING_FAILED"));
     }
 
     public override SaveResult TrySave(string pathWithExtension, DocumentViewModel document, ExportConfig config,
         ExportJob? job)
     {
         if (config.AnimationRenderer is null)
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_NO_RENDERER"));
 
         List<Image> frames = new();
 
@@ -95,6 +95,6 @@ internal abstract class VideoFileType : IoFileType
             frame.Dispose();
         }
 
-        return result ? SaveResult.Success : SaveResult.UnknownError;
+        return result ? new SaveResult(SaveResultType.Success) : new SaveResult(SaveResultType.CustomError, new LocalizedString("ERR_RENDERING_FAILED"));
     }
 }

+ 26 - 23
src/PixiEditor/Models/IO/Exporter.cs

@@ -13,33 +13,36 @@ using PixiEditor.ViewModels.Document;
 
 namespace PixiEditor.Models.IO;
 
-internal enum DialogSaveResult
+internal enum SaveResultType
 {
     Success = 0,
     InvalidPath = 1,
     ConcurrencyError = 2,
     SecurityError = 3,
     IoError = 4,
-    UnknownError = 5,
-    Cancelled = 6,
+    CustomError = 5,
+    UnknownError = 6,
+    Cancelled = 7,
 }
 
-internal enum SaveResult
+internal class SaveResult
 {
-    Success = 0,
-    InvalidPath = 1,
-    ConcurrencyError = 2,
-    SecurityError = 3,
-    IoError = 4,
-    UnknownError = 5,
+    public SaveResultType ResultType { get; set; }
+    public string? ErrorMessage { get; set; }
+
+    public SaveResult(SaveResultType resultType, string? errorMessage = null)
+    {
+        ResultType = resultType;
+        ErrorMessage = errorMessage;
+    }
 }
 
 internal class ExporterResult
 {
-    public DialogSaveResult Result { get; set; }
+    public SaveResult Result { get; set; }
     public string Path { get; set; }
 
-    public ExporterResult(DialogSaveResult result, string path)
+    public ExporterResult(SaveResult result, string path)
     {
         Result = result;
         Path = path;
@@ -54,7 +57,7 @@ internal class Exporter
     public static async Task<ExporterResult> TrySaveWithDialog(DocumentViewModel document, ExportConfig exportConfig,
         ExportJob? job)
     {
-        ExporterResult result = new(DialogSaveResult.UnknownError, null);
+        ExporterResult result = new(new SaveResult(SaveResultType.UnknownError), null);
 
         if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
         {
@@ -67,7 +70,7 @@ internal class Exporter
 
             if (file is null)
             {
-                result.Result = DialogSaveResult.Cancelled;
+                result.Result.ResultType = SaveResultType.Cancelled;
                 return result;
             }
 
@@ -75,12 +78,12 @@ internal class Exporter
 
             (SaveResult Result, string finalPath) saveResult =
                 await TrySaveUsingDataFromDialog(document, file.Path.LocalPath, fileType, exportConfig, job);
-            if (saveResult.Result == SaveResult.Success)
+            if (saveResult.Result.ResultType == SaveResultType.Success)
             {
                 result.Path = saveResult.finalPath;
             }
 
-            result.Result = (DialogSaveResult)saveResult.Result;
+            result.Result = saveResult.Result;
         }
 
         return result;
@@ -95,7 +98,7 @@ internal class Exporter
     {
         string finalPath = SupportedFilesHelper.FixFileExtension(pathFromDialog, fileTypeFromDialog);
         var saveResult = await TrySaveAsync(document, finalPath, exportConfig, job);
-        if (saveResult != SaveResult.Success)
+        if (saveResult.ResultType != SaveResultType.Success)
             finalPath = "";
 
         return (saveResult, finalPath);
@@ -109,12 +112,12 @@ internal class Exporter
     {
         string directory = Path.GetDirectoryName(pathWithExtension);
         if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
-            return SaveResult.InvalidPath;
+            return new SaveResult(SaveResultType.InvalidPath);
 
         var typeFromPath = SupportedFilesHelper.ParseImageFormat(Path.GetExtension(pathWithExtension));
 
         if (typeFromPath is null)
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, "ERR_UNKNOWN_FILE_FORMAT");
 
         try
         {
@@ -127,7 +130,7 @@ internal class Exporter
             job?.Finish();
             Console.WriteLine(e);
             CrashHelper.SendExceptionInfo(e);
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
     }
 
@@ -136,12 +139,12 @@ internal class Exporter
     {
         string directory = Path.GetDirectoryName(pathWithExtension);
         if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
-            return SaveResult.InvalidPath;
+            return new SaveResult(SaveResultType.InvalidPath);
 
         var typeFromPath = SupportedFilesHelper.ParseImageFormat(Path.GetExtension(pathWithExtension));
 
         if (typeFromPath is null)
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.CustomError, "ERR_UNKNOWN_FILE_FORMAT");
 
         try
         {
@@ -154,7 +157,7 @@ internal class Exporter
             job?.Finish();
             Console.WriteLine(e);
             CrashHelper.SendExceptionInfo(e);
-            return SaveResult.UnknownError;
+            return new SaveResult(SaveResultType.UnknownError);
         }
     }
 

+ 1 - 1
src/PixiEditor/ViewModels/Document/AutosaveDocumentViewModel.cs

@@ -99,7 +99,7 @@ internal class AutosaveDocumentViewModel : ObservableObject
             string filePath = AutosavePath;
             Directory.CreateDirectory(Directory.GetParent(filePath)!.FullName);
             ExportConfig config = new ExportConfig(Document.SizeBindable);
-            bool success = Exporter.TrySave(Document, filePath, config, null) == SaveResult.Success;
+            bool success = Exporter.TrySave(Document, filePath, config, null).ResultType == SaveResultType.Success;
             if (success)
             {
                 AddAutosaveHistoryEntry(type, AutosaveHistoryResult.SavedBackup);

+ 18 - 13
src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs

@@ -535,9 +535,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
             ExportConfig config = new ExportConfig(document.SizeBindable);
             var result = await Exporter.TrySaveWithDialog(document, config, null);
-            if (result.Result == DialogSaveResult.Cancelled)
+            if (result.Result.ResultType == SaveResultType.Cancelled)
                 return false;
-            if (result.Result != DialogSaveResult.Success)
+            if (result.Result.ResultType != SaveResultType.Success)
             {
                 ShowSaveError(result.Result);
                 return false;
@@ -550,9 +550,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
             ExportConfig config = new ExportConfig(document.SizeBindable);
             var result = await Exporter.TrySaveAsync(document, document.FullFilePath, config, null);
-            if (result != SaveResult.Success)
+            if (result.ResultType != SaveResultType.Success)
             {
-                ShowSaveError((DialogSaveResult)result);
+                ShowSaveError(result);
                 return false;
             }
 
@@ -595,7 +595,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
                             info.ExportConfig,
                             job);
 
-                    if (result.result == SaveResult.Success)
+                    if (result.result.ResultType == SaveResultType.Success)
                     {
                         Dispatcher.UIThread.Post(() =>
                         {
@@ -609,7 +609,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
                     {
                         Dispatcher.UIThread.Post(() =>
                         {
-                            ShowSaveError((DialogSaveResult)result.result);
+                            ShowSaveError(result.result);
                         });
                     }
                 });
@@ -623,23 +623,28 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    private void ShowSaveError(DialogSaveResult result)
+    private void ShowSaveError(SaveResult result)
     {
-        switch (result)
+        switch (result.ResultType)
         {
-            case DialogSaveResult.InvalidPath:
+            case SaveResultType.InvalidPath:
                 NoticeDialog.Show("ERROR_SAVE_LOCATION", "ERROR");
                 break;
-            case DialogSaveResult.ConcurrencyError:
+            case SaveResultType.ConcurrencyError:
                 NoticeDialog.Show("INTERNAL_ERROR", "ERROR_WHILE_SAVING");
                 break;
-            case DialogSaveResult.SecurityError:
+            case SaveResultType.SecurityError:
                 NoticeDialog.Show(title: "SECURITY_ERROR", message: "SECURITY_ERROR_MSG");
                 break;
-            case DialogSaveResult.IoError:
+            case SaveResultType.IoError:
                 NoticeDialog.Show(title: "IO_ERROR", message: "IO_ERROR_MSG");
                 break;
-            case DialogSaveResult.UnknownError:
+            case SaveResultType.CustomError:
+                NoticeDialog.Show(result.ErrorMessage, "ERROR");
+                break;
+            case SaveResultType.Cancelled:
+                break;
+            case SaveResultType.UnknownError:
                 NoticeDialog.Show("UNKNOWN_ERROR_SAVING", "ERROR");
                 break;
         }