mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-28 21:34:12 +02:00
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>
This commit is contained in:
@@ -437,6 +437,24 @@ void Diagram::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
|
||||
rememberSelection();
|
||||
//Clicking again on a member of a group that is selected whole picks
|
||||
//that member out, to edit it on its own (discussion #1070): noted
|
||||
//here, decided on release, since a drag must still move the group.
|
||||
//Ctrl keeps its usual meaning.
|
||||
m_member_to_pick.clear();
|
||||
if (event->button() == Qt::LeftButton
|
||||
&& !event->modifiers().testFlag(Qt::ControlModifier)) {
|
||||
QTransform view_transform;
|
||||
if (event->widget()) {
|
||||
if (auto view = qobject_cast<QGraphicsView *>(event->widget()->parentWidget())) {
|
||||
view_transform = view->transform();
|
||||
}
|
||||
}
|
||||
if (QGraphicsItem *member = ItemGroups::memberToPick(
|
||||
itemAt(event->scenePos(), view_transform))) {
|
||||
m_member_to_pick = member->toGraphicsObject();
|
||||
}
|
||||
}
|
||||
QGraphicsScene::mousePressEvent(event);
|
||||
completeGroupSelection();
|
||||
}
|
||||
@@ -477,6 +495,19 @@ void Diagram::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
|
||||
QGraphicsScene::mouseReleaseEvent(event);
|
||||
|
||||
//A click that did not drag, on a member of a group selected whole:
|
||||
//Qt has left only that member selected, and it stays so.
|
||||
QGraphicsObject *picked = m_member_to_pick.data();
|
||||
m_member_to_pick.clear();
|
||||
if (picked
|
||||
&& (event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength()
|
||||
< QApplication::startDragDistance()
|
||||
&& selectedItems() == QList<QGraphicsItem *>{picked}) {
|
||||
rememberSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
//A click on an already selected item changes the selection on
|
||||
//release, not on press (Ctrl toggles it, a plain click keeps only it).
|
||||
completeGroupSelection();
|
||||
|
||||
@@ -142,6 +142,9 @@ class Diagram : public QGraphicsScene
|
||||
//Selection before the current click, see completeGroupSelection()
|
||||
QList<QPointer<QGraphicsObject>> m_previous_selection;
|
||||
void rememberSelection();
|
||||
//Member of a wholly selected group under the current click, which
|
||||
//the click picks out on its own if it ends without a drag
|
||||
QPointer<QGraphicsObject> m_member_to_pick;
|
||||
|
||||
bool uuidUsedByOtherDiagram(const QUuid &uuid) const;
|
||||
QUuid derivedUuid(const QDomElement &root, const QString &reason) const;
|
||||
|
||||
@@ -62,6 +62,47 @@ QUuid ItemGroups::read(const QDomElement &xml)
|
||||
return QUuid(xml.attribute(QString::fromLatin1(xml_attribute)));
|
||||
}
|
||||
|
||||
/**
|
||||
@return @a item, or its nearest ancestor, that belongs to a group -- a
|
||||
click on a symbol's own text hits the text, but the symbol is the member
|
||||
-- or nullptr if none does.
|
||||
*/
|
||||
QGraphicsItem *ItemGroups::groupedItem(QGraphicsItem *item)
|
||||
{
|
||||
for (; item; item = item->parentItem()) {
|
||||
if (!groupOf(item).isNull()) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@return the member a click on @a hit may pick out on its own: the grouped
|
||||
item hit, when every member of its group is selected already. A first
|
||||
click selects the whole group; a second click, on a member of the group
|
||||
it selected, is how the user asks for that member alone (discussion
|
||||
#1070). nullptr when the click is not that.
|
||||
*/
|
||||
QGraphicsItem *ItemGroups::memberToPick(QGraphicsItem *hit)
|
||||
{
|
||||
QGraphicsItem *member = groupedItem(hit);
|
||||
if (!member || !member->isSelected() || !member->scene()) {
|
||||
return nullptr;
|
||||
}
|
||||
const QUuid group = groupOf(member);
|
||||
int members = 0;
|
||||
for (QGraphicsItem *item : member->scene()->items()) {
|
||||
if (groupOf(item) == group) {
|
||||
if (!item->isSelected()) {
|
||||
return nullptr;
|
||||
}
|
||||
++members;
|
||||
}
|
||||
}
|
||||
return members > 1 ? member : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
Make the selection of @a scene whole groups again after it changed.
|
||||
A group with a selected member is selected entirely, except when the
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace ItemGroups
|
||||
void write(QDomElement &xml, const QGraphicsItem *item);
|
||||
QUuid read(const QDomElement &xml);
|
||||
|
||||
QGraphicsItem *groupedItem(QGraphicsItem *item);
|
||||
QGraphicsItem *memberToPick(QGraphicsItem *hit);
|
||||
|
||||
bool completeSelection(QGraphicsScene *scene,
|
||||
const QList<QGraphicsItem *> &previous,
|
||||
bool toggling);
|
||||
|
||||
@@ -121,6 +121,48 @@ private slots:
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user