Merge pull request #1367 from bhangart/fix/image-pivot-flag-undo

Undo restores whether a picture's pivot was placed by hand
This commit is contained in:
ispyisail
2026-10-08 05:14:47 +13:00
committed by GitHub
3 changed files with 65 additions and 2 deletions
+11 -2
View File
@@ -805,6 +805,7 @@ void DiagramImageItem::handlerMousePressEvent(int index, Qt::KeyboardModifiers m
m_vector_index = index;
m_original_pos = pos();
m_original_transform = m_transform;
m_original_pivotIsCustom = m_pivotIsCustom;
if (m_handleRoles.at(index) == HandleRole::Resize)
{
@@ -921,7 +922,12 @@ void DiagramImageItem::handlerMouseReleaseEvent(int index)
undo = new QUndoCommand(tr("Déplacer le centre de rotation d'une image"));
new QPropertyUndoCommand(this, "pos", m_original_pos, pos(), undo);
new QPropertyUndoCommand(this, "rawPivot", m_original_transform.pivot, m_transform.pivot, undo);
// dragPivot() marked the pivot as hand-placed; undoing
// the move has to take that back as well.
new QPropertyUndoCommand(this, "pivotIsCustom", m_original_pivotIsCustom, m_pivotIsCustom, undo);
}
else
m_pivotIsCustom = m_original_pivotIsCustom; // dragged back to where it was: nothing to undo
break;
}
@@ -1991,7 +1997,7 @@ void DiagramImageItem::crop()
simply the whole base.
One undo step, which restores the pixmap, the position, the pivot
and the crop rectangle together.
(and whether it was hand-placed) and the crop rectangle together.
@return false if nothing was cropped: read-only folio, or a
rectangle that is empty, outside the original, or the current one.
*/
@@ -2032,7 +2038,10 @@ bool DiagramImageItem::applyCrop(const QRect &cropRect)
QVariant::fromValue(newSource), undo);
new QPropertyUndoCommand(this, "pos", oldPos, newPos, undo);
new QPropertyUndoCommand(this, "rawPivot", oldPivot, newOriginPoint, undo);
m_pivotIsCustom = false;
// The pivot goes to the centre of the kept region, so it is no longer
// hand-placed -- until the crop is undone, which brings the old pivot
// back and must bring its flag back with it.
new QPropertyUndoCommand(this, "pivotIsCustom", m_pivotIsCustom, false, undo);
diagram()->undoStack().push(undo);
return true;
}
@@ -65,6 +65,10 @@ class DiagramImageItem : public QetGraphicsItem {
// would silently overwrite that already-correct pos() a second
// time. rawPivot exists solely for that one caller.
Q_PROPERTY(QPointF rawPivot READ pivot WRITE setPivotRaw NOTIFY transformChanged)
// Whether the pivot was placed by hand: a hand-placed pivot is saved
// and kept through a resize. Changes in the same undo step as the
// pivot itself, so that Ctrl+Z restores both.
Q_PROPERTY(bool pivotIsCustom READ pivotIsCustom WRITE setPivotIsCustom)
// constructors, destructor
public:
@@ -120,6 +124,8 @@ class DiagramImageItem : public QetGraphicsItem {
void editProperty() override;
void setPixmap(const QPixmap &pixmap);
QPixmap pixmap() const { return pixmap_; }
bool pivotIsCustom() const { return m_pivotIsCustom; }
void setPivotIsCustom(bool custom) { m_pivotIsCustom = custom; }
QRectF boundingRect() const override;
QString name() const override;
@@ -259,6 +265,7 @@ class DiagramImageItem : public QetGraphicsItem {
int m_vector_index = -1;
QPointF m_original_pos; // scene position at the start of a resize/rotate/pivot drag, for Escape-to-cancel
ShapeTransform m_original_transform;
bool m_original_pivotIsCustom = false;
bool m_deferHandleReposition = false; // see setPivot()'s comment
// Optional caption drawn centred under the picture (issue #349).
// Empty by default, and then neither saved nor painted, so a picture
+47
View File
@@ -33,6 +33,11 @@
shown: before, a project saved after Ctrl+Z still recorded the undone
crop, and the picture came back cropped once reopened. Runs the real
binary on a script: add a picture, crop it, undo, save; redo, save.
It also restores whether the pivot was placed by hand: a crop moves
the pivot to the centre of the kept region and marks it as default,
and before, Ctrl+Z brought the hand-placed pivot back without its
mark, so the next save dropped it.
*/
class tst_imagecropundo : public QObject
{
@@ -152,6 +157,48 @@ qet.log('PROBE ' + JSON.stringify(r));
for (const char *attribute : {R"(x="10")", R"(y="5")", R"(w="20")", R"(h="10")"})
QVERIFY2(crop.captured(1).contains(QLatin1String(attribute)), attribute);
}
void undoneCropKeepsHandPlacedPivot()
{
// A picture whose pivot was placed by hand: saved as <transform>
// with pivotX/pivotY, which no script call writes, so the test
// adds it to a saved project itself.
const QString plain = m_dir.filePath(QStringLiteral("plain.qet"));
const QString pivot = m_dir.filePath(QStringLiteral("pivot.qet"));
QJsonObject r = run(QStringLiteral(R"JS(
var i = qet.addImage(0, '%1', 100, 100);
qet.save('%2');
qet.log('PROBE ' + JSON.stringify({added: i >= 0}));
)JS").arg(m_dir.filePath(QStringLiteral("pic.png")), plain),
QStringLiteral(QET_EXAMPLES_DIR "/741.qet"));
QVERIFY(r.value("added").toBool());
QByteArray xml = read(plain);
QCOMPARE(xml.count("</image>"), 1);
xml.replace("</image>", "<transform rotation=\"0\" skewX=\"0\" skewY=\"0\" "
"scaleX=\"1\" scaleY=\"1\" pivotX=\"5\" pivotY=\"7\"/></image>");
QFile f(pivot);
QVERIFY(f.open(QIODevice::WriteOnly));
f.write(xml);
f.close();
const QString before = m_dir.filePath(QStringLiteral("before.qet"));
const QString undone = m_dir.filePath(QStringLiteral("pivot-undone.qet"));
r = run(QStringLiteral(R"JS(
var r = {};
qet.save('%1');
r.cropped_ok = qet.cropImage(0, 0, 10, 5, 20, 10);
qet.undo();
qet.save('%2');
qet.log('PROBE ' + JSON.stringify(r));
)JS").arg(before, undone), pivot);
QVERIFY(r.value("cropped_ok").toBool());
// The fixture is read as intended: saved untouched, it keeps the pivot.
const QByteArray saved = read(before);
QVERIFY(saved.contains("pivotX=\"5\"") && saved.contains("pivotY=\"7\""));
const QByteArray afterUndo = read(undone);
QVERIFY2(afterUndo.contains("pivotX=\"5\"") && afterUndo.contains("pivotY=\"7\""),
"the hand-placed pivot was dropped after undoing the crop");
}
};
QTEST_GUILESS_MAIN(tst_imagecropundo)