Files
qelectrotech-source-mirror/tests/qttest/tst_itemgroups.cpp
T
ispyisail 6ed41bbfd0 Click again on a member of a selected group to pick it on its own
Clicking an item of a group selects the whole group (#1070). Clicking
again on one of its items is meant to select just that item, to edit it
on its own -- what discussion #1070 proposed -- but the second click
selected the whole group again: Qt left only the clicked item selected on
release, and the group completion pulled the others back in.

A press on a member of a group that is selected whole now notes that
member (ItemGroups::memberToPick(), which also finds the member when the
click lands on a symbol's own text). If the click ends without a drag and
Qt has left only that member selected, the selection stays so. A drag
still moves the whole group; Ctrl+click keeps its meaning; a group of one
is not picked from.

In the GUI, on two grouped texts: one click then Delete removes both
(master and this); click, click again, Delete removes only the clicked
text here, both on master; dragging after one click moves both texts by
the same amount on both. tst_itemgroups: 5 new checks; removing the
whole-group or the group-of-one condition fails one each. ctest 24/24.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:33:28 +13:00

185 lines
4.4 KiB
C++

#include <QtTest>
#include <QDomDocument>
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include "itemgroups.h"
class tst_itemgroups : public QObject
{
Q_OBJECT
QGraphicsScene *scene = nullptr;
QGraphicsRectItem *a = nullptr, *b = nullptr, *c = nullptr, *d = nullptr, *e = nullptr;
QUuid g1, g2;
QGraphicsRectItem *add()
{
auto item = scene->addRect(0, 0, 10, 10);
item->setFlag(QGraphicsItem::ItemIsSelectable);
return item;
}
QList<QGraphicsItem *> selection() const { return scene->selectedItems(); }
// Select exactly @a items, as Qt leaves the scene after a click.
void select(const QList<QGraphicsItem *> &items)
{
scene->clearSelection();
for (auto item : items) {
item->setSelected(true);
}
}
bool selected(std::initializer_list<QGraphicsItem *> expected) const
{
QList<QGraphicsItem *> got = selection();
if (got.size() != int(expected.size())) {
return false;
}
for (auto item : expected) {
if (!got.contains(item)) {
return false;
}
}
return true;
}
private slots:
// a, b in g1; c alone; d, e in g2
void init()
{
scene = new QGraphicsScene;
a = add(); b = add(); c = add(); d = add(); e = add();
g1 = QUuid::createUuid();
g2 = QUuid::createUuid();
ItemGroups::setGroup(a, g1);
ItemGroups::setGroup(b, g1);
ItemGroups::setGroup(d, g2);
ItemGroups::setGroup(e, g2);
}
void cleanup()
{
delete scene;
scene = nullptr;
}
void clickingAMemberSelectsTheGroup()
{
select({a});
QVERIFY(ItemGroups::completeSelection(scene, {}, false));
QVERIFY(selected({a, b}));
}
// Releasing a click on one member of the selected group leaves only it
// selected in Qt; the group must stay whole.
void reclickingAMemberKeepsTheGroup()
{
select({b});
ItemGroups::completeSelection(scene, {a, b}, false);
QVERIFY(selected({a, b}));
}
void ctrlClickingAMemberOffDeselectsTheGroup()
{
select({b});
ItemGroups::completeSelection(scene, {a, b}, true);
QVERIFY(selected({}));
}
void ctrlClickingAMemberOnAddsTheGroup()
{
select({c, a});
ItemGroups::completeSelection(scene, {c}, true);
QVERIFY(selected({a, b, c}));
}
void otherGroupsAreUntouched()
{
select({b, d, e});
ItemGroups::completeSelection(scene, {a, b, d, e}, true);
QVERIFY(selected({d, e}));
}
void ungroupedItemsAreUntouched()
{
select({c});
QVERIFY(!ItemGroups::completeSelection(scene, {}, false));
QVERIFY(selected({c}));
}
// A member taken off the scene (deleted, kept by the undo stack) must
// not count as "deselected".
void itemsLeavingTheSceneAreIgnored()
{
select({a, b});
scene->removeItem(b);
ItemGroups::completeSelection(scene, {a, b}, true);
QVERIFY(selected({a}));
delete b;
b = nullptr;
}
// A second click on a member of a group selected whole picks that member
// out; a click on a member of a group not selected whole does not.
void aMemberOfAWholeGroupCanBePicked()
{
select({a, b});
QCOMPARE(ItemGroups::memberToPick(a), a);
QCOMPARE(ItemGroups::memberToPick(b), b);
}
void aMemberOfAPartlySelectedGroupIsNotPicked()
{
select({a}); // after one member was picked
QCOMPARE(ItemGroups::memberToPick(a), nullptr);
select({});
QCOMPARE(ItemGroups::memberToPick(a), nullptr);
}
void anUngroupedItemIsNotPicked()
{
select({c});
QCOMPARE(ItemGroups::memberToPick(c), nullptr);
QCOMPARE(ItemGroups::memberToPick(nullptr), nullptr);
}
void aGroupOfOneIsNotPicked()
{
ItemGroups::setGroup(e, QUuid()); // g2 is now d alone
select({d});
QCOMPARE(ItemGroups::memberToPick(d), nullptr);
}
// A click lands on a symbol's own text, not on the symbol: the member is
// the nearest grouped ancestor.
void aClickOnAMembersChildPicksTheMember()
{
auto child = new QGraphicsRectItem(0, 0, 2, 2, a);
QCOMPARE(ItemGroups::groupedItem(child), a);
select({a, b});
QCOMPARE(ItemGroups::memberToPick(child), a);
QCOMPARE(ItemGroups::groupedItem(c), nullptr);
}
void xmlRoundTrip()
{
QDomDocument doc;
QDomElement grouped = doc.createElement(QStringLiteral("element"));
ItemGroups::write(grouped, a);
QCOMPARE(ItemGroups::read(grouped), g1);
QDomElement alone = doc.createElement(QStringLiteral("element"));
ItemGroups::write(alone, c);
QVERIFY(!alone.hasAttribute(QStringLiteral("group")));
QVERIFY(ItemGroups::read(alone).isNull());
ItemGroups::setGroup(a, QUuid());
QVERIFY(ItemGroups::groupOf(a).isNull());
}
};
QTEST_MAIN(tst_itemgroups)
#include "tst_itemgroups.moc"