Move bar: held Ctrl and Shift flip Auto Select and the lock, values apply as one step, resize handles snap (13.13)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-01 16:56:16 -04:00
1 parent 765e071c0c
commit b50657bcdb
17 files changed
+703 -78

No files matched your search

+121 -12
View File
@@ -1,4 +1,5 @@
#include "SessionFixtures.h"
#include "UI/HeldModifiers.h"
#include "UI/LayerIcons.h"
#include "UI/TransformInspector.h"
#include <QtTest>
@@ -57,6 +58,7 @@ private slots:
void aMenuBorrowsTheFocusAndTheBarsKeysApplyOrCancel();
void theLockIconFollowsThePalette();
void theFieldsTakeTheSpareRoom();
void heldKeysShowFlippedInTheBar();
};
void TransformInspectorTests::theFieldsShowTheLayerAndTypingPreviews()
@@ -73,12 +75,18 @@ void TransformInspectorTests::theFieldsShowTheLayerAndTypingPreviews()
QCOMPARE(bar.field("°").text(), QString("0"));
QCOMPARE(bar.control<QComboBox>("transformSampling").currentText(), QString("High quality"));
QCOMPARE(bar.field("X").accessibleName(), QString("X"));
// Typing begins a Ctrl+T edit and previews it.
// Typing previews in an edit of the fields' own.
const int base = bar.session.history.undoCount();
bar.type("X", "90");
QVERIFY(bar.session.transformEdit().value().persistent);
QVERIFY(!bar.session.transformEdit().value().persistent);
QVERIFY(bar.session.transformEdit().value().fromFields);
QCOMPARE(bar.draft().origin, QPointF(90, 40));
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin, QPointF(80, 40));
// Leaving the field applies it, one step.
bar.type("Y", "12.5");
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin, QPointF(90, 40));
QCOMPARE(bar.session.history.undoCount(), base + 1);
QCOMPARE(bar.session.history.undoName(), QString("Transform Layer"));
QCOMPARE(bar.draft().origin, QPointF(90, 12.5));
// Width locked carries the height; unlocked, alone.
bar.type("W", "80");
@@ -114,7 +122,7 @@ void TransformInspectorTests::theFieldsShowTheLayerAndTypingPreviews()
QVERIFY(bar.draft().flipX);
bar.control<QPushButton>("flipVertical").click();
QVERIFY(bar.draft().flipY);
// Apply commits the edit as one step; Cancel drops it.
// Apply commits the open edit; Cancel drops it.
const LayerTransform drafted = bar.draft();
bar.control<QPushButton>("applyTransform").click();
QVERIFY(!bar.session.transformEdit().has_value());
@@ -141,12 +149,16 @@ void TransformInspectorTests::returnEscapeAndArrowsInAField()
QTest::keyClick(&x, Qt::Key_Down, Qt::ShiftModifier);
QCOMPARE(bar.draft().origin.x(), 71.0);
QCOMPARE(x.text(), QString("71"));
// Return hands the canvas the keys; the edit waits there.
// Return applies and hands the canvas the keys.
const int request = bar.session.canvasFocusRequest();
const int steps = bar.session.history.undoCount();
QTest::keyClick(&x, Qt::Key_Return);
QVERIFY(!x.hasFocus());
QCOMPARE(bar.session.canvasFocusRequest(), request + 1);
QVERIFY(bar.session.transformEdit().has_value());
QVERIFY(!bar.session.transformEdit().has_value());
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin.x(), 71.0);
QCOMPARE(bar.session.history.undoCount(), steps + 1);
// Escape throws the fields' change away.
x.setFocus();
QTest::keyClicks(&x, "2");
QCOMPARE(x.text(), QString("712"));
@@ -154,13 +166,28 @@ void TransformInspectorTests::returnEscapeAndArrowsInAField()
QTest::keyClick(&x, Qt::Key_Escape);
QVERIFY(!x.hasFocus());
QCOMPARE(bar.session.canvasFocusRequest(), request + 2);
QCOMPARE(bar.draft().origin.x(), 712.0);
QVERIFY(!bar.session.transformEdit().has_value());
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin.x(), 71.0);
QCOMPARE(x.text(), QString("71"));
QCOMPARE(bar.session.history.undoCount(), steps + 1);
// Ctrl+T's edit takes typing and waits through Return and Escape.
bar.session.beginTransform();
x.setFocus();
QTest::keyClicks(&x, "5");
QCOMPARE(bar.draft().origin.x(), 715.0);
QVERIFY(!bar.session.transformEdit().value().fromFields);
QTest::keyClick(&x, Qt::Key_Return);
QCOMPARE(bar.draft().origin.x(), 715.0);
x.setFocus();
QTest::keyClick(&x, Qt::Key_Escape);
QCOMPARE(bar.draft().origin.x(), 715.0);
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin.x(), 71.0);
// While a field is typed in, the numbers wait outside.
x.setFocus();
LayerTransform moved = bar.draft();
moved.origin.setX(30);
bar.session.previewTransform(moved);
QCOMPARE(x.text(), QString("712"));
QCOMPARE(x.text(), QString("715"));
QCOMPARE(bar.field("Y").text(), QString("40"));
x.clearFocus();
QCOMPARE(x.text(), QString("30"));
@@ -172,7 +199,9 @@ void TransformInspectorTests::settingsButtonsAndTheTitleFollowTheSession()
QCheckBox &autoSelect = bar.control<QCheckBox>("transformAutoSelect");
QCheckBox &controls = bar.control<QCheckBox>("showTransformControls");
QVERIFY(!autoSelect.isChecked() && controls.isChecked());
QCOMPARE(autoSelect.toolTip(), QString("Select layers by clicking the canvas. When off, hold Ctrl to select a layer."));
QCOMPARE(autoSelect.toolTip(), QString("Select layers by clicking the canvas. Hold Ctrl to turn it the other way while you click."));
QCOMPARE(bar.control<QToolButton>("locksTransformRatio").toolTip(),
QString("Lock aspect ratio. Hold Shift while dragging a handle to turn it the other way."));
autoSelect.click();
QVERIFY(bar.session.transformAutoSelect());
controls.click();
@@ -258,14 +287,25 @@ void TransformInspectorTests::aMenuBorrowsTheFocusAndTheBarsKeysApplyOrCancel()
apply.setFocus(Qt::PopupFocusReason);
QCOMPARE(x.text(), QString("12.5"));
x.setFocus();
// Another window in front keeps it too.
apply.setFocus(Qt::ActiveWindowFocusReason);
QCOMPARE(x.text(), QString("12.5"));
QVERIFY(bar.session.transformEdit().has_value());
x.setFocus();
apply.setFocus(Qt::TabFocusReason);
QCOMPARE(x.text(), QString("12.50"));
// Return on Apply applies; Escape on a toggle cancels.
apply.setFocus();
QVERIFY(apply.hasFocus());
QTest::keyClick(&apply, Qt::Key_Return);
QVERIFY(!bar.session.transformEdit().has_value());
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin.x(), 12.5);
// Return on Apply applies Ctrl+T's; Escape on a toggle cancels.
bar.session.beginTransform();
bar.type("X", "14");
apply.setFocus();
QVERIFY(apply.hasFocus());
QVERIFY(bar.session.transformEdit().has_value());
QTest::keyClick(&apply, Qt::Key_Return);
QVERIFY(!bar.session.transformEdit().has_value());
QCOMPARE(layerWith(bar.session, bar.layer).transform.origin.x(), 14.0);
bar.session.beginTransform();
bar.type("Y", "7");
QCOMPARE(bar.draft().origin.y(), 7.0);
QCheckBox &controls = bar.control<QCheckBox>("showTransformControls");
@@ -306,5 +346,74 @@ void TransformInspectorTests::theFieldsTakeTheSpareRoom()
QCOMPARE(flip.width(), flip.sizeHint().width());
}
// Swift's HeldModifiers: Ctrl flips Auto Select, Shift the lock.
void TransformInspectorTests::heldKeysShowFlippedInTheBar()
{
Bar bar;
QVERIFY(bar.active);
QCheckBox &autoSelect = bar.control<QCheckBox>("transformAutoSelect");
QToolButton &lock = bar.control<QToolButton>("locksTransformRatio");
HeldModifiers &held = HeldModifiers::shared();
autoSelect.setFocus();
QVERIFY(!autoSelect.isChecked() && lock.isChecked());
// A modifier's own press counts it, whatever the event says.
QTest::keyPress(&autoSelect, Qt::Key_Control, Qt::NoModifier);
QCOMPARE(held.flags(), Qt::KeyboardModifiers(Qt::ControlModifier));
QVERIFY(autoSelect.isChecked());
QVERIFY(!bar.session.transformAutoSelect());
// Only a change is announced.
QSignalSpy announced(&held, &HeldModifiers::changed);
const auto press = [&](Qt::KeyboardModifiers modifiers) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, modifiers);
QApplication::sendEvent(&autoSelect, &event);
};
press(Qt::ControlModifier);
QCOMPARE(announced.count(), 0);
press(Qt::ControlModifier | Qt::ShiftModifier);
QCOMPARE(announced.count(), 1);
press(Qt::ControlModifier);
QCOMPARE(announced.count(), 2);
// A click while held writes the other way.
autoSelect.click();
QVERIFY(bar.session.transformAutoSelect());
QVERIFY(!autoSelect.isChecked());
QTest::keyRelease(&autoSelect, Qt::Key_Control, Qt::ControlModifier);
QCOMPARE(held.flags(), Qt::KeyboardModifiers());
QVERIFY(autoSelect.isChecked());
// Shift flips the lock likewise; Alt and Meta count, unshown.
QTest::keyPress(&autoSelect, Qt::Key_Shift, Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier);
QCOMPARE(held.flags(), Qt::ShiftModifier | Qt::AltModifier | Qt::MetaModifier);
QVERIFY(!lock.isChecked());
QVERIFY(autoSelect.isChecked());
lock.click();
QVERIFY(!bar.session.locksTransformRatio());
QVERIFY(lock.isChecked());
QTest::keyRelease(&autoSelect, Qt::Key_Shift, Qt::ShiftModifier);
QVERIFY(!lock.isChecked());
QTest::keyRelease(&autoSelect, Qt::Key_Alt, Qt::AltModifier);
QTest::keyRelease(&autoSelect, Qt::Key_Meta, Qt::MetaModifier);
QCOMPARE(held.flags(), Qt::KeyboardModifiers());
// Left presses and releases read modifiers; others do not.
QTest::mousePress(bar.header.get(), Qt::LeftButton, Qt::ControlModifier, QPoint(2, 2));
QCOMPARE(held.flags(), Qt::KeyboardModifiers(Qt::ControlModifier));
QTest::mouseRelease(bar.header.get(), Qt::RightButton, Qt::NoModifier, QPoint(2, 2));
QCOMPARE(held.flags(), Qt::KeyboardModifiers(Qt::ControlModifier));
QTest::mouseRelease(bar.header.get(), Qt::LeftButton, Qt::NoModifier, QPoint(2, 2));
QCOMPARE(held.flags(), Qt::KeyboardModifiers());
// Another app in front lets every key go.
QTest::keyPress(&autoSelect, Qt::Key_Shift, Qt::ShiftModifier);
QVERIFY(lock.isChecked());
emit qApp->applicationStateChanged(Qt::ApplicationInactive);
QCOMPARE(held.flags(), Qt::KeyboardModifiers());
QVERIFY(!lock.isChecked());
// Typing in a field, keys count as none.
QLineEdit &x = bar.field("X");
x.setFocus();
QTest::keyPress(&x, Qt::Key_Control, Qt::ControlModifier);
QCOMPARE(held.flags(), Qt::KeyboardModifiers());
QVERIFY(autoSelect.isChecked() == bar.session.transformAutoSelect());
QTest::keyRelease(&x, Qt::Key_Control, Qt::NoModifier);
}
QTEST_MAIN(TransformInspectorTests)
#include "TransformInspectorTests.moc"