diff --git a/src/qanDraggableCtrl.cpp b/src/qanDraggableCtrl.cpp index 73bcfbe8..eb417392 100644 --- a/src/qanDraggableCtrl.cpp +++ b/src/qanDraggableCtrl.cpp @@ -122,6 +122,11 @@ bool DraggableCtrl::handleMouseMoveEvent(QMouseEvent* event) if (_targetItem->getCollapsed()) return false; + if (_targetItem->getNode() != nullptr && + (_targetItem->getNode()->getLocked() || + _targetItem->getNode()->getIsProtected())) + return false; + const auto rootItem = getGraph()->getContainerItem(); if (rootItem != nullptr && // Root item exist, left button is pressed and the target item event->buttons().testFlag(Qt::LeftButton)) { // is draggable and not collapsed @@ -283,18 +288,23 @@ void DraggableCtrl::endDragMove(bool dragSelection) const auto graphContainerItem = graph->getContainerItem(); if (graphContainerItem == nullptr) return; - emit graph->nodeMoved(_target); + bool nodeGrouped = false; if (_targetItem->getDroppable()) { const auto targetContainerPos = _targetItem->mapToItem(graphContainerItem, QPointF{0., 0.}); qan::Group* group = graph->groupAt(targetContainerPos, { _targetItem->width(), _targetItem->height() }, _targetItem); - if ( group != nullptr && - static_cast(group->getItem()) != static_cast(_targetItem.data()) ) { // Do not drop a group in itself - if ( group->getGroupItem() != nullptr && // Do not allow grouping a node in a collapsed - !group->getGroupItem()->getCollapsed() ) // group item - graph->groupNode( group, _target.data() ); + if (group != nullptr && + static_cast(group->getItem()) != static_cast(_targetItem.data())) { // Do not drop a group in itself + if (group->getGroupItem() != nullptr && // Do not allow grouping a node in a collapsed + !group->getGroupItem()->getCollapsed()) { // group item + graph->groupNode(group, _target.data()); + nodeGrouped = true; + } } } + if (!nodeGrouped) // Do not emit nodeMoved() if it has been grouped + emit graph->nodeMoved(_target); + _targetItem->setDragged(false); if (dragSelection && // If there is a selection, end drag for the whole selection diff --git a/src/qanEdge.cpp b/src/qanEdge.cpp index 3ed63329..3b4011b1 100644 --- a/src/qanEdge.cpp +++ b/src/qanEdge.cpp @@ -105,7 +105,17 @@ bool Edge::setLabel(const QString& label) return false; } -bool Edge::setLocked(bool locked) noexcept +bool Edge::setIsProtected(bool isProtected) +{ + if (isProtected != _isProtected) { + _isProtected = isProtected; + emit isProtectedChanged(); + return true; + } + return false; +} + +bool Edge::setLocked(bool locked) { if (locked != _locked) { _locked = locked; diff --git a/src/qanEdge.h b/src/qanEdge.h index 8e0e5a37..1a9dff73 100644 --- a/src/qanEdge.h +++ b/src/qanEdge.h @@ -128,14 +128,30 @@ class Edge : public gtpo::edge //! \copydoc _label void labelChanged(); +public: + /*! \brief A protected edge can't be dragged by user (default to unprotected). + * + * Contrary to `enabled` and 'locked' properties: double click, right click and selection is + * active when protected. + * + * \note edgeDoubleClicked(), edgeRightClicked() signal are still emitted from protected edge. + */ + Q_PROPERTY(bool isProtected READ getIsProtected WRITE setIsProtected NOTIFY isProtectedChanged FINAL) + bool setIsProtected(bool isProtected); + bool getIsProtected() const { return _isProtected; } +private: + bool _isProtected = false; +signals: + void isProtectedChanged(); + public: /*! \brief A locked edge can't be selected / dragged by user (default to false ie unlocked). * * Might be usefull to prevent user inputs when the edge is laid out automatically. */ Q_PROPERTY(bool locked READ getLocked WRITE setLocked NOTIFY lockedChanged FINAL) - bool setLocked(bool locked) noexcept; - bool getLocked() const noexcept { return _locked; } + bool setLocked(bool locked); + bool getLocked() const { return _locked; } private: bool _locked = false; signals: diff --git a/src/qanEdgeDraggableCtrl.cpp b/src/qanEdgeDraggableCtrl.cpp index f0f46752..8cd63227 100644 --- a/src/qanEdgeDraggableCtrl.cpp +++ b/src/qanEdgeDraggableCtrl.cpp @@ -78,7 +78,9 @@ bool EdgeDraggableCtrl::handleMouseMoveEvent(QMouseEvent* event) auto dst = _targetItem->getDestinationItem() != nullptr ? _targetItem->getDestinationItem()->getNode() : nullptr; if ((src && src->getLocked()) || - (dst && dst->getLocked())) + (src && src->getIsProtected()) || + (dst && dst->getLocked()) || + (dst && dst->getIsProtected())) return false; const auto rootItem = graph->getContainerItem(); diff --git a/src/qanEdgeItem.cpp b/src/qanEdgeItem.cpp index f7283239..eaade4e5 100644 --- a/src/qanEdgeItem.cpp +++ b/src/qanEdgeItem.cpp @@ -1142,6 +1142,7 @@ void EdgeItem::mouseMoveEvent(QMouseEvent* event) { // Early exits if (getEdge() == nullptr || + getEdge()->getIsProtected() || getEdge()->getLocked()) { QQuickItem::mouseMoveEvent(event); return; diff --git a/src/qanGraph.cpp b/src/qanGraph.cpp index 873a73a1..dd9fca1d 100644 --- a/src/qanGraph.cpp +++ b/src/qanGraph.cpp @@ -872,7 +872,7 @@ qan::Group* Graph::insertGroup() bool Graph::insertGroup(Group* group, QQmlComponent* groupComponent, qan::NodeStyle* groupStyle) { // PRECONDITIONS: - // group must be dereferencable + // group can't be nullptr // groupComponent and groupStyle can be nullptr if (group == nullptr) return false; @@ -894,45 +894,47 @@ bool Graph::insertGroup(Group* group, QQmlComponent* groupComponent, qan::Nod nullptr, group)); } - if (groupItem == nullptr) { - qWarning() << "qan::Graph::insertGroup(): Error: Either group delegate or group style is invalid or nullptr."; - return false; - } if (!super_t::insert_group(group)) qWarning() << "qan::Graph::insertGroup(): Error: Internal topology error."; - groupItem->setGroup(group); - groupItem->setGraph(this); - group->setItem(groupItem); - auto notifyGroupClicked = [this] (qan::GroupItem* groupItem, QPointF p) { - if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) - emit this->groupClicked(groupItem->getGroup(), p); - }; - connect(groupItem, &qan::GroupItem::groupClicked, - this, notifyGroupClicked); + if (groupItem != nullptr) { // groupItem shouldn't be null, but throw only a warning to run concrete unit tests. + groupItem->setGroup(group); + groupItem->setGraph(this); + group->setItem(groupItem); - auto notifyGroupRightClicked = [this] (qan::GroupItem* groupItem, QPointF p) { - if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) - emit this->groupRightClicked(groupItem->getGroup(), p); - }; - connect(groupItem, &qan::GroupItem::groupRightClicked, - this, notifyGroupRightClicked); + auto notifyGroupClicked = [this] (qan::GroupItem* groupItem, QPointF p) { + if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) + emit this->groupClicked(groupItem->getGroup(), p); + }; + connect(groupItem, &qan::GroupItem::groupClicked, + this, notifyGroupClicked); - auto notifyGroupDoubleClicked = [this] (qan::GroupItem* groupItem, QPointF p) { - if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) - emit this->groupDoubleClicked(groupItem->getGroup(), p); - }; - connect(groupItem, &qan::GroupItem::groupDoubleClicked, - this, notifyGroupDoubleClicked); + auto notifyGroupRightClicked = [this] (qan::GroupItem* groupItem, QPointF p) { + if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) + emit this->groupRightClicked(groupItem->getGroup(), p); + }; + connect(groupItem, &qan::GroupItem::groupRightClicked, + this, notifyGroupRightClicked); + + auto notifyGroupDoubleClicked = [this] (qan::GroupItem* groupItem, QPointF p) { + if ( groupItem != nullptr && groupItem->getGroup() != nullptr ) + emit this->groupDoubleClicked(groupItem->getGroup(), p); + }; + connect(groupItem, &qan::GroupItem::groupDoubleClicked, + this, notifyGroupDoubleClicked); + + { // Send group item to front + _maxZ += 1.0; + groupItem->setZ(_maxZ); + } + } else + qWarning() << "qan::Graph::insertGroup(): Warning: Either group delegate or group style is invalid or nullptr."; - { // Send group item to front - _maxZ += 1.0; - groupItem->setZ(_maxZ); - } if (group != nullptr) { // Notify user. onNodeInserted(*group); emit nodeInserted(group); } + return true; } @@ -983,8 +985,6 @@ void Graph::removeGroupContent_rec(qan::Group* group) if (_selectedNodes.contains(group)) _selectedNodes.removeAll(group); - //auto nodeGroupPtr = std::static_pointer_cast(group->shared_from_this()); - //super_t::weak_group_t weakNodeGroupPtr = nodeGroupPtr; super_t::remove_group(group); } @@ -999,8 +999,8 @@ bool qan::Graph::groupNode(qan::Group* group, qan::Node* node, bool transform { // PRECONDITIONS: // group and node can't be nullptr - if ( group == nullptr || - node == nullptr ) + if (group == nullptr || + node == nullptr) return false; if (static_cast(group) == static_cast(node)) { qWarning() << "qan::Graph::groupNode(): Error, can't group a group in itself."; @@ -1330,8 +1330,14 @@ void Graph::alignHorizontalCenter(std::vector&& items) } qreal center = minLeft + (maxRight - minLeft) / 2.; - for (auto item: items) + for (auto item: items){ + const auto nodeItem = qobject_cast(item); // Works for qan::GroupItem* + if (nodeItem != nullptr) + emit nodeAboutToBeMoved(nodeItem->getNode()); item->setX(center - (item->width() / 2.)); + if (nodeItem != nullptr) + emit nodeMoved(nodeItem->getNode()); + } } void Graph::alignRight(std::vector&& items) @@ -1341,8 +1347,14 @@ void Graph::alignRight(std::vector&& items) qreal maxRight = std::numeric_limits::min(); for (const auto item: items) maxRight = std::max(maxRight, item->x() + item->width()); - for (auto item: items) + for (auto item: items) { + const auto nodeItem = qobject_cast(item); // Works for qan::GroupItem* + if (nodeItem != nullptr) + emit nodeAboutToBeMoved(nodeItem->getNode()); item->setX(maxRight - item->width()); + if (nodeItem != nullptr) + emit nodeMoved(nodeItem->getNode()); + } } void Graph::alignLeft(std::vector&& items) @@ -1352,8 +1364,14 @@ void Graph::alignLeft(std::vector&& items) qreal minLeft = std::numeric_limits::max(); for (const auto item: items) minLeft = std::min(minLeft, item->x()); - for (auto item: items) + for (auto item: items) { + const auto nodeItem = qobject_cast(item); // Works for qan::GroupItem* + if (nodeItem != nullptr) + emit nodeAboutToBeMoved(nodeItem->getNode()); item->setX(minLeft); + if (nodeItem != nullptr) + emit nodeMoved(nodeItem->getNode()); + } } void Graph::alignTop(std::vector&& items) @@ -1363,8 +1381,14 @@ void Graph::alignTop(std::vector&& items) qreal minTop = std::numeric_limits::max(); for (const auto item: items) minTop = std::min(minTop, item->y()); - for (auto item: items) + for (auto item: items) { + const auto nodeItem = qobject_cast(item); // Works for qan::GroupItem* + if (nodeItem != nullptr) + emit nodeAboutToBeMoved(nodeItem->getNode()); item->setY(minTop); + if (nodeItem != nullptr) + emit nodeMoved(nodeItem->getNode()); + } } void Graph::alignBottom(std::vector&& items) @@ -1374,8 +1398,14 @@ void Graph::alignBottom(std::vector&& items) qreal maxBottom = std::numeric_limits::min(); for (const auto item: items) maxBottom = std::max(maxBottom, item->y() + item->height()); - for (auto item: items) + for (auto item: items) { + const auto nodeItem = qobject_cast(item); // Works for qan::GroupItem* + if (nodeItem != nullptr) + emit nodeAboutToBeMoved(nodeItem->getNode()); item->setY(maxBottom - item->height()); + if (nodeItem != nullptr) + emit nodeMoved(nodeItem->getNode()); + } } //----------------------------------------------------------------------------- diff --git a/src/qanNode.cpp b/src/qanNode.cpp index f819a53b..14acefc6 100644 --- a/src/qanNode.cpp +++ b/src/qanNode.cpp @@ -165,7 +165,7 @@ void Node::installBehaviour(std::unique_ptr behaviour) //----------------------------------------------------------------------------- /* Appearance Management *///-------------------------------------------------- -bool Node::setLabel(const QString& label) noexcept +bool Node::setLabel(const QString& label) { if (label != _label) { _label = label; @@ -177,7 +177,18 @@ bool Node::setLabel(const QString& label) noexcept return false; } -bool Node::setLocked(bool locked) noexcept +bool Node::setIsProtected(bool isProtected) +{ + if (isProtected != _isProtected) { + _isProtected = isProtected; + emit isProtectedChanged(); + return true; + } + return false; +} + + +bool Node::setLocked(bool locked) { if (locked != _locked) { _locked = locked; diff --git a/src/qanNode.h b/src/qanNode.h index 27f7fa33..8ecf71e6 100644 --- a/src/qanNode.h +++ b/src/qanNode.h @@ -134,7 +134,7 @@ class Node : public gtpo::nodegetLocked()) { + (getNode()->getIsProtected() || + getNode()->getLocked())) { event->setAccepted(false); QQuickItem::dragEnterEvent(event); return; @@ -245,7 +246,8 @@ void NodeItem::dragEnterEvent(QDragEnterEvent* event) void NodeItem::dragMoveEvent(QDragMoveEvent* event) { if (getNode() != nullptr && - getNode()->getLocked()) { + (getNode()->getIsProtected() || + getNode()->getLocked())) { event->setAccepted(false); QQuickItem::dragMoveEvent(event); return; @@ -258,7 +260,8 @@ void NodeItem::dragMoveEvent(QDragMoveEvent* event) void NodeItem::dragLeaveEvent(QDragLeaveEvent* event) { if (getNode() != nullptr && - getNode()->getLocked()) { + (getNode()->getIsProtected() || + getNode()->getLocked())) { event->setAccepted(false); QQuickItem::dragLeaveEvent(event); return; @@ -286,7 +289,8 @@ void NodeItem::mouseDoubleClickEvent(QMouseEvent* event) void NodeItem::mouseMoveEvent(QMouseEvent* event) { if (getNode() != nullptr && - getNode()->getLocked()) { + (getNode()->getIsProtected() || + getNode()->getLocked())) { QQuickItem::mouseMoveEvent(event); return; } @@ -310,8 +314,8 @@ void NodeItem::mousePressEvent(QMouseEvent* event) event->button() == Qt::RightButton) && getNode() != nullptr && isSelectable() && - !getNode()->isGroup() && // Group selection is handled in qan::GroupItem::mousePressEvent() - !getNode()->getLocked()) { + !getNode()->isGroup() && // Group selection is handled in qan::GroupItem::mousePressEvent() + !getNode()->getLocked()) { // Selection allowed for protected if (_graph) _graph->selectNode(*getNode(), event->modifiers()); }