fix(editor): suppress spurious first-click element moves (#481)

When an item type is selected for the first time the properties dock
expands, causing the QGraphicsView viewport to shrink. Qt recalculates
scene coordinates and fires one or more synthetic mouseMoveEvents before
the user has actually moved the mouse.

The original code used a single-shot m_first_move flag in
CustomElementGraphicPart, which absorbed exactly one spurious event.
PartText and PartDynamicTextField had no protection at all.

Fix: compare screen-coordinate displacement against
QApplication::startDragDistance() (~4 px). Screen coordinates are
stable across viewport resizes, so the check correctly rejects
synthetic dock-expansion events while allowing genuine drags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Shane Ringrose
2026-06-21 12:09:25 +12:00
parent 1c764babd1
commit 2a115e4381
3 changed files with 33 additions and 24 deletions
@@ -20,6 +20,7 @@
#include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h"
#include "../elementscene.h" #include "../elementscene.h"
#include <QApplication>
#include <QRegularExpression> #include <QRegularExpression>
/** /**
@@ -35,6 +36,7 @@ CustomElementGraphicPart::CustomElementGraphicPart(QETElementEditor *editor,
QGraphicsObject (parent), QGraphicsObject (parent),
CustomElementPart(editor), CustomElementPart(editor),
m_hovered (false), m_hovered (false),
m_first_move (false),
_linestyle(NormalStyle), _linestyle(NormalStyle),
_lineweight(NormalWeight), _lineweight(NormalWeight),
_filling(NoneFilling), _filling(NoneFilling),
@@ -1332,27 +1334,25 @@ void CustomElementGraphicPart::mousePressEvent(QGraphicsSceneMouseEvent *event)
void CustomElementGraphicPart::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void CustomElementGraphicPart::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{ {
//m_first_move is used to avoid an unwanted behavior if (m_first_move) {
//when the properties dock widget is displayed : // Suppress spurious move events fired when the properties dock
//1 there is no selection // widget expands on first selection of a new item type, causing
//2 the dock widget width is set to minimum // the QGraphicsView to shrink and re-map coordinates. Screen
//3 select a part, the dock widget gain new widgets used to edit // coordinates are stable across viewport changes; scene coords
//the current selected part and the width of the dock grow // are not — so use screenPos() for the threshold check.
//so the width of the QGraphicsView is reduced and cause a mouse move event. const QPointF d = event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton);
//When this case occur the part is moved but they should not. This bool fix it. if (d.manhattanLength() < QApplication::startDragDistance())
if (Q_UNLIKELY(m_first_move)) {
m_first_move = false;
return; return;
m_first_move = false;
} }
if((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) if ((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) {
{
QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton)); QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton));
event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos)); event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos));
} } else {
else
QGraphicsObject::mouseMoveEvent(event); QGraphicsObject::mouseMoveEvent(event);
} }
}
void CustomElementGraphicPart::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void CustomElementGraphicPart::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
@@ -20,6 +20,7 @@
#include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h"
#include "../../qetapp.h" #include "../../qetapp.h"
#include "../elementscene.h" #include "../elementscene.h"
#include <QApplication>
#include <QColor> #include <QColor>
#include <QFont> #include <QFont>
@@ -496,12 +497,16 @@ bool PartDynamicTextField::keepVisualRotation() const {
*/ */
void PartDynamicTextField::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { void PartDynamicTextField::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if ((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) { if ((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) {
// Suppress spurious moves from the properties dock resizing the viewport.
const QPointF d = event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton);
if (d.manhattanLength() < QApplication::startDragDistance())
return;
QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton)); QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton));
event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos)); event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos));
} } else {
else
QGraphicsObject::mouseMoveEvent(event); QGraphicsObject::mouseMoveEvent(event);
} }
}
/** /**
@brief PartDynamicTextField::mousePressEvent @brief PartDynamicTextField::mousePressEvent
+6 -2
View File
@@ -18,6 +18,7 @@
#include "parttext.h" #include "parttext.h"
#include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h"
#include <QApplication>
#include "../../qetapp.h" #include "../../qetapp.h"
#include "../elementprimitivedecorator.h" #include "../elementprimitivedecorator.h"
#include "../elementscene.h" #include "../elementscene.h"
@@ -325,10 +326,13 @@ void PartText::setFont(const QFont &font) {
void PartText::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { void PartText::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if ((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) { if ((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable)) {
// Suppress spurious moves from the properties dock resizing the viewport.
const QPointF d = event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton);
if (d.manhattanLength() < QApplication::startDragDistance())
return;
QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton)); QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton));
event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos)); event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos));
} } else {
else {
QGraphicsObject::mouseMoveEvent(event); QGraphicsObject::mouseMoveEvent(event);
} }
} }