CropControl.axaml.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Input;
  4. using PicView.Avalonia.ViewModels;
  5. namespace PicView.Avalonia.Views.UC;
  6. public partial class CropControl : UserControl
  7. {
  8. private Point _dragStart;
  9. private bool _isDragging;
  10. private bool _isResizing;
  11. private Rect _originalRect;
  12. private Point _resizeStart;
  13. public CropControl()
  14. {
  15. InitializeComponent();
  16. Loaded += delegate
  17. {
  18. InitializeLayout();
  19. MainRectangle.PointerPressed += OnPointerPressed;
  20. MainRectangle.PointerReleased += OnPointerReleased;
  21. MainRectangle.PointerMoved += OnPointerMoved;
  22. TopLeftButton.PointerPressed += OnResizePointerPressed;
  23. TopRightButton.PointerPressed += OnResizePointerPressed;
  24. BottomLeftButton.PointerPressed += OnResizePointerPressed;
  25. BottomRightButton.PointerPressed += OnResizePointerPressed;
  26. LeftMiddleButton.PointerPressed += OnResizePointerPressed;
  27. RightMiddleButton.PointerPressed += OnResizePointerPressed;
  28. TopMiddleButton.PointerPressed += OnResizePointerPressed;
  29. BottomMiddleButton.PointerPressed += OnResizePointerPressed;
  30. TopLeftButton.PointerMoved += (_, e) => ResizeTopLeft(e);
  31. TopRightButton.PointerMoved += (_, e) => ResizeTopRight(e);
  32. BottomLeftButton.PointerMoved += (_, e) => ResizeBottomLeft(e);
  33. BottomRightButton.PointerMoved += (_, e) => ResizeBottomRight(e);
  34. LeftMiddleButton.PointerMoved += (_, e) => ResizeLeftMiddle(e);
  35. RightMiddleButton.PointerMoved += (_, e) => ResizeRightMiddle(e);
  36. TopMiddleButton.PointerMoved += (_, e) => ResizeTopMiddle(e);
  37. BottomMiddleButton.PointerMoved += (_, e) => ResizeBottomMiddle(e);
  38. TopLeftButton.PointerReleased += OnResizePointerReleased;
  39. TopRightButton.PointerReleased += OnResizePointerReleased;
  40. BottomLeftButton.PointerReleased += OnResizePointerReleased;
  41. BottomRightButton.PointerReleased += OnResizePointerReleased;
  42. LeftMiddleButton.PointerReleased += OnResizePointerReleased;
  43. RightMiddleButton.PointerReleased += OnResizePointerReleased;
  44. TopMiddleButton.PointerReleased += OnResizePointerReleased;
  45. BottomMiddleButton.PointerReleased += OnResizePointerReleased;
  46. LostFocus += delegate
  47. {
  48. _isResizing = false;
  49. _isDragging = false;
  50. };
  51. };
  52. }
  53. private void InitializeLayout()
  54. {
  55. if (DataContext is not ImageCropperViewModel vm)
  56. {
  57. return;
  58. }
  59. // Ensure image dimensions are valid before proceeding
  60. if (vm.ImageWidth <= 0 || vm.ImageHeight <= 0)
  61. {
  62. return;
  63. }
  64. // Set initial width and height for the crop rectangle
  65. vm.SelectionWidth = 200;
  66. vm.SelectionHeight = 200;
  67. // Calculate centered position
  68. vm.SelectionX = (vm.ImageWidth - vm.SelectionWidth) / 2;
  69. vm.SelectionY = (vm.ImageHeight - vm.SelectionHeight) / 2;
  70. // Apply the calculated position to the MainRectangle
  71. Canvas.SetLeft(MainRectangle, vm.SelectionX);
  72. Canvas.SetTop(MainRectangle, vm.SelectionY);
  73. // Set buttons positions based on MainRectangle's position
  74. UpdateButtonPositions(vm.SelectionX, vm.SelectionY, vm.SelectionWidth, vm.SelectionHeight);
  75. try
  76. {
  77. UpdateSurroundingRectangles();
  78. }
  79. catch (Exception e)
  80. {
  81. #if DEBUG
  82. Console.WriteLine(e);
  83. #endif
  84. }
  85. }
  86. private void OnResizePointerPressed(object? sender, PointerPressedEventArgs e)
  87. {
  88. if (DataContext is not ImageCropperViewModel vm || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
  89. {
  90. return;
  91. }
  92. // Capture the start position of the mouse and the initial size/position of the rectangle
  93. _resizeStart = e.GetPosition(RootCanvas);
  94. _originalRect = new Rect(Canvas.GetLeft(MainRectangle), Canvas.GetTop(MainRectangle), vm.SelectionWidth,
  95. vm.SelectionHeight);
  96. _isResizing = true;
  97. }
  98. private void UpdateButtonPositions(double selectionX, double selectionY, double selectionWidth,
  99. double selectionHeight)
  100. {
  101. try
  102. {
  103. // Get the bounds of the RootCanvas (the control container)
  104. const int rootCanvasLeft = 0;
  105. const int rootCanvasTop = 0;
  106. var rootCanvasRight = RootCanvas.Bounds.Width;
  107. var rootCanvasBottom = RootCanvas.Bounds.Height;
  108. // Calculate the positions for each button
  109. var topLeftX = selectionX - TopLeftButton.Width / 2;
  110. var topLeftY = selectionY - TopLeftButton.Height / 2;
  111. var topRightX = selectionX + selectionWidth - TopRightButton.Width / 2;
  112. var topRightY = selectionY - TopRightButton.Height / 2;
  113. var topMiddleX = selectionX + selectionWidth / 2 - TopMiddleButton.Width / 2;
  114. var topMiddleY = selectionY - TopMiddleButton.Height / 2;
  115. var bottomLeftX = selectionX - BottomLeftButton.Width / 2;
  116. var bottomLeftY = selectionY + selectionHeight - BottomLeftButton.Height / 2;
  117. var bottomRightX = selectionX + selectionWidth - BottomRightButton.Width / 2;
  118. var bottomRightY = selectionY + selectionHeight - BottomRightButton.Height / 2;
  119. var bottomMiddleX = selectionX + selectionWidth / 2 - BottomMiddleButton.Width / 2;
  120. var bottomMiddleY = selectionY + selectionHeight - BottomMiddleButton.Height / 2;
  121. var leftMiddleX = selectionX - LeftMiddleButton.Width / 2;
  122. var leftMiddleY = selectionY + selectionHeight / 2 - LeftMiddleButton.Height / 2;
  123. var rightMiddleX = selectionX + selectionWidth - RightMiddleButton.Width / 2;
  124. var rightMiddleY = selectionY + selectionHeight / 2 - RightMiddleButton.Height / 2;
  125. // Ensure buttons stay within RootCanvas bounds (by clamping positions)
  126. topLeftX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - TopLeftButton.Width, topLeftX));
  127. topLeftY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - TopLeftButton.Height, topLeftY));
  128. topRightX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - TopRightButton.Width, topRightX));
  129. topRightY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - TopRightButton.Height, topRightY));
  130. topMiddleX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - TopMiddleButton.Width, topMiddleX));
  131. topMiddleY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - TopMiddleButton.Height, topMiddleY));
  132. bottomLeftX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - BottomLeftButton.Width, bottomLeftX));
  133. bottomLeftY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - BottomLeftButton.Height, bottomLeftY));
  134. bottomRightX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - BottomRightButton.Width, bottomRightX));
  135. bottomRightY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - BottomRightButton.Height, bottomRightY));
  136. bottomMiddleX = Math.Max(rootCanvasLeft,
  137. Math.Min(rootCanvasRight - BottomMiddleButton.Width, bottomMiddleX));
  138. bottomMiddleY = Math.Max(rootCanvasTop,
  139. Math.Min(rootCanvasBottom - BottomMiddleButton.Height, bottomMiddleY));
  140. leftMiddleX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - LeftMiddleButton.Width, leftMiddleX));
  141. leftMiddleY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - LeftMiddleButton.Height, leftMiddleY));
  142. rightMiddleX = Math.Max(rootCanvasLeft, Math.Min(rootCanvasRight - RightMiddleButton.Width, rightMiddleX));
  143. rightMiddleY = Math.Max(rootCanvasTop, Math.Min(rootCanvasBottom - RightMiddleButton.Height, rightMiddleY));
  144. // Set the final button positions
  145. Canvas.SetLeft(TopLeftButton, topLeftX);
  146. Canvas.SetTop(TopLeftButton, topLeftY);
  147. Canvas.SetLeft(TopRightButton, topRightX);
  148. Canvas.SetTop(TopRightButton, topRightY);
  149. Canvas.SetLeft(TopMiddleButton, topMiddleX);
  150. Canvas.SetTop(TopMiddleButton, topMiddleY);
  151. Canvas.SetLeft(BottomLeftButton, bottomLeftX);
  152. Canvas.SetTop(BottomLeftButton, bottomLeftY);
  153. Canvas.SetLeft(BottomRightButton, bottomRightX);
  154. Canvas.SetTop(BottomRightButton, bottomRightY);
  155. Canvas.SetLeft(BottomMiddleButton, bottomMiddleX);
  156. Canvas.SetTop(BottomMiddleButton, bottomMiddleY);
  157. Canvas.SetLeft(LeftMiddleButton, leftMiddleX);
  158. Canvas.SetTop(LeftMiddleButton, leftMiddleY);
  159. Canvas.SetLeft(RightMiddleButton, rightMiddleX);
  160. Canvas.SetTop(RightMiddleButton, rightMiddleY);
  161. }
  162. catch (Exception e)
  163. {
  164. #if DEBUG
  165. Console.WriteLine(e);
  166. #endif
  167. }
  168. }
  169. private void UpdateSurroundingRectangles()
  170. {
  171. if (DataContext is not ImageCropperViewModel vm)
  172. {
  173. return;
  174. }
  175. // Converting to int fixes black border
  176. var left = Convert.ToInt32(Canvas.GetLeft(MainRectangle));
  177. var top = Convert.ToInt32(Canvas.GetTop(MainRectangle));
  178. var right = Convert.ToInt32(left + vm.SelectionWidth);
  179. var bottom = Convert.ToInt32(top + vm.SelectionHeight);
  180. // Calculate the positions and sizes for the surrounding rectangles
  181. // Top Rectangle (above MainRectangle)
  182. TopRectangle.Width = vm.ImageWidth;
  183. TopRectangle.Height = top < 0 ? 0 : top;
  184. Canvas.SetTop(TopRectangle, 0);
  185. // Bottom Rectangle (below MainRectangle)
  186. BottomRectangle.Width = vm.ImageWidth;
  187. var newBottomRectangleHeight = vm.ImageHeight - bottom < 0 ? 0 : vm.ImageHeight - bottom;
  188. BottomRectangle.Height = newBottomRectangleHeight;
  189. Canvas.SetTop(BottomRectangle, bottom);
  190. // Left Rectangle (left of MainRectangle)
  191. LeftRectangle.Width = left < 0 ? 0 : left;
  192. LeftRectangle.Height = vm.SelectionHeight;
  193. Canvas.SetLeft(LeftRectangle, 0);
  194. Canvas.SetTop(LeftRectangle, top);
  195. // Right Rectangle (right of MainRectangle)
  196. var newRightRectangleWidth = vm.ImageWidth - right < 0 ? 0 : vm.ImageWidth - right;
  197. RightRectangle.Width = newRightRectangleWidth;
  198. RightRectangle.Height = vm.SelectionHeight;
  199. Canvas.SetLeft(RightRectangle, right);
  200. Canvas.SetTop(RightRectangle, top);
  201. }
  202. private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
  203. {
  204. if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
  205. {
  206. return;
  207. }
  208. if (DataContext is not ImageCropperViewModel vm)
  209. {
  210. return;
  211. }
  212. _dragStart = e.GetPosition(RootCanvas); // Make sure to get position relative to RootCanvas
  213. // Get current left and top values; ensure they are initialized
  214. var currentLeft = Canvas.GetLeft(MainRectangle);
  215. var currentTop = Canvas.GetTop(MainRectangle);
  216. // Set default values if NaN
  217. if (double.IsNaN(currentLeft))
  218. {
  219. currentLeft = 0;
  220. }
  221. if (double.IsNaN(currentTop))
  222. {
  223. currentTop = 0;
  224. }
  225. _originalRect = new Rect(currentLeft, currentTop, vm.SelectionWidth, vm.SelectionHeight);
  226. _isDragging = true;
  227. }
  228. private void OnPointerMoved(object? sender, PointerEventArgs e)
  229. {
  230. if (DataContext is not ImageCropperViewModel vm)
  231. {
  232. return;
  233. }
  234. if (!_isDragging)
  235. {
  236. return;
  237. }
  238. var currentPos = e.GetPosition(RootCanvas); // Ensure it's relative to RootCanvas
  239. var delta = currentPos - _dragStart;
  240. // Calculate new left and top positions, ensure _originalRect is valid
  241. var newLeft = _originalRect.X + delta.X;
  242. var newTop = _originalRect.Y + delta.Y;
  243. // Clamp the newLeft and newTop values to keep the rectangle within bounds
  244. newLeft = Math.Max(0, Math.Min(vm.ImageWidth - vm.SelectionWidth, newLeft));
  245. newTop = Math.Max(0, Math.Min(vm.ImageHeight - vm.SelectionHeight, newTop));
  246. // Only proceed if new positions are valid (i.e., not NaN)
  247. if (double.IsNaN(newLeft) || double.IsNaN(newTop))
  248. {
  249. return;
  250. }
  251. // Update the main rectangle's position
  252. Canvas.SetLeft(MainRectangle, newLeft);
  253. Canvas.SetTop(MainRectangle, newTop);
  254. // Update view model values
  255. vm.SelectionX = newLeft;
  256. vm.SelectionY = newTop;
  257. // Update the surrounding rectangles to fill the space
  258. try
  259. {
  260. UpdateSurroundingRectangles();
  261. UpdateButtonPositions(newLeft, newTop, vm.SelectionWidth, vm.SelectionHeight);
  262. }
  263. catch (Exception exception)
  264. {
  265. #if DEBUG
  266. Console.WriteLine(exception);
  267. #endif
  268. }
  269. }
  270. private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
  271. {
  272. _isDragging = false;
  273. }
  274. private void ResizeTopLeft(PointerEventArgs e)
  275. {
  276. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  277. {
  278. return;
  279. }
  280. var currentPos = e.GetPosition(RootCanvas);
  281. var delta = currentPos - _resizeStart;
  282. // Calculate the new width and height based on the drag delta
  283. var newWidth = _originalRect.Width - delta.X;
  284. var newHeight = _originalRect.Height - delta.Y;
  285. // Ensure the rectangle stays within the canvas bounds
  286. var newLeft = Math.Max(_originalRect.X + delta.X, 0);
  287. var newTop = Math.Max(_originalRect.Y + delta.Y, 0);
  288. // Constrain the new width and height to not exceed the bounds
  289. newWidth = Math.Max(newWidth, 1); // Prevent width from becoming 0 or negative
  290. newHeight = Math.Max(newHeight, 1); // Prevent height from becoming 0 or negative
  291. // Ensure the right and bottom edges don't go beyond the canvas
  292. if (newLeft + newWidth > vm.ImageWidth)
  293. {
  294. newWidth = vm.ImageWidth - newLeft;
  295. }
  296. if (newTop + newHeight > vm.ImageHeight)
  297. {
  298. newHeight = vm.ImageHeight - newTop;
  299. }
  300. if (vm.SelectionX is 0 && vm.SelectionY is 0 && newLeft is 0 && newTop is 0)
  301. {
  302. return;
  303. }
  304. // Apply the new size and position
  305. vm.SelectionX = newLeft;
  306. vm.SelectionY = newTop;
  307. vm.SelectionWidth = newWidth;
  308. vm.SelectionHeight = newHeight;
  309. Canvas.SetLeft(MainRectangle, newLeft);
  310. Canvas.SetTop(MainRectangle, newTop);
  311. UpdateButtonPositions(vm.SelectionX, vm.SelectionY, vm.SelectionWidth, vm.SelectionHeight);
  312. UpdateSurroundingRectangles();
  313. }
  314. private void ResizeTopRight(PointerEventArgs e)
  315. {
  316. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  317. {
  318. return;
  319. }
  320. var currentPos = e.GetPosition(RootCanvas);
  321. var delta = currentPos - _resizeStart;
  322. // Calculate new width and height
  323. var newWidth = _originalRect.Width + delta.X;
  324. var newHeight = _originalRect.Height - delta.Y;
  325. var newY = _originalRect.Y + delta.Y;
  326. // Ensure the width doesn't exceed the canvas' right edge
  327. if (_originalRect.X + newWidth > vm.ImageWidth)
  328. {
  329. newWidth = vm.ImageWidth - _originalRect.X;
  330. }
  331. // Ensure the top doesn't move above the top edge of the canvas
  332. if (newY < 0)
  333. {
  334. newHeight = _originalRect.Height + _originalRect.Y; // Shrink height by the amount moved up
  335. newY = 0;
  336. }
  337. // Prevent the height from becoming too small
  338. newHeight = Math.Max(newHeight, 1);
  339. // Apply the new size and position
  340. vm.SelectionY = newY;
  341. vm.SelectionWidth = newWidth;
  342. vm.SelectionHeight = newHeight;
  343. Canvas.SetLeft(MainRectangle, _originalRect.X);
  344. Canvas.SetTop(MainRectangle, newY);
  345. UpdateButtonPositions(_originalRect.X, newY, newWidth, newHeight);
  346. UpdateSurroundingRectangles();
  347. }
  348. private void ResizeBottomLeft(PointerEventArgs e)
  349. {
  350. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  351. {
  352. return;
  353. }
  354. var currentPos = e.GetPosition(RootCanvas);
  355. var delta = currentPos - _resizeStart;
  356. var newWidth = _originalRect.Width - delta.X;
  357. var newHeight = _originalRect.Height + delta.Y;
  358. var newX = _originalRect.X + delta.X;
  359. // Ensure the left doesn't move beyond the left edge
  360. if (newX < 0)
  361. {
  362. newWidth = _originalRect.Width + _originalRect.X; // Shrink width by the amount moved left
  363. newX = 0;
  364. }
  365. // Ensure the height doesn't exceed the canvas' bottom edge
  366. if (_originalRect.Y + newHeight > vm.ImageHeight)
  367. {
  368. newHeight = vm.ImageHeight - _originalRect.Y;
  369. }
  370. // Prevent the width and height from becoming too small
  371. newWidth = Math.Max(newWidth, 1);
  372. newHeight = Math.Max(newHeight, 1);
  373. // Apply the new size and position
  374. vm.SelectionX = newX;
  375. vm.SelectionWidth = newWidth;
  376. vm.SelectionHeight = newHeight;
  377. Canvas.SetLeft(MainRectangle, newX);
  378. Canvas.SetTop(MainRectangle, _originalRect.Y);
  379. UpdateButtonPositions(newX, _originalRect.Y, newWidth, newHeight);
  380. UpdateSurroundingRectangles();
  381. }
  382. private void ResizeBottomRight(PointerEventArgs e)
  383. {
  384. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  385. {
  386. return;
  387. }
  388. var currentPos = e.GetPosition(RootCanvas);
  389. var delta = currentPos - _resizeStart;
  390. // Calculate the new width and height based on the drag delta
  391. var newWidth = _originalRect.Width + delta.X;
  392. var newHeight = _originalRect.Height + delta.Y;
  393. // Ensure the new width and height do not exceed the image bounds
  394. var newRight = _originalRect.X + newWidth;
  395. var newBottom = _originalRect.Y + newHeight;
  396. if (newRight > vm.ImageWidth)
  397. {
  398. newWidth = vm.ImageWidth - _originalRect.X;
  399. }
  400. if (newBottom > vm.ImageHeight)
  401. {
  402. newHeight = vm.ImageHeight - _originalRect.Y;
  403. }
  404. // Constrain the minimum size
  405. newWidth = Math.Max(newWidth, 1);
  406. newHeight = Math.Max(newHeight, 1);
  407. // Apply the new width and height
  408. vm.SelectionWidth = newWidth;
  409. vm.SelectionHeight = newHeight;
  410. UpdateButtonPositions(vm.SelectionX, vm.SelectionY, vm.SelectionWidth, vm.SelectionHeight);
  411. UpdateSurroundingRectangles();
  412. }
  413. private void ResizeLeftMiddle(PointerEventArgs e)
  414. {
  415. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  416. {
  417. return;
  418. }
  419. // Get the current mouse position relative to RootCanvas
  420. var currentPos = e.GetPosition(RootCanvas);
  421. var delta = currentPos - _resizeStart;
  422. // Calculate the new X position
  423. var newX = _originalRect.X + delta.X;
  424. // Calculate the new width based on horizontal movement
  425. var newWidth = _originalRect.Width - delta.X;
  426. // Ensure that the rectangle doesn't go beyond the right boundary
  427. if (newX < 0)
  428. {
  429. newWidth = _originalRect.Width + _originalRect.X;
  430. newX = 0;
  431. }
  432. // Ensure the new width doesn't go negative or too small
  433. newWidth = Math.Max(newWidth, 1);
  434. // Update the view model with the new X position and width
  435. vm.SelectionX = newX;
  436. vm.SelectionWidth = newWidth;
  437. // Update the rectangle on the canvas
  438. Canvas.SetLeft(MainRectangle, newX);
  439. Canvas.SetTop(MainRectangle, _originalRect.Y);
  440. // Update buttons and surrounding rectangles
  441. UpdateButtonPositions(newX, _originalRect.Y, vm.SelectionWidth, vm.SelectionHeight);
  442. UpdateSurroundingRectangles();
  443. }
  444. private void ResizeRightMiddle(PointerEventArgs e)
  445. {
  446. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  447. {
  448. return;
  449. }
  450. // Get the current mouse position relative to RootCanvas
  451. var currentPos = e.GetPosition(RootCanvas);
  452. var delta = currentPos - _resizeStart;
  453. // Calculate the new width based on horizontal movement
  454. var newWidth = _originalRect.Width + delta.X;
  455. // Ensure the new width doesn't go beyond the bounds of the RootCanvas
  456. if (_originalRect.X + newWidth > vm.ImageWidth)
  457. {
  458. newWidth = vm.ImageWidth - _originalRect.X;
  459. }
  460. // Constrain the width to a minimum value (e.g., 1 to prevent zero or negative width)
  461. newWidth = Math.Max(newWidth, 1);
  462. // Update the view model with the new width
  463. vm.SelectionWidth = newWidth;
  464. // Update the rectangle on the canvas
  465. Canvas.SetLeft(MainRectangle, _originalRect.X);
  466. Canvas.SetTop(MainRectangle, _originalRect.Y);
  467. // Update buttons and surrounding rectangles
  468. UpdateButtonPositions(_originalRect.X, _originalRect.Y, vm.SelectionWidth, vm.SelectionHeight);
  469. UpdateSurroundingRectangles();
  470. }
  471. private void ResizeTopMiddle(PointerEventArgs e)
  472. {
  473. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  474. {
  475. return;
  476. }
  477. // Get the current mouse position relative to RootCanvas
  478. var currentPos = e.GetPosition(RootCanvas);
  479. var delta = currentPos - _resizeStart;
  480. // Calculate the new top position and height
  481. var newTop = _originalRect.Y + delta.Y;
  482. var newHeight = _originalRect.Height - delta.Y;
  483. // Ensure the new height doesn't go negative or too small
  484. if (newHeight < 1)
  485. {
  486. newHeight = 1;
  487. newTop = _originalRect.Y + (_originalRect.Height - 1); // Adjust top to preserve min height
  488. }
  489. // Ensure the top edge doesn't go beyond the canvas bounds
  490. if (newTop < 0)
  491. {
  492. newTop = 0;
  493. newHeight = _originalRect.Height + _originalRect.Y; // Adjust height to compensate
  494. }
  495. // Update the view model with the new top and height
  496. vm.SelectionHeight = newHeight;
  497. vm.SelectionY = newTop;
  498. // Update the rectangle on the canvas
  499. Canvas.SetLeft(MainRectangle, _originalRect.X);
  500. Canvas.SetTop(MainRectangle, newTop);
  501. // Update buttons and surrounding rectangles
  502. UpdateButtonPositions(_originalRect.X, newTop, vm.SelectionWidth, newHeight);
  503. UpdateSurroundingRectangles();
  504. }
  505. private void ResizeBottomMiddle(PointerEventArgs e)
  506. {
  507. if (!_isResizing || DataContext is not ImageCropperViewModel vm)
  508. {
  509. return;
  510. }
  511. // Get the current mouse position relative to RootCanvas
  512. var currentPos = e.GetPosition(RootCanvas);
  513. var delta = currentPos - _resizeStart;
  514. // Calculate the new height based on vertical movement
  515. var newHeight = _originalRect.Height + delta.Y;
  516. // Ensure the new height doesn't go negative or too small
  517. newHeight = Math.Max(newHeight, 1);
  518. // Ensure the bottom edge doesn't go beyond the canvas bounds
  519. if (_originalRect.Y + newHeight > RootCanvas.Bounds.Height)
  520. {
  521. newHeight = RootCanvas.Bounds.Height - _originalRect.Y;
  522. }
  523. // Update the view model with the new height
  524. vm.SelectionHeight = newHeight;
  525. // Update the rectangle on the canvas
  526. Canvas.SetLeft(MainRectangle, _originalRect.X);
  527. Canvas.SetTop(MainRectangle, _originalRect.Y);
  528. // Update buttons and surrounding rectangles
  529. UpdateButtonPositions(_originalRect.X, _originalRect.Y, vm.SelectionWidth, newHeight);
  530. UpdateSurroundingRectangles();
  531. }
  532. private void OnResizePointerReleased(object? sender, PointerReleasedEventArgs e)
  533. {
  534. _isResizing = false;
  535. }
  536. }