14.17: the Layers panel grays only for what lasts

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 08:30:40 -04:00
1 parent ffee8ff178
commit 98f18f1221
12 files changed
+156 -12

No files matched your search

+3 -1
View File
@@ -160,8 +160,10 @@ void BlendModePickerTests::theMaskButtonAddsAWhiteMaskOnce()
QVERIFY(!button.isEnabled());
session.undo();
QVERIFY(!session.activeLayer().value().mask.has_value() && button.isEnabled());
// A moment's work does not gray it; lasting work does.
session.setIsProjectBusy(true);
QVERIFY(!button.isEnabled());
QVERIFY(button.isEnabled());
QTRY_VERIFY(session.showsBusy() && !button.isEnabled());
session.setIsProjectBusy(false);
QVERIFY(button.isEnabled());
}
+1 -1
View File
@@ -243,7 +243,7 @@ void LayerEffectRowTests::theEyeShowsOrHidesItsEffect()
QVERIFY(layerWith(listed.session, top).effects.value().stroke.value().isEnabled() && listed.session.history.undoName() == "Show Stroke");
// It rests while layers cannot be edited.
listed.session.setIsProjectBusy(true);
QVERIFY(!listed.eye(stroke).isEnabled());
QTRY_VERIFY(listed.session.showsBusy() && !listed.eye(stroke).isEnabled());
listed.click(listed.eye(stroke), QPoint(10, 11));
QVERIFY(layerWith(listed.session, top).effects.value().stroke.value().isEnabled() && !listed.chosen());
listed.session.setIsProjectBusy(false);
+122
View File
@@ -0,0 +1,122 @@
#include "UI/LayersPanel.h"
#include "UI/NativeLayerList.h"
#include <QtTest>
// Swift 1.4.8: the panel grays for what lasts, not moments.
namespace {
std::unique_ptr<EditorSession> withLayer()
{
auto session = std::make_unique<EditorSession>();
session->createDocument(20, 20);
QImage pixels(20, 20, QImage::Format_RGBA8888_Premultiplied);
pixels.fill(Qt::white);
session->insert(ImportedImage(pixels, pixels, QStringLiteral("White")));
return session;
}
using Steps = std::pair<std::function<void(EditorSession &)>, std::function<void(EditorSession &)>>;
}
class LayersLookEditableTests : public QObject {
Q_OBJECT
private slots:
void aMomentLooksEditableWhileTheGateStaysShut();
void whatLastsGraysThePanel();
void theFooterAndListFollowTheLook();
};
void LayersLookEditableTests::aMomentLooksEditableWhileTheGateStaysShut()
{
const std::vector<std::pair<const char *, Steps>> moments{
{"stroke", {[](EditorSession &s) { s.selectTool(NavigationTool::brush); s.beginBrush(QPointF(5, 5)); }, [](EditorSession &s) { s.cancelBrush(); }}},
{"warp", {[](EditorSession &s) { s.selectTool(NavigationTool::blur); s.setBlurMode(BlurToolMode::liquify); s.beginBrush(QPointF(5, 5)); },
[](EditorSession &s) { s.cancelBrush(); }}},
{"drag", {[](EditorSession &s) { s.selectTool(NavigationTool::move); s.beginTransform(false); }, [](EditorSession &s) { s.cancelTransform(); }}},
{"pixels", {[](EditorSession &s) { s.selectAll(); s.beginPixelMove(); }, [](EditorSession &s) { s.cancelPixelMove(); s.deselect(); }}},
{"busy", {[](EditorSession &s) { s.setIsProjectBusy(true); }, [](EditorSession &s) { s.setIsProjectBusy(false); }}}};
const auto session = withLayer();
QVERIFY(session->layersLookEditable() && session->canEditLayers());
for (const auto &[name, steps] : moments) {
steps.first(*session);
QVERIFY2(session->layersLookEditable() && !session->canEditLayers(), name);
steps.second(*session);
QVERIFY2(session->layersLookEditable() && session->canEditLayers(), name);
}
// Busy long enough to show it: then it grays.
session->setIsProjectBusy(true);
QTRY_VERIFY(session->showsBusy());
QVERIFY(!session->layersLookEditable());
session->setIsProjectBusy(false);
QVERIFY(session->layersLookEditable());
}
void LayersLookEditableTests::whatLastsGraysThePanel()
{
QVERIFY(!EditorSession().layersLookEditable());
const std::vector<std::pair<const char *, Steps>> lasting{
{"amount", {[](EditorSession &s) { s.setSelectionAmountOperation(SelectionAmountOperation::feather); },
[](EditorSession &s) { s.setSelectionAmountOperation(std::nullopt); }}},
{"range", {[](EditorSession &s) { s.beginColorRange(); }, [](EditorSession &s) { s.cancelColorRange(); }}},
{"text", {[](EditorSession &s) { s.selectTool(NavigationTool::type); s.beginText(QPointF(5, 5), true); }, [](EditorSession &s) { s.cancelText(); }}},
{"importing", {[](EditorSession &s) { s.setIsImporting(true); }, [](EditorSession &s) { s.setIsImporting(false); }}},
{"sheet", {[](EditorSession &s) { s.setShowsNewDocument(true); }, [](EditorSession &s) { s.setShowsNewDocument(false); }}},
{"importer", {[](EditorSession &s) { s.setShowsImporter(true); }, [](EditorSession &s) { s.setShowsImporter(false); }}},
{"rename", {[](EditorSession &s) { s.setRenamingLayerID(s.activeLayerID()); }, [](EditorSession &s) { s.setRenamingLayerID(std::nullopt); }}},
{"transform", {[](EditorSession &s) { s.selectTool(NavigationTool::move); s.beginTransform(); }, [](EditorSession &s) { s.cancelTransform(); }}},
{"crop", {[](EditorSession &s) { s.selectTool(NavigationTool::crop); }, [](EditorSession &s) { s.cancelCrop(); }}},
{"gradient", {[](EditorSession &s) { s.selectTool(NavigationTool::gradient); s.beginGradient(QPointF(2, 2)); s.moveGradient(std::nullopt, QPointF(12, 2)); },
[](EditorSession &s) { s.cancelGradient(); }}},
{"hue", {[](EditorSession &s) { s.beginHueSaturation(); }, [](EditorSession &s) { s.cancelHueSaturation(); }}},
{"levels", {[](EditorSession &s) { s.beginLevels(); }, [](EditorSession &s) { s.cancelLevels(); }}},
{"filter", {[](EditorSession &s) { s.beginFilter(FilterKind::gaussianBlur); }, [](EditorSession &s) { s.cancelFilter(); }}},
{"adjustment", {[](EditorSession &s) { s.setAdjustmentEditingID(s.activeLayerID()); }, [](EditorSession &s) { s.setAdjustmentEditingID(std::nullopt); }}}};
const auto session = withLayer();
for (const auto &[name, steps] : lasting) {
steps.first(*session);
QVERIFY2(!session->layersLookEditable(), name);
steps.second(*session);
QVERIFY2(session->layersLookEditable(), name);
}
}
void LayersLookEditableTests::theFooterAndListFollowTheLook()
{
const auto session = withLayer();
LayersPanel panel(*session);
panel.show();
QVERIFY(QTest::qWaitForWindowExposed(&panel));
auto *add = panel.findChild<QToolButton *>("addBlankLayer");
auto *effects = panel.findChild<QToolButton *>("layerEffects");
auto *mask = panel.findChild<QToolButton *>("addLayerMask");
auto *eye = panel.list().cells().at(0)->findChild<QToolButton *>("layerEye");
// Mid-stroke nothing flashes, and a click changes nothing.
session->selectTool(NavigationTool::brush);
session->beginBrush(QPointF(5, 5));
QVERIFY(add->isEnabled() && effects->isEnabled() && mask->isEnabled() && eye->isEnabled());
QTest::mouseClick(add, Qt::LeftButton);
QCOMPARE(session->document().value().layers.size(), size_t(1));
session->cancelBrush();
// A pending transform grays them.
session->selectTool(NavigationTool::move);
session->beginTransform();
QVERIFY(!add->isEnabled() && !effects->isEnabled() && !mask->isEnabled() && !eye->isEnabled());
session->cancelTransform();
// Effects want pixels: no folder, no adjustment, no layer.
session->addGroup();
QVERIFY(add->isEnabled() && !effects->isEnabled());
session->addAdjustment(AdjustmentKind::invert);
QVERIFY(!effects->isEnabled());
session->selectLayer(std::nullopt);
QVERIFY(!effects->isEnabled() && !mask->isEnabled() && !panel.findChild<QToolButton *>("deleteLayer")->isEnabled());
// The mask button takes one layer, without a mask.
const QUuid white = session->document().value().layers.front().id;
session->selectLayers({white, session->document().value().layers.back().id}, white);
QVERIFY(!mask->isEnabled());
session->selectLayer(white);
QVERIFY(mask->isEnabled() && effects->isEnabled());
session->addMask();
QVERIFY(!mask->isEnabled());
}
QTEST_MAIN(LayersLookEditableTests)
#include "LayersLookEditableTests.moc"
+6 -1
View File
@@ -137,8 +137,12 @@ void LayersPanelTests::theFooterAddsGroupsAndDeletesWithSwiftsWords()
QTest::mouseClick(&group, Qt::LeftButton);
QCOMPARE(session.document().value().layers.size(), size_t(2));
QVERIFY(session.activeLayer().value().isGroup);
// Busy, the footer waits.
// Busy a moment: no flash, and the session refuses.
session.setIsProjectBusy(true);
QVERIFY(add.isEnabled() && group.isEnabled() && remove.isEnabled());
QTest::mouseClick(&add, Qt::LeftButton);
QCOMPARE(session.document().value().layers.size(), size_t(2));
QTRY_VERIFY(session.showsBusy());
QVERIFY(!add.isEnabled() && !group.isEnabled() && !remove.isEnabled() && !mask.isEnabled());
}
@@ -231,6 +235,7 @@ void LayersPanelTests::footerButtonsTakeThePrimaryInkAndDimAsTheMenus()
QTRY_COMPARE(brightest(find<QToolButton>(live, "addBlankLayer").icon(), QIcon::Normal).rgb(), primary.rgb());
QVERIFY(find<QToolButton>(live, "addBlankLayer").isEnabled() && find<QToolButton>(live, "addLayerMask").isEnabled());
working.setIsProjectBusy(true);
QTRY_VERIFY(working.showsBusy());
for (const auto &[name, shape] : buttons) {
QToolButton &button = find<QToolButton>(live, name);
QVERIFY2(!button.isEnabled(), name);
+2 -2
View File
@@ -241,9 +241,9 @@ void NativeLayerListTests::theEyeTogglesAndSwipesInOneStep()
QCOMPARE(session->history.undoCount(), steps + 1);
session->undo();
QVERIFY(!layerWith(*session, shown.id(0)).isVisible && !layerWith(*session, shown.id(1)).isVisible);
// Busy, the eye is disabled.
// Busy, the eye rests once the indicator shows.
session->setIsProjectBusy(true);
QVERIFY(!eye->isEnabled());
QTRY_VERIFY(session->showsBusy() && !eye->isEnabled());
}
void NativeLayerListTests::disclosureLinkAndTheMenuReachTheSession()
+1
View File
@@ -311,6 +311,7 @@ compositor_test(CameraRawToneOrderTests)
compositor_test(LastFilterTests)
compositor_test(ToolHeaderScrollTests)
compositor_test(LayerRevealTests)
compositor_test(LayersLookEditableTests)
compositor_test(CameraRawSessionTests)
compositor_test(CameraRawToolsTests)
compositor_test(CameraRawRulesTests)