Files
OmaPhoto/tests/LayerAppearanceControlsTests.cpp
T
雷电芽衣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

176 lines
7.1 KiB
C++

#include "UI/LayerAppearanceControls.h"
#include "UI/BlendModePicker.h"
#include <QtTest>
// The opacity slider and field, one undo step a drag.
namespace {
ImportedImage white()
{
QImage image(2, 2, QImage::Format_RGBA8888_Premultiplied);
image.fill(Qt::white);
return ImportedImage(image, image, "White");
}
struct Controls {
EditorSession session;
LayerAppearanceControls widget{session};
QSlider &slider = *widget.findChild<QSlider *>("opacitySlider");
QLineEdit &field = *widget.findChild<QLineEdit *>("opacityPercent");
double opacity() { return session.activeLayer().value().opacity; }
};
}
class LayerAppearanceControlsTests : public QObject {
Q_OBJECT
private slots:
void theControlsFollowTheActiveLayerAndItsGate();
void aSliderDragIsOneUndoStep();
void theFieldAppliesOnReturnEscapeAndLeavingAndStepsWithArrows();
void aFieldLeftBehindByAnotherLayerAppliesNothing();
};
void LayerAppearanceControlsTests::theControlsFollowTheActiveLayerAndItsGate()
{
Controls controls;
QVERIFY(!controls.widget.isEnabled());
QCOMPARE(controls.field.text(), QString("100"));
controls.session.createDocument(4, 4);
controls.session.insert(white());
QVERIFY(controls.widget.isEnabled());
controls.session.setLayerOpacity(0.25);
QCOMPARE(controls.slider.value(), 250);
QCOMPARE(controls.field.text(), QString("25"));
controls.session.insert(white());
QCOMPARE(controls.slider.value(), 1000);
QCOMPARE(controls.field.text(), QString("100"));
controls.session.selectLayer(controls.session.document().value().layers.front().id);
QCOMPARE(controls.field.text(), QString("25"));
// Showing a finer opacity must not write the slider's back.
controls.session.setLayerOpacity(0.12345);
QCOMPARE(controls.slider.value(), 123);
QCOMPARE(controls.session.activeLayer().value().opacity, 0.12345);
QVERIFY(controls.widget.findChild<BlendModePicker *>() != nullptr);
controls.session.selectLayers({controls.session.document().value().layers.front().id, controls.session.document().value().layers.back().id}, std::nullopt);
QVERIFY(!controls.widget.isEnabled());
}
void LayerAppearanceControlsTests::aSliderDragIsOneUndoStep()
{
Controls controls;
controls.session.createDocument(4, 4);
controls.session.insert(white());
const int steps = controls.session.history.undoCount();
controls.slider.setSliderDown(true);
emit controls.slider.sliderPressed();
controls.slider.setValue(600);
controls.slider.setValue(400);
QCOMPARE(controls.opacity(), 0.4);
QVERIFY(!controls.session.canUndo());
controls.slider.setSliderDown(false);
emit controls.slider.sliderReleased();
QCOMPARE(controls.session.history.undoCount(), steps + 1);
QCOMPARE(controls.session.history.undoName(), QString("Layer Opacity"));
controls.session.undo();
QCOMPARE(controls.opacity(), 1.0);
QCOMPARE(controls.slider.value(), 1000);
// The keyboard moves the slider too, a step each.
controls.slider.setValue(900);
QCOMPARE(controls.opacity(), 0.9);
QCOMPARE(controls.session.history.undoCount(), steps + 1);
}
void LayerAppearanceControlsTests::theFieldAppliesOnReturnEscapeAndLeavingAndStepsWithArrows()
{
Controls controls;
controls.session.createDocument(4, 4);
controls.session.insert(white());
controls.widget.show();
QVERIFY(QTest::qWaitForWindowActive(&controls.widget));
controls.field.setFocus();
controls.field.selectAll();
QTest::keyClicks(&controls.field, "40");
QTest::keyClick(&controls.field, Qt::Key_Return);
QCOMPARE(controls.opacity(), 0.4);
QVERIFY(!controls.field.hasFocus());
QCOMPARE(controls.session.canvasFocusRequest(), 1);
controls.field.setFocus();
controls.field.selectAll();
QTest::keyClicks(&controls.field, "abc");
QTest::keyClick(&controls.field, Qt::Key_Escape);
QCOMPARE(controls.opacity(), 0.4);
QCOMPARE(controls.field.text(), QString("40"));
QCOMPARE(controls.session.canvasFocusRequest(), 2);
// Leaving applies without asking for the canvas; past 100 clamps.
controls.field.setFocus();
controls.field.selectAll();
QTest::keyClicks(&controls.field, "250");
controls.field.clearFocus();
QCOMPARE(controls.opacity(), 1.0);
QCOMPARE(controls.field.text(), QString("100"));
QCOMPARE(controls.session.canvasFocusRequest(), 2);
// Arrows step one percent, ten with Shift, within the range.
controls.field.setFocus();
QTest::keyClick(&controls.field, Qt::Key_Down);
QCOMPARE(controls.opacity(), 0.99);
QTest::keyClick(&controls.field, Qt::Key_Down, Qt::ShiftModifier);
QCOMPARE(controls.opacity(), 0.89);
QCOMPARE(controls.field.text(), QString("89"));
QTest::keyClick(&controls.field, Qt::Key_Up, Qt::ShiftModifier);
QTest::keyClick(&controls.field, Qt::Key_Up, Qt::ShiftModifier);
QCOMPARE(controls.opacity(), 1.0);
// A finer value is kept once, shown rounded.
const int steps = controls.session.history.undoCount();
controls.field.selectAll();
QTest::keyClicks(&controls.field, "12.345");
QTest::keyClick(&controls.field, Qt::Key_Return);
QCOMPARE(controls.opacity(), 0.12345);
QCOMPARE(controls.field.text(), QString("12"));
QCOMPARE(controls.session.history.undoCount(), steps + 1);
controls.field.setFocus();
QTest::keyClick(&controls.field, Qt::Key_Up);
QCOMPARE(controls.opacity(), 0.13);
controls.field.selectAll();
QTest::keyClicks(&controls.field, "100");
QTest::keyClick(&controls.field, Qt::Key_Return);
controls.field.setFocus();
// Typing goes on while the session moves; the slider follows.
controls.field.selectAll();
QTest::keyClicks(&controls.field, "5");
controls.session.setLayerOpacity(0.3);
QCOMPARE(controls.field.text(), QString("5"));
QCOMPARE(controls.slider.value(), 300);
QTest::keyClick(&controls.field, Qt::Key_Enter);
QCOMPARE(controls.opacity(), 0.05);
}
void LayerAppearanceControlsTests::aFieldLeftBehindByAnotherLayerAppliesNothing()
{
Controls controls;
controls.session.createDocument(4, 4);
controls.session.insert(white());
const QUuid first = controls.session.activeLayerID().value();
controls.session.insert(white());
controls.widget.show();
QVERIFY(QTest::qWaitForWindowActive(&controls.widget));
controls.field.setFocus();
controls.field.selectAll();
QTest::keyClicks(&controls.field, "20");
// Another layer active: the typed value belongs to no one.
controls.session.selectLayer(first);
QVERIFY(!controls.field.hasFocus());
QCOMPARE(controls.field.text(), QString("100"));
for (const ImageLayer &layer : controls.session.document().value().layers)
QCOMPARE(layer.opacity, 1.0);
// The controls end an open drag when they go.
controls.slider.setSliderDown(true);
emit controls.slider.sliderPressed();
controls.slider.setValue(500);
QVERIFY(!controls.session.canUndo());
auto *later = new LayerAppearanceControls(controls.session);
delete later;
QVERIFY(controls.session.canUndo());
}
QTEST_MAIN(LayerAppearanceControlsTests)
#include "LayerAppearanceControlsTests.moc"