DocumentManagerViewModel.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using System.Collections.ObjectModel;
  2. using System.Threading.Tasks;
  3. using Avalonia.Input;
  4. using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
  5. using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Evaluators;
  6. using PixiEditor.AvaloniaUI.Models.Dialogs;
  7. using PixiEditor.AvaloniaUI.Models.Handlers;
  8. using PixiEditor.AvaloniaUI.ViewModels.SubViewModels;
  9. using PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
  10. using PixiEditor.AvaloniaUI.Views;
  11. using PixiEditor.AvaloniaUI.Views.Overlays.SymmetryOverlay;
  12. using PixiEditor.ChangeableDocument.Enums;
  13. namespace PixiEditor.AvaloniaUI.ViewModels.Document;
  14. #nullable enable
  15. [Command.Group("PixiEditor.Document", "IMAGE")]
  16. internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocumentManagerHandler
  17. {
  18. public ObservableCollection<DocumentViewModel> Documents { get; } = new ObservableCollection<DocumentViewModel>();
  19. public event EventHandler<DocumentChangedEventArgs>? ActiveDocumentChanged;
  20. private DocumentViewModel? activeDocument;
  21. public DocumentViewModel? ActiveDocument
  22. {
  23. get => activeDocument;
  24. // Use WindowSubViewModel.MakeDocumentViewportActive(document);
  25. private set
  26. {
  27. if (activeDocument == value)
  28. return;
  29. DocumentViewModel? prevDoc = activeDocument;
  30. activeDocument = value;
  31. OnPropertyChanged(nameof(ActiveDocument));
  32. ActiveDocumentChanged?.Invoke(this, new(value, prevDoc));
  33. if (ViewModelMain.Current.ToolsSubViewModel.ActiveTool == null)
  34. {
  35. ViewModelMain.Current.ToolsSubViewModel.SetActiveTool<PenToolViewModel>(false);
  36. }
  37. }
  38. }
  39. IDocument? IDocumentManagerHandler.ActiveDocument
  40. {
  41. get => ActiveDocument;
  42. set => ActiveDocument = (DocumentViewModel)value;
  43. }
  44. public bool HasActiveDocument => ActiveDocument != null;
  45. public DocumentManagerViewModel(ViewModelMain owner) : base(owner)
  46. {
  47. owner.WindowSubViewModel.ActiveViewportChanged += (_, args) =>
  48. {
  49. ActiveDocument = args.Document;
  50. };
  51. }
  52. public void MakeActiveDocumentNull() => ActiveDocument = null;
  53. [Evaluator.CanExecute("PixiEditor.HasDocument", nameof(ActiveDocument))]
  54. public bool DocumentNotNull() => ActiveDocument != null;
  55. [Command.Basic("PixiEditor.Document.ClipCanvas", "CLIP_CANVAS", "CLIP_CANVAS", CanExecute = "PixiEditor.HasDocument", IconPath = "crop.png",
  56. MenuItemPath = "IMAGE/CLIP_CANVAS", MenuItemOrder = 2)]
  57. public void ClipCanvas() => ActiveDocument?.Operations.ClipCanvas();
  58. [Command.Basic("PixiEditor.Document.FlipImageHorizontal", FlipType.Horizontal, "FLIP_IMG_HORIZONTALLY", "FLIP_IMG_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument",
  59. MenuItemPath = "IMAGE/FLIP/FLIP_IMG_HORIZONTALLY", MenuItemOrder = 14)]
  60. [Command.Basic("PixiEditor.Document.FlipImageVertical", FlipType.Vertical, "FLIP_IMG_VERTICALLY", "FLIP_IMG_VERTICALLY", CanExecute = "PixiEditor.HasDocument",
  61. MenuItemPath = "IMAGE/FLIP/FLIP_IMG_VERTICALLY", MenuItemOrder = 15)]
  62. public void FlipImage(FlipType type) => ActiveDocument?.Operations.FlipImage(type);
  63. [Command.Basic("PixiEditor.Document.FlipLayersHorizontal", FlipType.Horizontal, "FLIP_LAYERS_HORIZONTALLY", "FLIP_LAYERS_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument",
  64. MenuItemPath = "IMAGE/FLIP/FLIP_LAYERS_HORIZONTALLY", MenuItemOrder = 16)]
  65. [Command.Basic("PixiEditor.Document.FlipLayersVertical", FlipType.Vertical, "FLIP_LAYERS_VERTICALLY", "FLIP_LAYERS_VERTICALLY", CanExecute = "PixiEditor.HasDocument",
  66. MenuItemPath = "IMAGE/FLIP/FLIP_LAYERS_VERTICALLY", MenuItemOrder = 17)]
  67. public void FlipLayers(FlipType type)
  68. {
  69. if (ActiveDocument?.SelectedStructureMember == null)
  70. return;
  71. ActiveDocument?.Operations.FlipImage(type, ActiveDocument.GetSelectedMembers());
  72. }
  73. [Command.Basic("PixiEditor.Document.Rotate90Deg", "ROT_IMG_90",
  74. "ROT_IMG_90", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D90,
  75. MenuItemPath = "IMAGE/ROTATION/ROT_IMG_90_D", MenuItemOrder = 8)]
  76. [Command.Basic("PixiEditor.Document.Rotate180Deg", "ROT_IMG_180",
  77. "ROT_IMG_180", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D180,
  78. MenuItemPath = "IMAGE/ROTATION/ROT_IMG_180_D", MenuItemOrder = 9)]
  79. [Command.Basic("PixiEditor.Document.Rotate270Deg", "ROT_IMG_-90",
  80. "ROT_IMG_-90", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D270,
  81. MenuItemPath = "IMAGE/ROTATION/ROT_IMG_-90_D", MenuItemOrder = 10)]
  82. public void RotateImage(RotationAngle angle) => ActiveDocument?.Operations.RotateImage(angle);
  83. [Command.Basic("PixiEditor.Document.Rotate90DegLayers", "ROT_LAYERS_90",
  84. "ROT_LAYERS_90", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D90,
  85. MenuItemPath = "IMAGE/ROTATION/ROT_LAYERS_90_D", MenuItemOrder = 11)]
  86. [Command.Basic("PixiEditor.Document.Rotate180DegLayers", "ROT_LAYERS_180",
  87. "ROT_LAYERS_180", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D180,
  88. MenuItemPath = "IMAGE/ROTATION/ROT_LAYERS_180_D", MenuItemOrder = 12)]
  89. [Command.Basic("PixiEditor.Document.Rotate270DegLayers", "ROT_LAYERS_-90",
  90. "ROT_LAYERS_-90", CanExecute = "PixiEditor.HasDocument", Parameter = RotationAngle.D270,
  91. MenuItemPath = "IMAGE/ROTATION/ROT_LAYERS_-90_D", MenuItemOrder = 13)]
  92. public void RotateLayers(RotationAngle angle)
  93. {
  94. if (ActiveDocument?.SelectedStructureMember == null)
  95. return;
  96. ActiveDocument?.Operations.RotateImage(angle, ActiveDocument.GetSelectedMembers());
  97. }
  98. [Command.Basic("PixiEditor.Document.ToggleVerticalSymmetryAxis", "TOGGLE_VERT_SYMMETRY_AXIS", "TOGGLE_VERT_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryVertical.png")]
  99. public void ToggleVerticalSymmetryAxis()
  100. {
  101. if (ActiveDocument is null)
  102. return;
  103. ActiveDocument.VerticalSymmetryAxisEnabledBindable ^= true;
  104. }
  105. [Command.Basic("PixiEditor.Document.ToggleHorizontalSymmetryAxis", "TOGGLE_HOR_SYMMETRY_AXIS", "TOGGLE_HOR_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryHorizontal.png")]
  106. public void ToggleHorizontalSymmetryAxis()
  107. {
  108. if (ActiveDocument is null)
  109. return;
  110. ActiveDocument.HorizontalSymmetryAxisEnabledBindable ^= true;
  111. }
  112. [Command.Internal("PixiEditor.Document.DragSymmetry", CanExecute = "PixiEditor.HasDocument")]
  113. public void DragSymmetry(SymmetryAxisDragInfo info)
  114. {
  115. if (ActiveDocument is null)
  116. return;
  117. ActiveDocument.EventInlet.OnSymmetryDragged(info);
  118. }
  119. [Command.Internal("PixiEditor.Document.StartDragSymmetry", CanExecute = "PixiEditor.HasDocument")]
  120. public void StartDragSymmetry(SymmetryAxisDirection dir)
  121. {
  122. if (ActiveDocument is null)
  123. return;
  124. ActiveDocument.EventInlet.OnSymmetryDragStarted(dir);
  125. ActiveDocument.Tools.UseSymmetry(dir);
  126. }
  127. [Command.Internal("PixiEditor.Document.EndDragSymmetry", CanExecute = "PixiEditor.HasDocument")]
  128. public void EndDragSymmetry(SymmetryAxisDirection dir)
  129. {
  130. if (ActiveDocument is null)
  131. return;
  132. ActiveDocument.EventInlet.OnSymmetryDragEnded(dir);
  133. }
  134. [Command.Basic("PixiEditor.Document.DeletePixels", "DELETE_PIXELS", "DELETE_PIXELS_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.Delete, IconPath = "Tools/EraserImage.png",
  135. MenuItemPath = "EDIT/DELETE_SELECTED_PIXELS", MenuItemOrder = 6)]
  136. public void DeletePixels()
  137. {
  138. Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.DeleteSelectedPixels();
  139. }
  140. [Command.Basic("PixiEditor.Document.ResizeDocument", false, "RESIZE_DOCUMENT", "RESIZE_DOCUMENT", CanExecute = "PixiEditor.HasDocument", Key = Key.I, Modifiers = KeyModifiers.Control | KeyModifiers.Shift,
  141. MenuItemPath = "IMAGE/RESIZE_IMAGE", MenuItemOrder = 0)]
  142. [Command.Basic("PixiEditor.Document.ResizeCanvas", true, "RESIZE_CANVAS", "RESIZE_CANVAS", CanExecute = "PixiEditor.HasDocument", Key = Key.C, Modifiers = KeyModifiers.Control | KeyModifiers.Shift,
  143. MenuItemPath = "IMAGE/RESIZE_CANVAS", MenuItemOrder = 1)]
  144. public async Task OpenResizePopup(bool canvas)
  145. {
  146. DocumentViewModel? doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
  147. if (doc is null)
  148. return;
  149. ResizeDocumentDialog dialog = new ResizeDocumentDialog(
  150. doc.Width,
  151. doc.Height,
  152. MainWindow.Current!,
  153. canvas);
  154. if (await dialog.ShowDialog())
  155. {
  156. if (canvas)
  157. {
  158. doc.Operations.ResizeCanvas(new(dialog.Width, dialog.Height), dialog.ResizeAnchor);
  159. }
  160. else
  161. {
  162. doc.Operations.ResizeImage(new(dialog.Width, dialog.Height), ResamplingMethod.NearestNeighbor);
  163. }
  164. }
  165. }
  166. [Command.Basic("PixiEditor.Document.CenterContent", "CENTER_CONTENT", "CENTER_CONTENT", CanExecute = "PixiEditor.HasDocument",
  167. MenuItemPath = "IMAGE/CLIP_CANVAS", MenuItemOrder = 3)]
  168. public void CenterContent()
  169. {
  170. if(ActiveDocument?.SelectedStructureMember == null)
  171. return;
  172. ActiveDocument.Operations.CenterContent(ActiveDocument.GetSelectedMembers());
  173. }
  174. }