ToolsViewModel.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. using System.Collections.Generic;
  2. using System.Collections.ObjectModel;
  3. using System.Linq;
  4. using Avalonia.Input;
  5. using Microsoft.Extensions.DependencyInjection;
  6. using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
  7. using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Evaluators;
  8. using PixiEditor.AvaloniaUI.Models.Controllers;
  9. using PixiEditor.AvaloniaUI.Models.Events;
  10. using PixiEditor.AvaloniaUI.Models.Handlers;
  11. using PixiEditor.AvaloniaUI.Models.Preferences;
  12. using PixiEditor.AvaloniaUI.ViewModels.Document;
  13. using PixiEditor.AvaloniaUI.ViewModels.Tools;
  14. using PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
  15. using PixiEditor.AvaloniaUI.ViewModels.Tools.ToolSettings.Toolbars;
  16. using PixiEditor.DrawingApi.Core.Numerics;
  17. using PixiEditor.Extensions.Common.UserPreferences;
  18. namespace PixiEditor.AvaloniaUI.ViewModels.SubViewModels;
  19. #nullable enable
  20. [Command.Group("PixiEditor.Tools", "TOOLS")]
  21. internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
  22. {
  23. private RightClickMode rightClickMode;
  24. public ZoomToolViewModel? ZoomTool => GetTool<ZoomToolViewModel>();
  25. public IToolHandler? LastActionTool { get; private set; }
  26. public RightClickMode RightClickMode
  27. {
  28. get => rightClickMode;
  29. set
  30. {
  31. if (SetProperty(ref rightClickMode, value))
  32. {
  33. IPreferences.Current.UpdatePreference(nameof(RightClickMode), value);
  34. }
  35. }
  36. }
  37. public bool EnableSharedToolbar
  38. {
  39. get => IPreferences.Current.GetPreference<bool>(nameof(EnableSharedToolbar));
  40. set
  41. {
  42. if (EnableSharedToolbar == value)
  43. {
  44. return;
  45. }
  46. IPreferences.Current.UpdatePreference(nameof(EnableSharedToolbar), value);
  47. OnPropertyChanged(nameof(EnableSharedToolbar));
  48. }
  49. }
  50. private Cursor? toolCursor;
  51. public Cursor? ToolCursor
  52. {
  53. get => toolCursor;
  54. set => SetProperty(ref toolCursor, value);
  55. }
  56. public BasicToolbar? ActiveBasicToolbar
  57. {
  58. get => ActiveTool?.Toolbar as BasicToolbar;
  59. }
  60. private IToolHandler? activeTool;
  61. public IToolHandler? ActiveTool
  62. {
  63. get => activeTool;
  64. private set
  65. {
  66. SetProperty(ref activeTool, value);
  67. OnPropertyChanged(nameof(ActiveBasicToolbar));
  68. }
  69. }
  70. ICollection<IToolHandler> IToolsHandler.ToolSet => ToolSet;
  71. public ObservableCollection<IToolHandler> ToolSet { get; private set; }
  72. public event EventHandler<SelectedToolEventArgs>? SelectedToolChanged;
  73. private bool shiftIsDown;
  74. private bool ctrlIsDown;
  75. private bool altIsDown;
  76. private ToolViewModel _preTransientTool;
  77. public ToolsViewModel(ViewModelMain owner)
  78. : base(owner)
  79. {
  80. rightClickMode = IPreferences.Current.GetPreference<RightClickMode>(nameof(RightClickMode));
  81. }
  82. public void SetupTools(IServiceProvider services)
  83. {
  84. ToolSet = new ObservableCollection<IToolHandler>(services.GetServices<IToolHandler>());
  85. }
  86. public void SetupToolsTooltipShortcuts(IServiceProvider services)
  87. {
  88. foreach (ToolViewModel tool in ToolSet!)
  89. {
  90. tool.Shortcut = Owner.ShortcutController.GetToolShortcut(tool.GetType());
  91. }
  92. }
  93. public T? GetTool<T>()
  94. where T : IToolHandler
  95. {
  96. return (T?)ToolSet?.Where(static tool => tool is T).FirstOrDefault();
  97. }
  98. public void SetActiveTool<T>(bool transient)
  99. where T : IToolHandler
  100. {
  101. SetActiveTool(typeof(T), transient);
  102. }
  103. [Command.Basic("PixiEditor.Tools.ApplyTransform", "APPLY_TRANSFORM", "", Key = Key.Enter)]
  104. public void ApplyTransform()
  105. {
  106. DocumentViewModel? doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
  107. if (doc is null)
  108. return;
  109. doc.EventInlet.OnApplyTransform();
  110. }
  111. [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
  112. public void SetActiveTool(ToolViewModel tool)
  113. {
  114. SetActiveTool(tool, false);
  115. }
  116. public void SetActiveTool(IToolHandler tool, bool transient)
  117. {
  118. if(Owner.DocumentManagerSubViewModel.ActiveDocument is { PointerDragChangeInProgress: true }) return;
  119. if (ActiveTool == tool)
  120. {
  121. ActiveTool.IsTransient = transient;
  122. return;
  123. }
  124. ActiveTool?.OnDeselecting();
  125. if (!tool.Toolbar.SettingsGenerated)
  126. tool.Toolbar.GenerateSettings();
  127. if (ActiveTool != null) ActiveTool.IsTransient = false;
  128. bool shareToolbar = EnableSharedToolbar;
  129. if (ActiveTool is not null)
  130. {
  131. ActiveTool.IsActive = false;
  132. if (shareToolbar)
  133. ActiveTool.Toolbar.SaveToolbarSettings();
  134. }
  135. LastActionTool = ActiveTool;
  136. ActiveTool = tool;
  137. if (shareToolbar)
  138. {
  139. ActiveTool.Toolbar.LoadSharedSettings();
  140. }
  141. if (LastActionTool != ActiveTool)
  142. SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(LastActionTool, ActiveTool));
  143. //update old tool
  144. LastActionTool?.ModifierKeyChanged(false, false, false);
  145. //update new tool
  146. ActiveTool.ModifierKeyChanged(ctrlIsDown, shiftIsDown, altIsDown);
  147. ActiveTool.OnSelected();
  148. tool.IsActive = true;
  149. ActiveTool.IsTransient = transient;
  150. SetToolCursor(tool.GetType());
  151. if (Owner.StylusSubViewModel != null)
  152. {
  153. Owner.StylusSubViewModel.ToolSetByStylus = false;
  154. }
  155. }
  156. public void SetTool(object parameter)
  157. {
  158. if (parameter is Type type)
  159. {
  160. SetActiveTool(type, false);
  161. return;
  162. }
  163. ToolViewModel tool = (ToolViewModel)parameter;
  164. SetActiveTool(tool.GetType(), false);
  165. }
  166. [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "INCREASE_TOOL_SIZE", "INCREASE_TOOL_SIZE", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemCloseBrackets)]
  167. [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "DECREASE_TOOL_SIZE", "DECREASE_TOOL_SIZE", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemOpenBrackets)]
  168. public void ChangeToolSize(int increment)
  169. {
  170. if (ActiveTool?.Toolbar is not BasicToolbar toolbar)
  171. return;
  172. int newSize = toolbar.ToolSize + increment;
  173. if (newSize > 0)
  174. toolbar.ToolSize = newSize;
  175. }
  176. [Evaluator.CanExecute("PixiEditor.Tools.CanChangeToolSize")]
  177. public bool CanChangeToolSize() => Owner.ToolsSubViewModel.ActiveTool?.Toolbar is BasicToolbar
  178. && Owner.ToolsSubViewModel.ActiveTool is not PenToolViewModel
  179. {
  180. PixelPerfectEnabled: true
  181. };
  182. public void SetActiveTool(Type toolType, bool transient)
  183. {
  184. if (!typeof(ToolViewModel).IsAssignableFrom(toolType))
  185. throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}");
  186. IToolHandler foundTool = ToolSet!.First(x => x.GetType().IsAssignableFrom(toolType));
  187. SetActiveTool(foundTool, transient);
  188. }
  189. public void RestorePreviousTool()
  190. {
  191. if (LastActionTool != null)
  192. {
  193. SetActiveTool(LastActionTool, false);
  194. }
  195. else
  196. {
  197. SetActiveTool<PenToolViewModel>(false);
  198. }
  199. }
  200. private void SetToolCursor(Type tool)
  201. {
  202. if (tool is not null)
  203. {
  204. ToolCursor = ActiveTool?.Cursor;
  205. }
  206. else
  207. {
  208. ToolCursor = new Cursor(StandardCursorType.Arrow);
  209. }
  210. }
  211. public void HandleToolRepeatShortcutDown()
  212. {
  213. if(ActiveTool == null) return;
  214. if (ActiveTool is null or { IsTransient: false })
  215. {
  216. ShortcutController.BlockShortcutExecution("ShortcutDown");
  217. ActiveTool.IsTransient = true;
  218. }
  219. }
  220. public void HandleToolShortcutUp()
  221. {
  222. if(ActiveTool == null) return;
  223. if (ActiveTool.IsTransient && LastActionTool is { } tool)
  224. SetActiveTool(tool, false);
  225. ShortcutController.UnblockShortcutExecution("ShortcutDown");
  226. }
  227. public void UseToolEventInlet(VecD canvasPos, MouseButton button)
  228. {
  229. if (ActiveTool == null) return;
  230. ActiveTool.UsedWith = button;
  231. if (ActiveTool.StopsLinkedToolOnUse)
  232. {
  233. ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
  234. }
  235. ActiveTool.UseTool(canvasPos);
  236. }
  237. public void ConvertedKeyDownInlet(FilteredKeyEventArgs args)
  238. {
  239. ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
  240. }
  241. public void ConvertedKeyUpInlet(FilteredKeyEventArgs args)
  242. {
  243. ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
  244. }
  245. }