#include "EyedropperFixtures.h" // Picking beside tools, the open picker and lost releases. class PickingTests : public QObject { Q_OBJECT private slots: void altPicksUnderTheBrushSpotHealingAndTheGradient(); void altLeavesAStrokeOrAGradientDragAlone(); void theOpenPickerTakesSamplesAndTheKeysBack(); void pickingEndingEndsTheSampling(); void altFirstSeenInThePressSamplesOn(); void aRightDragSizesTheTipWhilePicking(); void aLostReleaseEndsTheSampling(); void theInlineEditorStandsAsideForThePicker(); void aPolygonalLassoWaitsForThePicker(); }; void PickingTests::altPicksUnderTheBrushSpotHealingAndTheGradient() { Picking shown; for (const NavigationTool tool : {NavigationTool::brush, NavigationTool::spotHealing, NavigationTool::gradient}) { shown.tool(tool); shown.session.resetPaletteColors(); shown.press(QPointF(100, 150), Qt::AltModifier); QVERIFY(shown.ring().frame()); QVERIFY(!shown.session.brushStroke() && !shown.session.gradientEdit()); shown.release(QPointF(100, 150), Qt::AltModifier); QCOMPARE(shown.foreground(), QString("FF0000")); } // Clone Stamp's Alt sets its source instead. shown.tool(NavigationTool::cloneStamp); shown.session.resetPaletteColors(); shown.click(QPointF(300, 150), Qt::AltModifier); QVERIFY(shown.session.cloneSource()); QCOMPARE(shown.foreground(), QString("000000")); // The other tools take Alt for their own. shown.tool(NavigationTool::shape); shown.click(QPointF(300, 150), Qt::AltModifier); QCOMPARE(shown.foreground(), QString("000000")); } void PickingTests::altLeavesAStrokeOrAGradientDragAlone() { Picking shown; // A stroke begun paints on under Alt. shown.tool(NavigationTool::brush); shown.press(QPointF(100, 150)); QVERIFY(shown.session.brushStroke()); QTest::keyPress(shown.canvas, Qt::Key_Alt, Qt::AltModifier); QVERIFY(!shown.picks()); shown.move(QPointF(300, 150), Qt::AltModifier); QVERIFY(shown.session.brushStroke() && !shown.ring().frame()); shown.session.cancelBrush(); shown.release(QPointF(300, 150), Qt::AltModifier); QTest::keyRelease(shown.canvas, Qt::Key_Alt); QCOMPARE(shown.foreground(), QString("000000")); // So does a gradient's end being dragged. shown.tool(NavigationTool::gradient); shown.press(QPointF(100, 150)); QTest::keyPress(shown.canvas, Qt::Key_Alt, Qt::AltModifier); QVERIFY(!shown.picks()); shown.move(QPointF(300, 150), Qt::AltModifier); QCOMPARE(shown.session.gradientEdit().value().end, QPointF(300, 150)); QVERIFY(!shown.ring().frame()); shown.release(QPointF(300, 150), Qt::AltModifier); QTest::keyRelease(shown.canvas, Qt::Key_Alt); QCOMPARE(shown.foreground(), QString("000000")); // A refused gradient press drags nothing: Alt picks there. shown.session.cancelGradient(); shown.session.toggleLayerVisibility(shown.session.activeLayerID().value()); shown.press(QPointF(100, 150)); QVERIFY(!shown.session.gradientEdit()); QTest::keyPress(shown.canvas, Qt::Key_Alt, Qt::AltModifier); QVERIFY(shown.picks()); shown.release(QPointF(100, 150), Qt::AltModifier); QTest::keyRelease(shown.canvas, Qt::Key_Alt); } void PickingTests::theOpenPickerTakesSamplesAndTheKeysBack() { Picking shown; shown.tool(NavigationTool::brush); ColorPickerPanelController controller(shown.window); shown.session.openColorPicker(false); controller.show(shown.session.colorPicker().value(), shown.session); QWidget *panel = pickerPanel(); QVERIFY(panel); QTRY_COMPARE(QApplication::activeWindow(), panel); // Any tool picks while it is open. shown.canvas->synchronizeDisplay(); QVERIFY(shown.picks()); shown.window.activateWindow(); QTRY_COMPARE(QApplication::activeWindow(), &shown.window); // The ring's original is the picker's colour, not the swatch's. shown.session.setColorPickerHSB(PickerHSB(120, 1, 1)); shown.press(QPointF(300, 150)); QCOMPARE(shown.session.colorPicker().value().color().hex(), QString("0000FF")); QCOMPARE(shown.foreground(), QString("000000")); QCOMPARE(shown.ring().original(), (PaletteColor{0, 1, 0})); QCOMPARE(shown.ring().sampled().hex(), QString("0000FF")); QVERIFY(!shown.session.brushStroke()); // The release hands the picker the keys back. shown.release(QPointF(300, 150)); QTRY_COMPARE(QApplication::activeWindow(), panel); // A pan's release, sampling nothing, leaves them. shown.window.activateWindow(); QTRY_COMPARE(QApplication::activeWindow(), &shown.window); QTest::keyPress(shown.canvas, Qt::Key_Space); shown.drag(QPointF(300, 150), QPointF(310, 150)); QTest::keyRelease(shown.canvas, Qt::Key_Space); QTest::qWait(50); QCOMPARE(QApplication::activeWindow(), &shown.window); // Closed, the Brush paints again. shown.session.closeColorPicker(false); controller.close(); shown.canvas->synchronizeDisplay(); QVERIFY(!shown.picks()); } void PickingTests::pickingEndingEndsTheSampling() { Picking shown; shown.tool(NavigationTool::brush); QTest::keyPress(shown.canvas, Qt::Key_Alt, Qt::AltModifier); shown.press(QPointF(100, 150), Qt::AltModifier); QVERIFY(shown.ring().frame()); // Alt let go mid-drag: the ring goes, sampling stops. QTest::keyRelease(shown.canvas, Qt::Key_Alt); QVERIFY(!shown.ring().frame()); shown.move(QPointF(300, 150)); QCOMPARE(shown.foreground(), QString("FF0000")); QVERIFY(!shown.session.brushStroke()); shown.release(QPointF(300, 150)); // Another tool ends it too. shown.tool(NavigationTool::eyedropper); shown.press(QPointF(300, 150)); shown.tool(NavigationTool::brush); QVERIFY(!shown.ring().frame()); shown.move(QPointF(100, 150)); QCOMPARE(shown.foreground(), QString("0000FF")); shown.release(QPointF(100, 150)); } void PickingTests::altFirstSeenInThePressSamplesOn() { // As ContentView makes it: every change syncs the canvas. Picking shown; QObject::connect(&shown.session, &EditorSession::changed, shown.canvas, [&shown] { shown.canvas->synchronizeDisplay(); }); shown.tool(NavigationTool::brush); shown.press(QPointF(100, 150), Qt::AltModifier); QVERIFY(shown.ring().frame()); shown.move(QPointF(300, 150), Qt::AltModifier); QCOMPARE(shown.foreground(), QString("0000FF")); QCOMPARE(shown.ring().frame().value(), QRectF(242, 92, 116, 116)); shown.release(QPointF(300, 150), Qt::AltModifier); QVERIFY(!shown.ring().frame()); // Alt let go unseen mid-drag: the move ends the sampling. shown.press(QPointF(100, 150), Qt::AltModifier); shown.move(QPointF(300, 150)); QVERIFY(!shown.ring().frame()); QCOMPARE(shown.foreground(), QString("FF0000")); shown.release(QPointF(300, 150)); } void PickingTests::aRightDragSizesTheTipWhilePicking() { Picking shown; shown.tool(NavigationTool::brush); const auto rightDrag = [&shown](Qt::KeyboardModifiers modifiers) { QTest::mousePress(shown.canvas, Qt::RightButton, modifiers, QPoint(100, 150)); QMouseEvent move(QEvent::MouseMove, QPointF(120, 150), QPointF(120, 150), shown.canvas->mapToGlobal(QPoint(120, 150)), Qt::NoButton, Qt::RightButton, modifiers); QApplication::sendEvent(shown.canvas, &move); QTest::mouseRelease(shown.canvas, Qt::RightButton, modifiers, QPoint(120, 150)); }; // Alt held: the right-drag still sizes the tip, as Swift's. const double diameter = shown.session.brushSettings().diameter; QTest::keyPress(shown.canvas, Qt::Key_Alt, Qt::AltModifier); rightDrag(Qt::AltModifier); QCOMPARE(shown.session.brushSettings().diameter, diameter + 40); QTest::keyRelease(shown.canvas, Qt::Key_Alt); // So with the picker open. shown.session.openColorPicker(false); shown.canvas->synchronizeDisplay(); rightDrag(Qt::NoModifier); QCOMPARE(shown.session.brushSettings().diameter, diameter + 80); shown.session.closeColorPicker(false); } void PickingTests::aLostReleaseEndsTheSampling() { Picking shown; // A move without the button: its release was lost. shown.press(QPointF(100, 150)); shown.hover(QPointF(300, 150)); QVERIFY(!shown.ring().frame()); QCOMPARE(shown.foreground(), QString("FF0000")); shown.hover(QPointF(310, 150)); QCOMPARE(shown.foreground(), QString("FF0000")); // A lost focus ends it too. shown.press(QPointF(300, 150)); QVERIFY(shown.ring().frame()); shown.canvas->clearFocus(); QVERIFY(!shown.ring().frame()); shown.move(QPointF(100, 150)); QCOMPARE(shown.foreground(), QString("0000FF")); // So does the next press: with Space it pans instead. shown.canvas->setFocus(); shown.press(QPointF(100, 150)); QTest::keyPress(shown.canvas, Qt::Key_Space); const QPointF before = shown.session.viewport.documentPoint(QPointF(200, 150), shown.documentSize()); shown.press(QPointF(300, 150)); QVERIFY(!shown.ring().frame()); shown.move(QPointF(320, 150)); QCOMPARE(shown.foreground(), QString("FF0000")); QCOMPARE(shown.session.viewport.documentPoint(QPointF(200, 150), shown.documentSize()), before - QPointF(20, 0)); shown.release(QPointF(320, 150)); QTest::keyRelease(shown.canvas, Qt::Key_Space); } void PickingTests::theInlineEditorStandsAsideForThePicker() { Picking shown; QObject::connect(&shown.session, &EditorSession::changed, shown.canvas, [&shown] { shown.canvas->synchronizeDisplay(); }); shown.tool(NavigationTool::type); shown.session.beginText(QPointF(20, 30), true); QTest::keyClicks(shown.canvas, QStringLiteral("Hello there")); QTest::keyClick(shown.canvas, Qt::Key_Home); const InlineTextEditor &editor = *shown.canvas->inlineTextEditor(); const QPointF word = editor.textTransform().map(QPointF(LayerTextStyle::padding + 4, LayerTextStyle::padding + 4)); const QImage before = shown.canvas->grab(QRect(0, 0, 200, 140)).toImage(); shown.session.openTextColorPicker(); // A sampling ring elsewhere leaves the editor drawn whole. shown.press(QPointF(300, 200)); QVERIFY(shown.ring().frame()); QCOMPARE(shown.canvas->grab(QRect(0, 0, 200, 140)).toImage(), before); shown.release(QPointF(300, 200)); // Over the text the eyedropper shows; clicks sample. shown.hover(word); QVERIFY(shown.picks()); shown.click(word); QCOMPARE(shown.session.colorPicker().value().color().hex(), QString("FF0000")); QVERIFY(editor.anchor() == 0 && editor.caretPosition() == 0); // A double click samples too, selecting no word. shown.session.setColorPickerHSB(PickerHSB(240, 1, 1)); QTest::mouseDClick(shown.canvas, Qt::LeftButton, Qt::NoModifier, word.toPoint()); QCOMPARE(shown.session.colorPicker().value().color().hex(), QString("FF0000")); QVERIFY(editor.anchor() == 0 && editor.caretPosition() == 0); // Closed, the text view takes clicks again. shown.session.closeColorPicker(false); QTest::mouseDClick(shown.canvas, Qt::LeftButton, Qt::NoModifier, word.toPoint()); QVERIFY(editor.anchor() == 0 && editor.caretPosition() == 5); } void PickingTests::aPolygonalLassoWaitsForThePicker() { Picking shown; shown.tool(NavigationTool::lasso); shown.session.setLassoKind(LassoKind::polygonal); shown.click(QPointF(50, 50)); shown.hover(QPointF(80, 60)); const LassoDraft draft = shown.session.lassoDraft().value(); shown.session.openColorPicker(false); shown.canvas->synchronizeDisplay(); // A sampling drag adds no corner, moves no rubber band. shown.press(QPointF(300, 150)); shown.move(QPointF(310, 160)); QCOMPARE(shown.session.colorPicker().value().color().hex(), QString("0000FF")); QCOMPARE(shown.session.lassoDraft().value(), draft); shown.release(QPointF(310, 160)); // A double click samples, the outline still open. QTest::mouseDClick(shown.canvas, Qt::LeftButton, Qt::NoModifier, QPoint(100, 150)); QCOMPARE(shown.session.colorPicker().value().color().hex(), QString("FF0000")); QCOMPARE(shown.session.lassoDraft().value(), draft); shown.session.closeColorPicker(false); } QTEST_MAIN(PickingTests) #include "PickingTests.moc"