Files
OmaPhoto/tests/GridSettingsSheetTests.cpp
T

322 lines
17 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "UI/ColorPickerSheet+Dialog.h"
#include "UI/ColorPickerSheet.h"
#include "UI/GridSettingsSheet.h"
#include <QComboBox>
#include <QDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QSlider>
#include <QVBoxLayout>
#include <QtTest>
// Swift 1.3.5's GridSettingsSheet: the controls, previews and answers.
class GridSettingsSheetTests : public QObject {
Q_OBJECT
private slots:
// A user's English locale: Qt 6.10's C locale groups nothing.
void initTestCase() { QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); }
void theSheetLaysOutAsSwifts();
void eachChangePreviewsTheGrid();
void aPickedColourBecomesCustom();
void restoreDefaultsOkAndCancel();
void theRowsHoldSwiftsControls();
void aSliderOrScrubReplacesTyping();
};
namespace {
// Drags a scrubbable title `points` to the right.
void scrub(QLabel &title, int points)
{
QTest::mousePress(&title, Qt::LeftButton, Qt::NoModifier, QPoint(2, 2));
QMouseEvent move(QEvent::MouseMove, QPointF(2 + points, 2), title.mapToGlobal(QPointF(2 + points, 2)), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(&title, &move);
QTest::mouseRelease(&title, Qt::LeftButton, Qt::NoModifier, QPoint(2 + points, 2));
}
struct Sheet {
EditorSession session;
std::vector<GridSettingsSheet::Settings> previews;
std::optional<std::optional<GridSettingsSheet::Settings>> answer;
std::unique_ptr<GridSettingsSheet> sheet;
explicit Sheet(LayoutGrid grid = LayoutGrid(), GridAppearance appearance = GridAppearance())
: sheet(std::make_unique<GridSettingsSheet>(
session, grid, appearance, [this](const GridSettingsSheet::Settings &settings) { previews.push_back(settings); },
[this](std::optional<GridSettingsSheet::Settings> settings) { answer = settings; }))
{
sheet->show();
if (!QTest::qWaitForWindowActive(sheet.get()))
throw std::runtime_error("the sheet never became active");
}
template <typename Widget> Widget &find(const char *name)
{
Widget *found = sheet->findChild<Widget *>(QString::fromLatin1(name));
if (!found)
throw std::runtime_error(std::string("no widget named ") + name);
return *found;
}
// Typed, then Return: the field commits.
void type(const char *name, const QString &text)
{
auto &field = find<PickerField>(name);
field.setFocus();
field.selectAll();
QTest::keyClicks(&field, text);
QTest::keyClick(&field, Qt::Key_Return);
}
QString note() { return find<QLabel>("gridNote").text(); }
const GridSettingsSheet::Settings &last() const { return previews.back(); }
};
}
void GridSettingsSheetTests::theSheetLaysOutAsSwifts()
{
Sheet shown;
auto *column = qobject_cast<QVBoxLayout *>(shown.sheet->layout());
QVERIFY(column && shown.sheet->width() == 360 && column->spacing() == 18 && column->contentsMargins() == QMargins(24, 24, 24, 24));
auto *heading = qobject_cast<QLabel *>(column->itemAt(0)->widget());
QVERIFY(heading->text() == "Grid" && heading->font().pixelSize() == 17 && heading->font().bold());
// Titles 110 wide, each naming its control.
const auto rowOf = [column](int index) { return qobject_cast<QHBoxLayout *>(column->itemAt(index)->layout()); };
const QStringList titles{"Color", "Style", "Opacity"};
for (int index = 0; index < 3; ++index) {
auto *title = qobject_cast<QLabel *>(rowOf(index + 1)->itemAt(0)->widget());
QVERIFY(title->text() == titles[index] && title->width() == 110 && title->buddy());
}
auto &preset = shown.find<QComboBox>("gridPreset");
QCOMPARE(preset.count(), 10);
QVERIFY(preset.itemText(0) == "Light Gray" && preset.itemText(9) == "Custom" && preset.currentText() == "Light Gray");
auto &swatch = shown.find<DialogColorSwatch>("gridColor");
QVERIFY(swatch.accessibleName() == "Grid Color" && swatch.toolTip() == "Choose a custom grid color" && rowOf(1)->itemAt(2)->widget() == &swatch);
auto &style = shown.find<QComboBox>("gridStyle");
QVERIFY(style.count() == 3 && style.itemText(1) == "Dashed Lines" && style.currentText() == "Lines");
auto &opacity = shown.find<PickerField>("gridOpacity");
QVERIFY(opacity.text() == "45" && opacity.width() == 48 && opacity.alignment() == Qt::AlignRight && opacity.accessibleName() == "Opacity");
QVERIFY(shown.find<QSlider>("gridOpacitySlider").minimum() == 1 && shown.find<QSlider>("gridOpacitySlider").maximum() == 100);
QCOMPARE(shown.find<QSlider>("gridOpacitySlider").value(), 45);
auto *rule = qobject_cast<QFrame *>(column->itemAt(4)->widget());
QVERIFY(rule && rule->frameShape() == QFrame::HLine);
QVERIFY(qobject_cast<QLabel *>(rowOf(5)->itemAt(0)->widget())->text() == "Gridline every" && shown.find<PickerField>("gridSpacing").text() == "64");
QCOMPARE(qobject_cast<QLabel *>(rowOf(5)->itemAt(2)->widget())->text(), QString("pixels"));
QVERIFY(qobject_cast<QLabel *>(rowOf(6)->itemAt(0)->widget())->text() == "Subdivisions" && shown.find<PickerField>("gridSubdivisions").text() == "8");
QCOMPARE(shown.note(), QString("A subdivision every 8 pixels."));
QCOMPARE(shown.find<QLabel>("gridNote").foregroundRole(), QPalette::PlaceholderText);
auto *buttons = rowOf(8);
QVERIFY(qobject_cast<QPushButton *>(buttons->itemAt(0)->widget())->text() == "Cancel" && qobject_cast<QPushButton *>(buttons->itemAt(1)->widget())->text() == "Restore Defaults");
QVERIFY(buttons->itemAt(2)->spacerItem() && shown.find<QPushButton>("gridOK").isDefault());
QVERIFY(shown.previews.empty());
}
void GridSettingsSheetTests::eachChangePreviewsTheGrid()
{
Sheet shown;
auto &preset = shown.find<QComboBox>("gridPreset");
preset.setCurrentIndex(7);
emit preset.activated(7);
QVERIFY(shown.previews.size() == 1 && shown.last().second.preset == GridAppearance::Preset::cyan && shown.last().first == LayoutGrid());
shown.find<QComboBox>("gridStyle").setCurrentIndex(2);
emit shown.find<QComboBox>("gridStyle").activated(2);
QCOMPARE(shown.last().second.style, GridAppearance::Style::dots);
// Opacity: the slider, the field (clamped), its arrows.
shown.find<QSlider>("gridOpacitySlider").setValue(80);
QCOMPARE(shown.last().second.opacity, 80);
shown.type("gridOpacity", "150");
QVERIFY(shown.last().second.opacity == 100 && shown.find<PickerField>("gridOpacity").text() == "100");
QTest::keyClick(&shown.find<PickerField>("gridOpacity"), Qt::Key_Down, Qt::ShiftModifier);
QCOMPARE(shown.last().second.opacity, 90);
// The grid previews only while valid; the note says why.
shown.type("gridSpacing", "100");
QVERIFY(shown.last().first == LayoutGrid(100, 8));
QCOMPARE(shown.note(), QString("A subdivision every 12.5 pixels."));
shown.type("gridSubdivisions", "3");
QCOMPARE(shown.note(), QString("A subdivision every 33.33 pixels."));
const size_t seen = shown.previews.size();
shown.type("gridSpacing", "1");
QVERIFY(shown.previews.size() == seen && !shown.find<QPushButton>("gridOK").isEnabled());
QCOMPARE(shown.note(), QString("Use gridlines every 2–4,096 pixels and 1–64 subdivisions, no more than the pixels between gridlines."));
QCOMPARE(shown.find<QLabel>("gridNote").foregroundRole(), QPalette::BrightText);
shown.type("gridSpacing", "2");
QCOMPARE(shown.note().left(5), QString("Use g"));
shown.type("gridSubdivisions", "2");
QVERIFY(shown.last().first == LayoutGrid(2, 2) && shown.find<QPushButton>("gridOK").isEnabled());
// Text that is no number goes back.
shown.type("gridSpacing", "lots");
QCOMPARE(shown.find<PickerField>("gridSpacing").text(), QString("2"));
// The titles scrub: opacity a point per half.
auto &title = *qobject_cast<QLabel *>(qobject_cast<QHBoxLayout *>(shown.sheet->layout()->itemAt(3)->layout())->itemAt(0)->widget());
QTest::mousePress(&title, Qt::LeftButton, Qt::NoModifier, QPoint(2, 2));
QMouseEvent move(QEvent::MouseMove, QPointF(12, 2), title.mapToGlobal(QPointF(12, 2)), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(&title, &move);
QTest::mouseRelease(&title, Qt::LeftButton, Qt::NoModifier, QPoint(12, 2));
QCOMPARE(shown.last().second.opacity, 95);
}
void GridSettingsSheetTests::aPickedColourBecomesCustom()
{
GridAppearance cyan;
cyan.preset = GridAppearance::Preset::cyan;
Sheet shown(LayoutGrid(), cyan);
auto &swatch = shown.find<DialogColorSwatch>("gridColor");
swatch.click();
QCOMPARE(shown.session.colorPicker().value().target.title(), QString("Color Picker (Grid Color)"));
QCOMPARE(shown.session.colorPicker().value().original, (PaletteColor{0, 1, 1}));
// Its opening colour keeps the preset; another makes Custom.
shown.session.previewDialogColor();
QVERIFY(shown.previews.empty());
const PaletteColor teal{0, 128 / 255.0, 128 / 255.0};
shown.session.setColorPickerHSB(PickerHSB(teal));
QVERIFY(shown.last().second.preset == GridAppearance::Preset::custom && shown.last().second.customColor == teal);
QCOMPARE(shown.find<QComboBox>("gridPreset").currentText(), QString("Custom"));
// Picking the first preset's colour again returns to it.
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{0, 1, 1}));
QVERIFY(shown.last().second.preset == GridAppearance::Preset::cyan && shown.last().second.customColor == teal);
shown.session.closeColorPicker(false);
// Beyond Swift: Light Gray's 0.7, no byte, still stays.
auto &preset = shown.find<QComboBox>("gridPreset");
preset.setCurrentIndex(0);
emit preset.activated(0);
swatch.click();
shown.session.closeColorPicker(true);
QCOMPARE(shown.last().second.preset, GridAppearance::Preset::lightGray);
swatch.click();
shown.session.setColorPickerHSB(PickerHSB(teal));
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{0.7, 0.7, 0.7}));
QCOMPARE(shown.last().second.preset, GridAppearance::Preset::lightGray);
shown.session.closeColorPicker(false);
// The picker's Cancel hands back 0.7 itself: Light Gray stays.
const size_t seen = shown.previews.size();
swatch.click();
shown.session.closeColorPicker(false);
QCOMPARE(shown.previews.size(), seen);
swatch.click();
shown.session.setColorPickerHSB(PickerHSB(teal));
shown.session.closeColorPicker(false);
QCOMPARE(shown.last().second.preset, GridAppearance::Preset::lightGray);
// A preset chosen from the menu ends a pick.
swatch.click();
shown.session.setColorPickerHSB(PickerHSB(teal));
preset.setCurrentIndex(8);
emit preset.activated(8);
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{0.7, 0.7, 0.7}));
QVERIFY(shown.last().second.preset == GridAppearance::Preset::custom && shown.last().second.customColor == (PaletteColor{0.7, 0.7, 0.7}).quantized());
shown.session.closeColorPicker(false);
// Restore Defaults ends a pick too.
preset.setCurrentIndex(7);
emit preset.activated(7);
swatch.click();
shown.session.setColorPickerHSB(PickerHSB(teal));
shown.find<QPushButton>("gridDefaults").click();
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{0, 1, 1}));
QVERIFY(shown.last().second.preset == GridAppearance::Preset::custom && shown.last().second.customColor == (PaletteColor{0, 1, 1}));
// A changed preset repaints the swatch.
shown.session.closeColorPicker(false);
QCoreApplication::processEvents();
int paints = 0;
struct Counter : QObject {
int &count;
explicit Counter(int &count) : count(count) {}
bool eventFilter(QObject *, QEvent *event) override
{
count += event->type() == QEvent::Paint;
return false;
}
} counter(paints);
swatch.installEventFilter(&counter);
preset.setCurrentIndex(5);
emit preset.activated(5);
QTRY_VERIFY(paints > 0);
swatch.removeEventFilter(&counter);
shown.sheet.reset();
QVERIFY(!shown.session.colorPicker());
}
void GridSettingsSheetTests::restoreDefaultsOkAndCancel()
{
GridAppearance look{GridAppearance::Preset::custom, PaletteColor{0, 0.5, 0.5}, GridAppearance::Style::dots, 70};
Sheet shown(LayoutGrid(100, 4), look);
QVERIFY(shown.find<QComboBox>("gridPreset").currentText() == "Custom" && shown.find<PickerField>("gridSpacing").text() == "100");
QVERIFY(shown.find<QComboBox>("gridStyle").currentText() == "Dots" && shown.find<PickerField>("gridOpacity").text() == "70");
shown.find<QPushButton>("gridDefaults").click();
const GridSettingsSheet::Settings restored = shown.last();
QVERIFY(restored.first == LayoutGrid() && restored.second.preset == GridAppearance::Preset::lightGray && restored.second.style == GridAppearance::Style::lines);
QVERIFY(restored.second.opacity == 45 && restored.second.customColor == look.customColor);
QVERIFY(shown.find<PickerField>("gridSpacing").text() == "64" && shown.find<QComboBox>("gridStyle").currentText() == "Lines");
// OK closes an open picker and answers what is shown.
shown.find<DialogColorSwatch>("gridColor").click();
shown.find<QPushButton>("gridOK").click();
QVERIFY(!shown.session.colorPicker() && shown.answer.value().value() == restored);
Sheet cancelled;
cancelled.find<DialogColorSwatch>("gridColor").click();
cancelled.find<QPushButton>("gridCancel").click();
QVERIFY(!cancelled.session.colorPicker() && !cancelled.answer.value());
}
void GridSettingsSheetTests::theRowsHoldSwiftsControls()
{
Sheet shown;
auto *column = qobject_cast<QVBoxLayout *>(shown.sheet->layout());
const auto rowOf = [column](int index) { return qobject_cast<QHBoxLayout *>(column->itemAt(index)->layout()); };
for (const int index : {1, 2, 3, 5, 6})
QCOMPARE(rowOf(index)->spacing(), 8);
// Colour and Style: title, menu (and swatch), then room.
QVERIFY(rowOf(1)->count() == 4 && rowOf(1)->itemAt(3)->spacerItem());
QVERIFY(rowOf(2)->count() == 3 && rowOf(2)->itemAt(1)->widget() == &shown.find<QComboBox>("gridStyle") && rowOf(2)->itemAt(2)->spacerItem());
// Opacity: the slider stretches; % sits two after.
QVERIFY(rowOf(3)->itemAt(1)->widget() == &shown.find<QSlider>("gridOpacitySlider") && rowOf(3)->stretch(1) == 1);
auto *suffixed = qobject_cast<QHBoxLayout *>(rowOf(3)->itemAt(2)->layout());
QVERIFY(suffixed && suffixed->spacing() == 2 && suffixed->itemAt(0)->widget() == &shown.find<PickerField>("gridOpacity"));
QCOMPARE(qobject_cast<QLabel *>(suffixed->itemAt(1)->widget())->text(), QString("%"));
QCOMPARE(qobject_cast<QFrame *>(column->itemAt(4)->widget())->foregroundRole(), QPalette::Mid);
// The fields stretch, named by their titles.
auto &spacing = shown.find<PickerField>("gridSpacing");
auto &subdivisions = shown.find<PickerField>("gridSubdivisions");
QVERIFY(qobject_cast<QLabel *>(rowOf(5)->itemAt(0)->widget())->buddy() == &spacing && rowOf(5)->stretch(1) == 1);
QVERIFY(qobject_cast<QLabel *>(rowOf(6)->itemAt(0)->widget())->buddy() == &subdivisions && rowOf(6)->stretch(1) == 1);
QCOMPARE(qobject_cast<QLabel *>(rowOf(5)->itemAt(2)->widget())->foregroundRole(), QPalette::PlaceholderText);
QVERIFY(spacing.placeholderText() == "Gridline every" && subdivisions.placeholderText() == "Subdivisions" && shown.find<PickerField>("gridOpacity").placeholderText() == "Opacity");
auto &note = shown.find<QLabel>("gridNote");
QVERIFY(note.wordWrap() && note.font().pixelSize() == 12);
QCOMPARE(rowOf(8)->itemAt(3)->widget(), &shown.find<QPushButton>("gridOK"));
// In the controller's dialog only OK takes Return.
QDialog dialog;
shown.sheet->setParent(&dialog);
QVERIFY(!shown.find<QPushButton>("gridCancel").autoDefault() && !shown.find<QPushButton>("gridDefaults").autoDefault());
QVERIFY(shown.find<QPushButton>("gridOK").autoDefault());
shown.sheet->setParent(nullptr);
}
void GridSettingsSheetTests::aSliderOrScrubReplacesTyping()
{
Sheet shown;
auto &opacity = shown.find<PickerField>("gridOpacity");
opacity.setFocus();
opacity.selectAll();
QTest::keyClicks(&opacity, QStringLiteral("77"));
shown.find<QSlider>("gridOpacitySlider").setValue(60);
QCOMPARE(opacity.text(), QString("60"));
// Each title scrubs, and the note follows.
auto *column = qobject_cast<QVBoxLayout *>(shown.sheet->layout());
const auto titleOf = [column](int index) { return qobject_cast<QLabel *>(qobject_cast<QHBoxLayout *>(column->itemAt(index)->layout())->itemAt(0)->widget()); };
auto &spacing = shown.find<PickerField>("gridSpacing");
spacing.setFocus();
spacing.selectAll();
QTest::keyClicks(&spacing, QStringLiteral("99"));
scrub(*titleOf(5), 10);
QVERIFY(spacing.text() == "74" && shown.last().first == LayoutGrid(74, 8));
QCOMPARE(shown.note(), QString("A subdivision every 9.25 pixels."));
auto &subdivisions = shown.find<PickerField>("gridSubdivisions");
subdivisions.setFocus();
subdivisions.selectAll();
QTest::keyClicks(&subdivisions, QStringLiteral("33"));
scrub(*titleOf(6), 10);
QVERIFY(subdivisions.text() == "10" && shown.last().first == LayoutGrid(74, 10));
opacity.setFocus();
opacity.selectAll();
QTest::keyClicks(&opacity, QStringLiteral("33"));
scrub(*titleOf(3), 10);
QVERIFY(opacity.text() == "65" && shown.last().second.opacity == 65);
}
QTEST_MAIN(GridSettingsSheetTests)
#include "GridSettingsSheetTests.moc"