ViewModelMain.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. using PixiEditor.Helpers;
  2. using PixiEditor.Models.Enums;
  3. using PixiEditor.Models.Tools;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Windows;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Imaging;
  10. using PixiTools = PixiEditor.Models.Tools.Tools;
  11. using PixiEditor.Models.Controllers;
  12. using PixiEditor.Models.Dialogs;
  13. using PixiEditor.Models.IO;
  14. using PixiEditor.Models.Position;
  15. using PixiEditor.Models.DataHolders;
  16. using System.Linq;
  17. using System.Collections.ObjectModel;
  18. namespace PixiEditor.ViewModels
  19. {
  20. class ViewModelMain : ViewModelBase
  21. {
  22. public RelayCommand SelectToolCommand { get; set; } //Command that handles tool switching
  23. public RelayCommand GenerateDrawAreaCommand { get; set; } //Command that generates draw area
  24. public RelayCommand MouseMoveCommand { get; set; } //Command that is used to draw
  25. public RelayCommand MouseDownCommand { get; set; }
  26. public RelayCommand KeyDownCommand { get; set; }
  27. public RelayCommand SaveFileCommand { get; set; } //Command that is used to save file
  28. public RelayCommand UndoCommand { get; set; }
  29. public RelayCommand RedoCommand { get; set; }
  30. public RelayCommand MouseUpCommand { get; set; }
  31. public RelayCommand RecenterZoomboxCommand { get; set; }
  32. public RelayCommand OpenFileCommand { get; set; }
  33. public RelayCommand SetActiveLayerCommand { get; set; }
  34. public RelayCommand NewLayerCommand { get; set; }
  35. public RelayCommand ReloadImageCommand { get; set; }
  36. public RelayCommand DeleteLayerCommand { get; set; }
  37. public RelayCommand RenameLayerCommand { get; set; }
  38. public RelayCommand MoveToBackCommand { get; set; }
  39. public RelayCommand MoveToFrontCommand { get; set; }
  40. public RelayCommand SwapColorsCommand { get; set; }
  41. private double _mouseXonCanvas;
  42. public double MouseXOnCanvas //Mouse X coordinate relative to canvas
  43. {
  44. get => _mouseXonCanvas;
  45. set { _mouseXonCanvas = value; RaisePropertyChanged("MouseXonCanvas"); }
  46. }
  47. private double _mouseYonCanvas;
  48. public double MouseYOnCanvas //Mouse Y coordinate relative to canvas
  49. {
  50. get => _mouseYonCanvas;
  51. set { _mouseYonCanvas = value; RaisePropertyChanged("MouseYonCanvas"); }
  52. }
  53. private Color _primaryColor = Colors.White;
  54. public Color PrimaryColor //Primary color, hooked with left mouse button
  55. {
  56. get => _primaryColor;
  57. set
  58. {
  59. if (_primaryColor != value)
  60. {
  61. _primaryColor = value;
  62. BitmapUtility.PrimaryColor = value;
  63. RaisePropertyChanged("PrimaryColor");
  64. }
  65. }
  66. }
  67. private Color _secondaryColor = Colors.Black;
  68. public Color SecondaryColor //Secondary color, hooked with right mouse button
  69. {
  70. get => _secondaryColor;
  71. set { if (_secondaryColor != value) { _secondaryColor = value; RaisePropertyChanged("SecondaryColor"); } }
  72. }
  73. private ToolType _selectedTool;
  74. public ToolType SelectedTool
  75. {
  76. get { return _selectedTool; }
  77. set
  78. {
  79. if (_selectedTool != value)
  80. {
  81. _selectedTool = value;
  82. SetActiveTool(value);
  83. RaisePropertyChanged("SelectedTool"); } }
  84. }
  85. public ObservableCollection<Tool> ToolSet { get; set; }
  86. private LayerChanges _undoChanges;
  87. public LayerChanges UndoChanges
  88. {
  89. get { return _undoChanges; }
  90. set
  91. {
  92. _undoChanges = value;
  93. BitmapUtility.Layers[value.LayerIndex].ApplyPixels(value.PixelChanges);
  94. }
  95. }
  96. private Cursor _toolCursor;
  97. public Cursor ToolCursor
  98. {
  99. get { return _toolCursor; }
  100. set {
  101. _toolCursor = value;
  102. RaisePropertyChanged("ToolCursor");
  103. }
  104. }
  105. public BitmapOperationsUtility BitmapUtility { get; set; }
  106. public PixelChangesController ChangesController { get; set; }
  107. public ShortcutController ShortcutController { get; set; }
  108. public ViewModelMain()
  109. {
  110. PixiFilesManager.InitializeTempDirectories();
  111. BitmapUtility = new BitmapOperationsUtility();
  112. BitmapUtility.BitmapChanged += BitmapUtility_BitmapChanged;
  113. BitmapUtility.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
  114. ChangesController = new PixelChangesController();
  115. SelectToolCommand = new RelayCommand(SetTool);
  116. GenerateDrawAreaCommand = new RelayCommand(GenerateDrawArea);
  117. MouseMoveCommand = new RelayCommand(MouseMove);
  118. MouseDownCommand = new RelayCommand(MouseDown);
  119. SaveFileCommand = new RelayCommand(SaveFile, CanSave);
  120. UndoCommand = new RelayCommand(Undo, CanUndo);
  121. RedoCommand = new RelayCommand(Redo, CanRedo);
  122. MouseUpCommand = new RelayCommand(MouseUp);
  123. RecenterZoomboxCommand = new RelayCommand(RecenterZoombox);
  124. OpenFileCommand = new RelayCommand(OpenFile);
  125. SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
  126. NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
  127. DeleteLayerCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
  128. MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
  129. MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
  130. SwapColorsCommand = new RelayCommand(SwapColors);
  131. KeyDownCommand = new RelayCommand(KeyDown);
  132. RenameLayerCommand = new RelayCommand(RenameLayer);
  133. ToolSet = new ObservableCollection<Tool> { new PixiTools.PenTool(), new PixiTools.FloodFill(), new PixiTools.LineTool(),
  134. new PixiTools.CircleTool(), new PixiTools.RectangleTool(), new PixiTools.EarserTool(), new PixiTools.ColorPickerTool(), new PixiTools.BrightnessTool() };
  135. ShortcutController = new ShortcutController
  136. {
  137. Shortcuts = new List<Shortcut> {
  138. new Shortcut(Key.B, SelectToolCommand, ToolType.Pen),
  139. new Shortcut(Key.X, SwapColorsCommand),
  140. new Shortcut(Key.O, OpenFileCommand, null, ModifierKeys.Control),
  141. new Shortcut(Key.E, SelectToolCommand, ToolType.Earser),
  142. new Shortcut(Key.O, SelectToolCommand, ToolType.ColorPicker),
  143. new Shortcut(Key.R, SelectToolCommand, ToolType.Rectangle),
  144. new Shortcut(Key.C, SelectToolCommand, ToolType.Circle),
  145. new Shortcut(Key.L, SelectToolCommand, ToolType.Line),
  146. new Shortcut(Key.G, SelectToolCommand, ToolType.Bucket),
  147. new Shortcut(Key.U, SelectToolCommand, ToolType.Brightness),
  148. new Shortcut(Key.Y, RedoCommand, null, ModifierKeys.Control),
  149. new Shortcut(Key.Z, UndoCommand),
  150. new Shortcut(Key.S, SaveFileCommand, null, ModifierKeys.Control),
  151. new Shortcut(Key.N, GenerateDrawAreaCommand, null, ModifierKeys.Control),
  152. }
  153. };
  154. UndoManager.SetMainRoot(this);
  155. SetActiveTool(ToolType.Pen);
  156. BitmapUtility.PrimaryColor = PrimaryColor;
  157. }
  158. public void SetTool(object parameter)
  159. {
  160. SetActiveTool((ToolType)parameter);
  161. }
  162. public void RenameLayer(object parameter)
  163. {
  164. BitmapUtility.Layers[(int)parameter].IsRenaming = true;
  165. }
  166. public void KeyDown(object parameter)
  167. {
  168. ShortcutController.KeyPressed(((KeyEventArgs)parameter).Key);
  169. }
  170. private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
  171. {
  172. if (BitmapUtility.SelectedTool.PerformsOperationOnBitmap)
  173. {
  174. Tuple<LayerChanges, LayerChanges> changes = ChangesController.PopChanges();
  175. if (changes.Item1.PixelChanges.ChangedPixels.Count > 0)
  176. UndoManager.AddUndoChange(new Change("UndoChanges", changes.Item2, changes.Item1)); //Item2 is old value
  177. }
  178. }
  179. private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
  180. {
  181. ChangesController.AddChanges(new LayerChanges(e.PixelsChanged, e.ChangedLayerIndex),
  182. new LayerChanges(e.OldPixelsValues, e.ChangedLayerIndex));
  183. }
  184. public void SwapColors(object parameter)
  185. {
  186. var tmp = PrimaryColor;
  187. PrimaryColor = SecondaryColor;
  188. SecondaryColor = tmp;
  189. }
  190. public void MoveLayerToFront(object parameter)
  191. {
  192. int oldIndex = (int)parameter;
  193. BitmapUtility.Layers.Move(oldIndex, oldIndex + 1);
  194. if(BitmapUtility.ActiveLayerIndex == oldIndex)
  195. {
  196. BitmapUtility.SetActiveLayer(oldIndex + 1);
  197. }
  198. }
  199. public void MoveLayerToBack(object parameter)
  200. {
  201. int oldIndex = (int)parameter;
  202. BitmapUtility.Layers.Move(oldIndex, oldIndex - 1);
  203. if (BitmapUtility.ActiveLayerIndex == oldIndex)
  204. {
  205. BitmapUtility.SetActiveLayer(oldIndex - 1);
  206. }
  207. }
  208. public bool CanMoveToFront(object property)
  209. {
  210. return BitmapUtility.Layers.Count - 1 > (int)property;
  211. }
  212. public bool CanMoveToBack(object property)
  213. {
  214. return (int)property > 0;
  215. }
  216. public void SetActiveLayer(object parameter)
  217. {
  218. BitmapUtility.SetActiveLayer((int)parameter);
  219. }
  220. public void DeleteLayer(object parameter)
  221. {
  222. BitmapUtility.RemoveLayer((int)parameter);
  223. }
  224. public bool CanDeleteLayer(object property)
  225. {
  226. return BitmapUtility.Layers.Count > 1;
  227. }
  228. #region Undo/Redo
  229. /// <summary>
  230. /// Undo last action
  231. /// </summary>
  232. /// <param name="parameter"></param>
  233. public void Undo(object parameter)
  234. {
  235. UndoManager.Undo();
  236. }
  237. /// <summary>
  238. /// Returns true if undo can be done.
  239. /// </summary>
  240. /// <param name="property"></param>
  241. /// <returns></returns>
  242. private bool CanUndo(object property)
  243. {
  244. return UndoManager.CanUndo;
  245. }
  246. /// <summary>
  247. /// Redo last action
  248. /// </summary>
  249. /// <param name="parameter"></param>
  250. public void Redo(object parameter)
  251. {
  252. UndoManager.Redo();
  253. }
  254. /// <summary>
  255. /// Returns true if redo can be done.
  256. /// </summary>
  257. /// <param name="property"></param>
  258. /// <returns></returns>
  259. private bool CanRedo(object property)
  260. {
  261. return UndoManager.CanRedo;
  262. }
  263. #endregion
  264. private void SetActiveTool(ToolType tool)
  265. {
  266. BitmapUtility.SetActiveTool(ToolSet.First(x=> x.ToolType == tool));
  267. Tool activeTool = ToolSet.FirstOrDefault(x => x.IsActive);
  268. if(activeTool != null)
  269. {
  270. activeTool.IsActive = false;
  271. }
  272. BitmapUtility.SelectedTool.IsActive = true;
  273. SetToolCursor(tool);
  274. }
  275. private void SetToolCursor(ToolType tool)
  276. {
  277. if (tool != ToolType.None && tool != ToolType.ColorPicker)
  278. {
  279. ToolCursor = BitmapUtility.SelectedTool.Cursor;
  280. }
  281. else
  282. {
  283. ToolCursor = Cursors.Arrow;
  284. }
  285. }
  286. /// <summary>
  287. /// When mouse is up stops recording changes.
  288. /// </summary>
  289. /// <param name="parameter"></param>
  290. private void MouseUp(object parameter)
  291. {
  292. BitmapUtility.MouseController.StopRecordingMouseMovementChanges();
  293. }
  294. private void MouseDown(object parameter)
  295. {
  296. if (BitmapUtility.Layers.Count == 0) return;
  297. if(BitmapUtility.SelectedTool.ToolType == ToolType.ColorPicker)
  298. {
  299. ExecuteColorPicker();
  300. }
  301. else if(Mouse.LeftButton == MouseButtonState.Pressed && BitmapUtility.SelectedTool.PerformsOperationOnBitmap)
  302. {
  303. if (!BitmapUtility.MouseController.IsRecordingChanges)
  304. {
  305. BitmapUtility.MouseController.StartRecordingMouseMovementChanges();
  306. BitmapUtility.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
  307. }
  308. }
  309. }
  310. /// <summary>
  311. /// Method connected with command, it executes tool "activity"
  312. /// </summary>
  313. /// <param name="parameter"></param>
  314. private void MouseMove(object parameter)
  315. {
  316. Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
  317. MousePositionConverter.CurrentCoordinates = cords;
  318. if (BitmapUtility.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
  319. {
  320. BitmapUtility.MouseController.RecordMouseMovementChange(cords);
  321. }
  322. else
  323. {
  324. BitmapUtility.MouseController.MouseMoved(cords);
  325. }
  326. }
  327. private void ExecuteColorPicker()
  328. {
  329. if (Mouse.LeftButton == MouseButtonState.Pressed)
  330. {
  331. using (var bitmap = new System.Drawing.Bitmap(1, 1))
  332. {
  333. using (var graphics = System.Drawing.Graphics.FromImage(bitmap))
  334. {
  335. graphics.CopyFromScreen(MousePositionConverter.GetCursorPosition(), new System.Drawing.Point(0, 0), new System.Drawing.Size(1, 1));
  336. }
  337. var color = bitmap.GetPixel(0, 0);
  338. PrimaryColor = Color.FromArgb(color.A, color.R, color.G, color.B);
  339. }
  340. }
  341. }
  342. /// <summary>
  343. /// Generates new Layer and sets it as active one
  344. /// </summary>
  345. /// <param name="parameter"></param>
  346. public void GenerateDrawArea(object parameter)
  347. {
  348. NewFileDialog newFile = new NewFileDialog();
  349. if (newFile.ShowDialog())
  350. {
  351. NewDocument(newFile.Width, newFile.Height);
  352. }
  353. }
  354. #region SaveFile
  355. /// <summary>
  356. /// Generates export dialog or saves directly if save data is known.
  357. /// </summary>
  358. /// <param name="parameter"></param>
  359. private void SaveFile(object parameter)
  360. {
  361. WriteableBitmap bitmap = BitmapUtility.GetCombinedLayersBitmap();
  362. if (Exporter.SavePath == null || (string)parameter == "AsNew")
  363. {
  364. Exporter.Export(FileType.PNG, bitmap, new Size(bitmap.PixelWidth, bitmap.PixelHeight));
  365. }
  366. else
  367. {
  368. Exporter.ExportWithoutDialog(FileType.PNG, bitmap);
  369. }
  370. }
  371. /// <summary>
  372. /// Returns true if file save is possible.
  373. /// </summary>
  374. /// <param name="property"></param>
  375. /// <returns></returns>
  376. private bool CanSave(object property)
  377. {
  378. return BitmapUtility.ActiveLayer != null;
  379. }
  380. #endregion
  381. /// <summary>
  382. /// Opens file from path.
  383. /// </summary>
  384. /// <param name="parameter"></param>
  385. public void OpenFile(object parameter)
  386. {
  387. ImportFileDialog dialog = new ImportFileDialog();
  388. if (dialog.ShowDialog())
  389. {
  390. NewDocument(dialog.FileWidth, dialog.FileHeight);
  391. BitmapUtility.ActiveLayer.LayerBitmap = Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight);
  392. }
  393. }
  394. private void NewDocument(int width, int height)
  395. {
  396. BitmapUtility.Layers.Clear();
  397. BitmapUtility.AddNewLayer("Base Layer", width, height, true);
  398. BitmapUtility.PreviewLayer = null;
  399. }
  400. /// <summary>
  401. /// For now, shows not implemented info, lol.
  402. /// </summary>
  403. /// <param name="parameter"></param>
  404. public void RecenterZoombox(object parameter)
  405. {
  406. MessageBox.Show("This feature is not implemented yet.", "Feature not implemented", MessageBoxButton.OK, MessageBoxImage.Information);
  407. }
  408. public void NewLayer(object parameter)
  409. {
  410. BitmapUtility.AddNewLayer($"New Layer {BitmapUtility.Layers.Count}", BitmapUtility.Layers[0].Width, BitmapUtility.Layers[0].Height);
  411. }
  412. public bool CanCreateNewLayer(object parameter)
  413. {
  414. return BitmapUtility.Layers.Count > 0;
  415. }
  416. }
  417. }