Fix a group splitting apart when one of its items is locked (#1146)

Grouping two symbols and then locking one of them (Lock position in its
properties) left the group in a broken state: dragging the unlocked symbol
pulled it away while the locked one stayed, and dragging the locked one
did nothing. The move simply dropped locked items, so the rest of the
group went without them.

A group with a locked member now does not move at all, whichever member
is dragged, and the status bar says why. This is the rule the item-groups
proposal (discussion #1070) set out for this case. The same rule applies
to the arrow keys and to the Align commands, which share
DiagramContent::removeNonMovableItems().

Also fixed on the way, for a plain selection with a locked symbol: a wire
between the locked symbol and one being dragged kept its user-placed text
moving with the dragged end. Such a wire is now redrawn only, as a wire to
an unselected symbol already is.

Checked in the GUI on two symbols joined by a wire (grafcet example),
master against this branch, positions read from the saved file:
- drag the unlocked member: master moves it 190 px, this branch moves
  nothing and shows the message
- arrow keys on the selected group (3 runs each): master moves the
  unlocked member, this branch nothing
- the same two symbols ungrouped: both move the unlocked one, as before
- user-placed wire text: master shifts it 190 px, this branch keeps it
ctest: 34/34.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-29 23:23:32 +13:00
parent cbbb9fd0ac
commit 2dbaa69186
6 changed files with 91 additions and 2 deletions
+48 -1
View File
@@ -18,6 +18,7 @@
#include "diagramcontent.h"
#include "diagram.h"
#include "itemgroups.h"
#include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h"
#include "qetgraphicsitem/conductor.h"
#include "qetgraphicsitem/conductortextitem.h"
@@ -261,7 +262,7 @@ void DiagramContent::clear()
*/
int DiagramContent::removeNonMovableItems()
{
int count_ = 0;
int count_ = removePinnedGroups();
const QList<Element *> elements_set = m_elements;
for(Element *elmt : elements_set) {
@@ -287,9 +288,55 @@ int DiagramContent::removeNonMovableItems()
}
}
//A wire whose two ends no longer both move is redrawn, not moved,
//or its text would be carried off with the end that still moves
const QList<Conductor *> conductors_to_move = m_conductors_to_move;
for (Conductor *conductor : conductors_to_move) {
if (!m_elements.contains(conductor->terminal1->parentElement()) ||
!m_elements.contains(conductor->terminal2->parentElement())) {
m_conductors_to_move.removeAll(conductor);
if (!m_conductors_to_update.contains(conductor))
m_conductors_to_update << conductor;
}
}
return count_;
}
/**
@brief DiagramContent::removePinnedGroups
A group (#1070) with a locked member does not move at all: moving the
rest would pull the group apart around the member that stays (#1146).
Only a locked member that is in this content pins its group.
Called first by removeNonMovableItems(), while the locked items are
still here to be found.
@return the number of removed items
*/
int DiagramContent::removePinnedGroups()
{
QSet<QUuid> pinned;
for (Element *elmt : std::as_const(m_elements))
if (!elmt->isMovable())
pinned << ItemGroups::groupOf(elmt);
for (DiagramImageItem *img : std::as_const(m_images))
if (!img->isMovable())
pinned << ItemGroups::groupOf(img);
for (QetShapeItem *shape : std::as_const(m_shapes))
if (!shape->isMovable())
pinned << ItemGroups::groupOf(shape);
pinned.remove(QUuid());
if (pinned.isEmpty())
return 0;
auto isPinned = [&pinned](const QGraphicsItem *item) {
return pinned.contains(ItemGroups::groupOf(item));
};
return int(m_elements.removeIf(isPinned)
+ m_images.removeIf(isPinned)
+ m_shapes.removeIf(isPinned)
+ m_text_fields.removeIf(isPinned));
}
DiagramContent &DiagramContent::operator+=(const DiagramContent &other)
{
for(Element *elmt : other.m_elements)