diff --git a/sources/diagram.h b/sources/diagram.h index 7a11f6d07..e0587a96a 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -118,6 +118,7 @@ class Diagram : public QGraphicsScene ElementTextsMover m_element_texts_mover; QGIManager *qgi_manager_; QETProject *m_project; + QPointer m_keyboard_item; QDomDocument xml_document_; @@ -274,6 +275,11 @@ class Diagram : public QGraphicsScene QUndoStack &undoStack(); QGIManager &qgiManager(); + /// The item that gets keys before the view's own shortcuts, while + /// it is in a state Escape must end (a picture whose handle is + /// dragged). nullptr the rest of the time. + void setKeyboardItem(QGraphicsObject *item) { m_keyboard_item = item; } + QGraphicsObject *keyboardItem() const { return m_keyboard_item; } //methods related to element label Update Policy void freezeElements(bool freeze); diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index fe586a03b..092cd9ea5 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -1155,6 +1155,17 @@ void DiagramView::keyPressEvent(QKeyEvent *e) if (m_event_interface && m_event_interface->keyPressEvent(e)) return; + //An item in a state Escape must end -- a picture while one of its + //handles is dragged -- sees each key before the shortcuts below, + //which would otherwise clear the selection under it. + QGraphicsObject *keyboard_item = m_diagram ? m_diagram->keyboardItem() : nullptr; + if (keyboard_item && keyboard_item->scene() == m_diagram) { + e->ignore(); + m_diagram->sendEvent(keyboard_item, e); + if (e->isAccepted()) + return; + } + ProjectView *current_project = this->diagramEditor()->currentProjectView(); DiagramContent dc(m_diagram); switch(e -> key()) diff --git a/sources/qetgraphicsitem/diagramimageitem.cpp b/sources/qetgraphicsitem/diagramimageitem.cpp index e32464642..ed81b4619 100644 --- a/sources/qetgraphicsitem/diagramimageitem.cpp +++ b/sources/qetgraphicsitem/diagramimageitem.cpp @@ -33,6 +33,7 @@ #include "../utils/qetutils.h" #include "../QetGraphicsItemModeler/qetgraphicshandleritem.h" +#include #include #include #include @@ -599,6 +600,10 @@ void DiagramImageItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) */ void DiagramImageItem::clearHandles() { + // The handles go (deselected, or another handle mode): a drag of one + // of them cannot end with its release any more. + if (m_vector_index != -1) + endHandleDrag(); if (!m_handler_vector.isEmpty()) { qDeleteAll(m_handler_vector); @@ -726,17 +731,22 @@ bool DiagramImageItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event) if (event->type() == QEvent::GraphicsSceneMousePress) { + m_drag_cancelled = false; handlerMousePressEvent(index, static_cast(event)->modifiers()); return true; } if (event->type() == QEvent::GraphicsSceneMouseMove) { - handlerMouseMoveEvent(index, static_cast(event)); + if (!m_drag_cancelled) + handlerMouseMoveEvent(index, static_cast(event)); return true; } if (event->type() == QEvent::GraphicsSceneMouseRelease) { - handlerMouseReleaseEvent(index); + if (m_drag_cancelled) + m_drag_cancelled = false; + else + handlerMouseReleaseEvent(index); return true; } if (event->type() == QEvent::GraphicsSceneHoverEnter) @@ -806,6 +816,12 @@ void DiagramImageItem::handlerMousePressEvent(int index, Qt::KeyboardModifiers m m_original_pos = pos(); m_original_transform = m_transform; m_original_pivotIsCustom = m_pivotIsCustom; + // The handle has the mouse, not the keyboard: ask the view for the + // keys for the length of the drag, so that Escape reaches + // keyPressEvent() and not the view's own Escape, which clears the + // selection. + if (diagram()) + diagram()->setKeyboardItem(this); if (m_handleRoles.at(index) == HandleRole::Resize) { @@ -959,7 +975,60 @@ void DiagramImageItem::handlerMouseReleaseEvent(int index) } } + endHandleDrag(); +} + +/** + @brief DiagramImageItem::keyPressEvent + The view sends keys here only while one of the picture's handles is + dragged (see handlerMousePressEvent()): Escape cancels that drag. Any + other key is left to the view. +*/ +void DiagramImageItem::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape && m_vector_index != -1) + { + cancelHandleDrag(); + event->accept(); + return; + } + QetGraphicsItem::keyPressEvent(event); +} + +/** + @brief DiagramImageItem::endHandleDrag + The drag is over: no handle is dragged any more, and the keyboard + goes back to the view. +*/ +void DiagramImageItem::endHandleDrag() +{ m_vector_index = -1; + if (diagram() && diagram()->keyboardItem() == this) + diagram()->setKeyboardItem(nullptr); +} + +/** + @brief DiagramImageItem::cancelHandleDrag + Put the picture back exactly as it was when the handle was pressed -- + transform, pivot (Resize moves it for the drag) and position -- and + ignore the rest of the mouse gesture, so its release pushes no undo + command. +*/ +void DiagramImageItem::cancelHandleDrag() +{ + prepareGeometryChange(); + m_deferHandleReposition = true; + m_transform = m_original_transform; + m_pivotIsCustom = m_original_pivotIsCustom; // a pivot drag marks it as hand-placed + // Not the snapping setPos(): see handlerMousePressEvent(). + QGraphicsObject::setPos(m_original_pos); + setTransform(m_transform.toMatrix()); + m_deferHandleReposition = false; + emit transformChanged(); + repositionHandles(); + endHandleDrag(); + m_drag_cancelled = true; + clearStatusHint(); } /** diff --git a/sources/qetgraphicsitem/diagramimageitem.h b/sources/qetgraphicsitem/diagramimageitem.h index 820c9fd5c..61ede8ccb 100644 --- a/sources/qetgraphicsitem/diagramimageitem.h +++ b/sources/qetgraphicsitem/diagramimageitem.h @@ -174,6 +174,7 @@ class DiagramImageItem : public QetGraphicsItem { void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; private: @@ -203,6 +204,8 @@ class DiagramImageItem : public QetGraphicsItem { void handlerMousePressEvent(int index, Qt::KeyboardModifiers mods); void handlerMouseMoveEvent(int index, QGraphicsSceneMouseEvent *event); void handlerMouseReleaseEvent(int index); + void cancelHandleDrag(); + void endHandleDrag(); void dragResize(int index, const QPointF &localPos, Qt::KeyboardModifiers mods); void dragRotateHandle(int cornerIndex, const QPointF &scenePos, Qt::KeyboardModifiers mods); void dragSkewHandle(int edgeIndex, const QPointF &scenePos, Qt::KeyboardModifiers mods); @@ -264,6 +267,7 @@ class DiagramImageItem : public QetGraphicsItem { QVector m_handleRoles; int m_vector_index = -1; QPointF m_original_pos; // scene position at the start of a resize/rotate/pivot drag, for Escape-to-cancel + bool m_drag_cancelled = false; // Escape pressed: ignore the rest of this mouse gesture ShapeTransform m_original_transform; bool m_original_pivotIsCustom = false; bool m_deferHandleReposition = false; // see setPivot()'s comment