Viewport.axaml.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. using System.Collections.ObjectModel;
  2. using System.ComponentModel;
  3. using System.Windows.Input;
  4. using Avalonia;
  5. using Avalonia.Controls;
  6. using Avalonia.Input;
  7. using Avalonia.Interactivity;
  8. using Avalonia.VisualTree;
  9. using ChunkyImageLib;
  10. using ChunkyImageLib.DataHolders;
  11. using PixiEditor.Helpers;
  12. using PixiEditor.ViewModels;
  13. using PixiEditor.Views.Visuals;
  14. using Drawie.Backend.Core;
  15. using Drawie.Backend.Core.Numerics;
  16. using PixiEditor.Helpers.Behaviours;
  17. using PixiEditor.Helpers.UI;
  18. using PixiEditor.Models.Controllers.InputDevice;
  19. using PixiEditor.Models.DocumentModels;
  20. using PixiEditor.Models.Position;
  21. using Drawie.Numerics;
  22. using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
  23. using PixiEditor.ViewModels.Document;
  24. using PixiEditor.ViewModels.SubViewModels;
  25. using PixiEditor.ViewModels.Tools.Tools;
  26. using PixiEditor.Views.Overlays;
  27. using PixiEditor.Views.Rendering;
  28. using PixiEditor.Zoombox;
  29. using Point = Avalonia.Point;
  30. namespace PixiEditor.Views.Main.ViewportControls;
  31. #nullable enable
  32. internal partial class Viewport : UserControl, INotifyPropertyChanged
  33. {
  34. //TODO: IDK where to write this, but on close zoom level, when I drag line handle, it doesn't update the canvas
  35. public event PropertyChangedEventHandler? PropertyChanged;
  36. public static readonly StyledProperty<bool> FlipXProperty =
  37. AvaloniaProperty.Register<Viewport, bool>(nameof(FlipX), false);
  38. public static readonly StyledProperty<bool> FlipYProperty =
  39. AvaloniaProperty.Register<Viewport, bool>(nameof(FlipY), false);
  40. public static readonly StyledProperty<ZoomboxMode> ZoomModeProperty =
  41. AvaloniaProperty.Register<Viewport, ZoomboxMode>(nameof(ZoomMode), ZoomboxMode.Normal);
  42. public static readonly StyledProperty<DocumentViewModel> DocumentProperty =
  43. AvaloniaProperty.Register<Viewport, DocumentViewModel>(nameof(Document), null);
  44. public static readonly StyledProperty<ICommand> MouseDownCommandProperty =
  45. AvaloniaProperty.Register<Viewport, ICommand>(nameof(MouseDownCommand), null);
  46. public static readonly StyledProperty<ICommand> MouseMoveCommandProperty =
  47. AvaloniaProperty.Register<Viewport, ICommand>(nameof(MouseMoveCommand), null);
  48. public static readonly StyledProperty<ICommand> MouseUpCommandProperty =
  49. AvaloniaProperty.Register<Viewport, ICommand>(nameof(MouseUpCommand), null);
  50. public static readonly StyledProperty<bool> DelayedProperty =
  51. AvaloniaProperty.Register<Viewport, bool>(nameof(Delayed), false);
  52. public static readonly StyledProperty<bool> GridLinesVisibleProperty =
  53. AvaloniaProperty.Register<Viewport, bool>(nameof(GridLinesVisible), false);
  54. public static readonly StyledProperty<double> GridLinesXSizeProperty =
  55. AvaloniaProperty.Register<Viewport, double>(nameof(GridLinesXSize), 1.0);
  56. public static readonly StyledProperty<double> GridLinesYSizeProperty =
  57. AvaloniaProperty.Register<Viewport, double>(nameof(GridLinesYSize), 1.0);
  58. public static readonly StyledProperty<bool> ZoomOutOnClickProperty =
  59. AvaloniaProperty.Register<Viewport, bool>(nameof(ZoomOutOnClick), false);
  60. public static readonly StyledProperty<ExecutionTrigger<double>> ZoomViewportTriggerProperty =
  61. AvaloniaProperty.Register<Viewport, ExecutionTrigger<double>>(nameof(ZoomViewportTrigger), null);
  62. public static readonly StyledProperty<ExecutionTrigger<VecI>> CenterViewportTriggerProperty =
  63. AvaloniaProperty.Register<Viewport, ExecutionTrigger<VecI>>(nameof(CenterViewportTrigger), null);
  64. public static readonly StyledProperty<bool> UseTouchGesturesProperty =
  65. AvaloniaProperty.Register<Viewport, bool>(nameof(UseTouchGestures), false);
  66. public static readonly StyledProperty<ICommand> StylusButtonDownCommandProperty =
  67. AvaloniaProperty.Register<Viewport, ICommand>(nameof(StylusButtonDownCommand), null);
  68. public static readonly StyledProperty<ICommand> StylusButtonUpCommandProperty =
  69. AvaloniaProperty.Register<Viewport, ICommand>(nameof(StylusButtonUpCommand), null);
  70. public static readonly StyledProperty<ICommand> StylusGestureCommandProperty =
  71. AvaloniaProperty.Register<Viewport, ICommand>(nameof(StylusGestureCommand), null);
  72. public static readonly StyledProperty<ICommand> StylusOutOfRangeCommandProperty =
  73. AvaloniaProperty.Register<Viewport, ICommand>(nameof(StylusOutOfRangeCommand), null);
  74. public static readonly StyledProperty<ICommand> MiddleMouseClickedCommandProperty =
  75. AvaloniaProperty.Register<Viewport, ICommand>(nameof(MiddleMouseClickedCommand), null);
  76. public static readonly StyledProperty<ViewportColorChannels> ChannelsProperty =
  77. AvaloniaProperty.Register<Viewport, ViewportColorChannels>(
  78. nameof(Channels));
  79. public static readonly StyledProperty<bool> IsOverCanvasProperty = AvaloniaProperty.Register<Viewport, bool>(
  80. nameof(IsOverCanvas));
  81. public static readonly StyledProperty<SnappingViewModel> SnappingViewModelProperty =
  82. AvaloniaProperty.Register<Viewport, SnappingViewModel>(
  83. nameof(SnappingViewModel));
  84. public static readonly StyledProperty<bool> HighResPreviewProperty =
  85. AvaloniaProperty.Register<Viewport, bool>(nameof(HighResPreview), true);
  86. public SnappingViewModel SnappingViewModel
  87. {
  88. get => GetValue(SnappingViewModelProperty);
  89. set => SetValue(SnappingViewModelProperty, value);
  90. }
  91. public bool IsOverCanvas
  92. {
  93. get => GetValue(IsOverCanvasProperty);
  94. set => SetValue(IsOverCanvasProperty, value);
  95. }
  96. public ICommand? MiddleMouseClickedCommand
  97. {
  98. get => (ICommand?)GetValue(MiddleMouseClickedCommandProperty);
  99. set => SetValue(MiddleMouseClickedCommandProperty, value);
  100. }
  101. public ICommand? StylusOutOfRangeCommand
  102. {
  103. get => (ICommand?)GetValue(StylusOutOfRangeCommandProperty);
  104. set => SetValue(StylusOutOfRangeCommandProperty, value);
  105. }
  106. public ICommand? StylusGestureCommand
  107. {
  108. get => (ICommand?)GetValue(StylusGestureCommandProperty);
  109. set => SetValue(StylusGestureCommandProperty, value);
  110. }
  111. public ICommand? StylusButtonUpCommand
  112. {
  113. get => (ICommand?)GetValue(StylusButtonUpCommandProperty);
  114. set => SetValue(StylusButtonUpCommandProperty, value);
  115. }
  116. public ICommand? StylusButtonDownCommand
  117. {
  118. get => (ICommand?)GetValue(StylusButtonDownCommandProperty);
  119. set => SetValue(StylusButtonDownCommandProperty, value);
  120. }
  121. public bool UseTouchGestures
  122. {
  123. get => (bool)GetValue(UseTouchGesturesProperty);
  124. set => SetValue(UseTouchGesturesProperty, value);
  125. }
  126. public ExecutionTrigger<VecI>? CenterViewportTrigger
  127. {
  128. get => (ExecutionTrigger<VecI>)GetValue(CenterViewportTriggerProperty);
  129. set => SetValue(CenterViewportTriggerProperty, value);
  130. }
  131. public ExecutionTrigger<double>? ZoomViewportTrigger
  132. {
  133. get => (ExecutionTrigger<double>)GetValue(ZoomViewportTriggerProperty);
  134. set => SetValue(ZoomViewportTriggerProperty, value);
  135. }
  136. public bool ZoomOutOnClick
  137. {
  138. get => (bool)GetValue(ZoomOutOnClickProperty);
  139. set => SetValue(ZoomOutOnClickProperty, value);
  140. }
  141. public bool GridLinesVisible
  142. {
  143. get => (bool)GetValue(GridLinesVisibleProperty);
  144. set => SetValue(GridLinesVisibleProperty, value);
  145. }
  146. public double GridLinesXSize
  147. {
  148. get => (double)GetValue(GridLinesXSizeProperty);
  149. set => SetValue(GridLinesXSizeProperty, value);
  150. }
  151. public double GridLinesYSize
  152. {
  153. get => (double)GetValue(GridLinesYSizeProperty);
  154. set => SetValue(GridLinesYSizeProperty, value);
  155. }
  156. public bool Delayed
  157. {
  158. get => (bool)GetValue(DelayedProperty);
  159. set => SetValue(DelayedProperty, value);
  160. }
  161. public ICommand? MouseDownCommand
  162. {
  163. get => (ICommand?)GetValue(MouseDownCommandProperty);
  164. set => SetValue(MouseDownCommandProperty, value);
  165. }
  166. public ICommand? MouseMoveCommand
  167. {
  168. get => (ICommand?)GetValue(MouseMoveCommandProperty);
  169. set => SetValue(MouseMoveCommandProperty, value);
  170. }
  171. public ICommand? MouseUpCommand
  172. {
  173. get => (ICommand?)GetValue(MouseUpCommandProperty);
  174. set => SetValue(MouseUpCommandProperty, value);
  175. }
  176. public DocumentViewModel? Document
  177. {
  178. get => (DocumentViewModel)GetValue(DocumentProperty);
  179. set => SetValue(DocumentProperty, value);
  180. }
  181. public ZoomboxMode ZoomMode
  182. {
  183. get => (ZoomboxMode)GetValue(ZoomModeProperty);
  184. set => SetValue(ZoomModeProperty, value);
  185. }
  186. public bool FlipX
  187. {
  188. get => (bool)GetValue(FlipXProperty);
  189. set => SetValue(FlipXProperty, value);
  190. }
  191. public bool FlipY
  192. {
  193. get => (bool)GetValue(FlipYProperty);
  194. set => SetValue(FlipYProperty, value);
  195. }
  196. public static readonly StyledProperty<bool> HudVisibleProperty = AvaloniaProperty.Register<Viewport, bool>(
  197. nameof(HudVisible), true);
  198. public bool HudVisible
  199. {
  200. get => GetValue(HudVisibleProperty);
  201. set => SetValue(HudVisibleProperty, value);
  202. }
  203. public ViewportColorChannels Channels
  204. {
  205. get => GetValue(ChannelsProperty);
  206. set => SetValue(ChannelsProperty, value);
  207. }
  208. private double angleRadians = 0;
  209. public double AngleRadians
  210. {
  211. get => angleRadians;
  212. set
  213. {
  214. angleRadians = value;
  215. PropertyChanged?.Invoke(this, new(nameof(AngleRadians)));
  216. Document?.Operations.AddOrUpdateViewport(GetLocation());
  217. }
  218. }
  219. private VecD center = new(32, 32);
  220. public VecD Center
  221. {
  222. get => center;
  223. set
  224. {
  225. center = value;
  226. PropertyChanged?.Invoke(this, new(nameof(Center)));
  227. Document?.Operations.AddOrUpdateViewport(GetLocation());
  228. }
  229. }
  230. private VecD realDimensions = new(double.MaxValue, double.MaxValue);
  231. public VecD RealDimensions
  232. {
  233. get => realDimensions;
  234. set
  235. {
  236. ChunkResolution oldRes = CalculateResolution();
  237. realDimensions = value;
  238. ChunkResolution newRes = CalculateResolution();
  239. PropertyChanged?.Invoke(this, new(nameof(RealDimensions)));
  240. Document?.Operations.AddOrUpdateViewport(GetLocation());
  241. }
  242. }
  243. private VecD dimensions = new(64, 64);
  244. public VecD Dimensions
  245. {
  246. get => dimensions;
  247. set
  248. {
  249. ChunkResolution oldRes = CalculateResolution();
  250. dimensions = value;
  251. ChunkResolution newRes = CalculateResolution();
  252. PropertyChanged?.Invoke(this, new(nameof(Dimensions)));
  253. Document?.Operations.AddOrUpdateViewport(GetLocation());
  254. }
  255. }
  256. public static readonly StyledProperty<ObservableCollection<string>> AvailableRenderOutputsProperty =
  257. AvaloniaProperty.Register<Viewport, ObservableCollection<string>>(nameof(AvailableRenderOutputs));
  258. public static readonly StyledProperty<string> ViewportRenderOutputProperty = AvaloniaProperty.Register<Viewport, string>(
  259. nameof(ViewportRenderOutput), "DEFAULT");
  260. public string ViewportRenderOutput
  261. {
  262. get => GetValue(ViewportRenderOutputProperty);
  263. set => SetValue(ViewportRenderOutputProperty, value);
  264. }
  265. public ObservableCollection<Overlay> ActiveOverlays { get; } = new();
  266. public Guid GuidValue { get; } = Guid.NewGuid();
  267. private MouseUpdateController? mouseUpdateController;
  268. private ViewportOverlays builtInOverlays = new();
  269. public static readonly StyledProperty<bool> SnappingEnabledProperty =
  270. AvaloniaProperty.Register<Viewport, bool>("SnappingEnabled");
  271. static Viewport()
  272. {
  273. DocumentProperty.Changed.Subscribe(OnDocumentChange);
  274. ZoomViewportTriggerProperty.Changed.Subscribe(ZoomViewportTriggerChanged);
  275. CenterViewportTriggerProperty.Changed.Subscribe(CenterViewportTriggerChanged);
  276. HighResPreviewProperty.Changed.Subscribe(OnHighResPreviewChanged);
  277. }
  278. public Viewport()
  279. {
  280. InitializeComponent();
  281. builtInOverlays.Init(this);
  282. Scene!.Loaded += OnImageLoaded;
  283. Scene.SizeChanged += OnMainImageSizeChanged;
  284. Loaded += OnLoad;
  285. Unloaded += OnUnload;
  286. Scene.AttachedToVisualTree += OnAttachedToVisualTree;
  287. //TODO: It's weird that I had to do it this way, right click didn't raise Image_MouseUp otherwise.
  288. viewportGrid.AddHandler(PointerReleasedEvent, Image_MouseUp, RoutingStrategies.Tunnel);
  289. viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
  290. Scene.PointerExited += (sender, args) => IsOverCanvas = false;
  291. Scene.PointerEntered += (sender, args) => IsOverCanvas = true;
  292. Scene.ScaleChanged += OnScaleChanged;
  293. }
  294. private void OnScaleChanged(double newScale)
  295. {
  296. SnappingViewModel.SnappingController.SnapDistance = SnappingController.DefaultSnapDistance / newScale;
  297. }
  298. private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
  299. {
  300. TextBoxFocusBehavior.FallbackFocusElement.Focus();
  301. }
  302. public Scene Scene => scene;
  303. public bool HighResPreview
  304. {
  305. get { return (bool)GetValue(HighResPreviewProperty); }
  306. set { SetValue(HighResPreviewProperty, value); }
  307. }
  308. public bool SnappingEnabled
  309. {
  310. get { return (bool)GetValue(SnappingEnabledProperty); }
  311. set { SetValue(SnappingEnabledProperty, value); }
  312. }
  313. public ObservableCollection<string> AvailableRenderOutputs
  314. {
  315. get { return (ObservableCollection<string>)GetValue(AvailableRenderOutputsProperty); }
  316. set { SetValue(AvailableRenderOutputsProperty, value); }
  317. }
  318. private void ForceRefreshFinalImage()
  319. {
  320. Scene.InvalidateVisual();
  321. }
  322. private void OnUnload(object? sender, RoutedEventArgs e)
  323. {
  324. Document?.Operations.RemoveViewport(GuidValue);
  325. mouseUpdateController?.Dispose();
  326. }
  327. private void OnLoad(object? sender, RoutedEventArgs e)
  328. {
  329. Document?.Operations.AddOrUpdateViewport(GetLocation());
  330. mouseUpdateController = new MouseUpdateController(this, Image_MouseMove);
  331. }
  332. private static void OnDocumentChange(AvaloniaPropertyChangedEventArgs<DocumentViewModel> e)
  333. {
  334. Viewport? viewport = (Viewport)e.Sender;
  335. DocumentViewModel? oldDoc = e.OldValue.Value;
  336. DocumentViewModel? newDoc = e.NewValue.Value;
  337. oldDoc?.Operations.RemoveViewport(viewport.GuidValue);
  338. newDoc?.Operations.AddOrUpdateViewport(viewport.GetLocation());
  339. }
  340. private ChunkResolution CalculateResolution()
  341. {
  342. VecD densityVec = Dimensions.Divide(RealDimensions);
  343. double density = Math.Min(densityVec.X, densityVec.Y);
  344. if (density > 8.01)
  345. return ChunkResolution.Eighth;
  346. else if (density > 4.01)
  347. return ChunkResolution.Quarter;
  348. else if (density > 2.01)
  349. return ChunkResolution.Half;
  350. return ChunkResolution.Full;
  351. }
  352. private ViewportInfo GetLocation()
  353. {
  354. return new(AngleRadians, Center, RealDimensions, Dimensions, CalculateResolution(), GuidValue, Delayed,
  355. ForceRefreshFinalImage);
  356. }
  357. private void Image_MouseDown(object? sender, PointerPressedEventArgs e)
  358. {
  359. if (Document is null || e.Source != Scene)
  360. return;
  361. Scene.Focus(NavigationMethod.Pointer);
  362. bool isMiddle = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
  363. HandleMiddleMouse(isMiddle);
  364. if (MouseDownCommand is null)
  365. return;
  366. MouseButton mouseButton = e.GetMouseButton(this);
  367. var pos = e.GetPosition(Scene);
  368. VecD scenePos = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
  369. MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(mouseButton, scenePos, e.KeyModifiers, e.ClickCount);
  370. if (MouseDownCommand.CanExecute(parameter))
  371. MouseDownCommand.Execute(parameter);
  372. }
  373. private void Image_MouseMove(PointerEventArgs e)
  374. {
  375. if (MouseMoveCommand is null)
  376. return;
  377. Point pos = e.GetPosition(Scene);
  378. VecD conv = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
  379. MouseButton mouseButton = e.GetMouseButton(this);
  380. MouseOnCanvasEventArgs parameter = new(mouseButton, conv, e.KeyModifiers, 0);
  381. if (MouseMoveCommand.CanExecute(parameter))
  382. MouseMoveCommand.Execute(parameter);
  383. }
  384. private void Image_MouseUp(object? sender, PointerReleasedEventArgs e)
  385. {
  386. if (MouseUpCommand is null)
  387. return;
  388. Point pos = e.GetPosition(Scene);
  389. VecD conv = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
  390. MouseOnCanvasEventArgs parameter = new(e.InitialPressMouseButton, conv, e.KeyModifiers, 0);
  391. if (MouseUpCommand.CanExecute(parameter))
  392. MouseUpCommand.Execute(parameter);
  393. }
  394. private void CenterZoomboxContent(object? sender, VecI args)
  395. {
  396. scene.CenterContent(args);
  397. }
  398. private void ZoomZoomboxContent(object? sender, double delta)
  399. {
  400. scene.ZoomIntoCenter(delta);
  401. }
  402. private void OnImageLoaded(object sender, EventArgs e)
  403. {
  404. scene.CenterContent();
  405. }
  406. private void OnMainImageSizeChanged(object? sender, SizeChangedEventArgs e)
  407. {
  408. if (scene.Dimensions is { X: 0, Y: 0 }) return;
  409. scene.CenterContent();
  410. scene.ZoomIntoCenter(-1);
  411. scene.ZoomIntoCenter(1); // a bit hacky, but it resets brush overlay properly
  412. }
  413. private void ResetViewportClicked(object sender, RoutedEventArgs e)
  414. {
  415. scene.AngleRadians = 0;
  416. scene.CenterContent(Document.SizeBindable);
  417. }
  418. private static void CenterViewportTriggerChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<VecI>> e)
  419. {
  420. Viewport? viewport = (Viewport)e.Sender;
  421. if (e.OldValue.Value != null)
  422. e.OldValue.Value.Triggered -= viewport.CenterZoomboxContent;
  423. if (e.NewValue.Value != null)
  424. e.NewValue.Value.Triggered += viewport.CenterZoomboxContent;
  425. }
  426. private static void ZoomViewportTriggerChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<double>> e)
  427. {
  428. Viewport? viewport = (Viewport)e.Sender;
  429. if (e.OldValue.Value != null)
  430. e.OldValue.Value.Triggered -= viewport.ZoomZoomboxContent;
  431. if (e.NewValue.Value != null)
  432. e.NewValue.Value.Triggered += viewport.ZoomZoomboxContent;
  433. }
  434. private void HandleMiddleMouse(bool isMiddle)
  435. {
  436. if (MiddleMouseClickedCommand is null)
  437. return;
  438. if (isMiddle && MiddleMouseClickedCommand.CanExecute(null))
  439. MiddleMouseClickedCommand.Execute(null);
  440. }
  441. private static void OnHighResPreviewChanged(AvaloniaPropertyChangedEventArgs<bool> e)
  442. {
  443. Viewport? viewport = (Viewport)e.Sender;
  444. viewport.ForceRefreshFinalImage();
  445. }
  446. private void MenuItem_OnClick(object? sender, PointerReleasedEventArgs e)
  447. {
  448. Scene?.ContextFlyout?.Hide();
  449. }
  450. private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
  451. {
  452. Scene?.ContextFlyout?.Hide();
  453. }
  454. private void Scene_OnContextMenuOpening(object? sender, PointerPressedEventArgs e)
  455. {
  456. if (e.GetMouseButton(this) != MouseButton.Right) return;
  457. ViewportWindowViewModel vm = ((ViewportWindowViewModel)DataContext);
  458. var tools = vm.Owner.Owner.ToolsSubViewModel;
  459. var superSpecialBrightnessTool = tools.RightClickMode == RightClickMode.SecondaryColor &&
  460. tools.ActiveTool is BrightnessToolViewModel;
  461. var superSpecialColorPicker =
  462. tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool is ColorPickerToolViewModel;
  463. if (superSpecialBrightnessTool || superSpecialColorPicker)
  464. {
  465. return;
  466. }
  467. var useContextMenu = vm.Owner.Owner.ToolsSubViewModel.RightClickMode == RightClickMode.ContextMenu;
  468. var usesErase = tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool.IsErasable;
  469. var usesSecondaryColor = tools.RightClickMode == RightClickMode.SecondaryColor && tools.ActiveTool.UsesColor;
  470. if (!useContextMenu && (usesErase || usesSecondaryColor))
  471. {
  472. return;
  473. }
  474. Scene?.ContextFlyout?.ShowAt(Scene);
  475. e.Handled = true;
  476. }
  477. private void RenderOutput_SelectionChanged(object? sender, SelectionChangedEventArgs e)
  478. {
  479. if (e.Source is ComboBox comboBox)
  480. {
  481. if(!comboBox.IsAttachedToVisualTree()) return;
  482. if (e.AddedItems.Count > 0)
  483. {
  484. ViewportRenderOutput = (string)e.AddedItems[0];
  485. }
  486. }
  487. }
  488. }