469 lines
21 KiB
C++
469 lines
21 KiB
C++
#include "Document/BrushStroke.h"
|
||
#include "IO/CanvasResizer.h"
|
||
#include "IO/ImageResizer.h"
|
||
#include "SessionFixtures.h"
|
||
#include <QSignalSpy>
|
||
#include <QTemporaryDir>
|
||
#include <QtTest>
|
||
|
||
// Swift's GuideTests: guides, the layout grid and snapping.
|
||
namespace {
|
||
CanvasGuide vertical(double position)
|
||
{
|
||
return {QUuid::createUuid(), CanvasGuide::Axis::vertical, position};
|
||
}
|
||
|
||
CanvasGuide horizontal(double position)
|
||
{
|
||
return {QUuid::createUuid(), CanvasGuide::Axis::horizontal, position};
|
||
}
|
||
|
||
// A red 100 by 60 layer, centred on the canvas.
|
||
std::unique_ptr<EditorSession> paintedSession(int width = 400, int height = 300)
|
||
{
|
||
auto session = std::make_unique<EditorSession>();
|
||
session->createDocument(width, height);
|
||
QImage image = BrushRaster::context(100, 60, false);
|
||
image.fill(Qt::red);
|
||
session->insert(ImportedImage(image, image, QStringLiteral("Red")));
|
||
return session;
|
||
}
|
||
|
||
QSet<double> set(const std::vector<double> &values)
|
||
{
|
||
return QSet<double>(values.begin(), values.end());
|
||
}
|
||
|
||
const std::vector<CanvasGuide> &guides(const EditorSession &session)
|
||
{
|
||
return session.document().value().guides;
|
||
}
|
||
}
|
||
|
||
class GuideTests : public QObject {
|
||
Q_OBJECT
|
||
private slots:
|
||
void newGuidesUndoAndClear();
|
||
void lockPreventsCreatingAndMoving();
|
||
void projectRoundTripAndLegacyRejection();
|
||
void theStoreRefusesWhatSwiftRefuses();
|
||
void canvasAndImageSizeMoveGuides();
|
||
void flipCanvasMirrorsGuides();
|
||
void snapTargetsFollowViewMenu();
|
||
void layoutGridLinesIncludeMajorsAndSubdivisions();
|
||
void aDragCreatesMovesAndDeletesGuides();
|
||
void aDragSnapsAndHitsTheNearestGuide();
|
||
void drawnPointsSnapToTheSnapToTargets();
|
||
};
|
||
|
||
void GuideTests::newGuidesUndoAndClear()
|
||
{
|
||
EditorSession session;
|
||
session.createDocument(200, 100);
|
||
const CanvasGuide first = vertical(40), second = horizontal(25);
|
||
session.addGuide(first);
|
||
session.addGuide(second);
|
||
QCOMPARE(int(guides(session).size()), 2);
|
||
QCOMPARE(session.history.undoName(), QString("New Guide"));
|
||
QVERIFY(session.canClearGuides());
|
||
session.undo();
|
||
QCOMPARE(guides(session), std::vector<CanvasGuide>{first});
|
||
session.clearGuides();
|
||
QVERIFY(guides(session).empty());
|
||
QCOMPARE(session.history.undoName(), QString("Clear Guides"));
|
||
QVERIFY(!session.canClearGuides());
|
||
session.undo();
|
||
QCOMPARE(guides(session), std::vector<CanvasGuide>{first});
|
||
// A hidden guide shows again once one is added.
|
||
session.setShowsGuides(false);
|
||
session.addGuide(horizontal(5));
|
||
QVERIFY(session.showsGuides());
|
||
}
|
||
|
||
void GuideTests::lockPreventsCreatingAndMoving()
|
||
{
|
||
EditorSession session;
|
||
session.createDocument(200, 100);
|
||
session.setLocksGuides(true);
|
||
session.addGuide(vertical(10));
|
||
QVERIFY(guides(session).empty());
|
||
session.setLocksGuides(false);
|
||
const CanvasGuide guide = vertical(10);
|
||
session.addGuide(guide);
|
||
session.setLocksGuides(true);
|
||
session.beginGuideMove(guide);
|
||
QVERIFY(!session.guideDrag());
|
||
session.beginGuideCreation(CanvasGuide::Axis::vertical, 50);
|
||
QVERIFY(!session.guideDrag());
|
||
QVERIFY(!session.hitGuide(session.viewport.viewPoint(QPointF(10, 10), QSizeF(200, 100))));
|
||
// Clear Guides still works while locked.
|
||
session.clearGuides();
|
||
QVERIFY(guides(session).empty());
|
||
// Every gate Swift names closes the guides.
|
||
session.setLocksGuides(false);
|
||
QVERIFY(session.canEditGuides());
|
||
for (const auto &[name, close, open] : std::vector<std::tuple<const char *, std::function<void()>, std::function<void()>>>{
|
||
{"busy", [&] { session.setIsProjectBusy(true); }, [&] { session.setIsProjectBusy(false); }},
|
||
{"importing", [&] { session.setIsImporting(true); }, [&] { session.setIsImporting(false); }},
|
||
{"new document", [&] { session.setShowsNewDocument(true); }, [&] { session.setShowsNewDocument(false); }},
|
||
{"renaming", [&] { session.setRenamingLayerID(QUuid::createUuid()); }, [&] { session.setRenamingLayerID(std::nullopt); }}}) {
|
||
close();
|
||
QVERIFY2(!session.canEditGuides(), name);
|
||
open();
|
||
QVERIFY2(session.canEditGuides(), name);
|
||
}
|
||
// The colour edits close the guides too.
|
||
QImage red(10, 10, QImage::Format_RGBA8888_Premultiplied);
|
||
red.fill(Qt::red);
|
||
session.insert(ImportedImage(red, red, QStringLiteral("Red")));
|
||
session.beginLevels();
|
||
QVERIFY(session.levels() && !session.canEditGuides());
|
||
session.cancelLevels();
|
||
session.beginHueSaturation();
|
||
QVERIFY(session.hueSaturation() && !session.canEditGuides());
|
||
session.cancelHueSaturation();
|
||
session.beginFilter(FilterKind::gaussianBlur);
|
||
QVERIFY(session.filterEdit() && !session.canEditGuides());
|
||
session.cancelFilter();
|
||
QVERIFY(session.canEditGuides());
|
||
EditorSession empty;
|
||
QVERIFY(!empty.canEditGuides() && !empty.canClearGuides());
|
||
}
|
||
|
||
void GuideTests::projectRoundTripAndLegacyRejection()
|
||
{
|
||
QTemporaryDir root;
|
||
EditorSession session;
|
||
session.createDocument(80, 40);
|
||
QVERIFY(!session.projectSnapshot().value().manifest.guides);
|
||
const CanvasGuide first = vertical(16), second = horizontal(12);
|
||
session.addGuide(first);
|
||
session.addGuide(second);
|
||
ProjectSnapshot snapshot = session.projectSnapshot().value();
|
||
QCOMPARE(snapshot.manifest.version, qint64(11));
|
||
QCOMPARE(snapshot.manifest.guides.value(), (std::vector<CanvasGuide>{first, second}));
|
||
// Swift's keys: an array of id, axis and position.
|
||
const QByteArray json = snapshot.manifest.encoded();
|
||
QVERIFY(json.contains("\"axis\": \"vertical\"") && json.contains("\"position\": 16") && json.contains(uuidString(first.id).toUtf8()));
|
||
const QString path = root.filePath("Guides.comp");
|
||
ProjectStore::save(snapshot, path);
|
||
EditorSession reopened;
|
||
reopened.installProject(ProjectStore::load(path), path);
|
||
QCOMPARE(guides(reopened), (std::vector<CanvasGuide>{first, second}));
|
||
// Version 7 carries no guides.
|
||
snapshot.manifest.version = 7;
|
||
QVERIFY_THROWS_EXCEPTION(ProjectError, ProjectStore::save(snapshot, root.filePath("Legacy.comp")));
|
||
try {
|
||
ProjectStore::save(snapshot, root.filePath("Legacy.comp"));
|
||
} catch (const ProjectError &error) {
|
||
QCOMPARE(error.kind, ProjectError::Kind::invalid);
|
||
}
|
||
}
|
||
|
||
void GuideTests::theStoreRefusesWhatSwiftRefuses()
|
||
{
|
||
EditorSession session;
|
||
session.createDocument(80, 40);
|
||
const ProjectSnapshot plain = session.projectSnapshot().value();
|
||
const auto refusal = [&](const std::vector<CanvasGuide> &lines) -> std::optional<ProjectError::Kind> {
|
||
ProjectSnapshot snapshot = plain;
|
||
snapshot.manifest.guides = lines;
|
||
QTemporaryDir root;
|
||
try {
|
||
ProjectStore::save(snapshot, root.filePath("Rules.comp"));
|
||
} catch (const ProjectError &error) {
|
||
return error.kind;
|
||
}
|
||
return std::nullopt;
|
||
};
|
||
QCOMPARE(refusal({vertical(1'000'000), horizontal(-1'000'000)}), std::nullopt);
|
||
QCOMPARE(refusal({vertical(1'000'000.5)}), std::optional(ProjectError::Kind::invalid));
|
||
QCOMPARE(refusal({vertical(std::nan(""))}), std::optional(ProjectError::Kind::invalid));
|
||
const CanvasGuide twice = vertical(3);
|
||
QCOMPARE(refusal({twice, CanvasGuide{twice.id, CanvasGuide::Axis::horizontal, 4}}), std::optional(ProjectError::Kind::invalid));
|
||
std::vector<CanvasGuide> many(1'000);
|
||
for (CanvasGuide &guide : many)
|
||
guide = vertical(1);
|
||
QCOMPARE(refusal(many), std::nullopt);
|
||
many.push_back(vertical(2));
|
||
QCOMPARE(refusal(many), std::optional(ProjectError::Kind::tooLarge));
|
||
// The decoder refuses strange axes, missing keys, non-arrays.
|
||
const QByteArray base = plain.manifest.encoded();
|
||
const auto decodes = [&](const QByteArray &guidesJson) {
|
||
QByteArray json = base;
|
||
json.insert(json.lastIndexOf('}'), ",\n \"guides\": " + guidesJson + "\n");
|
||
try {
|
||
return ProjectManifest::decoded(json).guides.has_value();
|
||
} catch (const ProjectError &) {
|
||
return false;
|
||
}
|
||
};
|
||
const QByteArray id = uuidString(QUuid::createUuid()).toUtf8();
|
||
QVERIFY(decodes("[{\"id\": \"" + id + "\", \"axis\": \"vertical\", \"position\": 5}]"));
|
||
QVERIFY(decodes("null") == false);
|
||
QVERIFY(!decodes("[{\"id\": \"" + id + "\", \"axis\": \"diagonal\", \"position\": 5}]"));
|
||
QVERIFY(!decodes("[{\"id\": \"" + id + "\", \"axis\": \"vertical\"}]"));
|
||
QVERIFY(!decodes("[{\"id\": \"" + id + "\", \"axis\": \"vertical\", \"position\": \"5\"}]"));
|
||
QVERIFY(!decodes("[5]"));
|
||
QVERIFY(!decodes("{}"));
|
||
}
|
||
|
||
void GuideTests::canvasAndImageSizeMoveGuides()
|
||
{
|
||
EditorSession session;
|
||
session.createDocument(100, 50);
|
||
session.addGuide(vertical(20));
|
||
session.addGuide(horizontal(10));
|
||
const ProjectSnapshot source = session.projectSnapshot().value();
|
||
// Anchor 8 is bottom right: room goes left and above.
|
||
const ProjectSnapshot expanded = CanvasResizer::resize(source, CanvasSizeOptions{.width = 140, .height = 80, .anchor = 8});
|
||
QCOMPARE(expanded.manifest.guides.value()[0].position, 60.0);
|
||
QCOMPARE(expanded.manifest.guides.value()[1].position, 40.0);
|
||
const ProjectSnapshot scaled = ImageResizer::resize(source, ImageSizeOptions{.width = 200, .height = 100, .resolution = 72});
|
||
QCOMPARE(scaled.manifest.guides.value()[0].position, 40.0);
|
||
QCOMPARE(scaled.manifest.guides.value()[1].position, 20.0);
|
||
// Each axis scales by its own side.
|
||
const ProjectSnapshot wide = ImageResizer::resize(source, ImageSizeOptions{.width = 200, .height = 50, .resolution = 72});
|
||
QCOMPARE(wide.manifest.guides.value()[0].position, 40.0);
|
||
QCOMPARE(wide.manifest.guides.value()[1].position, 10.0);
|
||
const ProjectSnapshot resolved = ImageResizer::resize(source, ImageSizeOptions{.width = 100, .height = 50, .resolution = 144});
|
||
QCOMPARE(resolved.manifest.guides, source.manifest.guides);
|
||
// None stay none; the session's rebuild keeps them.
|
||
EditorSession bare;
|
||
bare.createDocument(10, 10);
|
||
QVERIFY(!CanvasResizer::resize(bare.projectSnapshot().value(), CanvasSizeOptions{.width = 20, .height = 20}).manifest.guides);
|
||
QVERIFY(!ImageResizer::resize(bare.projectSnapshot().value(), ImageSizeOptions{.width = 20, .height = 20, .resolution = 72}).manifest.guides);
|
||
session.applyDocumentSize(expanded, QStringLiteral("Canvas Size"));
|
||
QCOMPARE(guides(session), expanded.manifest.guides.value());
|
||
}
|
||
|
||
void GuideTests::flipCanvasMirrorsGuides()
|
||
{
|
||
EditorSession session;
|
||
session.createDocument(100, 40);
|
||
session.addGuide(vertical(20));
|
||
session.addGuide(horizontal(10));
|
||
session.flipCanvas(true);
|
||
QCOMPARE(guides(session)[0].position, 80.0);
|
||
QCOMPARE(guides(session)[1].position, 10.0);
|
||
session.flipCanvas(false);
|
||
QCOMPARE(guides(session)[0].position, 80.0);
|
||
QCOMPARE(guides(session)[1].position, 30.0);
|
||
session.undo();
|
||
QCOMPARE(guides(session)[1].position, 10.0);
|
||
}
|
||
|
||
void GuideTests::snapTargetsFollowViewMenu()
|
||
{
|
||
const auto session = paintedSession();
|
||
// The canvas and the centred layer: 150–250, 120–180.
|
||
const SnapGuides crop = session->cropSnapTargets();
|
||
QCOMPARE(set(crop.xs), (QSet<double>{0, 400, 150, 250}));
|
||
QCOMPARE(set(crop.ys), (QSet<double>{0, 300, 120, 180}));
|
||
QCOMPARE(session->transformSnapTargets({}).xs, (std::vector<double>{0, 400, 200, 150, 200, 250}));
|
||
session->setSnapEnabled(false);
|
||
QVERIFY(session->cropSnapTargets().xs.empty() && session->cropSnapTargets().ys.empty());
|
||
session->setSnapEnabled(true);
|
||
session->setSnapToLayers(false);
|
||
QCOMPARE(set(session->cropSnapTargets().xs), (QSet<double>{0, 400}));
|
||
session->setSnapToDocumentBounds(false);
|
||
QVERIFY(session->cropSnapTargets().xs.empty());
|
||
session->setSnapToGuides(true);
|
||
session->setShowsGuides(true);
|
||
session->addGuide(vertical(33));
|
||
QCOMPARE(session->cropSnapTargets().xs, std::vector<double>{33});
|
||
// Hidden extras do not snap.
|
||
session->setShowsGuides(false);
|
||
QVERIFY(session->cropSnapTargets().xs.empty());
|
||
session->setShowsGuides(true);
|
||
session->setSnapToGuides(false);
|
||
QVERIFY(session->cropSnapTargets().xs.empty());
|
||
session->setShowsGrid(true);
|
||
QVERIFY(session->cropSnapTargets().xs.empty());
|
||
session->setSnapToGrid(true);
|
||
QVERIFY(set(session->cropSnapTargets().xs).contains(64) && set(session->cropSnapTargets().xs).contains(8));
|
||
// Each axis takes the lines along its own edge.
|
||
QVERIFY(set(session->cropSnapTargets().xs).contains(392) && !set(session->cropSnapTargets().ys).contains(304));
|
||
session->setShowsGrid(false);
|
||
QVERIFY(session->cropSnapTargets().xs.empty());
|
||
// A move snaps to a guide through the same targets.
|
||
session->setSnapToGuides(true);
|
||
const QUuid red = session->activeLayerID().value();
|
||
QCOMPARE(session->snappedMove(LayerTransform{.origin = {35, 0}, .size = {100, 60}}, {red}, 5).origin.x(), 33.0);
|
||
}
|
||
|
||
void GuideTests::layoutGridLinesIncludeMajorsAndSubdivisions()
|
||
{
|
||
const LayoutGrid grid;
|
||
const std::vector<double> lines = grid.lines(64);
|
||
QCOMPARE(int(lines.size()), 9);
|
||
QVERIFY(lines.front() == 0 && lines.back() == 64 && lines[1] == 8);
|
||
QVERIFY(grid.isMajor(0) && grid.isMajor(64) && grid.isMajor(128) && !grid.isMajor(8));
|
||
QVERIFY(grid.isMajor(63.6) && !grid.isMajor(63.4));
|
||
QCOMPARE(grid.lines(-1), std::vector<double>{0});
|
||
QCOMPARE(grid.lines(qQNaN()), std::vector<double>{0});
|
||
QCOMPARE(grid.lines(7.9), std::vector<double>{0});
|
||
QCOMPARE(int(grid.lines(20).size()), 3);
|
||
}
|
||
|
||
void GuideTests::aDragCreatesMovesAndDeletesGuides()
|
||
{
|
||
const auto session = paintedSession();
|
||
session->setShowsGuides(false);
|
||
QSignalSpy changes(session.get(), &EditorSession::changed);
|
||
session->beginGuideCreation(CanvasGuide::Axis::vertical, 40.3);
|
||
QVERIFY(session->showsGuides() && changes.count() == 1);
|
||
QCOMPARE(session->displayedGuides().size(), size_t(1));
|
||
QVERIFY(guides(*session).empty());
|
||
session->moveGuideDrag(70);
|
||
QCOMPARE(session->displayedGuides()[0].position, 70.0);
|
||
session->finishGuideDrag(false);
|
||
QCOMPARE(guides(*session)[0].position, 70.0);
|
||
QCOMPARE(session->history.undoName(), QString("New Guide"));
|
||
QVERIFY(!session->guideDrag());
|
||
// A new guide dropped on a ruler is never made.
|
||
const int steps = session->history.undoCount();
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 30);
|
||
session->finishGuideDrag(true);
|
||
QCOMPARE(guides(*session).size(), size_t(1));
|
||
QCOMPARE(session->history.undoCount(), steps);
|
||
// An old one moves as one step, or none unmoved.
|
||
const CanvasGuide guide = guides(*session)[0];
|
||
session->beginGuideMove(guide);
|
||
QCOMPARE(session->guideDrag().value(), (GuideDrag{guide.id, guide.axis, 70, false, 70.0}));
|
||
session->finishGuideDrag(false);
|
||
QCOMPARE(session->history.undoCount(), steps);
|
||
session->beginGuideMove(guide);
|
||
session->moveGuideDrag(90);
|
||
QCOMPARE(session->displayedGuides(), (std::vector<CanvasGuide>{{guide.id, guide.axis, 90}}));
|
||
session->finishGuideDrag(false);
|
||
QCOMPARE(session->history.undoName(), QString("Move Guide"));
|
||
QCOMPARE(guides(*session)[0].position, 90.0);
|
||
// A guide cleared mid-drag no longer shows.
|
||
session->beginGuideMove(guides(*session)[0]);
|
||
session->clearGuides();
|
||
QVERIFY(session->displayedGuides().empty());
|
||
session->cancelGuideDrag();
|
||
session->undo();
|
||
QCOMPARE(guides(*session)[0].position, 90.0);
|
||
// Cancelled, nothing changes; dropped on a ruler, it goes.
|
||
session->beginGuideMove(guides(*session)[0]);
|
||
session->moveGuideDrag(20);
|
||
session->cancelGuideDrag();
|
||
QVERIFY(!session->guideDrag());
|
||
QCOMPARE(guides(*session)[0].position, 90.0);
|
||
session->beginGuideMove(guides(*session)[0]);
|
||
session->finishGuideDrag(true);
|
||
QVERIFY(guides(*session).empty());
|
||
QCOMPARE(session->history.undoName(), QString("Delete Guide"));
|
||
session->finishGuideDrag(false);
|
||
session->moveGuideDrag(5);
|
||
QVERIFY(guides(*session).empty() && !session->guideDrag());
|
||
// A project installed or cleared drops the drag.
|
||
session->beginGuideCreation(CanvasGuide::Axis::vertical, 10);
|
||
session->installProject(session->projectSnapshot().value(), QString());
|
||
QVERIFY(!session->guideDrag());
|
||
session->beginGuideCreation(CanvasGuide::Axis::vertical, 10);
|
||
session->clearProject();
|
||
QVERIFY(!session->guideDrag());
|
||
}
|
||
|
||
void GuideTests::aDragSnapsAndHitsTheNearestGuide()
|
||
{
|
||
const auto session = paintedSession();
|
||
session->viewport.resize(QSizeF(400, 300), 1, QSizeF(400, 300));
|
||
session->zoom(1);
|
||
// Within ten view points: the layer's left edge at 150.
|
||
session->beginGuideCreation(CanvasGuide::Axis::vertical, 144);
|
||
QCOMPARE(session->guideDrag().value().position, 150.0);
|
||
session->moveGuideDrag(139);
|
||
QCOMPARE(session->guideDrag().value().position, 139.0);
|
||
// The nearest wins: the middle 200 over 150 at 180.
|
||
session->moveGuideDrag(193);
|
||
QCOMPARE(session->guideDrag().value().position, 200.0);
|
||
session->finishGuideDrag(false);
|
||
// Another guide snaps, but not the dragged one itself.
|
||
const CanvasGuide placed = guides(*session)[0];
|
||
session->beginGuideCreation(CanvasGuide::Axis::vertical, 196);
|
||
QCOMPARE(session->guideDrag().value().position, 200.0);
|
||
session->cancelGuideDrag();
|
||
session->setSnapToLayers(false);
|
||
// The canvas's middle snaps too, from its bounds alone.
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 146);
|
||
QCOMPARE(session->guideDrag().value().position, 150.0);
|
||
session->cancelGuideDrag();
|
||
session->setSnapToDocumentBounds(false);
|
||
session->beginGuideMove(placed);
|
||
session->moveGuideDrag(203);
|
||
QCOMPARE(session->guideDrag().value().position, 203.0);
|
||
session->cancelGuideDrag();
|
||
// The grid's lines snap only while shown and asked.
|
||
session->setSnapToGrid(true);
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 61);
|
||
QCOMPARE(session->guideDrag().value().position, 61.0);
|
||
session->cancelGuideDrag();
|
||
session->setShowsGrid(true);
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 61);
|
||
QCOMPARE(session->guideDrag().value().position, 64.0);
|
||
session->cancelGuideDrag();
|
||
// Ten view points reach 2.5 pixels at 400%.
|
||
session->zoom(4);
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 60);
|
||
QCOMPARE(session->guideDrag().value().position, 60.0);
|
||
session->moveGuideDrag(62);
|
||
QCOMPARE(session->guideDrag().value().position, 64.0);
|
||
session->cancelGuideDrag();
|
||
session->zoom(1);
|
||
session->setSnapEnabled(false);
|
||
session->beginGuideCreation(CanvasGuide::Axis::horizontal, 61);
|
||
QCOMPARE(session->guideDrag().value().position, 61.0);
|
||
session->cancelGuideDrag();
|
||
// A press finds the nearest guide within five points.
|
||
session->addGuide(horizontal(100));
|
||
session->addGuide(horizontal(104));
|
||
const QPointF near = session->viewport.viewPoint(QPointF(0, 103), QSizeF(400, 300));
|
||
QCOMPARE(session->hitGuide(near).value().position, 104.0);
|
||
QVERIFY(!session->hitGuide(near + QPointF(0, 7)));
|
||
QCOMPARE(session->hitGuide(near + QPointF(0, 7), 7).value().position, 104.0);
|
||
QCOMPARE(session->hitGuide(session->viewport.viewPoint(QPointF(201, 0), QSizeF(400, 300))).value().id, placed.id);
|
||
session->setShowsGuides(false);
|
||
QVERIFY(!session->hitGuide(near));
|
||
}
|
||
|
||
// Swift's drawnPointsSnapToTheSnapToTargets, as written.
|
||
void GuideTests::drawnPointsSnapToTheSnapToTargets()
|
||
{
|
||
const auto session = paintedSession();
|
||
session->setSnapToLayers(false);
|
||
session->setSnapToDocumentBounds(true);
|
||
session->setShowsGrid(true);
|
||
session->setSnapToGrid(true);
|
||
// Each axis alone: grid every 8, the edge at 400.
|
||
QCOMPARE(session->snappedPoint(QPointF(62, 20), 3), QPointF(64, 20));
|
||
QCOMPARE(session->snappedPoint(QPointF(397.5, 9), 3), QPointF(400, 8));
|
||
QVERIFY(session->snapGuides.xs == std::vector<double>{400} && session->snapGuides.ys == std::vector<double>{8});
|
||
// A tie keeps the first line met; reach counts inclusively.
|
||
QCOMPARE(session->snappedPoint(QPointF(4, 17), 4), QPointF(0, 16));
|
||
// Two lines in reach: the nearer wins.
|
||
QCOMPARE(session->snappedPoint(QPointF(3, 19), 5), QPointF(0, 16));
|
||
QCOMPARE(session->snappedPoint(QPointF(5, 21), 5), QPointF(8, 24));
|
||
QCOMPARE(session->snappedPoint(QPointF(63, 11), 1), QPointF(64, 11));
|
||
// The canvas's middle is no target for a drawn point.
|
||
QCOMPARE(session->snappedPoint(QPointF(62, 149), 2), QPointF(64, 149));
|
||
QVERIFY(session->snapGuides.ys.empty());
|
||
session->setSnapEnabled(false);
|
||
session->snapGuides = {{1}, {2}};
|
||
QCOMPARE(session->snappedPoint(QPointF(62, 20), 3), QPointF(62, 20));
|
||
QVERIFY(session->snapGuides.xs.empty() && session->snapGuides.ys.empty());
|
||
session->setSnapEnabled(true);
|
||
session->setSnapEnabled(false);
|
||
QCOMPARE(session->snappedPoint(QPointF(62, 20), 3), QPointF(62, 20));
|
||
QVERIFY(session->snapGuides.xs.empty() && session->snapGuides.ys.empty());
|
||
session->setSnapEnabled(true);
|
||
session->setShowsGrid(false);
|
||
session->setSnapToGrid(false);
|
||
}
|
||
|
||
QTEST_GUILESS_MAIN(GuideTests)
|
||
#include "GuideTests.moc"
|