Scrub numbers by dragging their labels in the tool bars and layer opacity (12.12c1)
This commit is contained in:
1 parent
f335818e53
commit
5bc7889387
25 files changed
+1126
-32
No files matched your search
@@ -0,0 +1,282 @@
|
||||
#include "UI/NumericScrub.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QtTest>
|
||||
|
||||
// Swift's NumericScrub: a label dragged sideways sets its value.
|
||||
namespace {
|
||||
// Counts the moves that pass the scrub, which runs first.
|
||||
struct MoveCounter : QObject {
|
||||
int moves = 0;
|
||||
bool eventFilter(QObject *, QEvent *event) override
|
||||
{
|
||||
moves += event->type() == QEvent::MouseMove;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Probe {
|
||||
QWidget window;
|
||||
QLabel *label = new QLabel(QStringLiteral("Size"), &window);
|
||||
QLineEdit *other = new QLineEdit(&window);
|
||||
double value = 10;
|
||||
QStringList log;
|
||||
MoveCounter counter;
|
||||
explicit Probe(double sensitivity = 1, double low = 0, double high = 100, std::optional<double> step = std::nullopt)
|
||||
{
|
||||
auto *row = new QHBoxLayout(&window);
|
||||
row->addWidget(label);
|
||||
row->addWidget(other);
|
||||
window.resize(300, 40);
|
||||
label->installEventFilter(&counter);
|
||||
new NumericScrub(label, {.sensitivity = sensitivity, .low = low, .high = high, .step = step, .value = [this] { return value; },
|
||||
.set = [this](double set) { value = set; log << QString::number(set); },
|
||||
.onStart = [this] { log << "began"; }, .onEnd = [this] { log << "ended"; }});
|
||||
window.show();
|
||||
if (!QTest::qWaitForWindowExposed(&window))
|
||||
throw std::runtime_error("the probe never showed");
|
||||
}
|
||||
};
|
||||
|
||||
// A mouse event at a widget's point, sent directly.
|
||||
bool send(QWidget *widget, QEvent::Type type, QPointF at, Qt::MouseButton button, Qt::MouseButtons buttons)
|
||||
{
|
||||
QMouseEvent event(type, at, widget->mapToGlobal(at), button, buttons, Qt::NoModifier);
|
||||
QApplication::sendEvent(widget, &event);
|
||||
return event.isAccepted();
|
||||
}
|
||||
|
||||
void press(QWidget *widget, QPointF at, Qt::MouseButton button = Qt::LeftButton)
|
||||
{
|
||||
send(widget, QEvent::MouseButtonPress, at, button, button);
|
||||
}
|
||||
|
||||
void move(QWidget *widget, QPointF at, Qt::MouseButtons buttons = Qt::LeftButton)
|
||||
{
|
||||
send(widget, QEvent::MouseMove, at, Qt::NoButton, buttons);
|
||||
}
|
||||
|
||||
void release(QWidget *widget, QPointF at, Qt::MouseButton button = Qt::LeftButton)
|
||||
{
|
||||
send(widget, QEvent::MouseButtonRelease, at, button, Qt::NoButton);
|
||||
}
|
||||
}
|
||||
|
||||
class NumericScrubTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void theLabelShowsTheSidewaysCursor();
|
||||
void aDragFromThePressSetsTheValueOnceItMoves();
|
||||
void stepsSnapAndTheRangeClamps();
|
||||
void aClickAloneChangesNothing();
|
||||
void onlyTheLeftButtonScrubs();
|
||||
void aLostReleaseEndsTheDrag();
|
||||
void theDragOutlivesThePointerLeavingTheLabel();
|
||||
void aDisabledLabelScrubsNothing();
|
||||
void anyMoveOfAPointBeginsTheDrag();
|
||||
void aDoubleClicksSecondPressScrubs();
|
||||
};
|
||||
|
||||
void NumericScrubTests::theLabelShowsTheSidewaysCursor()
|
||||
{
|
||||
Probe probe;
|
||||
QCOMPARE(probe.label->cursor().shape(), Qt::SizeHorCursor);
|
||||
QVERIFY(probe.label->testAttribute(Qt::WA_SetCursor));
|
||||
QVERIFY(!probe.window.testAttribute(Qt::WA_SetCursor));
|
||||
// Tracked, so a move after a lost release arrives.
|
||||
QVERIFY(probe.label->hasMouseTracking());
|
||||
}
|
||||
|
||||
void NumericScrubTests::aDragFromThePressSetsTheValueOnceItMoves()
|
||||
{
|
||||
Probe probe(0.5);
|
||||
press(probe.label, QPointF(10, 5));
|
||||
// Unmoved, Swift's gesture has not begun.
|
||||
move(probe.label, QPointF(10, 5));
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
move(probe.label, QPointF(14, 5));
|
||||
QCOMPARE(probe.log, QStringList({"began", "12"}));
|
||||
// Only the sideways part counts; the start stays fixed.
|
||||
move(probe.label, QPointF(40, 30));
|
||||
QCOMPARE(probe.value, 25.0);
|
||||
move(probe.label, QPointF(9, 5));
|
||||
QCOMPARE(probe.value, 9.5);
|
||||
QCOMPARE(probe.log.count("began"), 1);
|
||||
release(probe.label, QPointF(9, 5));
|
||||
QCOMPARE(probe.log.last(), QString("ended"));
|
||||
QCOMPARE(probe.log.count("ended"), 1);
|
||||
// The next drag starts from where the last one left.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(30, 5));
|
||||
QCOMPARE(probe.value, 19.5);
|
||||
release(probe.label, QPointF(30, 5));
|
||||
QCOMPARE(probe.log.count("began"), 2);
|
||||
QCOMPARE(probe.log.count("ended"), 2);
|
||||
}
|
||||
|
||||
void NumericScrubTests::stepsSnapAndTheRangeClamps()
|
||||
{
|
||||
Probe probe(0.3, 5, 12, 1);
|
||||
press(probe.label, QPointF(100, 5));
|
||||
move(probe.label, QPointF(105, 5));
|
||||
// 10 + 1.5 rounds away from zero, as Swift's `rounded()`.
|
||||
QCOMPARE(probe.value, 12.0);
|
||||
move(probe.label, QPointF(104, 5));
|
||||
QCOMPARE(probe.value, 11.0);
|
||||
move(probe.label, QPointF(200, 5));
|
||||
QCOMPARE(probe.value, 12.0);
|
||||
move(probe.label, QPointF(0, 5));
|
||||
QCOMPARE(probe.value, 5.0);
|
||||
release(probe.label, QPointF(0, 5));
|
||||
// A step that is no step leaves the value unsnapped.
|
||||
Probe free(0.3, 0, 100, 0);
|
||||
press(free.label, QPointF(100, 5));
|
||||
move(free.label, QPointF(105, 5));
|
||||
QCOMPARE(free.value, 11.5);
|
||||
release(free.label, QPointF(105, 5));
|
||||
// Halves below zero round away from it, as Swift's.
|
||||
Probe negative(0.5, -100, 100, 1);
|
||||
press(negative.label, QPointF(100, 5));
|
||||
move(negative.label, QPointF(77, 5));
|
||||
QCOMPARE(negative.value, -2.0);
|
||||
release(negative.label, QPointF(77, 5));
|
||||
// A fractional step snaps to its multiples.
|
||||
Probe tenths(0.07, 0, 100, 0.25);
|
||||
press(tenths.label, QPointF(100, 5));
|
||||
move(tenths.label, QPointF(105, 5));
|
||||
QCOMPARE(tenths.value, 10.25);
|
||||
release(tenths.label, QPointF(105, 5));
|
||||
}
|
||||
|
||||
void NumericScrubTests::aClickAloneChangesNothing()
|
||||
{
|
||||
Probe probe;
|
||||
QVERIFY(send(probe.label, QEvent::MouseButtonPress, QPointF(10, 5), Qt::LeftButton, Qt::LeftButton));
|
||||
release(probe.label, QPointF(10, 5));
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
// Moves without a press scrub nothing.
|
||||
move(probe.label, QPointF(40, 5));
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
QCOMPARE(probe.value, 10.0);
|
||||
}
|
||||
|
||||
void NumericScrubTests::onlyTheLeftButtonScrubs()
|
||||
{
|
||||
Probe probe;
|
||||
// Unpressed, moves go on to the label untouched.
|
||||
send(probe.label, QEvent::MouseMove, QPointF(40, 5), Qt::NoButton, Qt::NoButton);
|
||||
QCOMPARE(probe.counter.moves, 1);
|
||||
// The label lets other presses go on to its parent.
|
||||
QVERIFY(!send(probe.label, QEvent::MouseButtonPress, QPointF(10, 5), Qt::RightButton, Qt::RightButton));
|
||||
move(probe.label, QPointF(40, 5), Qt::RightButton);
|
||||
release(probe.label, QPointF(40, 5), Qt::RightButton);
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
// Another button's release leaves a left drag going.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(20, 5));
|
||||
send(probe.label, QEvent::MouseButtonRelease, QPointF(20, 5), Qt::RightButton, Qt::LeftButton);
|
||||
send(probe.other, QEvent::MouseButtonRelease, QPointF(5, 5), Qt::RightButton, Qt::LeftButton);
|
||||
move(probe.label, QPointF(30, 5));
|
||||
QCOMPARE(probe.log, QStringList({"began", "20", "30"}));
|
||||
// The drag's own moves stop at the scrub.
|
||||
QCOMPARE(probe.counter.moves, 2);
|
||||
release(probe.label, QPointF(30, 5));
|
||||
QCOMPARE(probe.log.last(), QString("ended"));
|
||||
}
|
||||
|
||||
void NumericScrubTests::aLostReleaseEndsTheDrag()
|
||||
{
|
||||
Probe probe;
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(20, 5));
|
||||
// A move without the button: the release went elsewhere.
|
||||
move(probe.label, QPointF(60, 5), Qt::NoButton);
|
||||
QCOMPARE(probe.log, QStringList({"began", "20", "ended"}));
|
||||
move(probe.label, QPointF(70, 5));
|
||||
QCOMPARE(probe.value, 20.0);
|
||||
// A release a popup takes ends it, seen application-wide.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(15, 5));
|
||||
release(probe.other, QPointF(5, 5));
|
||||
QCOMPARE(probe.log.mid(3), QStringList({"began", "25", "ended"}));
|
||||
move(probe.label, QPointF(70, 5));
|
||||
QCOMPARE(probe.value, 25.0);
|
||||
// Another widget's release before any move ends only the press.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
release(probe.other, QPointF(5, 5));
|
||||
move(probe.label, QPointF(70, 5));
|
||||
QCOMPARE(probe.log.size(), 6);
|
||||
// A move holding another button alone lost the left release.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(12, 5));
|
||||
move(probe.label, QPointF(30, 5), Qt::RightButton);
|
||||
QCOMPARE(probe.log.mid(6), QStringList({"began", "27", "ended"}));
|
||||
}
|
||||
|
||||
void NumericScrubTests::theDragOutlivesThePointerLeavingTheLabel()
|
||||
{
|
||||
Probe probe;
|
||||
QWindow *window = probe.window.windowHandle();
|
||||
const QPoint start = probe.label->mapTo(&probe.window, QPoint(5, probe.label->height() / 2));
|
||||
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, start);
|
||||
QTest::mouseMove(window, start + QPoint(3, 0));
|
||||
// Past the label and the field, out of the window.
|
||||
QTest::mouseMove(window, start + QPoint(200, 0));
|
||||
QTest::mouseMove(window, start + QPoint(400, 0));
|
||||
QCOMPARE(probe.value, 100.0);
|
||||
QTest::mouseMove(window, start + QPoint(30, 0));
|
||||
QCOMPARE(probe.value, 40.0);
|
||||
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, start + QPoint(30, 0));
|
||||
QCOMPARE(probe.log, QStringList({"began", "13", "100", "100", "40", "ended"}));
|
||||
QCOMPARE(probe.value, 40.0);
|
||||
}
|
||||
|
||||
void NumericScrubTests::aDisabledLabelScrubsNothing()
|
||||
{
|
||||
Probe probe;
|
||||
probe.label->setEnabled(false);
|
||||
QWindow *window = probe.window.windowHandle();
|
||||
const QPoint start = probe.label->mapTo(&probe.window, QPoint(5, probe.label->height() / 2));
|
||||
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, start);
|
||||
QTest::mouseMove(window, start + QPoint(30, 0));
|
||||
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, start + QPoint(30, 0));
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
QCOMPARE(probe.value, 10.0);
|
||||
}
|
||||
|
||||
void NumericScrubTests::anyMoveOfAPointBeginsTheDrag()
|
||||
{
|
||||
Probe probe;
|
||||
press(probe.label, QPointF(10, 5));
|
||||
// Under a point, Swift's gesture has not begun.
|
||||
move(probe.label, QPointF(10.6, 5));
|
||||
QVERIFY(probe.log.isEmpty());
|
||||
move(probe.label, QPointF(11, 5));
|
||||
QCOMPARE(probe.log, QStringList({"began", "11"}));
|
||||
release(probe.label, QPointF(11, 5));
|
||||
// Straight down begins too, and sets the start again.
|
||||
press(probe.label, QPointF(10, 5));
|
||||
move(probe.label, QPointF(10, 8));
|
||||
QCOMPARE(probe.log.mid(3), QStringList({"began", "11"}));
|
||||
release(probe.label, QPointF(10, 8));
|
||||
}
|
||||
|
||||
void NumericScrubTests::aDoubleClicksSecondPressScrubs()
|
||||
{
|
||||
Probe probe;
|
||||
press(probe.label, QPointF(10, 5));
|
||||
release(probe.label, QPointF(10, 5));
|
||||
send(probe.label, QEvent::MouseButtonDblClick, QPointF(10, 5), Qt::LeftButton, Qt::LeftButton);
|
||||
move(probe.label, QPointF(30, 5));
|
||||
release(probe.label, QPointF(30, 5));
|
||||
QCOMPARE(probe.log, QStringList({"began", "30", "ended"}));
|
||||
// A disabled label's double click scrubs nothing either.
|
||||
probe.label->setEnabled(false);
|
||||
send(probe.label, QEvent::MouseButtonDblClick, QPointF(10, 5), Qt::LeftButton, Qt::LeftButton);
|
||||
move(probe.label, QPointF(60, 5));
|
||||
QCOMPARE(probe.value, 30.0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(NumericScrubTests)
|
||||
#include "NumericScrubTests.moc"
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "SelectionFixtures.h"
|
||||
#include "SessionFixtures.h"
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QtTest>
|
||||
|
||||
// Shared by the scrubbable label tests: finding, dragging, typing.
|
||||
|
||||
// The scrubbing labels saying `text`, left to right.
|
||||
inline QLabel &scrubbed(QWidget &root, const QString &text, int index = 0)
|
||||
{
|
||||
std::vector<QLabel *> found;
|
||||
for (QLabel *label : root.findChildren<QLabel *>())
|
||||
if (label->text() == text && label->testAttribute(Qt::WA_SetCursor) && label->cursor().shape() == Qt::SizeHorCursor)
|
||||
found.push_back(label);
|
||||
std::sort(found.begin(), found.end(), [&root](QLabel *a, QLabel *b) { return a->mapTo(&root, QPoint()).x() < b->mapTo(&root, QPoint()).x(); });
|
||||
if (size_t(index) >= found.size())
|
||||
throw std::runtime_error("no scrubbing label " + text.toStdString());
|
||||
return *found[size_t(index)];
|
||||
}
|
||||
|
||||
inline void send(QWidget &widget, QEvent::Type type, QPointF at, Qt::MouseButton button, Qt::MouseButtons buttons)
|
||||
{
|
||||
QMouseEvent event(type, at, widget.mapToGlobal(at), button, buttons, Qt::NoModifier);
|
||||
QApplication::sendEvent(&widget, &event);
|
||||
}
|
||||
|
||||
// Presses, drags `dx` points sideways, and lets go.
|
||||
inline void drag(QWidget &label, double dx, bool release = true)
|
||||
{
|
||||
send(label, QEvent::MouseButtonPress, QPointF(2, 2), Qt::LeftButton, Qt::LeftButton);
|
||||
send(label, QEvent::MouseMove, QPointF(2 + dx, 2), Qt::NoButton, Qt::LeftButton);
|
||||
if (release)
|
||||
send(label, QEvent::MouseButtonRelease, QPointF(2 + dx, 2), Qt::LeftButton, Qt::NoButton);
|
||||
}
|
||||
|
||||
// Types 7, which applies, then scrubs, holding the press.
|
||||
inline QString scrubOverTyping(QLineEdit &field, QWidget &label, double dx)
|
||||
{
|
||||
field.setFocus();
|
||||
field.selectAll();
|
||||
QTest::keyClicks(&field, QStringLiteral("7"));
|
||||
if (!field.hasFocus())
|
||||
throw std::runtime_error("the field lost its focus");
|
||||
drag(label, dx, false);
|
||||
const QString during = field.text();
|
||||
send(label, QEvent::MouseButtonRelease, QPointF(2 + dx, 2), Qt::LeftButton, Qt::NoButton);
|
||||
if (field.text() != during || !field.hasFocus())
|
||||
throw std::runtime_error("the release changed the field");
|
||||
return during;
|
||||
}
|
||||
|
||||
// Shown in an active window, so fields take the focus.
|
||||
inline void showActive(QWidget &widget)
|
||||
{
|
||||
widget.show();
|
||||
widget.activateWindow();
|
||||
if (!QTest::qWaitForWindowActive(&widget))
|
||||
throw std::runtime_error("the bar never became active");
|
||||
}
|
||||
|
||||
template <typename Widget> inline Widget &find(QWidget &root, const char *name)
|
||||
{
|
||||
auto *found = root.findChild<Widget *>(QString::fromUtf8(name));
|
||||
if (!found)
|
||||
throw std::runtime_error(name);
|
||||
return *found;
|
||||
}
|
||||
|
||||
inline ImportedImage pixels(int width, int height)
|
||||
{
|
||||
QImage image(width, height, QImage::Format_RGBA8888_Premultiplied);
|
||||
image.fill(Qt::white);
|
||||
return ImportedImage(image, image, "Pixels");
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#include "ScrubFixtures.h"
|
||||
#include "UI/BrushControls.h"
|
||||
#include "UI/GradientControls.h"
|
||||
#include "UI/LassoControls.h"
|
||||
#include "UI/ShapeControls.h"
|
||||
#include "UI/TypeControls.h"
|
||||
#include <QPushButton>
|
||||
|
||||
// Swift's scrubbable labels and units in the tool bars.
|
||||
class ScrubbableLabelsTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void theBrushsLabelsScrubItsTip();
|
||||
void theGradientsOpacityScrubs();
|
||||
void theSelectionBarsLabelsAndUnitsScrubWholeNumbers();
|
||||
void theAmountSheetsTitleScrubsItsInput();
|
||||
void theShapesWidthAndRadiusScrub();
|
||||
void theTypeBarScrubsWholeSizesTrackingAndLeading();
|
||||
void aScrubShowsItsValueInAFocusedField();
|
||||
void featherScrubsWithoutASelection();
|
||||
};
|
||||
|
||||
void ScrubbableLabelsTests::theBrushsLabelsScrubItsTip()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(100, 100, true);
|
||||
session.selectTool(NavigationTool::brush);
|
||||
BrushControls bar(session);
|
||||
bar.show();
|
||||
drag(scrubbed(bar, "Size"), 25);
|
||||
QCOMPARE(session.brushSettings().diameter, 65.0);
|
||||
QCOMPARE(find<QLineEdit>(bar, "brushSize").text(), QString("65"));
|
||||
drag(scrubbed(bar, "Size"), -5000);
|
||||
QCOMPARE(session.brushSettings().diameter, 1.0);
|
||||
drag(scrubbed(bar, "Size"), 5000);
|
||||
QCOMPARE(session.brushSettings().diameter, 2000.0);
|
||||
// Unstepped, a fraction of a point counts.
|
||||
drag(scrubbed(bar, "Size"), -1.5);
|
||||
QCOMPARE(session.brushSettings().diameter, 1998.5);
|
||||
drag(scrubbed(bar, "Hardness"), -30);
|
||||
QVERIFY(qAbs(session.brushSettings().hardness - 0.7) < 1e-12);
|
||||
drag(scrubbed(bar, "Hardness"), 90);
|
||||
QCOMPARE(session.brushSettings().hardness, 1.0);
|
||||
drag(scrubbed(bar, "Hardness"), -900);
|
||||
QCOMPARE(session.brushSettings().hardness, 0.0);
|
||||
drag(scrubbed(bar, "Opacity"), -50);
|
||||
QVERIFY(qAbs(session.brushSettings().opacity - 0.5) < 1e-12);
|
||||
drag(scrubbed(bar, "Opacity"), -500);
|
||||
QCOMPARE(session.brushSettings().opacity, 0.01);
|
||||
drag(scrubbed(bar, "Opacity"), 500);
|
||||
QCOMPARE(session.brushSettings().opacity, 1.0);
|
||||
drag(scrubbed(bar, "Smoothing"), 30);
|
||||
QCOMPARE(session.brushSettings().smoothing, 30.0);
|
||||
drag(scrubbed(bar, "Smoothing"), 300);
|
||||
QCOMPARE(session.brushSettings().smoothing, 100.0);
|
||||
drag(scrubbed(bar, "Smoothing"), -300);
|
||||
QCOMPARE(session.brushSettings().smoothing, 0.0);
|
||||
// The Smear's Strength is the same label, retitled.
|
||||
session.selectTool(NavigationTool::blur);
|
||||
const double strength = session.brushSettings().opacity;
|
||||
drag(scrubbed(bar, "Strength"), -10);
|
||||
QVERIFY(qAbs(session.brushSettings().opacity - (strength - 0.1)) < 1e-12);
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::theGradientsOpacityScrubs()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(100, 100, true);
|
||||
session.selectTool(NavigationTool::gradient);
|
||||
GradientControls bar(session);
|
||||
bar.show();
|
||||
// Unstepped: a point and a half is 1.5%.
|
||||
drag(scrubbed(bar, "Opacity"), -1.5);
|
||||
QVERIFY(qAbs(session.gradientSettings().opacity - 0.985) < 1e-12);
|
||||
drag(scrubbed(bar, "Opacity"), 1.5);
|
||||
drag(scrubbed(bar, "Opacity"), -40);
|
||||
QVERIFY(qAbs(session.gradientSettings().opacity - 0.6) < 1e-12);
|
||||
drag(scrubbed(bar, "Opacity"), -400);
|
||||
QCOMPARE(session.gradientSettings().opacity, 0.01);
|
||||
drag(scrubbed(bar, "Opacity"), 400);
|
||||
QCOMPARE(session.gradientSettings().opacity, 1.0);
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::theSelectionBarsLabelsAndUnitsScrubWholeNumbers()
|
||||
{
|
||||
std::unique_ptr<EditorSession> session = selectionSession();
|
||||
LassoControls bar(*session);
|
||||
bar.resize(1600, bar.height());
|
||||
bar.show();
|
||||
// Without a selection the amounts rest, units and all.
|
||||
QLabel &expand = scrubbed(bar, "px", 0);
|
||||
QVERIFY(!expand.isEnabled());
|
||||
QVERIFY(!scrubbed(bar, "px", 1).isEnabled());
|
||||
drag(scrubbed(bar, "px", 1), 9);
|
||||
QCOMPARE(session->selectionContractAmount(), 1);
|
||||
lasso(*session, square(10, 10, 20));
|
||||
QVERIFY(expand.isEnabled());
|
||||
drag(expand, 7);
|
||||
QCOMPARE(session->selectionExpandAmount(), 8);
|
||||
drag(expand, 900);
|
||||
QCOMPARE(session->selectionExpandAmount(), 500);
|
||||
drag(expand, -900);
|
||||
QCOMPARE(session->selectionExpandAmount(), 1);
|
||||
drag(scrubbed(bar, "px", 1), 4);
|
||||
QCOMPARE(session->selectionContractAmount(), 5);
|
||||
drag(scrubbed(bar, "px", 1), 900);
|
||||
QCOMPARE(session->selectionContractAmount(), 500);
|
||||
drag(scrubbed(bar, "px", 1), -900);
|
||||
QCOMPARE(session->selectionContractAmount(), 1);
|
||||
drag(scrubbed(bar, "px", 2), 3);
|
||||
QCOMPARE(session->selectionFeatherAmount(), 5);
|
||||
drag(scrubbed(bar, "px", 2), 900);
|
||||
QCOMPARE(session->selectionFeatherAmount(), 250);
|
||||
drag(scrubbed(bar, "px", 2), -900);
|
||||
QCOMPARE(session->selectionFeatherAmount(), 1);
|
||||
QCOMPARE(find<QLineEdit>(bar, "selectionFeatherAmount").text(), QString("1"));
|
||||
session->selectTool(NavigationTool::wand);
|
||||
// Whole numbers round, where the setter would truncate.
|
||||
drag(scrubbed(bar, "Tolerance"), 1.6);
|
||||
QCOMPARE(session->wandSettings().tolerance, 34);
|
||||
drag(scrubbed(bar, "Tolerance"), 8);
|
||||
QCOMPARE(session->wandSettings().tolerance, 42);
|
||||
drag(scrubbed(bar, "Tolerance"), 400);
|
||||
QCOMPARE(session->wandSettings().tolerance, 255);
|
||||
drag(scrubbed(bar, "Tolerance"), -400);
|
||||
QCOMPARE(session->wandSettings().tolerance, 0);
|
||||
session->setWandMode(WandMode::object);
|
||||
drag(scrubbed(bar, "Edge"), -1.5);
|
||||
QCOMPARE(session->objectSelectionSettings().edgeOffset, -2);
|
||||
drag(scrubbed(bar, "Edge"), -2);
|
||||
QCOMPARE(session->objectSelectionSettings().edgeOffset, -4);
|
||||
drag(scrubbed(bar, "Edge"), 50);
|
||||
QCOMPARE(session->objectSelectionSettings().edgeOffset, 10);
|
||||
drag(scrubbed(bar, "Edge"), -50);
|
||||
QCOMPARE(session->objectSelectionSettings().edgeOffset, -10);
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::theAmountSheetsTitleScrubsItsInput()
|
||||
{
|
||||
std::unique_ptr<EditorSession> session = selectionSession();
|
||||
SelectionAmountSheet sheet(*session, SelectionAmountOperation::feather);
|
||||
sheet.show();
|
||||
QLineEdit &input = find<QLineEdit>(sheet, "amountField");
|
||||
// Rounded to whole pixels, not truncated.
|
||||
drag(scrubbed(sheet, "Amount"), 1.6);
|
||||
QCOMPARE(input.text(), QString("4"));
|
||||
input.setText(QStringLiteral("2"));
|
||||
drag(scrubbed(sheet, "Amount"), 10);
|
||||
QCOMPARE(input.text(), QString("12"));
|
||||
drag(scrubbed(sheet, "Amount"), 900);
|
||||
QCOMPARE(input.text(), QString("250"));
|
||||
// Unreadable input starts the drag from one.
|
||||
input.setText(QStringLiteral("abc"));
|
||||
drag(scrubbed(sheet, "Amount"), 4);
|
||||
QCOMPARE(input.text(), QString("5"));
|
||||
drag(scrubbed(sheet, "Amount"), -40);
|
||||
QCOMPARE(input.text(), QString("1"));
|
||||
SelectionAmountSheet expand(*session, SelectionAmountOperation::expand);
|
||||
drag(scrubbed(expand, "Amount"), 900);
|
||||
QCOMPARE(find<QLineEdit>(expand, "amountField").text(), QString("500"));
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::theShapesWidthAndRadiusScrub()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(100, 100, true);
|
||||
session.selectTool(NavigationTool::shape);
|
||||
session.setShapeKind(ShapeKind::line);
|
||||
ShapeControls bar(session);
|
||||
bar.show();
|
||||
drag(scrubbed(bar, "Width"), 6.5);
|
||||
QCOMPARE(session.shapeLineWidth(), 10.5);
|
||||
drag(scrubbed(bar, "Width"), 9000);
|
||||
QCOMPARE(session.shapeLineWidth(), 5000.0);
|
||||
drag(scrubbed(bar, "Width"), -9000);
|
||||
QCOMPARE(session.shapeLineWidth(), 1.0);
|
||||
session.setShapeKind(ShapeKind::rectangle);
|
||||
drag(scrubbed(bar, "Radius"), 12.25);
|
||||
QCOMPARE(session.shapeCornerRadius(), 12.25);
|
||||
drag(scrubbed(bar, "Radius"), 9000);
|
||||
QCOMPARE(session.shapeCornerRadius(), 5000.0);
|
||||
drag(scrubbed(bar, "Radius"), -9000);
|
||||
QCOMPARE(session.shapeCornerRadius(), 0.0);
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::theTypeBarScrubsWholeSizesTrackingAndLeading()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(400, 300, true);
|
||||
session.selectTool(NavigationTool::type);
|
||||
TypeControls bar(session);
|
||||
bar.show();
|
||||
session.changeTextStyle([](LayerTextStyle &style) { style.fontSize = 24.4; });
|
||||
// Dragged sizes land on whole numbers, Swift's step of one.
|
||||
drag(scrubbed(bar, "px"), 3);
|
||||
QCOMPARE(session.currentTextStyle().fontSize, 27.0);
|
||||
drag(scrubbed(bar, "px"), 5000);
|
||||
QCOMPARE(session.currentTextStyle().fontSize, 2000.0);
|
||||
drag(scrubbed(bar, "px"), -5000);
|
||||
QCOMPARE(session.currentTextStyle().fontSize, 1.0);
|
||||
session.changeTextStyle([](LayerTextStyle &style) { style.tracking = 2.6; });
|
||||
drag(scrubbed(bar, "Tracking"), -5);
|
||||
QCOMPARE(session.currentTextStyle().tracking, -2.0);
|
||||
drag(scrubbed(bar, "Tracking"), -500);
|
||||
QCOMPARE(session.currentTextStyle().tracking, -100.0);
|
||||
drag(scrubbed(bar, "Tracking"), 5000);
|
||||
QCOMPARE(session.currentTextStyle().tracking, 1000.0);
|
||||
// Auto is zero, so a drag starts there.
|
||||
drag(scrubbed(bar, "Leading"), 12);
|
||||
QCOMPARE(session.currentTextStyle().leading, 12.0);
|
||||
drag(scrubbed(bar, "Leading"), -50);
|
||||
QCOMPARE(session.currentTextStyle().leading, 0.0);
|
||||
drag(scrubbed(bar, "Leading"), 9000);
|
||||
QCOMPARE(session.currentTextStyle().leading, 5000.0);
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::aScrubShowsItsValueInAFocusedField()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(100, 100, true);
|
||||
session.selectTool(NavigationTool::brush);
|
||||
{
|
||||
BrushControls bar(session);
|
||||
showActive(bar);
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "brushSize"), scrubbed(bar, "Size"), 25), QString("32"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "brushHardness"), scrubbed(bar, "Hardness"), 30), QString("37"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "brushOpacity"), scrubbed(bar, "Opacity"), 40), QString("47"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "brushSmoothing"), scrubbed(bar, "Smoothing"), 12), QString("19"));
|
||||
}
|
||||
session.selectTool(NavigationTool::gradient);
|
||||
{
|
||||
GradientControls bar(session);
|
||||
showActive(bar);
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "gradientOpacity"), scrubbed(bar, "Opacity"), 25), QString("32"));
|
||||
}
|
||||
session.selectTool(NavigationTool::shape);
|
||||
session.setShapeKind(ShapeKind::line);
|
||||
{
|
||||
ShapeControls bar(session);
|
||||
showActive(bar);
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "shapeWidth"), scrubbed(bar, "Width"), 6), QString("13"));
|
||||
}
|
||||
session.selectTool(NavigationTool::wand);
|
||||
{
|
||||
LassoControls bar(session);
|
||||
bar.resize(1600, bar.height());
|
||||
showActive(bar);
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "wandTolerance"), scrubbed(bar, "Tolerance"), 10), QString("17"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "selectionFeatherAmount"), scrubbed(bar, "px", 2), 3), QString("10"));
|
||||
}
|
||||
session.selectTool(NavigationTool::type);
|
||||
{
|
||||
TypeControls bar(session);
|
||||
showActive(bar);
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "typeTracking"), scrubbed(bar, "Tracking"), 4), QString("11"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "typeLeading"), scrubbed(bar, "Leading"), 30), QString("37"));
|
||||
QCOMPARE(scrubOverTyping(find<QLineEdit>(bar, "typeSize"), scrubbed(bar, "px"), -2), QString("5"));
|
||||
}
|
||||
}
|
||||
|
||||
void ScrubbableLabelsTests::featherScrubsWithoutASelection()
|
||||
{
|
||||
std::unique_ptr<EditorSession> session = selectionSession();
|
||||
LassoControls bar(*session);
|
||||
bar.resize(1600, bar.height());
|
||||
bar.show();
|
||||
// Swift rests Feather's button alone; its amount stays.
|
||||
drag(scrubbed(bar, "px", 2), 7);
|
||||
QCOMPARE(session->selectionFeatherAmount(), 9);
|
||||
QVERIFY(find<QLineEdit>(bar, "selectionFeatherAmount").isEnabled());
|
||||
QVERIFY(!find<QLineEdit>(bar, "selectionExpandAmount").isEnabled());
|
||||
QVERIFY(!find<QLineEdit>(bar, "selectionContractAmount").isEnabled());
|
||||
for (QPushButton *button : bar.findChildren<QPushButton *>())
|
||||
if (button->text() == "Expand" || button->text() == "Contract" || button->text() == "Feather")
|
||||
QVERIFY(!button->isEnabled());
|
||||
drag(scrubbed(bar, "px", 0), 7);
|
||||
QCOMPARE(session->selectionExpandAmount(), 1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(ScrubbableLabelsTests)
|
||||
#include "ScrubbableLabelsTests.moc"
|
||||
@@ -0,0 +1,213 @@
|
||||
#include "ScrubFixtures.h"
|
||||
#include "UI/LayerAppearanceControls.h"
|
||||
#include "UI/NavigationToolHeader.h"
|
||||
#include "UI/TransformInspector.h"
|
||||
|
||||
// Swift's scrubbable Move bar, zoom unit and layer opacity.
|
||||
class ScrubbableTransformTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void theTransformLabelsScrubWholeNumbersOverTyping();
|
||||
void scaleScrubsDownToATenth();
|
||||
void theZoomUnitScrubsThePercentage();
|
||||
void theLayerOpacityScrubIsOneStep();
|
||||
void anOpacityScrubEndsWithItsLayer();
|
||||
void aTransformScrubEndsWithItsLayer();
|
||||
};
|
||||
|
||||
void ScrubbableTransformTests::theTransformLabelsScrubWholeNumbersOverTyping()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(200, 100);
|
||||
session.insert(pixels(40, 20));
|
||||
session.selectTool(NavigationTool::move);
|
||||
session.setLocksTransformRatio(false);
|
||||
TransformInspector bar(session);
|
||||
bar.show();
|
||||
bar.activateWindow();
|
||||
QVERIFY(QTest::qWaitForWindowActive(&bar));
|
||||
const QPointF origin = session.activeLayer().value().transform.origin;
|
||||
// The first drag begins the edit, as typing does.
|
||||
drag(scrubbed(bar, "X"), 7);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), std::round(origin.x()) + 7);
|
||||
// A scrub writes over what is typed in its field.
|
||||
QLineEdit &width = find<QLineEdit>(bar, "transformW");
|
||||
width.setFocus();
|
||||
width.selectAll();
|
||||
QTest::keyClicks(&width, QStringLiteral("3"));
|
||||
drag(scrubbed(bar, "W"), 10);
|
||||
QCOMPARE(session.transformEdit().value().draft.size, QSizeF(13, 20));
|
||||
QCOMPARE(width.text(), QString("13"));
|
||||
drag(scrubbed(bar, "W"), -100);
|
||||
QCOMPARE(session.transformEdit().value().draft.size.width(), 1.0);
|
||||
drag(scrubbed(bar, "H"), 40000);
|
||||
QCOMPARE(session.transformEdit().value().draft.size.height(), 30000.0);
|
||||
drag(scrubbed(bar, "°"), 30);
|
||||
QCOMPARE(session.transformEdit().value().draft.rotation, 30.0);
|
||||
drag(scrubbed(bar, "°"), -900);
|
||||
QCOMPARE(session.transformEdit().value().draft.rotation, -0.0);
|
||||
// Unfocused, the field shows what the session keeps.
|
||||
QCOMPARE(find<QLineEdit>(bar, "transform°").text(), QString("0"));
|
||||
QCOMPARE(width.text(), QString("1"));
|
||||
drag(scrubbed(bar, "Y"), -90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.y(), -30000.0);
|
||||
// A fractional start lands on a whole number.
|
||||
QLineEdit &x = find<QLineEdit>(bar, "transformX");
|
||||
x.setFocus();
|
||||
x.selectAll();
|
||||
QTest::keyClicks(&x, QStringLiteral("10.4"));
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), 10.4);
|
||||
drag(scrubbed(bar, "X"), 3);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), 13.0);
|
||||
QCOMPARE(x.text(), QString("13"));
|
||||
// Every range reaches its ends exactly.
|
||||
drag(scrubbed(bar, "X"), -90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), -30000.0);
|
||||
drag(scrubbed(bar, "X"), 90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), 30000.0);
|
||||
drag(scrubbed(bar, "Y"), 90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.y(), 30000.0);
|
||||
drag(scrubbed(bar, "W"), 90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.size.width(), 30000.0);
|
||||
drag(scrubbed(bar, "H"), -90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.size.height(), 1.0);
|
||||
// 360 reaches the setter, whose remainder leaves none.
|
||||
drag(scrubbed(bar, "°"), 9000);
|
||||
QCOMPARE(session.transformEdit().value().draft.rotation, 0.0);
|
||||
drag(scrubbed(bar, "°"), 359);
|
||||
QCOMPARE(session.transformEdit().value().draft.rotation, 359.0);
|
||||
// Scale snaps whole percents too.
|
||||
session.cancelTransform();
|
||||
drag(scrubbed(bar, "Scale"), 5);
|
||||
QCOMPARE(session.transformEdit().value().draft.size, QSizeF(42, 21));
|
||||
session.cancelTransform();
|
||||
drag(scrubbed(bar, "Scale"), 90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.size, QSizeF(12000, 6000));
|
||||
QCOMPARE(find<QLineEdit>(bar, "transformScale").text(), QString("30000"));
|
||||
// While distorted the numbers rest, labels included.
|
||||
session.cancelTransform();
|
||||
session.beginTransform();
|
||||
session.beginDistort();
|
||||
QVERIFY(!scrubbed(bar, "X").isEnabled());
|
||||
}
|
||||
|
||||
// Scale's floor is a tenth of a percent.
|
||||
void ScrubbableTransformTests::scaleScrubsDownToATenth()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(4000, 2000);
|
||||
session.insert(pixels(2000, 1000));
|
||||
session.selectTool(NavigationTool::move);
|
||||
TransformInspector bar(session);
|
||||
bar.show();
|
||||
drag(scrubbed(bar, "Scale"), -90000);
|
||||
QCOMPARE(session.transformEdit().value().draft.size, QSizeF(2, 1));
|
||||
QCOMPARE(find<QLineEdit>(bar, "transformScale").text(), QString("0.10"));
|
||||
}
|
||||
|
||||
void ScrubbableTransformTests::theZoomUnitScrubsThePercentage()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(800, 600);
|
||||
session.selectTool(NavigationTool::zoom);
|
||||
NavigationToolHeader bar(session);
|
||||
bar.show();
|
||||
session.zoom(1);
|
||||
drag(scrubbed(bar, "%"), 25.5);
|
||||
QVERIFY(qAbs(session.viewport.zoom() - 1.255) < 1e-9);
|
||||
QCOMPARE(find<QLineEdit>(bar, "zoomPercentage").text(), QString("125.5"));
|
||||
drag(scrubbed(bar, "%"), -900);
|
||||
QVERIFY(qAbs(session.viewport.zoom() - 0.001) < 1e-9);
|
||||
drag(scrubbed(bar, "%"), 9000);
|
||||
QVERIFY(qAbs(session.viewport.zoom() - 32) < 1e-9);
|
||||
}
|
||||
|
||||
void ScrubbableTransformTests::theLayerOpacityScrubIsOneStep()
|
||||
{
|
||||
EditorSession session;
|
||||
LayerAppearanceControls controls(session);
|
||||
session.createDocument(4, 4);
|
||||
session.insert(pixels(2, 2));
|
||||
controls.show();
|
||||
QLabel &opacity = scrubbed(controls, "Opacity");
|
||||
// One drag is one opacity edit, open until the release.
|
||||
drag(opacity, -30, false);
|
||||
QVERIFY(qAbs(session.activeLayer().value().opacity - 0.7) < 1e-12);
|
||||
QVERIFY(!session.canUndo());
|
||||
send(opacity, QEvent::MouseMove, QPoint(-18, 2), Qt::NoButton, Qt::LeftButton);
|
||||
QVERIFY(qAbs(session.activeLayer().value().opacity - 0.8) < 1e-12);
|
||||
send(opacity, QEvent::MouseButtonRelease, QPoint(-18, 2), Qt::LeftButton, Qt::NoButton);
|
||||
QVERIFY(session.canUndo());
|
||||
QCOMPARE(find<QLineEdit>(controls, "opacityPercent").text(), QString("80"));
|
||||
// One undo takes the whole drag back; the layer stays.
|
||||
session.undo();
|
||||
QCOMPARE(session.activeLayer().value().opacity, 1.0);
|
||||
QCOMPARE(session.document().value().layers.size(), size_t(1));
|
||||
drag(opacity, -30.5);
|
||||
QVERIFY(qAbs(session.activeLayer().value().opacity - 0.695) < 1e-12);
|
||||
drag(opacity, -500);
|
||||
QCOMPARE(session.activeLayer().value().opacity, 0.0);
|
||||
drag(opacity, 500);
|
||||
QCOMPARE(session.activeLayer().value().opacity, 1.0);
|
||||
}
|
||||
|
||||
void ScrubbableTransformTests::anOpacityScrubEndsWithItsLayer()
|
||||
{
|
||||
EditorSession session;
|
||||
LayerAppearanceControls controls(session);
|
||||
session.createDocument(4, 4);
|
||||
session.insert(pixels(2, 2));
|
||||
const QUuid lower = session.activeLayerID().value();
|
||||
session.setLayerOpacity(0.3);
|
||||
session.insert(pixels(2, 2));
|
||||
const QUuid upper = session.activeLayerID().value();
|
||||
controls.show();
|
||||
QLabel &opacity = scrubbed(controls, "Opacity");
|
||||
drag(opacity, -10, false);
|
||||
QVERIFY(qAbs(layerWith(session, upper).opacity - 0.9) < 1e-12);
|
||||
// Another layer ends the drag; held moves change nothing.
|
||||
session.selectLayer(lower);
|
||||
send(opacity, QEvent::MouseMove, QPointF(-18, 2), Qt::NoButton, Qt::LeftButton);
|
||||
send(opacity, QEvent::MouseButtonRelease, QPointF(-18, 2), Qt::LeftButton, Qt::NoButton);
|
||||
QCOMPARE(layerWith(session, lower).opacity, 0.3);
|
||||
QVERIFY(qAbs(layerWith(session, upper).opacity - 0.9) < 1e-12);
|
||||
QVERIFY(session.canUndo());
|
||||
session.undo();
|
||||
QCOMPARE(layerWith(session, upper).opacity, 1.0);
|
||||
QCOMPARE(layerWith(session, lower).opacity, 0.3);
|
||||
// A fresh press on the new layer scrubs it.
|
||||
session.selectLayer(lower);
|
||||
drag(opacity, 20);
|
||||
QVERIFY(qAbs(layerWith(session, lower).opacity - 0.5) < 1e-12);
|
||||
}
|
||||
|
||||
void ScrubbableTransformTests::aTransformScrubEndsWithItsLayer()
|
||||
{
|
||||
EditorSession session;
|
||||
session.createDocument(200, 100);
|
||||
session.insert(pixels(40, 20));
|
||||
const QUuid first = session.activeLayerID().value();
|
||||
session.insert(pixels(40, 20));
|
||||
const QUuid second = session.activeLayerID().value();
|
||||
session.selectTool(NavigationTool::move);
|
||||
TransformInspector bar(session);
|
||||
bar.show();
|
||||
const double x = layerWith(session, second).transform.origin.x();
|
||||
QLabel &label = scrubbed(bar, "X");
|
||||
drag(label, 10, false);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), std::round(x) + 10);
|
||||
session.commitTransform();
|
||||
// Another layer ends the drag; held moves open no edit.
|
||||
session.selectLayer(first);
|
||||
send(label, QEvent::MouseMove, QPointF(22, 2), Qt::NoButton, Qt::LeftButton);
|
||||
send(label, QEvent::MouseButtonRelease, QPointF(22, 2), Qt::LeftButton, Qt::NoButton);
|
||||
QVERIFY(!session.transformEdit().has_value());
|
||||
QCOMPARE(layerWith(session, first).transform.origin.x(), x);
|
||||
QCOMPARE(layerWith(session, second).transform.origin.x(), std::round(x) + 10);
|
||||
// A fresh press scrubs the new layer.
|
||||
drag(label, 5);
|
||||
QCOMPARE(session.transformEdit().value().draft.origin.x(), std::round(x) + 5);
|
||||
}
|
||||
|
||||
QTEST_MAIN(ScrubbableTransformTests)
|
||||
#include "ScrubbableTransformTests.moc"
|
||||
@@ -120,7 +120,8 @@ void SelectionAmountSheetTests::theBarFeathersByItsAmount()
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&controls));
|
||||
auto &feather = find<QPushButton>(controls, "featherSelection");
|
||||
auto &amount = find<QLineEdit>(controls, "selectionFeatherAmount");
|
||||
QVERIFY(!feather.isEnabled() && !amount.isEnabled());
|
||||
// Swift rests the button alone; the amount stays editable.
|
||||
QVERIFY(!feather.isEnabled() && amount.isEnabled());
|
||||
QCOMPARE(amount.text(), QString("2"));
|
||||
lasso(*session, square(20, 20, 40));
|
||||
QVERIFY(feather.isEnabled() && amount.isEnabled());
|
||||
|
||||
@@ -144,6 +144,9 @@ compositor_test(NativeLayerListTests)
|
||||
compositor_test(LayerContextMenuTests)
|
||||
compositor_test(LayerContextMenuEdgeTests)
|
||||
compositor_test(NavigationToolHeaderTests)
|
||||
compositor_test(NumericScrubTests)
|
||||
compositor_test(ScrubbableLabelsTests)
|
||||
compositor_test(ScrubbableTransformTests)
|
||||
compositor_test(NewCanvasSheetTests)
|
||||
compositor_test(OmarchyThemeTests)
|
||||
compositor_test(ProjectControllerTests)
|
||||
|
||||
Reference in new issue
Block a user