|
26 | 26 |
|
27 | 27 | using System; |
28 | 28 | using System.Collections.Generic; |
| 29 | +using System.Collections.ObjectModel; |
| 30 | +using System.Linq; |
29 | 31 | using Cairo; |
30 | 32 | using Gtk; |
| 33 | +using HarfBuzz; |
31 | 34 | using Pinta.Core; |
32 | 35 |
|
33 | 36 | namespace Pinta.Effects; |
@@ -57,12 +60,13 @@ private sealed class ControlPointDrawingInfo |
57 | 60 |
|
58 | 61 | private int channels; |
59 | 62 | //last added control point x; |
60 | | - private int last_cpx; |
| 63 | + private int? last_cpx; |
61 | 64 | private PointI last_mouse_pos = new (0, 0); |
| 65 | + // Keys of existing control points which cannot be overwritten by a new control point. |
| 66 | + private HashSet<int> orig_cps = new (); |
62 | 67 |
|
63 | 68 | //control points for luminosity transfer mode |
64 | 69 | private SortedList<int, int>[] luminosity_cps = null!; // NRT - Set via code flow |
65 | | - //control points for rg transfer mode |
66 | 70 | private SortedList<int, int>[] rgb_cps = null!; |
67 | 71 |
|
68 | 72 | public SortedList<int, int>[] ControlPoints { |
@@ -262,26 +266,67 @@ private void HandleDrawingMotionNotifyEvent (EventControllerMotion controller, E |
262 | 266 | return; |
263 | 267 |
|
264 | 268 | if (controller.GetCurrentEventState () == Gdk.ModifierType.Button1Mask) { |
265 | | - // first and last control point cannot be removed |
266 | | - if (last_cpx != 0 && last_cpx != size - 1) { |
267 | | - foreach (var controlPoints in GetActiveControlPoints ()) { |
268 | | - if (controlPoints.ContainsKey (last_cpx)) |
269 | | - controlPoints.Remove (last_cpx); |
| 269 | + if (last_cpx is not null) { |
| 270 | + // The first and last control points cannot be removed, so also forbid dragging them away. |
| 271 | + if (last_cpx == 0) |
| 272 | + x = 0; |
| 273 | + else if (last_cpx == size - 1) |
| 274 | + x = size - 1; |
| 275 | + else { |
| 276 | + // Remove the old version of the control point being edited. |
| 277 | + foreach (var controlPoints in GetActiveControlPoints ()) { |
| 278 | + if (controlPoints.ContainsKey (last_cpx.Value)) |
| 279 | + controlPoints.Remove (last_cpx.Value); |
| 280 | + } |
270 | 281 | } |
271 | 282 | } |
272 | 283 |
|
273 | | - AddControlPoint (x, y); |
| 284 | + // Don't allow overwriting any of the original control points while dragging. |
| 285 | + if (!orig_cps.Contains (x)) |
| 286 | + AddControlPoint (x, y); |
| 287 | + else |
| 288 | + last_cpx = null; |
274 | 289 | } |
275 | 290 |
|
276 | 291 | InvalidateDrawing (); |
277 | 292 | } |
278 | 293 |
|
| 294 | + /// <summary> |
| 295 | + /// If the provided coordinates are close to an existing control point, snap to the control point's coordinates. |
| 296 | + /// </summary> |
| 297 | + private static bool SnapToControlPointProximity (IEnumerable<SortedList<int, int>> activeControlPoints, ref int x, ref int y) |
| 298 | + { |
| 299 | + foreach (var controlPoints in activeControlPoints) { |
| 300 | + for (int i = 0; i < controlPoints.Count; i++) { |
| 301 | + int cpx = controlPoints.Keys[i]; |
| 302 | + int cpy = size - 1 - (int) controlPoints.Values[i]; |
| 303 | + |
| 304 | + if (CheckControlPointProximity (cpx, cpy, x, y)) { |
| 305 | + x = cpx; |
| 306 | + y = cpy; |
| 307 | + return true; |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + |
| 312 | + return false; |
| 313 | + } |
| 314 | + |
279 | 315 | private void HandleDrawingButtonPressEvent (GestureClick controller, GestureClick.PressedSignalArgs args) |
280 | 316 | { |
281 | 317 | int x = (int) args.X; |
282 | 318 | int y = (int) args.Y; |
283 | 319 |
|
284 | 320 | if (controller.GetCurrentMouseButton () == MouseButton.Left) { |
| 321 | + |
| 322 | + orig_cps.Clear (); |
| 323 | + foreach (var controlPoints in GetActiveControlPoints ()) { |
| 324 | + orig_cps.UnionWith (controlPoints.Keys); |
| 325 | + } |
| 326 | + |
| 327 | + if (SnapToControlPointProximity (GetActiveControlPoints (), ref x, ref y)) |
| 328 | + orig_cps.Remove (x); // Allow dragging the snapped control point. |
| 329 | + |
285 | 330 | AddControlPoint (x, y); |
286 | 331 | } |
287 | 332 |
|
|
0 commit comments