Enter paste-ghost mode: clipboard items follow the cursor, click to place.
(self)
| 321 | self._start_paste_ghost() |
| 322 | |
| 323 | def _start_paste_ghost(self) -> None: |
| 324 | """Enter paste-ghost mode: clipboard items follow the cursor, click to place.""" |
| 325 | from PySide6.QtWidgets import QGraphicsSimpleTextItem as _ST |
| 326 | if not self._clipboard: |
| 327 | return |
| 328 | self._end_wire(commit=False) |
| 329 | self._cancel_placement() # clears any prior ghost / mode |
| 330 | |
| 331 | # Reference point: first component's position (always on-grid). |
| 332 | # Using a component origin guarantees delta = snapped_cursor - ref |
| 333 | # is a multiple of GRID_SIZE, so all pasted components land on-grid. |
| 334 | ref = None |
| 335 | for data in self._clipboard: |
| 336 | if data['kind'] == 'component': |
| 337 | ref = QPointF(data['x'], data['y']) |
| 338 | break |
| 339 | if ref is None: |
| 340 | # Clipboard has no components; fall back to first item position. |
| 341 | for data in self._clipboard: |
| 342 | if 'x' in data and 'y' in data: |
| 343 | ref = QPointF(data['x'], data['y']) |
| 344 | break |
| 345 | if data['kind'] == 'wire' and data['points']: |
| 346 | x, y = data['points'][0] |
| 347 | ref = QPointF(x, y) |
| 348 | break |
| 349 | if ref is None: |
| 350 | return |
| 351 | self._paste_ref = ref |
| 352 | |
| 353 | for data in self._clipboard: |
| 354 | kind = data['kind'] |
| 355 | if kind == 'component': |
| 356 | ghost = make_ghost(data['svg_bytes']) |
| 357 | ghost.setTransform(QTransform().scale( |
| 358 | -1 if data['h_flip'] else 1, |
| 359 | -1 if data['v_flip'] else 1, |
| 360 | )) |
| 361 | ghost.setRotation(data['rotation']) |
| 362 | ghost.setPos(QPointF(data['x'], data['y'])) |
| 363 | self.addItem(ghost) |
| 364 | self._paste_ghost_items.append(('point', ghost, QPointF(data['x'], data['y']))) |
| 365 | elif kind == 'wire': |
| 366 | pts = [QPointF(x, y) for x, y in data['points']] |
| 367 | ghost = QGraphicsPathItem(_build_path(pts)) |
| 368 | ghost.setPen(QPen(Qt.black, 1.2)) |
| 369 | ghost.setOpacity(0.4) |
| 370 | ghost.setAcceptedMouseButtons(Qt.NoButton) |
| 371 | self.addItem(ghost) |
| 372 | self._paste_ghost_items.append(('wire', ghost, pts)) |
| 373 | elif kind in ('command', 'free_text'): |
| 374 | ghost = _ST(data['text'][:30]) |
| 375 | ghost.setOpacity(0.4) |
| 376 | ghost.setAcceptedMouseButtons(Qt.NoButton) |
| 377 | ghost.setPos(QPointF(data['x'], data['y'])) |
| 378 | self.addItem(ghost) |
| 379 | self._paste_ghost_items.append(('point', ghost, QPointF(data['x'], data['y']))) |
| 380 | elif kind == 'analysis': |
no test coverage detected