Files
OmaPhoto/tests/NavigationToolHeaderTests.cpp
雷电芽衣andClaude c864304695
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s
OmaPhoto 1.0.0: the Linux port of Compositor
A layered image editor for Linux and Omarchy, ported from Compositor
(github.com/robbietilton/Compositor), whose Swift source is its
specification. It reads and writes Compositor's .comp projects.

Packages for Ubuntu, Fedora and Arch come from the release workflow;
scripts/install/ holds one install script per distribution family,
and flake.nix serves NixOS.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-26 00:44:52 -04:00

231 lines
8.5 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/NavigationToolHeader.h"
#include <QtTest>
// The Hand and Zoom tools' bar, and Zoom's percentage field.
namespace {
struct Bar {
EditorSession session;
std::unique_ptr<NavigationToolHeader> header;
QLineEdit *field = nullptr;
explicit Bar(bool withCanvas = true)
{
if (withCanvas)
session.createDocument(800, 600);
session.selectTool(NavigationTool::zoom);
header = std::make_unique<NavigationToolHeader>(session);
field = header->findChild<QLineEdit *>("zoomPercentage");
header->show();
// Focus is real only in an active window.
header->activateWindow();
active = QTest::qWaitForWindowActive(header.get());
}
bool active = false;
void type(const QString &text)
{
field->setFocus();
field->selectAll();
QTest::keyClicks(field, text);
}
};
}
class NavigationToolHeaderTests : public QObject {
Q_OBJECT
private slots:
void theTitleAndTheFieldFollowTheTool();
void theFieldShowsTheZoomWithoutIdleZeros();
void returnEscapeAndLeavingAllApplyWhatWasTyped();
void whatIsNoZoomIsPutBack();
void arrowsStepOnePercentAndShiftTen();
void theFieldNeedsACanvasAndAnIdleSession();
void anOutsideZoomLeavesTypingAlone();
};
void NavigationToolHeaderTests::theTitleAndTheFieldFollowTheTool()
{
Bar bar;
QVERIFY(bar.active);
QCOMPARE(bar.header->title->text(), QString("Zoom"));
QVERIFY(bar.field->isVisible());
QCOMPARE(bar.field->width(), 72);
QCOMPARE(bar.field->alignment(), Qt::AlignRight | Qt::AlignVCenter);
QCOMPARE(bar.field->accessibleName(), QString("Zoom percentage"));
QCOMPARE(bar.field->toolTip(), QString("Zoom percentage (0.1–3200%). Press Return to apply."));
QCOMPARE(bar.header->height(), 42);
// The unit sits right of its field, two pixels away.
const QList<QLabel *> labels = bar.header->findChildren<QLabel *>();
const auto unit = std::find_if(labels.begin(), labels.end(), [](const QLabel *label) { return label->text() == "%"; });
QVERIFY(unit != labels.end());
QCoreApplication::processEvents();
QCOMPARE((*unit)->geometry().left() - bar.field->geometry().right() - 1, 2);
bar.session.selectTool(NavigationTool::hand);
QCOMPARE(bar.header->title->text(), QString("Pan"));
QVERIFY(!bar.field->isVisible() && !(*unit)->isVisible());
bar.session.selectTool(NavigationTool::zoom);
QVERIFY(bar.field->isVisible() && (*unit)->isVisible());
}
void NavigationToolHeaderTests::theFieldShowsTheZoomWithoutIdleZeros()
{
Bar bar;
// A field in use is left alone; not this one.
bar.field->clearFocus();
// A bar made over a zoomed session shows that zoom.
bar.session.zoom(0.5);
NavigationToolHeader late(bar.session);
QCOMPARE(late.findChild<QLineEdit *>("zoomPercentage")->text(), QString("50"));
const auto shown = [&](double zoom) {
bar.session.zoom(zoom);
return bar.field->text();
};
QCOMPARE(shown(1), QString("100"));
QCOMPARE(shown(0.125), QString("12.5"));
QCOMPARE(shown(1.0 / 3), QString("33.33"));
QCOMPARE(shown(2.5), QString("250"));
QCOMPARE(shown(0.001), QString("0.1"));
QCOMPARE(shown(32), QString("3200"));
QCOMPARE(shown(0.1), QString("10"));
}
void NavigationToolHeaderTests::returnEscapeAndLeavingAllApplyWhatWasTyped()
{
Bar bar;
bar.type("250");
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), 2.5);
QVERIFY(!bar.field->hasFocus());
QCOMPARE(bar.session.canvasFocusRequest(), 1);
// Applied once: Return asks for the canvas, zooms no more.
QSignalSpy changes(&bar.session, &EditorSession::changed);
bar.field->setFocus();
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(changes.count(), 1);
QCOMPARE(bar.session.viewport.zoom(), 2.5);
QCOMPARE(bar.session.canvasFocusRequest(), 2);
bar.type(" 50% ");
QTest::keyClick(bar.field, Qt::Key_Escape);
QCOMPARE(bar.session.viewport.zoom(), 0.5);
QCOMPARE(bar.field->text(), QString("50"));
QVERIFY(!bar.field->hasFocus());
QCOMPARE(bar.session.canvasFocusRequest(), 3);
bar.type("75");
QTest::keyClick(bar.field, Qt::Key_Enter);
QCOMPARE(bar.session.viewport.zoom(), 0.75);
QCOMPARE(bar.session.canvasFocusRequest(), 4);
// Leaving the field applies as well, asking for no focus.
bar.type("125");
bar.field->clearFocus();
QCOMPARE(bar.session.viewport.zoom(), 1.25);
QCOMPARE(bar.session.canvasFocusRequest(), 4);
// Past the range the viewport decides; the field follows.
bar.type("99999");
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), 32.0);
QCOMPARE(bar.field->text(), QString("3200"));
// Behind another window nothing loses focus: Return applies.
Bar behind;
QWidget front;
front.show();
front.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&front));
behind.field->setFocus();
behind.field->selectAll();
QTest::keyClicks(behind.field, "42");
QTest::keyClick(behind.field, Qt::Key_Return);
QCOMPARE(behind.session.viewport.zoom(), 0.42);
}
void NavigationToolHeaderTests::whatIsNoZoomIsPutBack()
{
Bar bar;
bar.session.zoom(2);
QSignalSpy changes(&bar.session, &EditorSession::changed);
for (const QString &bad : {QString("wide"), QString(""), QString("0"), QString("-40"), QString("nan"), QString("inf"), QString("1e999")}) {
bar.type(bad);
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), 2.0);
QCOMPARE(bar.field->text(), QString("200"));
}
// Each Return asks for the canvas; none zooms.
QCOMPARE(changes.count(), 7);
QCOMPARE(bar.session.canvasFocusRequest(), 7);
// A busy project takes no zoom from the field.
bar.session.setIsProjectBusy(true);
bar.type("300");
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), 2.0);
QCOMPARE(bar.field->text(), QString("200"));
// Untouched text is not applied: no rounding creeps in.
bar.session.setIsProjectBusy(false);
bar.session.zoom(1.0 / 3);
const double third = bar.session.viewport.zoom();
bar.field->setFocus();
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), third);
}
void NavigationToolHeaderTests::arrowsStepOnePercentAndShiftTen()
{
Bar bar;
bar.session.zoom(1);
bar.field->setFocus();
QTest::keyClick(bar.field, Qt::Key_Up);
QCOMPARE(bar.session.viewport.zoom(), 1.01);
QCOMPARE(bar.field->text(), QString("101"));
QVERIFY(bar.field->hasFocus());
QTest::keyClick(bar.field, Qt::Key_Down, Qt::ShiftModifier);
QCOMPARE(bar.session.viewport.zoom(), 0.91);
QTest::keyClick(bar.field, Qt::Key_Up, Qt::ShiftModifier);
QTest::keyClick(bar.field, Qt::Key_Down);
QCOMPARE(bar.field->text(), QString("100"));
// A step starts from what is typed, percent sign included.
bar.type("40%");
QTest::keyClick(bar.field, Qt::Key_Up);
QCOMPARE(bar.session.viewport.zoom(), 0.41);
// The ends of the range hold.
bar.type("3195");
QTest::keyClick(bar.field, Qt::Key_Up, Qt::ShiftModifier);
QCOMPARE(bar.field->text(), QString("3200"));
bar.type("0.5");
QTest::keyClick(bar.field, Qt::Key_Down);
QCOMPARE(bar.field->text(), QString("0.1"));
// Text that is no number steps from the current zoom.
bar.session.zoom(2);
bar.type("wide");
QTest::keyClick(bar.field, Qt::Key_Up);
QCOMPARE(bar.field->text(), QString("201"));
}
void NavigationToolHeaderTests::theFieldNeedsACanvasAndAnIdleSession()
{
Bar bare(false);
QVERIFY(!bare.field->isEnabled());
bare.session.createDocument(8, 8);
QVERIFY(bare.field->isEnabled());
// Busy dims only once it shows, a quarter second in.
bare.session.setIsProjectBusy(true);
QVERIFY(bare.field->isEnabled());
QTRY_VERIFY(!bare.field->isEnabled());
bare.session.setIsProjectBusy(false);
QVERIFY(bare.field->isEnabled());
}
void NavigationToolHeaderTests::anOutsideZoomLeavesTypingAlone()
{
Bar bar;
bar.type("77");
// A zoom from elsewhere arrives while the user types.
bar.session.zoom(3);
QVERIFY(bar.field->hasFocus());
QCOMPARE(bar.field->text(), QString("77"));
QTest::keyClick(bar.field, Qt::Key_Return);
QCOMPARE(bar.session.viewport.zoom(), 0.77);
bar.session.zoom(3);
QCOMPARE(bar.field->text(), QString("300"));
}
QTEST_MAIN(NavigationToolHeaderTests)
#include "NavigationToolHeaderTests.moc"