Raised by plc-user in discussion #618: the diagram editor allows moving
elements by as little as 1px, and asked that rotation not undershoot
that. Checking the actual constraint (Settings -> DiagramEditor_xGrid_sb
/ _yGrid_sb, both independently configurable, minimum 1, maximum 30)
turned up a real, verified gap this PR's existing fractional-pivot fix
doesn't cover: an ASYMMETRIC grid (xGrid != yGrid).
Swapping X/Y deltas for a 90-degree turn -- the exact-arithmetic path
already in this file -- only stays on the configured grid if
xGrid == yGrid. With an asymmetric grid, a delta that was a clean
multiple of xGrid lands on the Y axis after the swap, where the grid
unit is yGrid, and one is not generally a multiple of the other.
Verified on a real build (not just derived): two elements at (100,210)
and (150,420), both on-grid under xGrid=10/yGrid=7, selected and
group-rotated 90 degrees via a temporary local CLI harness.
before this change: (242,292) and (32,342) -- off-grid on both axes
after this change: (240,294) and ( 30,343) -- exactly on-grid
Confirmed the same drift is present without this change too (i.e. not
something introduced elsewhere) and that xGrid==yGrid, the common case,
is unaffected: snapping an already-on-grid point is a no-op.
Fix: re-snap the final computed position to Diagram::snapToGrid(), not
just the shared pivot, for the exact-90-degree path. Left the
arbitrary-angle trig fallback alone -- it has no caller today (the
diagram editor only ever passes multiples of 90) and "on-grid" doesn't
have a clean meaning for an arbitrary angle regardless of grid shape.
Does not attempt to fix a separate, pre-existing property surfaced
while testing this: four consecutive 90-degree turns do not reliably
return a selection to its exact starting position, even on a symmetric
grid, because each RotateSelectionCommand recomputes the pivot fresh
from the selection's current sceneBoundingRect(), and an item whose
bounding box isn't rotationally symmetric reports a different box (and
therefore a different centre) at 0 and 90 degrees. Verified this drift
is identical with and without this change, so it is not a regression --
just a different, harder guarantee this change does not attempt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rotating a selection around the raw bounding-box centre moved
grid-aligned elements off the grid permanently. sceneBoundingRect()
comes from font metrics and pen widths, so the centre is almost never
a round number: with a pivot of (132.67, 101.11), an element sitting
at x=100 landed at x=133.78, and no further rotation brought it back.
Positions are written with QString::number() (%.6g), which hides the
floating-point noise but keeps the offset, so the diagram ends up
subtly misaligned with no way to repair it from the UI.
Snap the pivot with Diagram::snapToGrid(), which also follows the
user's configured X/Y grid rather than assuming the 10 px default.
Also compute the rotated offset exactly for multiples of 90 degrees
instead of going through qCos()/qSin(). The rotate actions only ever
pass right angles, and qCos(90 deg) is 6.12e-17 rather than 0, so the
trig path added error for no benefit -- four 90 degree steps did not
return a point to where it started. A quadrant is an axis swap, which
is exact; trig is kept as the fallback for any other angle.
With both, four 90 degree rotations of a grid-aligned element return
it exactly to its original position and every intermediate step stays
on the grid.
Reported by plc-user, who hit the same problem rotating graphical
primitives in the Element Editor -- discussion #618.
RotateSelectionCommand's existing "Pivoter" action (Space) only ever
bumps each selected item's own rotation property -- QGraphicsItem's
setRotation() spins an item around its own local origin and never
touches pos(). Select three elements arranged in a row and rotate:
each spins 90 degrees individually, but the row stays a row. That's
"rotate each item," not "rotate the group."
Add a rotate_as_group parameter to RotateSelectionCommand (default
false, so the existing action and its one call site are unchanged).
When set, it computes a shared pivot once -- the bounding-box center
of the whole selection -- and queues a second, parallel "pos"
QPropertyUndoCommand alongside the existing "rotation" one, rotating
each item's position around that pivot by the same angle.
Scoped the position change to Element/IndependentTextItem/
DiagramImageItem only: these are the only selectable types with
scene-space pos(). ConductorTextItem, DynamicElementTextItem and
ElementTextItemGroup are all parent-relative children (confirmed by
reading their constructors), so when their owning Element is also
selected and gets its own pos() rotated, they're carried along for
free by Qt's normal parent/child transform propagation -- exactly
what the existing "skip rotation if parent is also selected" guard
already assumes for those three cases.
Exposed as a new, separate action ("Pivoter le groupe", Shift+Space)
next to the existing one rather than changing Space's behavior, since
some workflows may rely on the current per-item rotation.
clazy is a compiler plugin which allows clang to understand Qt
semantics. You get more than 50 Qt related compiler warnings, ranging
from unneeded memory allocations to misusage of API, including fix-its
for automatic refactoring.
https://invent.kde.org/sdk/clazy
The keybord shortcut for texts group alignment change. Now it's ctrl + arrow-left/up/right
git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@5141 bfdf4180-ca20-0410-9c96-a3a8aa849046