The Marquee, shapes and a moved selection snap to View › Snap To; Ctrl draws freely (13.5a)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-01 09:55:24 -04:00
1 parent 64ebd1cdf9
commit 3992bc913d
11 files changed
+271 -11

No files matched your search

+4
View File
@@ -3,6 +3,10 @@ public:
SnapGuides transformSnapTargets(const QSet<QUuid> &moving) const;
LayerTransform snappedMove(const LayerTransform &draft, const QSet<QUuid> &moving, double tolerance);
SnapGuides cropSnapTargets() const;
// A drawn corner, each axis to a target within reach.
QPointF snappedPoint(QPointF point, double tolerance);
// A moved selection's edges or middle; a locked axis stays.
QSizeF snappedSelectionOffset(QSizeF offset, double tolerance, bool horizontal = true, bool vertical = true);
// The Crop tool's frame: the canvas until one is drawn.
std::optional<QRectF> visibleCropRect() const;
std::optional<double> cropRatio() const;
+35
View File
@@ -174,6 +174,41 @@ SnapGuides EditorSession::cropSnapTargets() const
return alignmentSnapTargets({}, false);
}
QPointF EditorSession::snappedPoint(QPointF point, double tolerance)
{
if (!m_snappingEnabled) {
snapGuides = {};
return point;
}
const SnapGuides targets = cropSnapTargets();
// The first of the nearest, as Swift's `min`.
const auto nearest = [tolerance](double value, const std::vector<double> &lines) {
std::optional<double> best;
for (const double line : lines)
if (std::abs(line - value) <= tolerance && (!best || std::abs(line - value) < std::abs(*best - value)))
best = line;
return best;
};
const std::optional<double> x = nearest(point.x(), targets.xs), y = nearest(point.y(), targets.ys);
snapGuides = {x ? std::vector<double>{*x} : std::vector<double>{}, y ? std::vector<double>{*y} : std::vector<double>{}};
return QPointF(x.value_or(point.x()), y.value_or(point.y()));
}
QSizeF EditorSession::snappedSelectionOffset(QSizeF offset, double tolerance, bool horizontal, bool vertical)
{
if (!m_snappingEnabled || !m_selectionMoveOrigin) {
snapGuides = {};
return offset;
}
const QSizeF whole(std::round(offset.width()), std::round(offset.height()));
const QRectF box = m_selectionMoveOrigin->path.boundingRect().translated(whole.width(), whole.height());
const SnapGuides targets = cropSnapTargets();
const TransformSnap::Offset snap =
TransformSnap::offset(box, horizontal ? targets.xs : std::vector<double>{}, vertical ? targets.ys : std::vector<double>{}, tolerance);
snapGuides = {snap.x ? std::vector<double>{*snap.x} : std::vector<double>{}, snap.y ? std::vector<double>{*snap.y} : std::vector<double>{}};
return whole + snap.offset;
}
std::optional<QRectF> EditorSession::visibleCropRect() const
{
if (m_tool != NavigationTool::crop || !m_document)
+2 -2
View File
@@ -183,7 +183,7 @@ void CanvasView::press(QMouseEvent *event, int clicks)
} else if (m_session.tool() == NavigationTool::gradient) {
beginGradientDrag(point);
} else if (m_session.tool() == NavigationTool::shape) {
m_session.beginShape(m_session.viewport.documentPoint(point, m_session.document()->size()));
m_session.beginShape(snappedCorner(m_session.viewport.documentPoint(point, m_session.document()->size()), event->modifiers()));
} else if (m_session.tool() == NavigationTool::crop) {
beginCropDrag(point);
} else if (m_session.tool() == NavigationTool::type) {
@@ -271,7 +271,7 @@ void CanvasView::mouseMoveEvent(QMouseEvent *event)
return;
if (m_session.shapeDraft() && m_session.document()) {
// Alt has no other job here: it grows from centre.
m_session.dragShape(m_session.viewport.documentPoint(point, m_session.document()->size()),
m_session.dragShape(snappedCorner(m_session.viewport.documentPoint(point, m_session.document()->size()), event->modifiers()),
event->modifiers().testFlag(Qt::ShiftModifier), event->modifiers().testFlag(Qt::AltModifier));
synchronizeDisplay();
return;
+23 -4
View File
@@ -67,7 +67,7 @@ void CanvasView::lassoMouseDown(QPointF point, Qt::KeyboardModifiers modifiers)
m_session.magicWand(pixel, mode);
return;
}
m_session.beginLasso(pixel, mode);
m_session.beginLasso(m_session.tool() == NavigationTool::marquee ? snappedCorner(pixel, modifiers) : pixel, mode);
synchronizeDisplay();
return;
}
@@ -218,22 +218,41 @@ void CanvasView::dragSelection(QPointF point, Qt::KeyboardModifiers modifiers)
return;
const QPointF pixel = m_session.viewport.documentPoint(point, m_session.document()->size());
QSizeF offset(pixel.x() - m_selectionDragStart->x(), pixel.y() - m_selectionDragStart->y());
bool horizontal = true, vertical = true;
if (modifiers.testFlag(Qt::ShiftModifier)) {
if (std::abs(offset.width()) >= std::abs(offset.height()))
if (std::abs(offset.width()) >= std::abs(offset.height())) {
offset.setHeight(0);
else
vertical = false;
} else {
offset.setWidth(0);
horizontal = false;
}
}
// Snaps as a drawn Marquee does, unless Ctrl is held.
if (modifiers.testFlag(Qt::ControlModifier))
m_session.snapGuides = {};
else
offset = m_session.snappedSelectionOffset(offset, TransformSnap::distance / std::max(m_session.viewport.pointsPerPixel(), 0.0001), horizontal,
vertical);
m_session.moveSelection(offset);
}
QPointF CanvasView::snappedCorner(QPointF pixel, Qt::KeyboardModifiers modifiers)
{
if (modifiers.testFlag(Qt::ControlModifier)) {
m_session.snapGuides = {};
return pixel;
}
return m_session.snappedPoint(pixel, TransformSnap::distance / std::max(m_session.viewport.pointsPerPixel(), 0.0001));
}
// Alt subtracts, never from the centre; Shift squares once armed.
void CanvasView::dragMarqueeDraft(QPointF pixel, Qt::KeyboardModifiers modifiers)
{
if (!modifiers.testFlag(Qt::ShiftModifier))
m_marqueeConstrainArmed = true;
m_marqueeDragPixel = pixel;
m_session.dragMarquee(pixel, m_marqueeConstrainArmed && modifiers.testFlag(Qt::ShiftModifier), false);
m_session.dragMarquee(snappedCorner(pixel, modifiers), m_marqueeConstrainArmed && modifiers.testFlag(Qt::ShiftModifier), false);
}
// A frame's pan toward a pointer at the edge.
+2
View File
@@ -210,6 +210,8 @@ private:
void endSelectionGestures();
void dragSelection(QPointF point, Qt::KeyboardModifiers modifiers);
void dragMarqueeDraft(QPointF pixel, Qt::KeyboardModifiers modifiers);
// A Marquee or shape corner, snapped unless Ctrl is held.
QPointF snappedCorner(QPointF pixel, Qt::KeyboardModifiers modifiers);
QSizeF marqueeAutoscrollDelta(QPointF point) const;
void updateMarqueeAutoscroll(QPointF point);
void stepMarqueeAutoscroll();