Merge pull request #6718 from dotCooCoo/fix/issue-6634

Fix #6634: Pen closing drag no longer gives the first anchor symmetric handles
This commit is contained in:
BFlat
2026-11-12 09:08:12 +07:00
committed by GitHub
10 changed files with 309 additions and 61 deletions
+9 -2
View File
@@ -4007,6 +4007,13 @@ an `anchor` or `segment` in the same way. The document does not change and no un
after the new anchor on its subpath are numbered one higher, and a selected segment that the new anchor splits stays
selected as its two halves, with the new anchor as an end of both that moves with them. Undo restores the path and
the selection in one step.
`path.close {id, subpath?, in?, out?, independent?}` closes an open subpath (by default the last open one). `in` places
the first anchor's incoming handle as `path.setHandle` places a handle: a smooth anchor turns its outgoing handle to
stay in line and keeps that handle's length, and a corner keeps its outgoing handle where it is. With
`independent: true` the anchor becomes a corner first, so its outgoing handle does not move. `out`, placed after `in`,
is the first anchor's outgoing handle, and makes the anchor a corner. A Pen press on the first anchor sends `in` at
the anchor with `independent: true`. The Pen's and the Smart Pen's closing drags send the mirror of the pointer as
`in`, and once Alt is held they also send `out` at the outgoing handle's position when Alt went down.
`path.reshape {id, x, y, dx, dy, tol?, focal?, radius?}` (a Reshape tool drag) grabs the path at `x`, `y`, adding an
anchor there unless one lies within `tol` (3 by default), and moves that point by `dx`, `dy`.
@@ -4055,8 +4062,8 @@ an `x` without a `y`, or a `y` without an `x`, with "x and y must be given toget
`path.blob` refuse a point in `points` whose x or y is not a number, such as `["10", 10]` or a point written as a
string, with "x and y must both be numbers". All of them except `path.curvature` refuse a pen pressure, tilt or
rotation that is neither a number nor null with `` `pressure` must be a number, not "0.5" ``, naming the key
(`pressure`, `tiltX`, `tiltY` or `rotation`). `path.close` refuses an `in` that is not `[x, y]`
with `` `in` must be [x, y] ``.
(`pressure`, `tiltX`, `tiltY` or `rotation`). `path.close` refuses an `in` or `out` that is not `[x, y]`
with `` `in` must be [x, y] ``, naming the key.
These commands refuse a string, a number or another value that is not true or false with
`` `closed` must be true or false ``, naming the key; the document does not change and no undo step is recorded:
+1 -1
View File
@@ -959,7 +959,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Align, Distribute, Distribute Spacing, Reflect and Shear return an error for any `horizontal`, `vertical`, `to`, `axis`, `bounds` or `spacing` value they don't take, listing the choices, and Align returns one when a call gives neither `horizontal` nor `vertical`; nothing moves and no undo step is recorded. Align and Distribute act on the anchor points selected with Direct Selection: the points of one path align to each other, not to the artboard, and Distribute spaces them (#1641, fixes #1636; aligning to a key anchor is still missing).
Transform Each opens on the scale, move, rotate, reflect, Random, reference point, Transform Objects and Preview it was last confirmed with (kept with the UI state; Copy never is), and its Copy button, left of Cancel, transforms a copy of each object as the [Scale multiple objects](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/scale-multiple-objects.html) help page has it; the Copy checkbox is gone (#1732; the remembered values follow a maintainer comment on the issue, not the help page). Its Scale section has the Relative and Absolute radio buttons the same page describes: percentages of each object's size, or the width and height it becomes (`object.transformEach` `relative`, which Transform Again repeats); Absolute measures each object's own box, and the scales act along its axes about that box's reference point, so a turned shape keeps its shape and objects of different sizes come out alike; the fields show the width and height the selected objects share and read "Mixed" when they differ or a side is 0 (a line's), each object measured by its own box, rotated with it, as the Transform panel and the Control bar show it (sizes they show alike count as the same), and a size typed into them sets every object to it (a side left Mixed keeps its size), our choices where the page leaves them open; a live Repeat scales its spacing by the geometric mean of the two factors, so its Absolute size is approximate. The link beside the two scales keeps them in proportion, as the shape dialogs' Width and Height link does (`__constrain`, kept with the dialog's values; where it sits is our choice, the public pages don't establish it). The panel lists Scale, Move and Rotate, then the Options in two columns (Transform Objects, Transform Patterns, Scale Strokes & Effects, Scale Corners; Reflect X, Reflect Y, Random), the Reference Point, and Preview at the left of the button row, level with Copy, Cancel and OK.
Transform Again uses the Copy, Transform Patterns, Scale Strokes & Effects and Scale Corners choices of the transform it repeats; after a bounding-box handle drag on area type, it resizes the type area again and the type keeps its size (`object.transformAgain`). With anchors selected with Direct Selection, Move, Rotate, Scale, Reflect, Shear and Transform Again (the commands, their dialogs and the four tools' drags) transform only those anchors, their handles with them, about the centre of the box around them unless a point was clicked; a copy still copies the whole object (#1408; behaviour reference: [adjust path segments](https://helpx.adobe.com/illustrator/using/adjust-path-segments.html); the default reference point and the copy are our choices). The Move, Rotate, Scale, Reflect and Shear dialogs have a Preview checkbox, off when they open: while it is on the canvas follows the values, Cancel rolls it back and OK is still one undo step; Up/Down step the Rotate and Shear angles and the Scale percentages by 1, Shift by 10 (#1412). The five dialogs and Transform Each have Transform Objects beside Transform Patterns: off, the art stays and only its pattern tiles transform (`objects` on the transform commands), and Transform Again repeats the choice (#1448; behaviour reference: [scale objects](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/scale-objects.html); the checkboxes are always enabled and the Scale page's pair is applied to all of them, our choices). Reflect's axis is Horizontal, Vertical or an angle (#1460; [reflect objects along an axis](https://helpx.adobe.com/illustrator/desktop/manage-objects/arrange-objects/reflect-objects-along-an-axis.html)).
- **Pen:** Alt pressed in the middle of dragging out an anchor splits its handles and keeps the curve already shaped into it (only the outgoing handle follows the pointer); Space held while dragging out an anchor moves it, handles and all. A press on the first anchor of the path being drawn closes it whatever the drag then does: the drag shapes the closing curve (symmetric handles, Alt breaks them) and the release closes the path wherever it ends (#501). Clicking the last anchor of the path being drawn retracts its outgoing handle (convert cursor), and dragging from it pulls out a new one on its own. Auto Add/Delete: over a selected path, a click on a segment adds an anchor and a click on an anchor deletes it (add and delete cursors); Shift held, or Preferences › General › Disable Auto Add/Delete, starts a new path instead. Alt held over a handle end or an anchor of a selected path (the one being drawn too) works as the Anchor Point tool, with its convert cursor: dragging a handle moves it alone, clicking a smooth anchor makes it a corner, dragging an anchor pulls out new symmetric handles, dragging a segment reshapes it; drawing then goes on. Cmd (Ctrl) held drags with the selection tool chosen last, Direct Selection until one is (handles, anchors and segments of the path being drawn or another), and the Pen then goes on drawing its path while that stays selected; the pointer shows that tool's while Cmd is held. The same holds for agents (`pointer_gesture` with `mods.cmd`), as the borrowing now lives in the engine (#494). A drag's preview (the Pen's, the Anchor Point tool's, Direct Selection's handles and anchors) follows every position the pointer is seen at (#834): the canvas sends the tool a drag whenever the pointer's document position differs from the last one it sent, not only when egui's frame delta is non-zero (zero when a compositor coalesces motion and in the frame the pointer comes back into the window after leaving it), and a move that arrives in the release's own frame shapes the preview before the release commits it, once the press has dragged. With the Pen, a press that moves under 3 screen pixels is a click (a corner without handles), and a Precise Cursors command toggles the existing preference and can take a shortcut (#1663).
- **Pen:** Alt pressed in the middle of dragging out an anchor splits its handles and keeps the curve already shaped into it (only the outgoing handle follows the pointer); Space held while dragging out an anchor moves it, handles and all. A press on the first anchor of the path being drawn closes it whatever the drag then does: the drag shapes the closing curve and the release closes the path wherever it ends (#501). The first anchor's incoming handle mirrors the pointer, and a click leaves it retracted and the anchor a corner. Its outgoing handle stays where it is on a corner, so the first segment keeps its shape, and on a smooth anchor it turns to stay in line at its own length; Alt makes the anchor a corner and leaves the outgoing handle where it was when Alt went down. The Smart Pen's closing drag does the same (fixes #6634; `path.close`'s `in` and `out`; behavior reference: [draw with the Pen, Curvature, or Pencil tool](https://web.archive.org/web/20220614224656/https://helpx.adobe.com/illustrator/using/drawing-pen-curvature-or-pencil.html), where a click or drag on the first anchor closes the path and Alt breaks the pairing of the closing anchor's handles; which handles the drag sets are our choices). Moving the closing anchor with Space held, which that page also describes, is still missing. Clicking the last anchor of the path being drawn retracts its outgoing handle (convert cursor), and dragging from it pulls out a new one on its own. Auto Add/Delete: over a selected path, a click on a segment adds an anchor and a click on an anchor deletes it (add and delete cursors); Shift held, or Preferences › General › Disable Auto Add/Delete, starts a new path instead. Alt held over a handle end or an anchor of a selected path (the one being drawn too) works as the Anchor Point tool, with its convert cursor: dragging a handle moves it alone, clicking a smooth anchor makes it a corner, dragging an anchor pulls out new symmetric handles, dragging a segment reshapes it; drawing then goes on. Cmd (Ctrl) held drags with the selection tool chosen last, Direct Selection until one is (handles, anchors and segments of the path being drawn or another), and the Pen then goes on drawing its path while that stays selected; the pointer shows that tool's while Cmd is held. The same holds for agents (`pointer_gesture` with `mods.cmd`), as the borrowing now lives in the engine (#494). A drag's preview (the Pen's, the Anchor Point tool's, Direct Selection's handles and anchors) follows every position the pointer is seen at (#834): the canvas sends the tool a drag whenever the pointer's document position differs from the last one it sent, not only when egui's frame delta is non-zero (zero when a compositor coalesces motion and in the frame the pointer comes back into the window after leaving it), and a move that arrives in the release's own frame shapes the preview before the release commits it, once the press has dragged. With the Pen, a press that moves under 3 screen pixels is a click (a corner without handles), and a Precise Cursors command toggles the existing preference and can take a shortcut (#1663).
- **Direct Selection (#1278):** clicking a segment picks its connection, with hollow endpoint anchors and only that curve's direction handles. Delete leaves the neighboring curves and appearance intact, opens/splits contours in one undo step, and another Delete removes the remaining selected path. A whole-path drag and deliberate anchor selection keep their existing behavior; egui frames and native control/art checked, OS mouse/visual QA pending. Where anchors of overlapping objects coincide, a click or drag takes the anchor of the selected object, also when another object lies over it (#1509, fixes #1508). Delete removes every selected anchor of a closed path (it used to remove the wrong ones), removes objects selected whole along with the selected anchors of others, and keeps the remainder selected so a second Delete removes it (#1674). In isolation mode Direct Selection and Group Selection pick only inside the isolated object (#1666). Escape cancels a Direct Selection drag (#1803, #1743). Double-clicking type (including type nested inside a group) switches to the Type tool and puts the caret at the clicked character.
With Direct Selection and Group Selection, a drag that starts on an object while the selection also holds direct-selected anchors or segments moves those anchors and the objects selected whole in one undo step (`path.moveAnchors`), as a drag of a selected anchor does, and the other anchors of a partly selected path stay in place. While Alt is held during the drag, it copies the selected objects instead (`object.transform`). When direct-selected anchors are moved or transformed together with objects selected whole (such a drag, an anchor drag, an arrow key, or Move, Rotate, Scale, Reflect and Shear without a copy), a path inside a selected group is transformed once, with the group.
A path segment with a control point whose x or y is infinite or larger in size than `f64::MAX / 16` is measured by its control points, so its bounds hold the whole curve and the canvas check refuses an edit that leaves such a path selected (`path.create` included).