mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-06 03:04:13 +02:00
Fix: when pasting the picture or graphics object is nowunder the top left corner instead far away.
This commit is contained in:
@@ -41,7 +41,6 @@
|
||||
DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos) :
|
||||
DiagramEventInterface(diagram)
|
||||
{
|
||||
Q_UNUSED(start_pos); // items stay at their original XML position
|
||||
//DiagramEventInterface::init() is called by Diagram::setEventInterface
|
||||
//only when it is replacing an earlier interface, so call it here as
|
||||
//DiagramEventAddMacro does.
|
||||
@@ -77,21 +76,26 @@
|
||||
const QList<QGraphicsItem *> movable = m_content.items(MovableItems);
|
||||
if (movable.isEmpty()) return;
|
||||
|
||||
//Compute the top-left of all items' positions (not bounding
|
||||
//rects) and snap to grid: this is the point that gets placed
|
||||
//under the cursor, and the baseline moveTo() measures from.
|
||||
QPointF top_left;
|
||||
bool first = true;
|
||||
//Compute the top-left of all items' actual on-screen bounding
|
||||
//boxes (not their raw pos()) and snap to grid: this is the point
|
||||
//that gets placed under the cursor, and the baseline moveTo()
|
||||
//measures from. mapToScene(boundingRect()) matters here, not
|
||||
//pos() alone: pos() is the scene location of an item's local
|
||||
//origin, but for anything with a pivot-centered transform (a
|
||||
//scaled or rotated image, in particular) that origin can sit far
|
||||
//from where the item is actually drawn -- pivot + scale*(0 -
|
||||
//pivot) is nowhere near (0, 0) once scale is well under 1. Using
|
||||
//pos() here silently pasted content at the right *delta* from a
|
||||
//point that wasn't actually where the content visually was,
|
||||
//producing a constant, scale-dependent offset between the cursor
|
||||
//and the pasted picture. Diagram::fromXml()'s own position
|
||||
//parameter already gets this right the same way, for the same
|
||||
//reason.
|
||||
QRectF items_rect;
|
||||
for (auto *item : movable) {
|
||||
const QPointF p = item->pos();
|
||||
if (first) {
|
||||
top_left = p;
|
||||
first = false;
|
||||
} else {
|
||||
if (p.x() < top_left.x()) top_left.setX(p.x());
|
||||
if (p.y() < top_left.y()) top_left.setY(p.y());
|
||||
}
|
||||
items_rect = items_rect.united(item->mapToScene(item->boundingRect()).boundingRect());
|
||||
}
|
||||
const QPointF top_left = items_rect.topLeft();
|
||||
QSettings settings;
|
||||
const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
|
||||
Diagram::xGrid).toInt();
|
||||
@@ -102,9 +106,24 @@
|
||||
qRound(p.x() / xGrid) * xGrid,
|
||||
qRound(p.y() / yGrid) * yGrid);
|
||||
};
|
||||
const QPointF grid_origin = snapGrid(top_left);
|
||||
const QPointF grid_origin = snapGrid(start_pos);
|
||||
|
||||
//Store each item's original position. moveTo() applies a
|
||||
//Land the pasted content under the cursor immediately, rather than
|
||||
//leaving it at the copied source's own coordinates: fromXml() above
|
||||
//loads items at their original position purely because it doesn't
|
||||
//know the target yet, not because that is where a paste should end
|
||||
//up. The previous approach instead left items there and warped the
|
||||
//OS cursor to match -- QCursor::setPos() is silently ignored by
|
||||
//many window managers and compositors (Wayland in particular), so
|
||||
//on any of those the warp simply never happened and the paste was
|
||||
//left wherever it had originally been copied from, which could be
|
||||
//anywhere on the folio -- exactly the "far from the cursor" bug.
|
||||
const QPointF initial_delta = grid_origin - snapGrid(top_left);
|
||||
for (auto *item : movable) {
|
||||
item->setPos(item->pos() + initial_delta);
|
||||
}
|
||||
|
||||
//Store each item's now-placed position. moveTo() applies a
|
||||
//grid-snapped delta from the baseline to these, so items
|
||||
//preserve their layout and move in whole grid steps.
|
||||
for (auto *item : movable) {
|
||||
@@ -134,14 +153,6 @@
|
||||
if (const auto qde = QETApp::diagramEditorAncestorOf(view)) {
|
||||
m_status_bar = qde->statusBar();
|
||||
}
|
||||
//Warp the cursor to the group's grid-snapped origin so
|
||||
//the actual cursor position matches m_initial_cursor.
|
||||
//Without this the first mouseMoveEvent computes a large
|
||||
//delta (cursor is still at the Ctrl+V press location)
|
||||
//and the items jump on first touch.
|
||||
const QPoint view_pos = view->mapFromScene(m_initial_cursor);
|
||||
const QPoint global_pos = view->viewport()->mapToGlobal(view_pos);
|
||||
QCursor::setPos(global_pos);
|
||||
}
|
||||
}
|
||||
showHint();
|
||||
|
||||
@@ -584,6 +584,7 @@ void DiagramView::mousePressEvent(QMouseEvent *e)
|
||||
*/
|
||||
void DiagramView::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
m_last_mouse_pos = e->pos();
|
||||
setToolTip(tr("X: %1 Y: %2").arg(e->pos().x()).arg(e->pos().y()));
|
||||
if (m_event_interface && m_event_interface->mouseMoveEvent(e)) return;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ class DiagramView : public PaletteGraphicsView
|
||||
QAction *m_multi_paste = nullptr;
|
||||
QAction *m_create_template = nullptr;
|
||||
QPoint m_paste_here_pos;
|
||||
QPoint m_last_mouse_pos = QPoint(-1, -1);
|
||||
QPointF m_drag_last_pos;
|
||||
bool m_fresh_focus_in,
|
||||
m_first_activation = true;
|
||||
@@ -71,6 +72,12 @@ class DiagramView : public PaletteGraphicsView
|
||||
void editSelection();
|
||||
void setEventInterface (DVEventInterface *event_interface);
|
||||
QList<QAction *> contextMenuActions() const;
|
||||
/// Last mouse position seen by mouseMoveEvent(), in viewport
|
||||
/// coordinates -- (-1, -1) if the mouse hasn't moved over this
|
||||
/// view yet. Filled from ordinary Qt mouse events, not a global
|
||||
/// cursor query (QCursor::pos()/setPos() are silently ignored by
|
||||
/// several window managers and compositors, Wayland included).
|
||||
QPoint lastMousePos() const { return m_last_mouse_pos; }
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *) override;
|
||||
|
||||
@@ -367,10 +367,18 @@ void QETDiagramEditor::setUpActions()
|
||||
//original, where it was easy to miss entirely; now it appears
|
||||
//under the cursor and follows it until a click, Return, or Escape
|
||||
//to cancel -- the same interaction as placing a new element.
|
||||
const QPoint view_pos = dv->viewport()->mapFromGlobal(QCursor::pos());
|
||||
const QPointF start_pos = dv->viewport()->rect().contains(view_pos)
|
||||
? dv->mapToScene(view_pos)
|
||||
: dv->mapToScene(dv->viewport()->rect().center());
|
||||
//
|
||||
//dv->lastMousePos() (an ordinary Qt mouse-move position), not
|
||||
//QCursor::pos() (a global, OS-level cursor query): several
|
||||
//window managers and compositors -- Wayland in particular --
|
||||
//silently refuse that query, returning a stale or wrong
|
||||
//position, which is exactly what made the pasted content land
|
||||
//far from the cursor instead of under it.
|
||||
const QPoint last_pos = dv->lastMousePos();
|
||||
const QPoint view_pos = (last_pos.x() >= 0 && dv->viewport()->rect().contains(last_pos))
|
||||
? last_pos
|
||||
: dv->viewport()->rect().center();
|
||||
const QPointF start_pos = dv->mapToScene(view_pos);
|
||||
|
||||
dv->diagram()->setEventInterface(
|
||||
new DiagramEventAddPaste(dv->diagram(), start_pos));
|
||||
|
||||
Reference in New Issue
Block a user