Files
OmaPhoto/tests/FloatingPanelTests.cpp
T

311 lines
14 KiB
C++

#include "CanvasFixtures.h"
#include "UI/ColorPickerSheet.h"
#include "UI/FilterSheet.h"
#include "UI/FloatingPanel.h"
#include "UI/HueSaturationSheet.h"
#include "UI/LevelsSheet.h"
#include <QApplication>
#include <QLabel>
#include <QVBoxLayout>
#include <QWindow>
#include <QtTest>
// Swift's FloatingPanelTests.
namespace {
QWidget *visiblePanel(const QString &name)
{
for (QWidget *widget : QApplication::topLevelWidgets()) {
if (widget->objectName() == name && widget->isVisible())
return widget;
}
return nullptr;
}
}
class FloatingPanelTests : public QObject {
Q_OBJECT
private slots:
void colorPickerPanelSurvivesALayoutPass();
void levelsPanelSurvivesALayoutPass();
void adjustmentPanelSurvivesALayoutPass();
void adjustmentEditorsUseMovableNonmodalPanels_data();
void adjustmentEditorsUseMovableNonmodalPanels();
void aPanelOpensOverTheCanvasThenWhereItWasLeft();
void itsCloseButtonAndEscapeCancel();
void aPanelRemembersWhereItIsMovedAndCentresWithoutACanvas();
void dockedPlacementLeavesTheSavedFilterPosition();
};
void FloatingPanelTests::colorPickerPanelSurvivesALayoutPass()
{
Shown shown(QSize(40, 20));
QImage image(40, 20, QImage::Format_RGBA8888_Premultiplied);
image.fill(Qt::red);
shown.session.insert(ImportedImage(image, image, QStringLiteral("Red")));
shown.session.openColorPicker(false);
ColorPickerPanelController controller(shown.window);
controller.show(shown.session.colorPicker().value(), shown.session);
QTest::qWait(400);
QWidget *panel = visiblePanel(ColorPickerPanelController::identifier());
QVERIFY(panel);
QVERIFY(panel->findChild<ColorPickerSheet *>());
QVERIFY(!panel->isModal());
QVERIFY(panel->windowFlags().testFlag(Qt::Tool));
controller.close();
shown.session.closeColorPicker(false);
QVERIFY(!panel->isVisible());
}
void FloatingPanelTests::levelsPanelSurvivesALayoutPass()
{
Shown shown(QSize(40, 20));
QImage image(40, 20, QImage::Format_RGBA8888_Premultiplied);
image.fill(Qt::red);
shown.session.insert(ImportedImage(image, image, QStringLiteral("Red")));
shown.session.beginLevels();
FloatingPanel panel(QStringLiteral("testLevelsPanel"), shown.window);
panel.show(QStringLiteral("Levels"), new LevelsSheet(shown.session));
QTest::qWait(400);
QVERIFY(panel.isVisible());
// A preview landing while hosted survives a pass too.
LevelsSettings settings;
settings.setCurrent(LevelRange{0, 2, 255, 0, 255});
shown.session.updateLevels(settings, true);
QTRY_VERIFY(shown.session.levels().value().preparedPreview);
QTest::qWait(400);
panel.close();
shown.session.cancelLevels();
QVERIFY(!panel.isVisible() && !shown.session.levels());
}
void FloatingPanelTests::adjustmentPanelSurvivesALayoutPass()
{
Shown shown(QSize(40, 20));
QImage image(40, 20, QImage::Format_RGBA8888_Premultiplied);
image.fill(Qt::red);
shown.session.insert(ImportedImage(image, image, QStringLiteral("Red")));
shown.session.beginHueSaturation();
FloatingPanel panel(QStringLiteral("testAdjustmentPanel"), shown.window);
panel.show(QStringLiteral("Hue/Saturation"), new HueSaturationSheet(shown.session));
QTest::qWait(400);
QVERIFY(panel.isVisible());
const int height = visiblePanel(QStringLiteral("testAdjustmentPanel"))->height();
// A range's spectrum grows it; a landed preview passes too.
shown.session.updateHueSaturation(HueSaturationSettings(120, 0, 0, false, ColorRange::reds), true);
QTRY_VERIFY(shown.session.hueSaturation().value().previewImage(shown.session.activeLayerID().value()));
QTest::qWait(400);
QVERIFY(visiblePanel(QStringLiteral("testAdjustmentPanel"))->height() > height);
panel.close();
shown.session.cancelHueSaturation();
QVERIFY(!panel.isVisible() && !shown.session.hueSaturation());
}
void FloatingPanelTests::adjustmentEditorsUseMovableNonmodalPanels_data()
{
QTest::addColumn<AdjustmentKind>("kind");
// Invert has no settings, so no editor, as Swift's.
for (const AdjustmentKind kind : allAdjustmentKinds)
if (isEditable(kind))
QTest::newRow(qPrintable(rawValue(kind))) << kind;
}
void FloatingPanelTests::adjustmentEditorsUseMovableNonmodalPanels()
{
QFETCH(AdjustmentKind, kind);
Shown shown(QSize(40, 20));
QImage image(40, 20, QImage::Format_RGBA8888_Premultiplied);
image.fill(Qt::red);
shown.session.insert(ImportedImage(image, image, QStringLiteral("Red")));
shown.session.addAdjustment(kind);
bool opened = false;
shown.session.beginAdjustmentEditing(shown.session.adjustmentEditingID().value(), [&opened] { opened = true; });
QTRY_VERIFY(opened);
FloatingPanel controller(QStringLiteral("testDynamicAdjustmentPanel"), shown.window);
controller.onClose = [&shown] { shown.session.finishAdjustmentEditing(false); };
if (kind == AdjustmentKind::levels)
controller.show(QStringLiteral("Levels"), new LevelsSheet(shown.session));
else if (kind == AdjustmentKind::hsv)
controller.show(QStringLiteral("Hue/Saturation"), new HueSaturationSheet(shown.session));
else
controller.show(rawValue(kind), new FilterSheet(shown.session));
QTest::qWait(400);
QWidget *panel = visiblePanel(QStringLiteral("testDynamicAdjustmentPanel"));
QVERIFY(panel);
// A tool window beside the canvas: movable, never modal.
QVERIFY(panel->windowFlags().testFlag(Qt::Tool) && !panel->isModal() && !shown.session.showsBusy());
const QPoint origin = panel->pos();
panel->move(origin + QPoint(20, 20));
QCOMPARE(panel->pos(), origin + QPoint(20, 20));
panel->close();
QVERIFY(!shown.session.adjustmentEditingID());
QVERIFY(!shown.session.levels() && !shown.session.hueSaturation() && !shown.session.filterEdit());
}
void FloatingPanelTests::aPanelOpensOverTheCanvasThenWhereItWasLeft()
{
Shown shown;
// Wider than the canvas: the canvas's middle, not the window's.
shown.window.resize(600, 400);
FloatingPanel panel(QStringLiteral("testPlacedPanel"), shown.window);
QVERIFY(!panel.isVisible());
panel.close();
// First over the canvas's middle.
panel.show(QStringLiteral("First"), new QLabel(QStringLiteral("One")));
QWidget *window = visiblePanel(QStringLiteral("testPlacedPanel"));
QVERIFY(window);
const QPoint middle = shown.canvas->mapToGlobal(shown.canvas->rect().center());
QVERIFY(std::abs(window->pos().x() + window->width() / 2 - middle.x()) <= 1);
QVERIFY(std::abs(window->pos().y() + window->height() / 2 - middle.y()) <= 1);
// It takes the keys, fixed to its content's size.
QTRY_COMPARE(QApplication::activeWindow(), window);
QCOMPARE(window->size(), window->findChild<QLabel *>()->sizeHint());
QCOMPARE(window->minimumSize(), window->maximumSize());
// Moved, it keeps its place through content and a close.
const QPoint spot(30, 20);
window->move(spot);
// Shown again, it takes the keys back.
shown.window.activateWindow();
QTRY_COMPARE(QApplication::activeWindow(), &shown.window);
auto *old = new QLabel(QStringLiteral("Old"));
panel.show(QStringLiteral("Second"), old);
QTRY_COMPARE(QApplication::activeWindow(), window);
QCOMPARE(window->pos(), spot);
QCOMPARE(window->windowTitle(), QString("Second"));
QTRY_VERIFY(old->isVisible());
QPointer<QLabel> gone(old);
auto *wider = new QLabel(QStringLiteral("Wider content than before"));
panel.show(QStringLiteral("Third"), wider);
QVERIFY(gone && !gone->isVisible());
QCOMPARE(window->size(), wider->sizeHint());
QTRY_VERIFY(!gone);
panel.close();
QVERIFY(!panel.isVisible());
panel.show(QStringLiteral("Fourth"), new QLabel(QStringLiteral("Four")));
QCOMPARE(window->pos(), spot);
// Another panel of that name, later, opens there too.
panel.close();
FloatingPanel again(QStringLiteral("testPlacedPanel"), shown.window);
again.show(QStringLiteral("Again"), new QLabel(QStringLiteral("Five")));
QCOMPARE(visiblePanel(QStringLiteral("testPlacedPanel"))->pos(), spot);
}
void FloatingPanelTests::itsCloseButtonAndEscapeCancel()
{
Shown shown;
// Without a listener, its close button just closes.
FloatingPanel quiet(QStringLiteral("testQuietPanel"), shown.window);
quiet.show(QStringLiteral("Quiet"), new QLabel(QStringLiteral("Content")));
visiblePanel(QStringLiteral("testQuietPanel"))->close();
QVERIFY(!quiet.isVisible());
// A hidden panel is never handed the keys.
shown.window.activateWindow();
QTRY_COMPARE(QApplication::activeWindow(), &shown.window);
FloatingPanel::refocus(QStringLiteral("testQuietPanel"));
QTest::qWait(50);
QCOMPARE(QApplication::activeWindow(), &shown.window);
FloatingPanel panel(QStringLiteral("testClosedPanel"), shown.window);
int closes = 0;
panel.onClose = [&closes] { ++closes; };
panel.show(QStringLiteral("Panel"), new QLabel(QStringLiteral("Content")));
QWidget *window = visiblePanel(QStringLiteral("testClosedPanel"));
QVERIFY(window);
window->close();
QCOMPARE(closes, 1);
QVERIFY(!panel.isVisible());
panel.show(QStringLiteral("Panel"), new QLabel(QStringLiteral("Content")));
QTest::keyClick(window, Qt::Key_Escape);
QCOMPARE(closes, 2);
QVERIFY(!panel.isVisible());
// Hidden by its owner, it reports nothing.
panel.show(QStringLiteral("Panel"), new QLabel(QStringLiteral("Content")));
panel.close();
QCOMPARE(closes, 2);
// A click on the canvas hands it the keys back.
panel.show(QStringLiteral("Panel"), new QLabel(QStringLiteral("Content")));
shown.window.activateWindow();
QTRY_COMPARE(QApplication::activeWindow(), &shown.window);
FloatingPanel::refocus(QStringLiteral("testClosedPanel"));
QTRY_COMPARE(QApplication::activeWindow(), window);
// Only the panel of that name.
quiet.show(QStringLiteral("Quiet"), new QLabel(QStringLiteral("Content")));
QWidget *other = visiblePanel(QStringLiteral("testQuietPanel"));
QTRY_COMPARE(QApplication::activeWindow(), other);
FloatingPanel::refocus(QStringLiteral("testClosedPanel"));
QTRY_COMPARE(QApplication::activeWindow(), window);
// A miss activates no widget; a bare window keeps focus.
QWindow bare;
bare.show();
bare.requestActivate();
QTRY_COMPARE(QGuiApplication::focusWindow(), &bare);
FloatingPanel::refocus(QStringLiteral("testMissingPanel"));
QTest::qWait(50);
QCOMPARE(QGuiApplication::focusWindow(), &bare);
}
void FloatingPanelTests::aPanelRemembersWhereItIsMovedAndCentresWithoutACanvas()
{
Shown shown;
QPointer<QWidget> dropped;
{
FloatingPanel moved(QStringLiteral("testMovedPanel"), shown.window);
moved.show(QStringLiteral("Moved"), new QLabel(QStringLiteral("Content")));
dropped = visiblePanel(QStringLiteral("testMovedPanel"));
dropped->move(QPoint(25, 35));
QTRY_COMPARE(dropped->pos(), QPoint(25, 35));
}
// Its window goes with it.
QVERIFY(!dropped);
// Gone without a close, it reopens where it was moved.
FloatingPanel again(QStringLiteral("testMovedPanel"), shown.window);
again.show(QStringLiteral("Again"), new QLabel(QStringLiteral("Content")));
QCOMPARE(visiblePanel(QStringLiteral("testMovedPanel"))->pos(), QPoint(25, 35));
// Shown, never moved, it keeps that place for the next.
QPoint first;
{
FloatingPanel still(QStringLiteral("testStillPanel"), shown.window);
still.show(QStringLiteral("Still"), new QLabel(QStringLiteral("Content")));
first = visiblePanel(QStringLiteral("testStillPanel"))->pos();
}
const QPoint away = shown.window.pos() + QPoint(40, 30);
shown.window.move(away);
QTRY_COMPARE(shown.window.pos(), away);
FloatingPanel next(QStringLiteral("testStillPanel"), shown.window);
next.show(QStringLiteral("Next"), new QLabel(QStringLiteral("Content")));
QCOMPARE(visiblePanel(QStringLiteral("testStillPanel"))->pos(), first);
// A window without a canvas centres it on itself.
QWidget plain;
plain.setGeometry(100, 80, 500, 300);
plain.show();
QVERIFY(QTest::qWaitForWindowExposed(&plain));
FloatingPanel centred(QStringLiteral("testCentredPanel"), plain);
centred.show(QStringLiteral("Centred"), new QLabel(QStringLiteral("Content")));
QWidget *window = visiblePanel(QStringLiteral("testCentredPanel"));
const QPoint middle = plain.geometry().center();
QVERIFY(std::abs(window->pos().x() + window->width() / 2 - middle.x()) <= 1);
QVERIFY(std::abs(window->pos().y() + window->height() / 2 - middle.y()) <= 1);
}
// Swift's test: a docked frame never sets where panels reopen.
void FloatingPanelTests::dockedPlacementLeavesTheSavedFilterPosition()
{
Shown shown;
auto *slot = new QWidget(&shown.window);
auto *layout = new QVBoxLayout(slot);
slot->setGeometry(250, 0, 150, 300);
FloatingPanel panel(QStringLiteral("testDockedPanel"), shown.window);
panel.show(QStringLiteral("Docked"), new QLabel(QStringLiteral("Docked")), slot);
QVERIFY(!visiblePanel(QStringLiteral("testDockedPanel")) && panel.isVisible());
// The docked panel moves inside its slot; nothing is kept.
layout->setContentsMargins(20, 20, 0, 0);
layout->activate();
panel.close();
QVERIFY(!slot->isVisible() && !panel.isVisible());
panel.show(QStringLiteral("Floating"), new QLabel(QStringLiteral("Floating")));
QWidget *window = visiblePanel(QStringLiteral("testDockedPanel"));
const QPoint middle = shown.canvas->mapToGlobal(shown.canvas->rect().center());
QVERIFY(std::abs(window->pos().x() + window->width() / 2 - middle.x()) <= 1);
}
QTEST_MAIN(FloatingPanelTests)
#include "FloatingPanelTests.moc"