mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-28 21:34:12 +02:00
f7591e0c9d
Symbols, free texts, shapes and pictures can be grouped from the Edit menu, the selection's context menu or the command search. Clicking one member selects the group, so moving, copying and deleting act on all of it; Ctrl+click on a member deselects the group; a rubber band touching part of a group selects all of it when released. No default shortcut: Ctrl+G is "jump to element". A group is not an object in the scene. Each member keeps its place and carries the group's uuid (QGraphicsItem::data()), saved as a "group" attribute written only when set: a project without groups saves exactly as before, and older versions open a grouped one and ignore the groups. Re-parenting under a QGraphicsItemGroup would have made every member's position group-relative; ElementTextItemGroup already needs nine special cases for that. - Selection is completed on clicks and at the end of a rubber band, not on every selectionChanged(): export, search and Tab select items themselves and must not have groups pulled back in. - Project database: group_uuid on element, shape, independent_text and image, kept in step by projectDataBase::itemGroupChanged(). - Paste and folio duplication give each source group one new uuid. - Undo of Ungroup restores each item's exact group. Builds on #1065 (uuids and database rows for texts, shapes and images). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
143 lines
3.2 KiB
C++
143 lines
3.2 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;
|
|
}
|
|
|
|
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"
|