Skip to content

Commit 9ead6ff

Browse files
Ctrl+Drag rubber band image creation for visual imageset
1 parent 5c88074 commit 9ead6ff

2 files changed

Lines changed: 74 additions & 12 deletions

File tree

src/editors/imageset/ImagesetVisualMode.cpp

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
#include "qevent.h"
1616
#include "qmenu.h"
1717
#include "qdom.h"
18+
#include <qrubberband.h>
1819

1920
constexpr qreal newImageHalfSize = 25.0;
2021

2122
ImagesetVisualMode::ImagesetVisualMode(MultiModeEditor& editor)
2223
: IEditMode(editor)
23-
, lastCursorPosition(newImageHalfSize, newImageHalfSize)
24+
, _lastCursorScenePos(newImageHalfSize, newImageHalfSize)
2425
{
2526
wheelZoomEnabled = true;
2627
middleButtonDragScrollEnabled = true;
@@ -30,7 +31,7 @@ ImagesetVisualMode::ImagesetVisualMode(MultiModeEditor& editor)
3031
setFocusPolicy(Qt::ClickFocus);
3132
setFrameStyle(QFrame::NoFrame);
3233

33-
//setRenderHint(QPainter::TextAntialiasing, true);
34+
setRenderHint(QPainter::TextAntialiasing, true);
3435

3536
auto&& settings = qobject_cast<Application*>(qApp)->getSettings();
3637
if (settings->getEntryValue("imageset/visual/partial_updates").toBool())
@@ -176,9 +177,7 @@ void ImagesetVisualMode::refreshSceneRect()
176177
scene()->setSceneRect(boundingRect);
177178
}
178179

179-
// Centre position is the position of the centre of the newly created image,
180-
// the newly created image will then 'encapsulate' the centrepoint
181-
void ImagesetVisualMode::createImageEntry(QPointF pos)
180+
void ImagesetVisualMode::createImageEntry(const QRectF& rect)
182181
{
183182
// Find an unique image name
184183
QString name = "NewImage";
@@ -191,10 +190,24 @@ void ImagesetVisualMode::createImageEntry(QPointF pos)
191190
name = QString("NewImage_%1").arg(index++);
192191
}
193192

194-
QPointF imgPos = pos - QPointF(newImageHalfSize, newImageHalfSize);
195-
QSizeF imgSize(2.0 * newImageHalfSize, 2.0 * newImageHalfSize);
196193
QPoint imgOffset(0, 0);
197-
_editor.getUndoStack()->push(new ImagesetCreateCommand(*this, name, imgPos, imgSize, imgOffset));
194+
_editor.getUndoStack()->push(new ImagesetCreateCommand(*this, name, rect.topLeft(), rect.size(), imgOffset));
195+
196+
// Select just created entry for convenience
197+
if (auto createdEntry = imagesetEntry->getImageEntry(name))
198+
{
199+
scene()->clearSelection();
200+
createdEntry->setSelected(true);
201+
}
202+
}
203+
204+
// Centre position is the position of the centre of the newly created image,
205+
// the newly created image will then 'encapsulate' the centrepoint
206+
void ImagesetVisualMode::createImageEntry(QPointF pos)
207+
{
208+
const QPointF imgPos = pos - QPointF(newImageHalfSize, newImageHalfSize);
209+
const QSizeF imgSize(2.0 * newImageHalfSize, 2.0 * newImageHalfSize);
210+
createImageEntry(QRectF(imgPos, imgSize));
198211
}
199212

200213
bool ImagesetVisualMode::moveImageEntries(const std::vector<ImageEntry*>& imageEntries, QPointF delta)
@@ -364,7 +377,7 @@ bool ImagesetVisualMode::paste()
364377

365378
_editor.getUndoStack()->push(new ImagesetPasteCommand(*this, std::move(undo)));
366379

367-
// Select just the pasted image definitions for convenience
380+
// Select just pasted image definitions for convenience
368381
scene()->clearSelection();
369382
for (const auto& name : newNames)
370383
imagesetEntry->getImageEntry(name)->setSelected(true);
@@ -459,14 +472,40 @@ void ImagesetVisualMode::slot_customContextMenu(QPoint point)
459472
contextMenu->exec(mapToGlobal(point));
460473
}
461474

475+
QPoint ImagesetVisualMode::roundToImagePixels(QPoint src) const
476+
{
477+
return mapFromScene(mapToScene(src).toPoint());
478+
}
479+
462480
void ImagesetVisualMode::mouseMoveEvent(QMouseEvent* event)
463481
{
464-
lastCursorPosition = mapToScene(event->pos());
482+
_lastCursorScenePos = mapToScene(event->pos());
483+
484+
if (_rubberBand && _rubberBand->isVisible())
485+
{
486+
_rubberBand->setGeometry(QRect(_mouseDownPos, roundToImagePixels(event->pos())).normalized());
487+
event->accept();
488+
return;
489+
}
490+
// rect = QRectF(QPointF(round(rect.topLeft().x()), round(rect.topLeft().y())),
491+
// QPointF(round(rect.bottomRight().x()), round(rect.bottomRight().y())));
492+
465493
ResizableGraphicsView::mouseMoveEvent(event);
466494
}
467495

468496
void ImagesetVisualMode::mousePressEvent(QMouseEvent* event)
469497
{
498+
// Ctrl+Drag to create new image rect with a rubber band
499+
if ((event->buttons() & Qt::LeftButton) && (event->modifiers() & Qt::ControlModifier))
500+
{
501+
_mouseDownPos = roundToImagePixels(event->pos());
502+
if (!_rubberBand) _rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
503+
_rubberBand->setGeometry(QRect(_mouseDownPos, QSize()));
504+
_rubberBand->show();
505+
event->accept();
506+
return;
507+
}
508+
470509
ResizableGraphicsView::mousePressEvent(event);
471510

472511
if (event->buttons() & Qt::LeftButton)
@@ -494,6 +533,15 @@ void ImagesetVisualMode::mousePressEvent(QMouseEvent* event)
494533
// AFAIK Qt doesn't give us any move finished notification so I do this manually
495534
void ImagesetVisualMode::mouseReleaseEvent(QMouseEvent* event)
496535
{
536+
if (_rubberBand && _rubberBand->isVisible())
537+
{
538+
_rubberBand->hide();
539+
if (_rubberBand->geometry().isValid())
540+
createImageEntry(mapToScene(_rubberBand->geometry()).boundingRect().toRect());
541+
event->accept();
542+
return;
543+
}
544+
497545
ResizableGraphicsView::mouseReleaseEvent(event);
498546

499547
std::vector<ImageEntry*> imageEntries;
@@ -603,6 +651,15 @@ void ImagesetVisualMode::keyReleaseEvent(QKeyEvent* event)
603651

604652
switch (event->key())
605653
{
654+
case Qt::Key_Control:
655+
{
656+
if (_rubberBand && _rubberBand->isVisible())
657+
{
658+
_rubberBand->hide();
659+
handled = true;
660+
}
661+
break;
662+
}
606663
case Qt::Key_Q:
607664
{
608665
handled = cycleOverlappingImages();

src/editors/imageset/ImagesetVisualMode.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class ImagesetEntry;
1111
class ImagesetEditorDockWidget;
1212
class QDomElement;
1313
class QMenu;
14+
class QRubberBand;
1415

1516
class ImagesetVisualMode : public ResizableGraphicsView, public IEditMode
1617
{
@@ -27,8 +28,9 @@ class ImagesetVisualMode : public ResizableGraphicsView, public IEditMode
2728

2829
void refreshSceneRect();
2930

31+
void createImageEntry(const QRectF& rect);
3032
void createImageEntry(QPointF pos);
31-
void createImageEntryAtCursor() { createImageEntry(lastCursorPosition); }
33+
void createImageEntryAtCursor() { createImageEntry(_lastCursorScenePos); }
3234
bool moveImageEntries(const std::vector<ImageEntry*>& imageEntries, QPointF delta);
3335
bool resizeImageEntries(const std::vector<ImageEntry*>& imageEntries, QPointF topLeftDelta, QPointF bottomRightDelta);
3436
bool deleteImageEntries(const std::vector<ImageEntry*>& imageEntries);
@@ -59,11 +61,14 @@ protected slots:
5961

6062
void createActiveStateConnections();
6163
QString getNewImageName(const QString& desiredName, QString copyPrefix = "", QString copySuffix = "_copy");
64+
QPoint roundToImagePixels(QPoint src) const;
6265

63-
QPointF lastCursorPosition;
6466
ImagesetEntry* imagesetEntry = nullptr;
6567
ImagesetEditorDockWidget* dockWidget = nullptr;
6668
QMenu* contextMenu = nullptr;
69+
QRubberBand* _rubberBand = nullptr;
70+
QPoint _mouseDownPos;
71+
QPointF _lastCursorScenePos;
6772

6873
QAction* editOffsetsAction = nullptr;
6974
QAction* cycleOverlappingAction = nullptr;

0 commit comments

Comments
 (0)