Coloured slider tracks with double-click reset, aligned filter titles; entries release focus before their owners go (12.14c)

This commit is contained in:
雷电芽衣 committed 2026-09-30 03:32:51 -04:00
1 parent 2183348aa6
commit 2fa8d9921d
39 files changed
+1387 -311

No files matched your search

+35 -6
View File
@@ -5,6 +5,15 @@
// Swift's CameraRawSliderTests.
namespace {
// A two-colour track's ends.
std::pair<QColor, QColor> ends(const CameraRawSliderTrack &track)
{
const std::vector<QColor> colors = track.colors().value();
if (colors.size() != 2)
throw std::runtime_error("not a two-colour track");
return {colors[0], colors[1]};
}
struct Probe {
double changed = std::nan("");
int resets = 0;
@@ -38,16 +47,17 @@ private slots:
void trackClickValueMatchesTheClickedPosition();
void aFamilysTracksCentreOnItsHue();
void aDragKeepsItsKnob();
void aReshapeLeavesADragAlone();
};
void CameraRawSliderTests::colorTracksRunFromTheCoolOrMutedEndToTheWarmOrStrongEnd()
{
using Kind = CameraRawSliderTrack::Kind;
const auto [cool, warm] = CameraRawSliderTrack{Kind::temperature}.colors().value();
const auto [cool, warm] = ends(CameraRawSliderTrack{Kind::temperature});
QVERIFY(cool.blueF() > warm.blueF());
const auto [green, mauve] = CameraRawSliderTrack{Kind::tint}.colors().value();
const auto [green, mauve] = ends(CameraRawSliderTrack{Kind::tint});
QVERIFY(green.greenF() > mauve.greenF());
const auto [gray, red] = CameraRawSliderTrack{Kind::chroma}.colors().value();
const auto [gray, red] = ends(CameraRawSliderTrack{Kind::chroma});
QVERIFY(std::abs(gray.redF() - gray.greenF()) < 0.05);
QVERIFY(red.redF() > red.greenF() + 0.4);
QVERIFY(!CameraRawSliderTrack{Kind::plain}.colors());
@@ -90,11 +100,11 @@ void CameraRawSliderTests::aFamilysTracksCentreOnItsHue()
{
using Kind = CameraRawSliderTrack::Kind;
// Hue runs 50 degrees either side; red wraps below zero.
const auto [below, above] = CameraRawSliderTrack{Kind::hue, 0}.colors().value();
const auto [below, above] = ends(CameraRawSliderTrack{Kind::hue, 0});
QVERIFY(std::abs(below.hueF() * 360 - 310) < 1 && std::abs(above.hueF() * 360 - 50) < 1);
const auto [muted, strong] = CameraRawSliderTrack{Kind::saturation, 240}.colors().value();
const auto [muted, strong] = ends(CameraRawSliderTrack{Kind::saturation, 240});
QVERIFY(muted.hsvSaturationF() < 0.05 && std::abs(strong.hueF() * 360 - 240) < 1);
const auto [dark, light] = CameraRawSliderTrack{Kind::luminance, 120}.colors().value();
const auto [dark, light] = ends(CameraRawSliderTrack{Kind::luminance, 120});
QVERIFY(dark.valueF() < 0.2 && light.valueF() > 0.9 && std::abs(dark.hueF() * 360 - 120) < 1);
}
@@ -113,5 +123,24 @@ void CameraRawSliderTests::aDragKeepsItsKnob()
QVERIFY(std::isnan(probe.changed));
}
void CameraRawSliderTests::aReshapeLeavesADragAlone()
{
using Kind = CameraRawSliderTrack::Kind;
Probe probe;
probe.slider.display(20);
probe.slider.setSliderDown(true);
// Mid-drag: the new track shows, the knob and value stay.
probe.slider.reshape(-50, 50, CameraRawSliderTrack{Kind::spectrum, 90}, 30);
QVERIFY(probe.slider.isSliderDown());
QCOMPARE(probe.slider.value(), 600);
QCOMPARE(probe.slider.track().kind, Kind::spectrum);
QVERIFY(std::isnan(probe.changed));
probe.slider.setSliderDown(false);
// Let go, new bounds place the value anew.
probe.slider.reshape(0, 100, CameraRawSliderTrack{Kind::chroma}, 25);
QCOMPARE(probe.slider.value(), 250);
QCOMPARE(probe.slider.shown(), 25.0);
}
QTEST_MAIN(CameraRawSliderTests)
#include "CameraRawSliderTests.moc"
+440
View File
@@ -0,0 +1,440 @@
#include "Document/BrushStroke.h"
#include "Document/EditorSession.h"
#include "UI/FilterSheet.h"
#include "UI/SliderSnap.h"
#include <QApplication>
#include <QLabel>
#include <QMouseEvent>
#include <QLayout>
#include <QLineEdit>
#include <QtTest>
// Swift 1.3.2's coloured filter sliders: tracks, resets, aligned titles.
namespace {
using Kind = CameraRawSliderTrack::Kind;
QImage solid(QColor colour)
{
QImage image(4, 4, QImage::Format_RGBA8888_Premultiplied);
image.fill(colour);
return image;
}
struct Sheet {
EditorSession session;
std::unique_ptr<FilterSheet> sheet;
explicit Sheet(FilterKind kind)
{
session.createDocument(4, 4);
const QImage image = solid(QColor::fromRgbF(0.8f, 0.3f, 0.2f));
session.insert(ImportedImage(image, image, QStringLiteral("Color")));
session.beginFilter(kind);
sheet = std::make_unique<FilterSheet>(session);
sheet->show();
}
const FilterSettings &settings() const { return session.filterEdit().value().settings; }
void set(const std::function<void(FilterSettings &)> &change, bool preview = true)
{
FilterSettings changed = settings();
change(changed);
session.updateFilter(changed, preview);
}
// The slider a title names, and that title.
QSlider &slider(const QString &title) const
{
for (QLabel *label : sheet->findChildren<QLabel *>()) {
if (label->text() == title && label->buddy())
return *static_cast<QSlider *>(label->buddy());
}
throw std::runtime_error("no slider titled " + title.toStdString());
}
QLabel &title(const QString &text) const
{
for (QLabel *label : sheet->findChildren<QLabel *>()) {
if (label->text() == text && label->buddy())
return *label;
}
throw std::runtime_error("no title " + text.toStdString());
}
CameraRawSlider &coloured(const QString &title) const
{
auto *found = qobject_cast<CameraRawSlider *>(&slider(title));
if (!found)
throw std::runtime_error("no coloured slider titled " + title.toStdString());
return *found;
}
};
// Swift's Color Balance ends, written out.
const std::array<std::vector<QColor>, 3> swiftEnds{
std::vector{QColor::fromRgbF(0.10f, 0.72f, 0.80f), QColor::fromRgbF(0.86f, 0.18f, 0.20f)},
std::vector{QColor::fromRgbF(0.80f, 0.22f, 0.70f), QColor::fromRgbF(0.24f, 0.70f, 0.30f)},
std::vector{QColor::fromRgbF(0.95f, 0.82f, 0.18f), QColor::fromRgbF(0.22f, 0.40f, 0.92f)}};
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);
}
// Counts the paints a widget receives.
class PaintCount : public QObject {
public:
explicit PaintCount(QWidget &widget) { widget.installEventFilter(this); }
int paints = 0;
protected:
bool eventFilter(QObject *, QEvent *event) override
{
paints += event->type() == QEvent::Paint;
return false;
}
};
// A point on the knob, found along the slider's middle.
QPoint knob(const CameraRawSlider &slider)
{
for (int x = 0; x < slider.width(); ++x) {
const QPoint point(x, slider.height() / 2);
if (slider.isOnKnob(point))
return point;
}
throw std::runtime_error("no knob");
}
QList<QColor> pixels(const QImage &image)
{
QList<QColor> colours;
for (int y = 0; y < image.height(); ++y)
for (int x = 0; x < image.width(); ++x)
colours << image.pixelColor(x, y);
return colours;
}
// Shown rows' titles: the widest's width, sixty at least.
void checkAligned(const Sheet &shown, const QStringList &visible, const QStringList &hidden)
{
int widest = 60;
for (const QString &text : visible)
widest = std::max(widest, shown.title(text).sizeHint().width());
for (const QString &text : visible + hidden) {
QCOMPARE(shown.title(text).minimumWidth(), widest);
QCOMPARE(shown.title(text).maximumWidth(), widest);
}
}
}
class ColoredSliderTests : public QObject {
Q_OBJECT
private slots:
void initTestCase() { SliderSnap::install(); }
void colorBalanceTracksRunFromEachColorToItsOpposite();
void theSpectrumCentresOnItsHue();
void resettingAColoredFilterSliderRestoresTheDefault();
void blackAndWhiteAndColorBalanceSlidersAreColoured();
void aDoubleClickResetsItsOwnSliderOnly();
void titlesLineUpToTheWidestShown();
void rowsLayOutAsSwifts();
void aResetReplacesTheFieldsTyping();
void theTrackPaintsEveryStopInPlace();
void aTitleResetsOnlyForALeftDoubleClickWhileEnabled();
void aResetThenScrubsFromTheDefault();
void colorBalanceResetsEachOfItsNineRows();
void aNewTrackRepaintsTheSlider();
};
void ColoredSliderTests::colorBalanceTracksRunFromEachColorToItsOpposite()
{
const std::vector<QColor> cyanRed = FilterSheet::cyanRedTrack().colors().value();
QVERIFY(cyanRed[0].blueF() > cyanRed[0].redF() && cyanRed[1].redF() > cyanRed[1].blueF());
const std::vector<QColor> magentaGreen = FilterSheet::magentaGreenTrack().colors().value();
QVERIFY(magentaGreen[0].redF() > magentaGreen[0].greenF() && magentaGreen[1].greenF() > magentaGreen[1].redF());
const std::vector<QColor> yellowBlue = FilterSheet::yellowBlueTrack().colors().value();
QVERIFY(yellowBlue[0].greenF() > yellowBlue[0].blueF() && yellowBlue[1].blueF() > yellowBlue[1].greenF());
const QColor greens = CameraRawSliderTrack{Kind::luminance, 120}.colors().value().back();
QVERIFY(greens.greenF() > greens.redF() && greens.greenF() > greens.blueF());
// Swift's exact ends.
QVERIFY(cyanRed == swiftEnds[0] && magentaGreen == swiftEnds[1] && yellowBlue == swiftEnds[2]);
}
void ColoredSliderTests::theSpectrumCentresOnItsHue()
{
// Thirteen stops every 30 degrees, the hue in the middle.
const std::vector<QColor> circle = CameraRawSliderTrack{Kind::spectrum, 60}.colors().value();
QCOMPARE(int(circle.size()), 13);
for (size_t index = 0; index < circle.size(); ++index) {
const double hue = std::fmod(60 - 180 + 30.0 * double(index) + 720, 360);
QVERIFY2(std::abs(circle[index].hsvHueF() * 360 - hue) < 0.5 || std::abs(circle[index].hsvHueF() * 360 - hue) > 359.5, qPrintable(QString::number(index)));
QVERIFY(std::abs(circle[index].hsvSaturationF() - 0.85) < 0.01 && std::abs(circle[index].valueF() - 0.9) < 0.01);
}
// Black to white, as Lightness runs.
const std::vector<QColor> ends = CameraRawSliderTrack{.kind = Kind::opposing, .from = Qt::black, .to = Qt::white}.colors().value();
QVERIFY(ends == (std::vector<QColor>{Qt::black, Qt::white}));
}
void ColoredSliderTests::resettingAColoredFilterSliderRestoresTheDefault()
{
EditorSession session;
session.createDocument(4, 4);
const QImage base = solid(QColor::fromRgbF(0.8f, 0.3f, 0.2f));
session.insert(ImportedImage(base, base, QStringLiteral("Color")));
session.beginFilter(FilterKind::blackWhite);
FilterSettings settings = session.filterEdit().value().settings;
settings.blackWhite.reds = 250;
settings.blackWhite.blues = -100;
session.updateFilter(settings, true);
settings = FilterSheet::resetting([](FilterSettings &each) -> double & { return each.blackWhite.reds; }, settings);
QCOMPARE(settings.blackWhite.reds, BlackWhiteSettings().reds);
// Only the double-clicked slider resets.
QCOMPARE(settings.blackWhite.blues, -100.0);
session.updateFilter(FilterSheet::resetting([](FilterSettings &each) -> double & { return each.blackWhite.blues; }, settings), true);
QVERIFY(session.filterEdit().value().settings.blackWhite == BlackWhiteSettings());
QVERIFY(session.filterEdit().value().preview);
bool done = false;
session.commitFilter([&done] { done = true; });
QTRY_VERIFY(done);
QCOMPARE(pixels(session.activeLayer().value().asset.value().image()), pixels(BlackWhiteSettings().apply(base)));
session.undo();
session.beginFilter(FilterKind::colorBalance);
FilterSettings balance = session.filterEdit().value().settings;
balance.colorBalance.midCyanRed = -80;
session.updateFilter(balance, false);
session.updateFilter(FilterSheet::resetting([](FilterSettings &each) -> double & { return each.colorBalance.midCyanRed; }, balance), false);
QVERIFY(session.filterEdit().value().settings.colorBalance == ColorBalanceSettings());
// A reset leaves Preview off.
QVERIFY(!session.filterEdit().value().preview);
session.cancelFilter();
QCOMPARE(pixels(session.activeLayer().value().asset.value().image()), pixels(base));
}
void ColoredSliderTests::blackAndWhiteAndColorBalanceSlidersAreColoured()
{
Sheet blackWhite(FilterKind::blackWhite);
const std::pair<const char *, double> families[] = {{"Reds", 0}, {"Yellows", 60}, {"Greens", 120}, {"Cyans", 180}, {"Blues", 240}, {"Magentas", 300}};
for (const auto &[title, degrees] : families) {
const CameraRawSlider &slider = blackWhite.coloured(QString::fromLatin1(title));
QVERIFY(slider.track().kind == Kind::luminance && slider.track().degrees == degrees);
QCOMPARE(slider.toolTip(), QString::fromLatin1(title) + QStringLiteral(". Double-click to reset."));
}
// Tint's Hue is plain; Saturation follows the tint's hue.
QCOMPARE(blackWhite.coloured(QStringLiteral("Hue")).track().kind, Kind::plain);
QVERIFY(blackWhite.coloured(QStringLiteral("Saturation")).track().kind == Kind::saturation
&& blackWhite.coloured(QStringLiteral("Saturation")).track().degrees == 40);
blackWhite.set([](FilterSettings &settings) { settings.blackWhite.tintHue = 200; });
QCOMPARE(blackWhite.coloured(QStringLiteral("Saturation")).track().degrees, 200.0);
// Its knob sits at 40, between -200 and 300.
QCOMPARE(blackWhite.coloured(QStringLiteral("Reds")).shown(), 40.0);
// Dragged, the value lands whole.
blackWhite.coloured(QStringLiteral("Reds")).setValue(501);
QCOMPARE(blackWhite.settings().blackWhite.reds, 51.0);
Sheet balance(FilterKind::colorBalance);
const QList<CameraRawSlider *> sliders = balance.sheet->findChildren<CameraRawSlider *>();
QCOMPARE(sliders.size(), 9);
for (int index = 0; index < 9; ++index)
QVERIFY(sliders[index]->track().kind == Kind::opposing && sliders[index]->track().colors().value() == swiftEnds[size_t(index % 3)]);
// Other filters keep the plain slider.
Sheet blur(FilterKind::gaussianBlur);
QVERIFY(!qobject_cast<CameraRawSlider *>(&blur.slider(QStringLiteral("Radius"))));
}
void ColoredSliderTests::aDoubleClickResetsItsOwnSliderOnly()
{
Sheet shown(FilterKind::blackWhite);
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
shown.set([](FilterSettings &settings) {
settings.blackWhite.reds = 250;
settings.blackWhite.blues = -100;
settings.blackWhite.tint = true;
settings.blackWhite.tintHue = 90;
}, false);
// On the title: that row alone, Preview left off.
QTest::mouseDClick(&shown.title(QStringLiteral("Reds")), Qt::LeftButton);
QVERIFY(shown.settings().blackWhite.reds == 40 && shown.settings().blackWhite.blues == -100);
QVERIFY(!shown.session.filterEdit().value().preview);
QCOMPARE(shown.sheet->findChild<QLineEdit *>(QStringLiteral("redsField"))->text(), QString("40"));
// On the knob.
CameraRawSlider &blues = shown.coloured(QStringLiteral("Blues"));
QTest::mouseDClick(&blues, Qt::LeftButton, {}, knob(blues));
QCOMPARE(shown.settings().blackWhite.blues, 20.0);
// The plain Hue resets too.
QTest::mouseDClick(&shown.title(QStringLiteral("Hue")), Qt::LeftButton);
QCOMPARE(shown.settings().blackWhite.tintHue, 40.0);
// A plain filter's title keeps its value.
Sheet blur(FilterKind::gaussianBlur);
blur.set([](FilterSettings &settings) { settings.radius = 25; });
QTest::mouseDClick(&blur.title(QStringLiteral("Radius")), Qt::LeftButton);
QCOMPARE(blur.settings().radius, 25.0);
}
void ColoredSliderTests::titlesLineUpToTheWidestShown()
{
const QStringList pairs{"Cyan / Red", "Magenta / Green", "Yellow / Blue"};
Sheet balance(FilterKind::colorBalance);
checkAligned(balance, pairs, {});
QVERIFY(balance.title(QStringLiteral("Magenta / Green")).width() > 60);
// Tint's rows count only while shown.
Sheet blackWhite(FilterKind::blackWhite);
const QStringList families{"Reds", "Yellows", "Greens", "Cyans", "Blues", "Magentas"};
checkAligned(blackWhite, families, {"Hue", "Saturation"});
blackWhite.set([](FilterSettings &settings) { settings.blackWhite.tint = true; });
checkAligned(blackWhite, families + QStringList{"Hue", "Saturation"}, {});
// Hidden again, they stop counting.
blackWhite.set([](FilterSettings &settings) { settings.blackWhite.tint = false; });
checkAligned(blackWhite, families, {"Hue", "Saturation"});
// A lone short title keeps sixty.
Sheet blur(FilterKind::gaussianBlur);
QCOMPARE(blur.title(QStringLiteral("Radius")).width(), 60);
}
void ColoredSliderTests::rowsLayOutAsSwifts()
{
// Title, slider 10 on, field 10 on, unit 2 past.
for (const auto &[kind, text, field] : {std::tuple(FilterKind::gaussianBlur, "Radius", "radiusField"), std::tuple(FilterKind::blackWhite, "Reds", "redsField")}) {
Sheet shown(kind);
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
QLabel &title = shown.title(QString::fromLatin1(text));
QSlider &slider = shown.slider(QString::fromLatin1(text));
QLineEdit &entry = *shown.sheet->findChild<QLineEdit *>(QString::fromLatin1(field));
QWidget *box = title.parentWidget();
QCOMPARE(title.x(), 0);
QCOMPARE(slider.x() - title.geometry().right() - 1, 10);
QCOMPARE(entry.x() - slider.geometry().right() - 1, 10);
QLabel *unit = box->findChildren<QLabel *>().last();
QCOMPARE(unit->x() - entry.geometry().right() - 1, 2);
QCOMPARE(unit->geometry().right() + 1, box->width());
QCOMPARE(box->height(), box->sizeHint().height());
// The row sits in the sheet's column, among the rest.
QVERIFY(shown.sheet->layout()->indexOf(box) >= 0);
}
}
void ColoredSliderTests::aResetReplacesTheFieldsTyping()
{
Sheet shown(FilterKind::blackWhite);
QVERIFY(QTest::qWaitForWindowActive(shown.sheet.get()));
shown.set([](FilterSettings &settings) { settings.blackWhite.reds = 250; });
auto &field = *shown.sheet->findChild<QLineEdit *>(QStringLiteral("redsField"));
field.setFocus();
field.selectAll();
QTest::keyClicks(&field, QStringLiteral("7"));
QVERIFY(field.isModified());
QTest::mouseDClick(&shown.title(QStringLiteral("Reds")), Qt::LeftButton);
QVERIFY(!field.isModified());
QCOMPARE(field.text(), QString("40"));
// Leaving the field applies nothing more.
shown.sheet->findChild<QLineEdit *>(QStringLiteral("bluesField"))->setFocus();
QCOMPARE(shown.settings().blackWhite.reds, 40.0);
}
void ColoredSliderTests::theTrackPaintsEveryStopInPlace()
{
// Round red: violet a quarter in, red in the middle.
CameraRawSlider slider(-100, 100, CameraRawSliderTrack{Kind::spectrum, 0}, QStringLiteral("Probe"), [](double) {}, [] {});
slider.resize(400, 22);
slider.display(100);
const QImage painted = slider.grab().toImage();
const auto hueAt = [&](double share) { return painted.pixelColor(int(share * painted.width()), painted.height() / 2).hsvHueF() * 360; };
QVERIFY2(std::abs(hueAt(0.25) - 270) < 25, qPrintable(QString::number(hueAt(0.25))));
const double middle = hueAt(0.5);
QVERIFY2(middle < 20 || middle > 340, qPrintable(QString::number(middle)));
QVERIFY2(std::abs(hueAt(0.75) - 90) < 25, qPrintable(QString::number(hueAt(0.75))));
}
void ColoredSliderTests::aTitleResetsOnlyForALeftDoubleClickWhileEnabled()
{
Sheet shown(FilterKind::blackWhite);
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
shown.set([](FilterSettings &settings) { settings.blackWhite.reds = 250; });
for (const Qt::MouseButton button : {Qt::RightButton, Qt::MiddleButton}) {
QTest::mouseDClick(&shown.title(QStringLiteral("Reds")), button);
QCOMPARE(shown.settings().blackWhite.reds, 250.0);
}
// A resting sheet resets nothing.
shown.sheet->setEnabled(false);
send(shown.title(QStringLiteral("Reds")), QEvent::MouseButtonDblClick, QPointF(2, 2), Qt::LeftButton, Qt::LeftButton);
QCOMPARE(shown.settings().blackWhite.reds, 250.0);
shown.sheet->setEnabled(true);
QTest::mouseDClick(&shown.title(QStringLiteral("Reds")), Qt::LeftButton);
QCOMPARE(shown.settings().blackWhite.reds, 40.0);
}
void ColoredSliderTests::aResetThenScrubsFromTheDefault()
{
// A real double click: the second press goes on scrubbing.
Sheet shown(FilterKind::blackWhite);
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
shown.set([](FilterSettings &settings) { settings.blackWhite.reds = 250; });
QLabel &title = shown.title(QStringLiteral("Reds"));
send(title, QEvent::MouseButtonPress, QPointF(2, 2), Qt::LeftButton, Qt::LeftButton);
send(title, QEvent::MouseButtonRelease, QPointF(2, 2), Qt::LeftButton, Qt::NoButton);
send(title, QEvent::MouseButtonDblClick, QPointF(2, 2), Qt::LeftButton, Qt::LeftButton);
QCOMPARE(shown.settings().blackWhite.reds, 40.0);
send(title, QEvent::MouseMove, QPointF(14, 2), Qt::NoButton, Qt::LeftButton);
send(title, QEvent::MouseButtonRelease, QPointF(14, 2), Qt::LeftButton, Qt::NoButton);
QCOMPARE(shown.settings().blackWhite.reds, 52.0);
}
void ColoredSliderTests::colorBalanceResetsEachOfItsNineRows()
{
Sheet shown(FilterKind::colorBalance);
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
const std::array<double ColorBalanceSettings::*, 9> fields{
&ColorBalanceSettings::shadowCyanRed, &ColorBalanceSettings::shadowMagentaGreen, &ColorBalanceSettings::shadowYellowBlue,
&ColorBalanceSettings::midCyanRed, &ColorBalanceSettings::midMagentaGreen, &ColorBalanceSettings::midYellowBlue,
&ColorBalanceSettings::highlightCyanRed, &ColorBalanceSettings::highlightMagentaGreen, &ColorBalanceSettings::highlightYellowBlue};
const QList<CameraRawSlider *> sliders = shown.sheet->findChildren<CameraRawSlider *>();
QCOMPARE(sliders.size(), 9);
for (size_t index = 0; index < fields.size(); ++index) {
shown.set([&fields](FilterSettings &settings) {
for (size_t each = 0; each < fields.size(); ++each)
settings.colorBalance.*fields[each] = double(each + 1) * 10;
}, false);
CameraRawSlider &slider = *sliders[qsizetype(index)];
// Titles on even rows, knobs on odd ones.
if (index % 2 == 0) {
QLabel *title = nullptr;
for (QLabel *label : shown.sheet->findChildren<QLabel *>())
title = label->buddy() == &slider ? label : title;
QTest::mouseDClick(title, Qt::LeftButton);
// The double click's own release ends its scrub.
send(*title, QEvent::MouseButtonRelease, QPointF(2, 2), Qt::LeftButton, Qt::NoButton);
} else {
QTest::mouseDClick(&slider, Qt::LeftButton, {}, knob(slider));
}
for (size_t each = 0; each < fields.size(); ++each)
QCOMPARE(shown.settings().colorBalance.*fields[each], each == index ? 0.0 : double(each + 1) * 10);
QVERIFY(!shown.session.filterEdit().value().preview);
}
}
void ColoredSliderTests::aNewTrackRepaintsTheSlider()
{
// Swift's needsDisplay: the knob stays, the gradient changes.
CameraRawSlider slider(-100, 100, CameraRawSliderTrack{Kind::saturation, 0}, QStringLiteral("Probe"), [](double) {}, [] {});
slider.resize(200, 22);
slider.display(20);
slider.show();
QVERIFY(QTest::qWaitForWindowExposed(&slider));
QTest::qWait(50);
PaintCount count(slider);
slider.reshape(-100, 100, CameraRawSliderTrack{Kind::saturation, 200}, 20);
QTRY_COMPARE(count.paints, 1);
// Mid-drag too.
slider.setSliderDown(true);
slider.reshape(-100, 100, CameraRawSliderTrack{Kind::spectrum, 90}, 60);
QTRY_COMPARE(count.paints, 2);
slider.setSliderDown(false);
// In a sheet: Tint's saturation follows its hue.
Sheet shown(FilterKind::blackWhite);
shown.set([](FilterSettings &settings) { settings.blackWhite.tint = true; });
QVERIFY(QTest::qWaitForWindowExposed(shown.sheet.get()));
QTest::qWait(50);
PaintCount saturation(shown.coloured(QStringLiteral("Saturation")));
shown.set([](FilterSettings &settings) { settings.blackWhite.tintHue = 200; });
QTRY_VERIFY(saturation.paints >= 1);
}
QTEST_MAIN(ColoredSliderTests)
#include "ColoredSliderTests.moc"
+2 -2
View File
@@ -101,11 +101,11 @@ void FilterControlsTests::eachKindShowsSwiftsControls()
QVERIFY(field.alignment() & Qt::AlignRight);
QVERIFY(!field.accessibleName().isEmpty() && field.placeholderText() == field.accessibleName());
QSlider &slider = sheet.child<QSlider>(qPrintable(name + QStringLiteral("Slider")));
// Swift's title: sixty points at least, naming the slider.
// Swift's title: the widest title's width, sixty at least.
QLabel *title = nullptr;
for (QLabel *each : slider.parentWidget()->findChildren<QLabel *>())
title = each->buddy() == &slider ? each : title;
QVERIFY(title && title->text() == field.accessibleName() && title->minimumWidth() == 60);
QVERIFY(title && title->text() == field.accessibleName() && title->minimumWidth() >= 60 && title->maximumWidth() == title->minimumWidth());
}
QCOMPARE(sheet.sheet->findChild<CurvesControls *>() != nullptr, kind == FilterKind::curves);
QCOMPARE(sheet.sheet->findChild<GradientMapControls *>() != nullptr, kind == FilterKind::gradientMap);
+189 -22
View File
@@ -11,6 +11,13 @@ private slots:
void samplersAndTargetingToggle();
void theControlsFollowTheSession();
void togglesResetAndTheButtons();
void tracksFollowTheRangeAndColorize();
void aDoubleClickResetsToNoneOrColorizesStart();
void aResetReplacesTheFieldsTyping();
void arrowKeysWalkEverySlider();
void aResetKeepsPreviewOffInBothModes();
void aRangeUnderColorizeKeepsItsTracks();
void aTitleResetsOnALeftDoubleClickThenScrubs();
};
void HueSaturationSheetTests::theSheetIsLaidOutAsSwifts()
@@ -41,11 +48,12 @@ void HueSaturationSheetTests::theSheetIsLaidOutAsSwifts()
if (frame->frameShape() == QFrame::VLine)
QVERIFY(frame->isHidden());
}
const std::pair<const char *, int> rows[] = {{"Hue", 18000}, {"Saturation", 10000}, {"Lightness", 10000}};
for (const auto &[title, reach] : rows) {
for (const char *title : {"Hue", "Saturation", "Lightness"}) {
const QString lower = QString::fromLatin1(title).toLower();
QSlider &slider = *shown.sheet->findChild<QSlider *>(lower + QStringLiteral("Slider"));
QVERIFY(slider.minimum() == -reach && slider.maximum() == reach && slider.value() == 0);
// Swift's coloured slider: centred, its help naming the reset.
CameraRawSlider &slider = *shown.sheet->findChild<CameraRawSlider *>(lower + QStringLiteral("Slider"));
QVERIFY(slider.minimum() == 0 && slider.maximum() == 1000 && slider.value() == 500 && slider.shown() == 0);
QCOMPARE(slider.toolTip(), QString(title) + QStringLiteral(". Double-click to reset."));
QCOMPARE(labelled(slider), QString(title));
QLineEdit &field = *shown.sheet->findChild<QLineEdit *>(lower + QStringLiteral("Field"));
QCOMPARE(field.text(), QString("0"));
@@ -127,10 +135,11 @@ void HueSaturationSheetTests::aRangeShowsItsSpectrumAndSamplers()
QVERIFY(shown.settings() == HueSaturationSettings::colorizeStart());
QVERIFY(!range.isEnabled());
QVERIFY(!shown.child<QToolButton>("hueTargeting").isVisible() && !shown.child<QWidget>("hueSpectrum").isVisible());
QSlider &hue = *shown.sheet->findChild<QSlider *>(QStringLiteral("hueSlider"));
QSlider &saturation = *shown.sheet->findChild<QSlider *>(QStringLiteral("saturationSlider"));
QVERIFY(hue.minimum() == 0 && hue.maximum() == 36000);
QVERIFY(saturation.minimum() == 0 && saturation.maximum() == 10000 && saturation.value() == 2500);
CameraRawSlider &hue = *shown.sheet->findChild<CameraRawSlider *>(QStringLiteral("hueSlider"));
CameraRawSlider &saturation = *shown.sheet->findChild<CameraRawSlider *>(QStringLiteral("saturationSlider"));
// Both ranges now start at zero.
QVERIFY(hue.value() == 0 && hue.shown() == 0);
QVERIFY(saturation.value() == 250 && saturation.shown() == 25);
QCOMPARE(shown.child<QLineEdit>("saturationField").text(), QString("25"));
// Off again: everything back to the start.
shown.titled<QCheckBox>(QStringLiteral("Colorize")).click();
@@ -145,10 +154,11 @@ void HueSaturationSheetTests::slidersAndFieldsWriteTheirValue()
{
Sheet shown;
QVERIFY(shown.show());
QSlider &hue = *shown.sheet->findChild<QSlider *>(QStringLiteral("hueSlider"));
QSlider &saturationSlider = *shown.sheet->findChild<QSlider *>(QStringLiteral("saturationSlider"));
hue.setValue(3060);
QCOMPARE(shown.settings().hue(), 30.6);
CameraRawSlider &hue = *shown.sheet->findChild<CameraRawSlider *>(QStringLiteral("hueSlider"));
CameraRawSlider &saturationSlider = *shown.sheet->findChild<CameraRawSlider *>(QStringLiteral("saturationSlider"));
// 30.6 along the track: Swift sets the whole number.
hue.setValue(585);
QCOMPARE(shown.settings().hue(), 31.0);
QLineEdit &hueField = shown.child<QLineEdit>("hueField");
QCOMPARE(hueField.text(), QString("31"));
// Visited without typing, the shown whole number stays unread.
@@ -157,27 +167,27 @@ void HueSaturationSheetTests::slidersAndFieldsWriteTheirValue()
QLineEdit &saturation = shown.child<QLineEdit>("saturationField");
saturation.setFocus();
QTRY_VERIFY(saturation.hasFocus());
QCOMPARE(shown.settings().hue(), 30.6);
QCOMPARE(shown.settings().hue(), 31.0);
// Typed and left, a number applies unclamped.
type(saturation, QStringLiteral("250"));
hueField.setFocus();
QCOMPARE(shown.settings().saturation(), 250.0);
QCOMPARE(saturationSlider.value(), 10000);
QCOMPARE(saturationSlider.value(), 1000);
QCOMPARE(saturation.text(), QString("250"));
// Applied, the field follows the session again.
saturationSlider.setValue(-2000);
saturationSlider.setValue(400);
QCOMPARE(saturation.text(), QString("-20"));
// What is no finite number reverts.
for (const char *text : {"abc", "inf", "nan"}) {
type(hueField, QString::fromLatin1(text));
saturation.setFocus();
QCOMPARE(shown.settings().hue(), 30.6);
QCOMPARE(shown.settings().hue(), 31.0);
QCOMPARE(hueField.text(), QString("31"));
}
// Past any int, the slider rests at its end.
type(saturation, QStringLiteral("1e300"));
hueField.setFocus();
QCOMPARE(saturationSlider.value(), 10000);
QCOMPARE(saturationSlider.value(), 1000);
// Sliders show Colorize's range without writing.
HueSaturationSettings colour = HueSaturationSettings::colorizeStart();
colour.setHue(-40);
@@ -187,12 +197,12 @@ void HueSaturationSheetTests::slidersAndFieldsWriteTheirValue()
QCOMPARE(hueField.text(), QString("-40"));
// Typing waits through other values; its own slider replaces it.
type(hueField, QStringLiteral("4"));
saturationSlider.setValue(1000);
saturationSlider.setValue(100);
QVERIFY(hueField.text() == "4" && hueField.isModified());
hue.setValue(1000);
QVERIFY(hueField.text() == "10" && !hueField.isModified() && hueField.hasFocus());
hue.setValue(250);
QVERIFY(hueField.text() == "90" && !hueField.isModified() && hueField.hasFocus());
saturation.setFocus();
QCOMPARE(shown.settings().hue(), 10.0);
QCOMPARE(shown.settings().hue(), 90.0);
}
void HueSaturationSheetTests::fieldsClampOnReturnAndPrintWhole()
@@ -273,7 +283,7 @@ void HueSaturationSheetTests::togglesResetAndTheButtons()
preview.click();
QVERIFY(!shown.session.hueSaturation().value().preview);
// Off, the controls keep Preview off.
shown.sheet->findChild<QSlider *>(QStringLiteral("lightnessSlider"))->setValue(500);
shown.sheet->findChild<QSlider *>(QStringLiteral("lightnessSlider"))->setValue(525);
QVERIFY(!shown.session.hueSaturation().value().preview && shown.settings().lightness() == 5);
preview.click();
QVERIFY(shown.session.hueSaturation().value().preview);
@@ -302,5 +312,162 @@ void HueSaturationSheetTests::togglesResetAndTheButtons()
QCOMPARE(shown.session.history.undoCount(), steps);
}
void HueSaturationSheetTests::tracksFollowTheRangeAndColorize()
{
using Kind = CameraRawSliderTrack::Kind;
Sheet shown;
const auto track = [&](const char *name) { return shown.child<CameraRawSlider>(name).track(); };
// Master: circle round red, gray to red, black to white.
QVERIFY(track("hueSlider").kind == Kind::spectrum && track("hueSlider").degrees == 0);
QCOMPARE(track("saturationSlider").kind, Kind::chroma);
QVERIFY(track("lightnessSlider").kind == Kind::opposing && track("lightnessSlider").colors() == (std::vector<QColor>{Qt::black, Qt::white}));
// A range centres both on its own hue.
shown.set(HueSaturationSettings(0, 0, 0, false, ColorRange::cyans));
QVERIFY(track("hueSlider").kind == Kind::spectrum && track("hueSlider").degrees == 180);
QVERIFY(track("saturationSlider").kind == Kind::saturation && track("saturationSlider").degrees == 180);
shown.set(HueSaturationSettings(0, 0, 0, false, ColorRange::magentas));
QCOMPARE(track("hueSlider").degrees, 300.0);
// Colorize: red to red; saturation towards the chosen hue.
shown.set(HueSaturationSettings(200, 25, 0, true));
QVERIFY(track("hueSlider").kind == Kind::spectrum && track("hueSlider").degrees == 180);
QVERIFY(track("saturationSlider").kind == Kind::saturation && track("saturationSlider").degrees == 200);
// Its knob at 200 of 0 to 360.
QCOMPARE(shown.child<CameraRawSlider>("hueSlider").value(), 556);
}
void HueSaturationSheetTests::aDoubleClickResetsToNoneOrColorizesStart()
{
Sheet shown;
QVERIFY(shown.show());
const auto title = [&](const char *text) -> QLabel & { return shown.titled<QLabel>(QString::fromLatin1(text)); };
shown.set(HueSaturationSettings(30, -40, 20, false, ColorRange::reds));
// The title: that value alone back to none.
QTest::mouseDClick(&title("Hue"), Qt::LeftButton);
QVERIFY(shown.settings().hue() == 0 && shown.settings().saturation() == -40 && shown.settings().lightness() == 20);
// The knob.
CameraRawSlider &lightness = shown.child<CameraRawSlider>("lightnessSlider");
QPoint knob;
for (int x = 0; x < lightness.width() && knob.isNull(); ++x)
knob = lightness.isOnKnob(QPoint(x, lightness.height() / 2)) ? QPoint(x, lightness.height() / 2) : knob;
QTest::mouseDClick(&lightness, Qt::LeftButton, {}, knob);
QVERIFY(shown.settings().lightness() == 0 && shown.settings().saturation() == -40);
// Under Colorize, back to its start: saturation 25.
shown.set(HueSaturationSettings(200, 70, 10, true));
QTest::mouseDClick(&title("Saturation"), Qt::LeftButton);
QVERIFY(shown.settings().saturation() == 25 && shown.settings().hue() == 200 && shown.settings().lightness() == 10);
QCOMPARE(shown.child<QLineEdit>("saturationField").text(), QString("25"));
}
void HueSaturationSheetTests::aResetReplacesTheFieldsTyping()
{
Sheet shown;
QVERIFY(shown.show());
shown.set(HueSaturationSettings(200, 70, 10, true));
QLineEdit &hueField = shown.child<QLineEdit>("hueField");
hueField.setFocus();
QTRY_VERIFY(hueField.hasFocus());
hueField.selectAll();
QTest::keyClicks(&hueField, QStringLiteral("77"));
QTest::mouseDClick(&shown.titled<QLabel>(QStringLiteral("Hue")), Qt::LeftButton);
QCOMPARE(shown.settings().hue(), 0.0);
QCOMPARE(hueField.text(), QString("0"));
QVERIFY(!hueField.isModified());
}
void HueSaturationSheetTests::arrowKeysWalkEverySlider()
{
// A hundred steps: whole values, the knob keeping its travel.
const auto walk = [](Sheet &shown, const char *name, Qt::Key key) {
CameraRawSlider &slider = shown.child<CameraRawSlider>(name);
slider.setFocus();
for (int step = 0; step < 100; ++step)
QTest::keyClick(&slider, key);
};
const std::tuple<bool, const char *, Qt::Key, double> cases[] = {
{false, "hueSlider", Qt::Key_Right, 53}, {false, "hueSlider", Qt::Key_Left, -53}, {false, "saturationSlider", Qt::Key_Right, 33},
{false, "lightnessSlider", Qt::Key_Left, -33}, {true, "hueSlider", Qt::Key_Right, 53}, {true, "saturationSlider", Qt::Key_Right, 45},
{true, "saturationSlider", Qt::Key_Left, 9}, {true, "lightnessSlider", Qt::Key_Right, 33}};
for (const auto &[colorize, name, key, expected] : cases) {
Sheet shown;
QVERIFY(shown.show());
shown.set(colorize ? HueSaturationSettings::colorizeStart() : HueSaturationSettings());
walk(shown, name, key);
const HueSaturationSettings &now = shown.settings();
const double value = QByteArray(name) == "hueSlider" ? now.hue() : QByteArray(name) == "saturationSlider" ? now.saturation() : now.lightness();
QVERIFY2(value == expected, qPrintable(QStringLiteral("%1 %2: %3").arg(QString::fromLatin1(name)).arg(colorize).arg(value)));
}
// An unchanged step leaves the field's typing.
Sheet shown;
QVERIFY(shown.show());
QLineEdit &field = shown.child<QLineEdit>("hueField");
field.setFocus();
QTRY_VERIFY(field.hasFocus());
field.selectAll();
QTest::keyClicks(&field, QStringLiteral("4"));
QTest::keyClick(&shown.child<CameraRawSlider>("hueSlider"), Qt::Key_Right);
QVERIFY(field.isModified() && field.text() == "4" && shown.settings().hue() == 0);
}
void HueSaturationSheetTests::aResetKeepsPreviewOffInBothModes()
{
for (const bool colorize : {false, true}) {
Sheet shown;
QVERIFY(shown.show());
shown.session.updateHueSaturation(HueSaturationSettings(90, 60, 30, colorize), false);
QTest::mouseDClick(&shown.titled<QLabel>(QStringLiteral("Saturation")), Qt::LeftButton);
QCOMPARE(shown.settings().saturation(), colorize ? 25.0 : 0.0);
QVERIFY(shown.settings().hue() == 90 && shown.settings().lightness() == 30);
QCOMPARE(shown.child<QLineEdit>("saturationField").text(), colorize ? QString("25") : QString("0"));
QVERIFY(!shown.session.hueSaturation().value().preview);
CameraRawSlider &lightness = shown.child<CameraRawSlider>("lightnessSlider");
QPoint knob;
for (int x = 0; x < lightness.width() && knob.isNull(); ++x)
knob = lightness.isOnKnob(QPoint(x, lightness.height() / 2)) ? QPoint(x, lightness.height() / 2) : knob;
QTest::mouseDClick(&lightness, Qt::LeftButton, {}, knob);
QVERIFY(shown.settings().lightness() == 0 && shown.settings().hue() == 90);
QVERIFY(!shown.session.hueSaturation().value().preview);
}
}
void HueSaturationSheetTests::aRangeUnderColorizeKeepsItsTracks()
{
using Kind = CameraRawSliderTrack::Kind;
Sheet shown;
shown.set(HueSaturationSettings(200, 25, 0, true, ColorRange::greens));
CameraRawSlider &hue = shown.child<CameraRawSlider>("hueSlider");
CameraRawSlider &saturation = shown.child<CameraRawSlider>("saturationSlider");
// Red to red, towards the chosen hue, Colorize's bounds.
QVERIFY(hue.track().kind == Kind::spectrum && hue.track().degrees == 180);
QVERIFY(saturation.track().kind == Kind::saturation && saturation.track().degrees == 200);
QVERIFY(hue.value() == 556 && saturation.value() == 250);
}
void HueSaturationSheetTests::aTitleResetsOnALeftDoubleClickThenScrubs()
{
Sheet shown;
QVERIFY(shown.show());
shown.set(HueSaturationSettings(30, 0, 0, false));
QLabel &title = shown.titled<QLabel>(QStringLiteral("Hue"));
const auto send = [&title](QEvent::Type type, double x, Qt::MouseButton button, Qt::MouseButtons buttons) {
QMouseEvent event(type, QPointF(x, 2), title.mapToGlobal(QPointF(x, 2)), button, buttons, Qt::NoModifier);
QApplication::sendEvent(&title, &event);
};
// Other buttons and a resting sheet reset nothing.
for (const Qt::MouseButton button : {Qt::RightButton, Qt::MiddleButton})
send(QEvent::MouseButtonDblClick, 2, button, button);
shown.sheet->setEnabled(false);
send(QEvent::MouseButtonDblClick, 2, Qt::LeftButton, Qt::LeftButton);
shown.sheet->setEnabled(true);
QCOMPARE(shown.settings().hue(), 30.0);
// A real double click resets; its second press scrubs.
send(QEvent::MouseButtonPress, 2, Qt::LeftButton, Qt::LeftButton);
send(QEvent::MouseButtonRelease, 2, Qt::LeftButton, Qt::NoButton);
send(QEvent::MouseButtonDblClick, 2, Qt::LeftButton, Qt::LeftButton);
QCOMPARE(shown.settings().hue(), 0.0);
send(QEvent::MouseMove, 14, Qt::NoButton, Qt::LeftButton);
send(QEvent::MouseButtonRelease, 14, Qt::LeftButton, Qt::NoButton);
QCOMPARE(shown.settings().hue(), 12.0);
}
QTEST_MAIN(HueSaturationSheetTests)
#include "HueSaturationSheetTests.moc"
+133
View File
@@ -0,0 +1,133 @@
#include "Document/BrushStroke.h"
#include "ScrubFixtures.h"
#include "UI/CameraRawColorControls.h"
#include "UI/CameraRawControls.h"
#include "UI/CameraRawDetailOpticsControls.h"
#include "UI/CameraRawGeometryCalibrationControls.h"
#include "UI/CameraRawRow.h"
#include "UI/CanvasSizeSheet.h"
#include "UI/ColorPickerSheet.h"
#include "UI/EffectsSheet.h"
#include "UI/FilterSheet.h"
#include "UI/HueSaturationSheet.h"
#include "UI/ImageSizeSheet.h"
#include "UI/LevelsSheet.h"
// A sheet destroyed while its entry holds keys and typing.
namespace {
void paint(EditorSession &session)
{
session.createDocument(8, 8);
QImage image = BrushRaster::context(8, 8, false);
image.fill(QColor(120, 160, 200));
session.insert(ImportedImage(image, image, QStringLiteral("Colour")));
}
// Names the owner's class as its entry loses the keys.
class FocusWitness : public QObject {
public:
FocusWitness(QWidget &owner, QWidget &field) : m_owner(owner) { field.installEventFilter(this); }
QString seen;
protected:
bool eventFilter(QObject *, QEvent *event) override
{
if (event->type() == QEvent::FocusOut && seen.isEmpty())
seen = QString::fromLatin1(m_owner.metaObject()->className());
return false;
}
private:
QWidget &m_owner;
};
// Past its destructor, the owner reads as QWidget.
void typeThenDestroy(std::unique_ptr<QWidget> owner, const QString &typed)
{
showActive(*owner);
PickerField *field = owner->findChildren<PickerField *>().value(0);
if (!field)
throw std::runtime_error(std::string("no entry in ") + owner->metaObject()->className());
field->setFocus();
if (!QTest::qWaitFor([field] { return field->hasFocus(); }))
throw std::runtime_error("the entry never took the keys");
field->selectAll();
QTest::keyClicks(field, typed);
if (!field->isModified())
throw std::runtime_error("the typing did not land");
const QString name = QString::fromLatin1(owner->metaObject()->className());
FocusWitness witness(*owner, *field);
owner.reset();
// Past its own destructor, the owner would read as QWidget.
if (witness.seen != name)
throw std::runtime_error("the entry committed into " + witness.seen.toStdString() + ", not " + name.toStdString());
}
}
class SheetTeardownTests : public QObject {
Q_OBJECT
private slots:
void theColourSheetsCommitAsTheyGo();
void theDocumentSheetsGoQuietly();
void aCameraRawRowGoesQuietly();
void theCameraRawGroupsGoWhole();
};
void SheetTeardownTests::theColourSheetsCommitAsTheyGo()
{
// Each commits its typing as hiding would, members alive.
EditorSession session;
paint(session);
session.beginHueSaturation();
typeThenDestroy(std::make_unique<HueSaturationSheet>(session), QStringLiteral("12"));
QCOMPARE(session.hueSaturation().value().settings.hue(), 12.0);
session.cancelHueSaturation();
session.beginFilter(FilterKind::gaussianBlur);
typeThenDestroy(std::make_unique<FilterSheet>(session), QStringLiteral("7"));
QCOMPARE(session.filterEdit().value().settings.radius, 7.0);
session.cancelFilter();
session.beginLevels();
typeThenDestroy(std::make_unique<LevelsSheet>(session), QStringLiteral("9"));
QCOMPARE(session.levels().value().settings.current().black, 9.0);
session.cancelLevels();
session.addEffect(LayerEffectKind::stroke);
typeThenDestroy(std::make_unique<EffectsSheet>(session, LayerEffectKind::stroke), QStringLiteral("5"));
QCOMPARE(session.editingEffects().stroke.value().size, 5.0);
session.openColorPicker(false);
typeThenDestroy(std::make_unique<ColorPickerSheet>(session, [](bool) {}), QStringLiteral("10"));
QCOMPARE(session.colorPicker().value().color().red, 10.0 / 255);
}
void SheetTeardownTests::theDocumentSheetsGoQuietly()
{
const CanvasDocument canvas{120, 80};
typeThenDestroy(std::make_unique<ImageSizeSheet>(canvas, [](std::optional<ImageSizeOptions>) {}), QStringLiteral("60"));
typeThenDestroy(std::make_unique<CanvasSizeSheet>(canvas, PaletteColor::black(), PaletteColor::white(), [](std::optional<CanvasSizeOptions>) {}),
QStringLiteral("60"));
}
void SheetTeardownTests::aCameraRawRowGoesQuietly()
{
double typed = 0;
typeThenDestroy(std::make_unique<CameraRawRow>(CameraRawRow::Spec{.name = QStringLiteral("probe"), .title = QStringLiteral("Probe"), .help = QString()},
[] { return 0.0; }, [](double) {}, [&typed](double value) { typed = value; }, [] {}),
QStringLiteral("30"));
QCOMPARE(typed, 30.0);
}
void SheetTeardownTests::theCameraRawGroupsGoWhole()
{
EditorSession session;
paint(session);
session.beginFilter(FilterKind::cameraRaw);
typeThenDestroy(std::make_unique<CameraRawControls>(session), QStringLiteral("1"));
typeThenDestroy(std::make_unique<CameraRawCurveControls>(session), QStringLiteral("10"));
typeThenDestroy(std::make_unique<CameraRawMixerControls>(session), QStringLiteral("10"));
typeThenDestroy(std::make_unique<CameraRawDetailControls>(session), QStringLiteral("10"));
typeThenDestroy(std::make_unique<CameraRawOpticsControls>(session), QStringLiteral("10"));
typeThenDestroy(std::make_unique<CameraRawGeometryControls>(session), QStringLiteral("10"));
typeThenDestroy(std::make_unique<CameraRawCalibrationControls>(session), QStringLiteral("10"));
}
QTEST_MAIN(SheetTeardownTests)
#include "SheetTeardownTests.moc"
+2
View File
@@ -167,6 +167,8 @@ compositor_test(ProjectReplaceTests)
compositor_test(ProjectWorkspaceFlowTests)
compositor_test(QuitWhileEditingTextTests)
compositor_test(OpenRecentTests)
compositor_test(ColoredSliderTests)
compositor_test(SheetTeardownTests)
compositor_test(RecentProjectsCoreTests)
compositor_test(ProjectWorkspaceSignalTests)
compositor_test(ProjectWorkspaceTests)