354 lines
18 KiB
C++
354 lines
18 KiB
C++
#include "CanvasFixtures.h"
|
|
#include <QSignalSpy>
|
|
|
|
// The canvas: Zoom, Hand, Space, the wheel, a pinch.
|
|
class CanvasNavigationTests : public QObject {
|
|
Q_OBJECT
|
|
private slots:
|
|
void theZoomToolClicksAndDrags();
|
|
void theHandToolAndSpaceDragTheView();
|
|
void theWheelPansAndZoomsWithAModifier();
|
|
void aPinchZoomsAboutThePointer();
|
|
void theMiddleButtonPansFromAnyTool();
|
|
void ctrlOrMetaWithTheMiddleButtonZooms();
|
|
};
|
|
|
|
void CanvasNavigationTests::theZoomToolClicksAndDrags()
|
|
{
|
|
Shown shown;
|
|
CanvasView &canvas = *shown.canvas;
|
|
EditorSession &session = shown.session;
|
|
shown.settle();
|
|
session.zoom(1);
|
|
session.selectTool(NavigationTool::zoom);
|
|
const QSizeF size = shown.documentSize();
|
|
const QPointF click(120, 90);
|
|
const QPointF under = session.viewport.documentPoint(click, size);
|
|
QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, click.toPoint());
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
QCOMPARE(session.viewport.documentPoint(click, size), under);
|
|
QTest::mouseClick(&canvas, Qt::LeftButton, Qt::AltModifier, click.toPoint());
|
|
QCOMPARE(session.viewport.zoom(), 1.0);
|
|
// Dragging 100 points doubles about the start; release adds nothing.
|
|
const QPointF start(260, 110);
|
|
const QPointF anchor = session.viewport.documentPoint(start, size);
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, start.toPoint());
|
|
drag(canvas, QPointF(360, 110));
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
QCOMPARE(session.viewport.documentPoint(start, size), anchor);
|
|
drag(canvas, QPointF(310, 110));
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), std::sqrt(2.0)));
|
|
QCOMPARE(session.viewport.documentPoint(start, size), anchor);
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(310, 110));
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), std::sqrt(2.0)));
|
|
// Two points of drift still count as a click.
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
|
|
drag(canvas, QPointF(102, 100));
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(102, 100));
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), 2 * std::sqrt(2.0)));
|
|
// Busy, importing or without a document a press does nothing.
|
|
session.setIsProjectBusy(true);
|
|
QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, click.toPoint());
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), 2 * std::sqrt(2.0)));
|
|
session.setIsProjectBusy(false);
|
|
session.setIsImporting(true);
|
|
QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, click.toPoint());
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), 2 * std::sqrt(2.0)));
|
|
session.setIsImporting(false);
|
|
QTest::mouseClick(&canvas, Qt::RightButton, Qt::NoModifier, click.toPoint());
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), 2 * std::sqrt(2.0)));
|
|
Shown empty(std::nullopt);
|
|
empty.session.selectTool(NavigationTool::zoom);
|
|
QTest::mouseClick(empty.canvas, Qt::LeftButton, Qt::NoModifier, click.toPoint());
|
|
QCOMPARE(empty.session.viewport.zoom(), 1.0);
|
|
}
|
|
|
|
void CanvasNavigationTests::theHandToolAndSpaceDragTheView()
|
|
{
|
|
Shown shown;
|
|
CanvasView &canvas = *shown.canvas;
|
|
EditorSession &session = shown.session;
|
|
shown.settle();
|
|
session.zoom(1);
|
|
session.selectTool(NavigationTool::hand);
|
|
canvas.synchronizeDisplay();
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
QSignalSpy changes(&session, &EditorSession::changed);
|
|
const QSizeF pan = session.viewport.pan;
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
drag(canvas, QPointF(130, 120));
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(30, 20));
|
|
QVERIFY(!session.viewport.followsFit());
|
|
QCOMPARE(changes.count(), 1);
|
|
drag(canvas, QPointF(135, 120));
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(35, 20));
|
|
QCOMPARE(changes.count(), 2);
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(135, 120));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
drag(canvas, QPointF(150, 120));
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(35, 20));
|
|
// Space drags the view in any tool.
|
|
session.selectTool(NavigationTool::move);
|
|
canvas.synchronizeDisplay();
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ArrowCursor);
|
|
QTRY_VERIFY(canvas.hasFocus());
|
|
QTest::keyPress(&canvas, Qt::Key_Space);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
// Swift 1.4.7: Space's repeats keep the hand closed all through.
|
|
for (int repeat = 0; repeat < 3; ++repeat) {
|
|
QKeyEvent release(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier, QStringLiteral(" "), true);
|
|
QApplication::sendEvent(&canvas, &release);
|
|
QKeyEvent press(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, QStringLiteral(" "), true);
|
|
QApplication::sendEvent(&canvas, &press);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
}
|
|
drag(canvas, QPointF(60, 70));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(45, 40));
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(60, 70));
|
|
// Space still held: the hand opens with the button.
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
// A repeat's release is no release: the hand stays.
|
|
QKeyEvent repeated(QEvent::KeyRelease, Qt::Key_Space, Qt::NoModifier, QStringLiteral(" "), true);
|
|
QApplication::sendEvent(&canvas, &repeated);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
QTest::keyRelease(&canvas, Qt::Key_Space);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ArrowCursor);
|
|
// Space let go mid-pan: the pan goes on, closed.
|
|
QTest::keyPress(&canvas, Qt::Key_Space);
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
|
|
QTest::keyRelease(&canvas, Qt::Key_Space);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
drag(canvas, QPointF(55, 50));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(50, 40));
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(55, 50));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ArrowCursor);
|
|
// Importing, a press starts no drag.
|
|
session.selectTool(NavigationTool::hand);
|
|
session.setIsImporting(true);
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
|
|
drag(canvas, QPointF(90, 90));
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(50, 40));
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(90, 90));
|
|
}
|
|
|
|
void CanvasNavigationTests::theWheelPansAndZoomsWithAModifier()
|
|
{
|
|
Shown shown;
|
|
CanvasView &canvas = *shown.canvas;
|
|
EditorSession &session = shown.session;
|
|
shown.settle();
|
|
session.zoom(1);
|
|
const QSizeF size = shown.documentSize();
|
|
const QPointF pointer(120, 90);
|
|
const double lines = QApplication::wheelScrollLines();
|
|
QSizeF pan = session.viewport.pan;
|
|
// Pixels count as they are, and win over notches.
|
|
wheel(canvas, pointer, QPoint(0, -30), QPoint(0, -240), Qt::NoModifier);
|
|
pan += QSizeF(0, -30);
|
|
QCOMPARE(session.viewport.pan, pan);
|
|
QVERIFY(!session.viewport.followsFit());
|
|
// A notch is a few lines, twelve points each.
|
|
wheel(canvas, pointer, QPoint(), QPoint(0, 120), Qt::NoModifier);
|
|
pan += QSizeF(0, 12 * lines);
|
|
QCOMPARE(session.viewport.pan, pan);
|
|
wheel(canvas, pointer, QPoint(), QPoint(-60, 0), Qt::NoModifier);
|
|
pan += QSizeF(-6 * lines, 0);
|
|
QCOMPARE(session.viewport.pan, pan);
|
|
// Ctrl or Alt zooms about the pointer, by Swift's curve.
|
|
const QPointF under = session.viewport.documentPoint(pointer, size);
|
|
wheel(canvas, pointer, QPoint(), QPoint(0, 120), Qt::ControlModifier);
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), std::exp(-lines * 0.015)));
|
|
QCOMPARE(session.viewport.documentPoint(pointer, size), under);
|
|
wheel(canvas, pointer, QPoint(0, 10), QPoint(), Qt::AltModifier);
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), std::exp(-lines * 0.015) * std::exp(-10 * 0.015)));
|
|
QCOMPARE(session.viewport.documentPoint(pointer, size), under);
|
|
// Without a document the wheel does nothing.
|
|
Shown empty(std::nullopt);
|
|
wheel(*empty.canvas, pointer, QPoint(0, -30), QPoint(), Qt::NoModifier);
|
|
QCOMPARE(empty.session.viewport.pan, QSizeF(0, 0));
|
|
wheel(*empty.canvas, pointer, QPoint(), QPoint(0, 120), Qt::ControlModifier);
|
|
QCOMPARE(empty.session.viewport.zoom(), 1.0);
|
|
}
|
|
|
|
void CanvasNavigationTests::aPinchZoomsAboutThePointer()
|
|
{
|
|
Shown shown;
|
|
shown.settle();
|
|
shown.session.zoom(1);
|
|
const QSizeF size = shown.documentSize();
|
|
const QPointF pointer(120, 90);
|
|
const QPointF under = shown.session.viewport.documentPoint(pointer, size);
|
|
QNativeGestureEvent pinch(Qt::ZoomNativeGesture, QPointingDevice::primaryPointingDevice(), 2, pointer, pointer,
|
|
shown.canvas->mapToGlobal(pointer.toPoint()), 0.5, QPointF());
|
|
QVERIFY(QApplication::sendEvent(shown.canvas, &pinch));
|
|
QCOMPARE(shown.session.viewport.zoom(), 1.5);
|
|
QCOMPARE(shown.session.viewport.documentPoint(pointer, size), under);
|
|
// Other gestures pass on.
|
|
QNativeGestureEvent swipe(Qt::SwipeNativeGesture, QPointingDevice::primaryPointingDevice(), 3, pointer, pointer,
|
|
shown.canvas->mapToGlobal(pointer.toPoint()), 0.5, QPointF());
|
|
QApplication::sendEvent(shown.canvas, &swipe);
|
|
QCOMPARE(shown.session.viewport.zoom(), 1.5);
|
|
}
|
|
|
|
|
|
void CanvasNavigationTests::theMiddleButtonPansFromAnyTool()
|
|
{
|
|
Shown shown;
|
|
CanvasView &canvas = *shown.canvas;
|
|
EditorSession &session = shown.session;
|
|
shown.settle();
|
|
session.zoom(1);
|
|
session.selectTool(NavigationTool::brush);
|
|
canvas.synchronizeDisplay();
|
|
const auto move = [&](QPointF to, Qt::MouseButtons buttons) {
|
|
QMouseEvent event(QEvent::MouseMove, to, to, canvas.mapToGlobal(to.toPoint()), Qt::NoButton, buttons, Qt::NoModifier);
|
|
QApplication::sendEvent(&canvas, &event);
|
|
};
|
|
move(QPointF(100, 100), Qt::NoButton);
|
|
const Qt::CursorShape brush = canvas.cursor().shape();
|
|
QVERIFY(canvas.brushCursor().circle().has_value());
|
|
const QSizeF pan = session.viewport.pan;
|
|
QSignalSpy changes(&session, &EditorSession::changed);
|
|
// The circle hides and nothing paints; the view follows.
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(100, 100));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
QVERIFY(!canvas.brushCursor().circle().has_value());
|
|
move(QPointF(130, 120), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(30, 20));
|
|
QCOMPARE(changes.count(), 1);
|
|
move(QPointF(150, 150), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(50, 50));
|
|
move(QPointF(130, 120), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(30, 20));
|
|
QVERIFY(!session.brushStroke() && !canvas.brushCursor().circle().has_value());
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(130, 120));
|
|
QCOMPARE(canvas.cursor().shape(), brush);
|
|
QVERIFY(canvas.brushCursor().circle().has_value());
|
|
// Beside a Hand drag each button keeps its own point.
|
|
session.selectTool(NavigationTool::hand);
|
|
canvas.synchronizeDisplay();
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(200, 200));
|
|
move(QPointF(60, 50), Qt::LeftButton | Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.pan, pan + QSizeF(30, 20) + QSizeF(10, 0) + QSizeF(-140, -150));
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(60, 50));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::ClosedHandCursor);
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(60, 50));
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
// A lost release: a buttonless move ends the pan.
|
|
const QSizeF held = session.viewport.pan;
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
|
|
move(QPointF(70, 70), Qt::NoButton);
|
|
QCOMPARE(canvas.cursor().shape(), Qt::OpenHandCursor);
|
|
move(QPointF(90, 90), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.pan, held);
|
|
// Without a document the middle button does nothing.
|
|
Shown empty(std::nullopt);
|
|
empty.settle();
|
|
const Qt::CursorShape before = empty.canvas->cursor().shape();
|
|
QTest::mousePress(empty.canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
|
|
QCOMPARE(empty.canvas->cursor().shape(), before);
|
|
}
|
|
|
|
// Swift 1.4.9 (`e03923f`), as Blender: up zooms in, down out.
|
|
void CanvasNavigationTests::ctrlOrMetaWithTheMiddleButtonZooms()
|
|
{
|
|
Shown shown;
|
|
CanvasView &canvas = *shown.canvas;
|
|
EditorSession &session = shown.session;
|
|
shown.settle();
|
|
session.zoom(1);
|
|
// The Zoom tool's own magnifiers, plus and minus.
|
|
session.selectTool(NavigationTool::zoom);
|
|
canvas.synchronizeDisplay();
|
|
const QImage plus = canvas.cursor().pixmap().toImage();
|
|
QTest::keyPress(&canvas, Qt::Key_Alt, Qt::AltModifier);
|
|
const QImage minus = canvas.cursor().pixmap().toImage();
|
|
QTest::keyRelease(&canvas, Qt::Key_Alt);
|
|
QVERIFY(!plus.isNull() && plus != minus);
|
|
session.selectTool(NavigationTool::brush);
|
|
canvas.synchronizeDisplay();
|
|
const auto move = [&](QPointF to, Qt::MouseButtons buttons) {
|
|
QMouseEvent event(QEvent::MouseMove, to, to, canvas.mapToGlobal(to.toPoint()), Qt::NoButton, buttons, Qt::NoModifier);
|
|
QApplication::sendEvent(&canvas, &event);
|
|
};
|
|
move(QPointF(100, 100), Qt::NoButton);
|
|
const Qt::CursorShape brush = canvas.cursor().shape();
|
|
const QSizeF size = shown.documentSize();
|
|
// Off the view's middle, so the anchor shows.
|
|
const QPointF start(120, 210);
|
|
const QPointF anchor = session.viewport.documentPoint(start, size);
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::ControlModifier, start.toPoint());
|
|
QCOMPARE(canvas.cursor().pixmap().toImage(), plus);
|
|
QVERIFY(!canvas.brushCursor().circle().has_value());
|
|
// A hundred points up doubles about the press.
|
|
move(QPointF(180, 110), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
QCOMPARE(session.viewport.documentPoint(start, size), anchor);
|
|
// Below the press it zooms out, with the minus.
|
|
move(QPointF(120, 260), Qt::MiddleButton);
|
|
QVERIFY(qFuzzyCompare(session.viewport.zoom(), std::sqrt(0.5)));
|
|
QCOMPARE(session.viewport.documentPoint(start, size), anchor);
|
|
QCOMPARE(canvas.cursor().pixmap().toImage(), minus);
|
|
move(QPointF(120, 200), Qt::MiddleButton);
|
|
QCOMPARE(canvas.cursor().pixmap().toImage(), plus);
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::ControlModifier, QPoint(120, 200));
|
|
QCOMPARE(canvas.cursor().shape(), brush);
|
|
QVERIFY(canvas.brushCursor().circle().has_value());
|
|
const double kept = session.viewport.zoom();
|
|
move(QPointF(200, 40), Qt::NoButton);
|
|
QCOMPARE(session.viewport.zoom(), kept);
|
|
// Meta zooms too; a lost release ends it.
|
|
session.zoom(1);
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::MetaModifier, start.toPoint());
|
|
move(QPointF(120, 110), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
move(QPointF(200, 60), Qt::NoButton);
|
|
QCOMPARE(canvas.cursor().shape(), brush);
|
|
move(QPointF(200, -50), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
// A plain middle drag still pans, at the same zoom.
|
|
const QSizeF before = session.viewport.pan;
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(100, 100));
|
|
move(QPointF(110, 60), Qt::MiddleButton);
|
|
QVERIFY(session.viewport.zoom() == 2.0 && session.viewport.pan == before + QSizeF(10, -40));
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::NoModifier, QPoint(110, 60));
|
|
// From 75%, a hundred points up is 150%.
|
|
session.zoom(0.75);
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::ControlModifier, start.toPoint());
|
|
move(start - QPointF(0, 100), Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.zoom(), 1.5);
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::ControlModifier, (start - QPointF(0, 100)).toPoint());
|
|
// A right-button tip drag waits while the zoom goes on.
|
|
const double diameter = session.brushSettings().diameter;
|
|
QTest::mousePress(&canvas, Qt::RightButton, Qt::NoModifier, QPoint(100, 100));
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::ControlModifier, QPoint(100, 150));
|
|
move(QPointF(300, 50), Qt::RightButton | Qt::MiddleButton);
|
|
QVERIFY(session.viewport.zoom() == 3.0 && session.brushSettings().diameter == diameter);
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::ControlModifier, QPoint(300, 50));
|
|
QTest::mouseRelease(&canvas, Qt::RightButton, Qt::NoModifier, QPoint(100, 100));
|
|
// Beside a Hand drag: the zoom, then the pan.
|
|
session.zoom(1);
|
|
session.selectTool(NavigationTool::hand);
|
|
canvas.synchronizeDisplay();
|
|
CanvasViewport expected = session.viewport;
|
|
expected.setZoom(2, QPointF(200, 200), size);
|
|
expected.translate(QSizeF(10, 50));
|
|
QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
|
|
QTest::mousePress(&canvas, Qt::MiddleButton, Qt::ControlModifier, QPoint(200, 200));
|
|
move(QPointF(60, 100), Qt::LeftButton | Qt::MiddleButton);
|
|
QCOMPARE(session.viewport.zoom(), 2.0);
|
|
QCOMPARE(session.viewport.pan, expected.pan);
|
|
QTest::mouseRelease(&canvas, Qt::MiddleButton, Qt::ControlModifier, QPoint(60, 100));
|
|
QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, QPoint(60, 100));
|
|
}
|
|
|
|
QTEST_MAIN(CanvasNavigationTests)
|
|
#include "CanvasNavigationTests.moc"
|