mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-03 01:04:13 +02:00
Ctrl+Shift+V pastes at the origin point, Ctrl+V keeps pasting under the cursor
Ctrl+V moves the pasted group under the pointer as before. The new
Ctrl+Shift+V ("Coller au point d'origine", diagrameditor.paste_origin)
leaves the group exactly where it was copied from and warps the OS
cursor to the group's grid-snapped origin, so the paste baseline and
the physical cursor agree and the group starts following the mouse from
its original place instead of jumping to the pointer.
The cursor warp is only trusted as the baseline when it verifiably
lands (QCursor::pos() agrees with the target): QCursor::setPos() needs
pointer focus and is a silent no-op on compositors without
wp_pointer_warp_v1. Otherwise m_baseline_captured stays false and
moveTo() re-baselines on the first real mouse move, so a refused warp
costs at most the first movement rather than flinging the group across
the folio. An origin outside the viewport is scrolled into view first,
since the compositor rejects warp targets outside the window.
This commit is contained in:
@@ -28,7 +28,9 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QCursor>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsView>
|
||||
#include <QKeyEvent>
|
||||
#include <QStatusBar>
|
||||
|
||||
@@ -36,11 +38,18 @@
|
||||
@brief DiagramEventAddPaste::DiagramEventAddPaste
|
||||
@param diagram : diagram to paste into
|
||||
@param start_pos : where the pasted items first appear, in scene
|
||||
coordinates -- normally the cursor
|
||||
coordinates -- normally the cursor. Only used for UnderCursor; an
|
||||
AtOrigin paste stays at the coordinates it was copied from.
|
||||
@param placement : UnderCursor moves the group under the cursor
|
||||
(default), AtOrigin leaves it at its original XML position and warps
|
||||
the OS cursor to the group's origin so baseline and screen match.
|
||||
*/
|
||||
DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos) :
|
||||
DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos,
|
||||
PastePlacement placement) :
|
||||
DiagramEventInterface(diagram)
|
||||
{
|
||||
m_placement = placement;
|
||||
|
||||
//DiagramEventInterface::init() is called by Diagram::setEventInterface
|
||||
//only when it is replacing an earlier interface, so call it here as
|
||||
//DiagramEventAddMacro does.
|
||||
@@ -106,21 +115,31 @@
|
||||
qRound(p.x() / xGrid) * xGrid,
|
||||
qRound(p.y() / yGrid) * yGrid);
|
||||
};
|
||||
const QPointF grid_origin = snapGrid(start_pos);
|
||||
const QPointF grid_origin = (m_placement == AtOrigin)
|
||||
? snapGrid(top_left)
|
||||
: snapGrid(start_pos);
|
||||
|
||||
//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);
|
||||
if (m_placement == UnderCursor) {
|
||||
//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);
|
||||
}
|
||||
} else {
|
||||
//AtOrigin: the items keep the coordinates they were copied from,
|
||||
//so the copy stands exactly where the original stands. No delta is
|
||||
//applied; what has to move is the cursor -- it is warped to the
|
||||
//group's grid-snapped origin below, once m_group_origin is set,
|
||||
//so the baseline and the physical cursor position agree.
|
||||
}
|
||||
|
||||
//Store each item's now-placed position. moveTo() applies a
|
||||
@@ -138,10 +157,41 @@
|
||||
conductor->updatePath();
|
||||
}
|
||||
|
||||
//The baseline is the group's grid-snapped origin, so moveTo()
|
||||
//does not have to capture one from the first mouse movement.
|
||||
m_initial_cursor = m_group_origin;
|
||||
m_baseline_captured = true;
|
||||
//The baseline is the group's grid-snapped origin. For UnderCursor
|
||||
//the group was just put there, so the baseline is known outright.
|
||||
//For AtOrigin the group stayed where it was copied from and the
|
||||
//OS cursor is warped to match instead -- but only a warp that
|
||||
//verifiably landed may be trusted as the baseline: QCursor::setPos()
|
||||
//needs pointer focus inside the target window (on Wayland it goes
|
||||
//through wp_pointer_warp_v1, and is a silent no-op without it), and
|
||||
//a baseline that assumes a warp which did not happen would move the
|
||||
//whole group by the warp-sized delta on the first mouse move. So
|
||||
//when the warp cannot be confirmed, leave m_baseline_captured
|
||||
//false: moveTo() re-baselines from the first real mouse position
|
||||
//instead, which costs at most the first movement but never flings
|
||||
//the group across the folio.
|
||||
if (m_placement == AtOrigin && !m_diagram->views().isEmpty()) {
|
||||
if (auto *view = m_diagram->views().at(0)) {
|
||||
//An origin outside the visible area cannot be warped to
|
||||
//(the compositor rejects targets outside the window) and
|
||||
//would leave the paste invisible, so scroll it into view
|
||||
//first.
|
||||
if (!view->viewport()->rect().contains(view->mapFromScene(m_group_origin))) {
|
||||
view->ensureVisible(items_rect, 50, 50);
|
||||
}
|
||||
const QPoint view_pos = view->mapFromScene(m_group_origin);
|
||||
const QPoint global_pos = view->viewport()->mapToGlobal(view_pos);
|
||||
QCursor::setPos(global_pos);
|
||||
if ((QCursor::pos() - global_pos).manhattanLength() <= 4) {
|
||||
m_initial_cursor = m_group_origin;
|
||||
m_baseline_captured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_placement == UnderCursor) {
|
||||
m_initial_cursor = m_group_origin;
|
||||
m_baseline_captured = true;
|
||||
}
|
||||
|
||||
m_diagram->clearSelection();
|
||||
for (auto *item : movable) {
|
||||
|
||||
Reference in New Issue
Block a user