diff --git a/src/Rendering/EditorCanvas+Selection.cpp b/src/Rendering/EditorCanvas+Selection.cpp index 4bd78f2..c55c102 100644 --- a/src/Rendering/EditorCanvas+Selection.cpp +++ b/src/Rendering/EditorCanvas+Selection.cpp @@ -85,7 +85,15 @@ bool CanvasView::moveSelectionTool(QPointF point, Qt::KeyboardModifiers modifier { if (m_pixelDragStart && m_session.document()) { const QPointF pixel = m_session.viewport.documentPoint(point, m_session.document()->size()); - m_session.movePixels(QSizeF(pixel.x() - m_pixelDragStart->x(), pixel.y() - m_pixelDragStart->y())); + QSizeF offset(pixel.x() - m_pixelDragStart->x(), pixel.y() - m_pixelDragStart->y()); + // Shift keeps the pixels on a line, the longer way. + if (modifiers.testFlag(Qt::ShiftModifier)) { + if (std::abs(offset.width()) >= std::abs(offset.height())) + offset.setHeight(0); + else + offset.setWidth(0); + } + m_session.movePixels(offset); updateCursor(); synchronizeDisplay(); return true; diff --git a/src/UI/CompositorMenus.cpp b/src/UI/CompositorMenus.cpp index 26f002d..3fbae05 100644 --- a/src/UI/CompositorMenus.cpp +++ b/src/UI/CompositorMenus.cpp @@ -2,20 +2,10 @@ #include "IO/RecentProjects.h" #include "Logging.h" #include "UI/KeyboardShortcuts.h" -#include "UI/NativeLayerList.h" #include #include namespace { -// The layer list holding the keys, as Swift's table. -NativeLayerList *focusedList() -{ - for (QWidget *widget = QApplication::focusWidget(); widget; widget = widget->parentWidget()) - if (auto *list = qobject_cast(widget)) - return list; - return nullptr; -} - // An entry's name: Hue/Saturation is newHueSaturationAdjustment. QString adjustmentName(AdjustmentKind kind) { @@ -186,8 +176,6 @@ CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, QWi m_field->selectAll(); else if (InlineTextEditor *text = typedText()) text->selectAll(); - else if (NativeLayerList *list = focusedList()) - list->selectAllRows(); else session().selectAll(); }); diff --git a/src/UI/NativeLayerList.cpp b/src/UI/NativeLayerList.cpp index 69a3743..10719e6 100644 --- a/src/UI/NativeLayerList.cpp +++ b/src/UI/NativeLayerList.cpp @@ -261,13 +261,3 @@ void NativeLayerList::changeEvent(QEvent *event) if (event->type() == QEvent::PaletteChange) QMetaObject::invokeMethod(this, &NativeLayerList::update, Qt::QueuedConnection); } - -void NativeLayerList::selectAllRows() -{ - if (m_rows.empty()) - return; - QSet ids; - for (const ImageLayer &row : m_rows) - ids.insert(row.id); - m_session.selectLayers(ids, m_rows.front().id); -} diff --git a/src/UI/NativeLayerList.h b/src/UI/NativeLayerList.h index 33028f8..d941dc9 100644 --- a/src/UI/NativeLayerList.h +++ b/src/UI/NativeLayerList.h @@ -215,8 +215,6 @@ public: QString dragToken() const { return m_dragToken; } EditorSession &session() const { return m_session; } - // The table's Select All: every row, the top one primary. - void selectAllRows(); void update(); std::vector cells() const { return m_cells; } // The row under a point of this list, or -1. diff --git a/tests/SelectMenuTests.cpp b/tests/SelectMenuTests.cpp index 44ae4b0..432dac6 100644 --- a/tests/SelectMenuTests.cpp +++ b/tests/SelectMenuTests.cpp @@ -121,17 +121,22 @@ void SelectMenuTests::selectAllInAFieldIsTheFields() QVERIFY(!bar.session().selection().has_value()); QTest::keyClick(editor, Qt::Key_Escape); QTRY_VERIFY(!bar.session().renamingLayerID().has_value()); - // The list holds the keys: its rows, as Swift's table. + // The list holds the keys: the canvas, never every layer. bar.session().addBlankLayer(); NativeLayerList *list = bar.window.findChild(); list->setFocus(); QTRY_VERIFY(list->hasFocus()); - const std::vector &layers = bar.session().document().value().layers; - bar.action("selectAll").trigger(); - QCOMPARE(bar.session().selectedLayerIDs(), (QSet{layers[0].id, layers[1].id})); - QCOMPARE(bar.session().activeLayerID(), std::optional(layers[1].id)); - QVERIFY(!bar.session().selection().has_value()); - // Elsewhere it selects the canvas. + const QSet selected = bar.session().selectedLayerIDs(); + const std::optional active = bar.session().activeLayerID(); + QCOMPARE(selected.size(), 1); + QTest::keyClick(list, Qt::Key_A, Qt::ControlModifier); + QCOMPARE(bar.session().selection().value().path.boundingRect(), QRectF(0, 0, 50, 50)); + QCOMPARE(bar.session().history.undoName(), QString("Select All")); + QCOMPARE(bar.session().selectedLayerIDs(), selected); + QCOMPARE(bar.session().activeLayerID(), active); + QVERIFY(list->hasFocus()); + // Elsewhere it selects the canvas too. + bar.session().deselect(); bar.window.findChild()->setFocus(); QTRY_VERIFY(!list->hasFocus()); bar.action("selectAll").trigger(); diff --git a/tests/SelectionCanvasTests.cpp b/tests/SelectionCanvasTests.cpp index aae405e..4abf118 100644 --- a/tests/SelectionCanvasTests.cpp +++ b/tests/SelectionCanvasTests.cpp @@ -11,6 +11,7 @@ private slots: void focusLossEndsAMarqueeDraftAndAnOutlineMove(); void shiftDeleteBeginsAContentAwareFill(); void aCtrlDragMovesSelectedPixelsAndCtrlArrowsNudgeThem(); + void shiftKeepsMovedPixelsOnALine(); }; void SelectionCanvasTests::marqueeDragsSelectShiftAddsAltSubtractsAndAFreshShiftSquares() @@ -322,5 +323,37 @@ void SelectionCanvasTests::aCtrlDragMovesSelectedPixelsAndCtrlArrowsNudgeThem() QVERIFY(session.lassoDraft().has_value() && !session.pixelMove()); } +void SelectionCanvasTests::shiftKeepsMovedPixelsOnALine() +{ + Canvas shown; + EditorSession &session = shown.session; + QImage red = BrushRaster::context(400, 300, false); + red.fill(Qt::red); + session.insert(ImportedImage(red, red, "Red")); + session.applySelection(rectPath(QRectF(20, 20, 40, 40)), SelectionMode::replace, "Select"); + shown.canvas->synchronizeDisplay(); + const auto box = [&] { return session.displayedSelection().value().path.boundingRect(); }; + shown.press(QPointF(40, 40), Qt::ControlModifier); + QVERIFY(session.pixelMove()); + // Across: the drag went further that way. + shown.move(QPointF(100, 55), Qt::ControlModifier | Qt::ShiftModifier); + QCOMPARE(box(), QRectF(80, 20, 40, 40)); + // Down, then a tie keeps across. + shown.move(QPointF(50, 100), Qt::ControlModifier | Qt::ShiftModifier); + QCOMPARE(box(), QRectF(20, 80, 40, 40)); + shown.move(QPointF(10, 10), Qt::ControlModifier | Qt::ShiftModifier); + QCOMPARE(box(), QRectF(-10, 20, 40, 40)); + // Lengths, not signs: left beats a shorter down. + shown.move(QPointF(0, 50), Qt::ControlModifier | Qt::ShiftModifier); + QCOMPARE(box(), QRectF(-20, 20, 40, 40)); + // Without Shift both ways. + shown.move(QPointF(60, 55), Qt::ControlModifier); + QCOMPARE(box(), QRectF(40, 35, 40, 40)); + shown.move(QPointF(70, 45), Qt::ControlModifier | Qt::ShiftModifier); + shown.release(QPointF(70, 45), Qt::ControlModifier | Qt::ShiftModifier); + QTRY_VERIFY(!session.pixelMove()); + QCOMPARE(session.selection().value().path.boundingRect(), QRectF(50, 20, 40, 40)); +} + QTEST_MAIN(SelectionCanvasTests) #include "SelectionCanvasTests.moc"