Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • tool
selecttool.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17#include "selecttool.h"
18#include <QSettings>
19#include "pointerevent.h"
20#include "vectorimage.h"
21#include "editor.h"
22#include "layervector.h"
23#include "scribblearea.h"
24#include "layermanager.h"
25#include "toolmanager.h"
26#include "selectionmanager.h"
27#include "undoredomanager.h"
28
29SelectTool::SelectTool(QObject* parent) : BaseTool(parent)
30{
31}
32
33void SelectTool::loadSettings()
34{
35 properties.width = -1;
36 properties.feather = -1;
37 properties.stabilizerLevel = -1;
38 properties.useAA = -1;
39 QSettings settings(PENCIL2D, PENCIL2D);
40 properties.showSelectionInfo = settings.value("ShowSelectionInfo").toBool();
41 mPropertyEnabled[SHOWSELECTIONINFO] = true;
42}
43
44void SelectTool::saveSettings()
45{
46 QSettings settings(PENCIL2D, PENCIL2D);
47
48 settings.setValue("ShowSelectionInfo", properties.showSelectionInfo);
49
50 settings.sync();
51}
52
53QCursor SelectTool::cursor()
54{
55 // Don't update cursor while we're moving the selection
56 if (mScribbleArea->isPointerInUse()) { return QCursor(mCursorPixmap); }
57
58 MoveMode mode = mEditor->select()->getMoveMode();
59
60 mCursorPixmap.fill(QColor(255, 255, 255, 0));
61 QPainter cursorPainter(&mCursorPixmap);
62 cursorPainter.setRenderHint(QPainter::Antialiasing);
63
64 switch(mode)
65 {
66 case MoveMode::TOPLEFT:
67 case MoveMode::BOTTOMRIGHT:
68 {
69 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-left.svg"));
70 break;
71 }
72 case MoveMode::TOPRIGHT:
73 case MoveMode::BOTTOMLEFT:
74 {
75 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-right.svg"));
76 break;
77 }
78 case MoveMode::MIDDLE:
79 {
80 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-move.svg"));
81 break;
82 }
83 case MoveMode::NONE:
84 cursorPainter.drawPixmap(QPoint(3,3), QPixmap(":icons/general/cross.png"));
85 break;
86 default:
87 Q_UNREACHABLE();
88 break;
89 }
90 return QCursor(mCursorPixmap);
91}
92
93void SelectTool::resetToDefault()
94{
95 setShowSelectionInfo(false);
96}
97
98void SelectTool::setShowSelectionInfo(const bool b)
99{
100 properties.showSelectionInfo = b;
101}
102
103void SelectTool::beginSelection(Layer* currentLayer, const QPointF& pos)
104{
105 auto selectMan = mEditor->select();
106
107 if (selectMan->somethingSelected() && mMoveMode != MoveMode::NONE) // there is something selected
108 {
109 if (currentLayer->type() == Layer::VECTOR)
110 {
111 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
112 if (vectorImage != nullptr) {
113 vectorImage->deselectAll();
114 }
115 }
116 mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect();
117 }
118 else
119 {
120 selectMan->setSelection(QRectF(pos.x(), pos.y(), 1, 1), mEditor->layers()->currentLayer()->type() == Layer::BITMAP);
121 mAnchorOriginPoint = pos;
122 }
123
124 mScribbleArea->updateFrame();
125}
126
127void SelectTool::pointerPressEvent(PointerEvent* event)
128{
129 Layer* currentLayer = mEditor->layers()->currentLayer();
130 if (currentLayer == nullptr) return;
131 if (!currentLayer->isPaintable()) { return; }
132 if (event->button() != Qt::LeftButton) { return; }
133 auto selectMan = mEditor->select();
134
135 mUndoState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
136
137 mPressPoint = event->canvasPos();
138 selectMan->setMoveModeForAnchorInRange(mPressPoint);
139 mMoveMode = selectMan->getMoveMode();
140 mStartMoveMode = mMoveMode;
141
142 beginSelection(currentLayer, mPressPoint);
143}
144
145void SelectTool::pointerMoveEvent(PointerEvent* event)
146{
147 Layer* currentLayer = mEditor->layers()->currentLayer();
148 if (currentLayer == nullptr) { return; }
149 if (!currentLayer->isPaintable()) { return; }
150 auto selectMan = mEditor->select();
151
152 if (!selectMan->somethingSelected()) { return; }
153
154 selectMan->setMoveModeForAnchorInRange(event->canvasPos());
155 mMoveMode = selectMan->getMoveMode();
156 mScribbleArea->updateToolCursor();
157
158 if (mScribbleArea->isPointerInUse())
159 {
160 controlOffsetOrigin(event->canvasPos(), mAnchorOriginPoint);
161
162 if (currentLayer->type() == Layer::VECTOR)
163 {
164 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
165 if (vectorImage != nullptr) {
166 vectorImage->select(selectMan->mapToSelection(QPolygonF(selectMan->mySelectionRect())).boundingRect());
167 }
168 }
169 }
170
171 mScribbleArea->updateFrame();
172}
173
174void SelectTool::pointerReleaseEvent(PointerEvent* event)
175{
176 Layer* currentLayer = mEditor->layers()->currentLayer();
177 if (currentLayer == nullptr) return;
178 if (event->button() != Qt::LeftButton) return;
179
180 // if there's a small very small distance between current and last point
181 // discard the selection...
182 // TODO: improve by adding a timer to check if the user is deliberately selecting
183 if (QLineF(mAnchorOriginPoint, event->canvasPos()).length() < 5.0)
184 {
185 mEditor->deselectAll();
186 }
187 if (maybeDeselect(event->canvasPos()))
188 {
189 mEditor->deselectAll();
190 }
191 else
192 {
193 keepSelection(currentLayer);
194 }
195
196 mEditor->undoRedo()->record(mUndoState, typeName());
197
198 mStartMoveMode = MoveMode::NONE;
199 mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect();
200
201 mScribbleArea->updateToolCursor();
202 mScribbleArea->updateFrame();
203}
204
205bool SelectTool::maybeDeselect(const QPointF& pos)
206{
207 return (!isSelectionPointValid(pos) && mEditor->select()->getMoveMode() == MoveMode::NONE);
208}
209
214void SelectTool::keepSelection(Layer* currentLayer)
215{
216 if (currentLayer->type() == Layer::VECTOR)
217 {
218 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
219 if (vectorImage == nullptr) { return; }
220 auto selectMan = mEditor->select();
221 selectMan->setSelection(vectorImage->getSelectionRect(), false);
222 }
223}
224
225void SelectTool::controlOffsetOrigin(QPointF currentPoint, QPointF anchorPoint)
226{
227 // when the selection is none, manage the selection Origin
228 if (mStartMoveMode != MoveMode::NONE) {
229 QRectF rect = mSelectionRect;
230
231 QPointF offset = offsetFromPressPos(currentPoint);
232 if (mStartMoveMode == MoveMode::TOPLEFT) {
233 rect.adjust(offset.x(), offset.y(), 0, 0);
234 } else if (mStartMoveMode == MoveMode::TOPRIGHT) {
235 rect.adjust(0, offset.y(), offset.x(), 0);
236 } else if (mStartMoveMode == MoveMode::BOTTOMRIGHT) {
237 rect.adjust(0, 0, offset.x(), offset.y());
238 } else if (mStartMoveMode == MoveMode::BOTTOMLEFT) {
239 rect.adjust(offset.x(), 0, 0, offset.y());
240 } else {
241 rect.translate(offset.x(), offset.y());
242 }
243
244 rect = rect.normalized();
245 if (rect.isValid()) {
246 mEditor->select()->setSelection(rect, true);
247 }
248 } else {
249 manageSelectionOrigin(currentPoint, anchorPoint);
250 }
251}
252
257void SelectTool::manageSelectionOrigin(QPointF currentPoint, QPointF originPoint)
258{
259 qreal mouseX = currentPoint.x();
260 qreal mouseY = currentPoint.y();
261
262 QRectF selectRect = mSelectionRect;
263
264 if (mouseX <= originPoint.x())
265 {
266 selectRect.setLeft(mouseX);
267 selectRect.setRight(originPoint.x());
268 }
269 else
270 {
271 selectRect.setLeft(originPoint.x());
272 selectRect.setRight(mouseX);
273 }
274
275 if (mouseY <= originPoint.y())
276 {
277 selectRect.setTop(mouseY);
278 selectRect.setBottom(originPoint.y());
279 }
280 else
281 {
282 selectRect.setTop(originPoint.y());
283 selectRect.setBottom(mouseY);
284 }
285
286 if (selectRect.width() <= 0) {
287 selectRect.setWidth(1);
288 }
289 if (selectRect.height() <= 0) {
290 selectRect.setHeight(1);
291 }
292
293 editor()->select()->setSelection(selectRect);
294}
295
296bool SelectTool::keyPressEvent(QKeyEvent* event)
297{
298 switch (event->key())
299 {
300 case Qt::Key_Alt:
301 if (mEditor->tools()->setTemporaryTool(MOVE, {}, Qt::AltModifier))
302 {
303 return true;
304 }
305 break;
306 default:
307 break;
308 }
309
310 // Follow the generic behavior anyway
311 return BaseTool::keyPressEvent(event);
312}
313
314QPointF SelectTool::offsetFromPressPos(const QPointF& pos)
315{
316 return pos - mPressPoint;
317}
BaseTool
Definition: basetool.h:70
Layer
Definition: layer.h:33
LayerVector
Definition: layervector.h:26
PointerEvent
Definition: pointerevent.h:8
ScribbleArea::updateFrame
void updateFrame()
Update frame.
Definition: scribblearea.cpp:199
SelectTool::keepSelection
void keepSelection(Layer *currentLayer)
SelectTool::keepSelection Keep selection rect and normalize if invalid.
Definition: selecttool.cpp:214
SelectTool::manageSelectionOrigin
void manageSelectionOrigin(QPointF currentPoint, QPointF originPoint)
SelectTool::manageSelectionOrigin switches anchor point when crossing threshold.
Definition: selecttool.cpp:257
UndoRedoManager::record
void record(const UndoSaveState *&undoState, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:95
UndoRedoManager::state
const UndoSaveState * state(UndoRedoRecordType recordType) const
Prepares and returns a save state with the given scope.
Definition: undoredomanager.cpp:199
VectorImage
Definition: vectorimage.h:32
VectorImage::deselectAll
void deselectAll()
VectorImage::deselectAll.
Definition: vectorimage.cpp:839
QColor
QCursor
QKeyEvent
QLineF
QLineF::length
qreal length() const const
QObject
QObject::event
virtual bool event(QEvent *e)
QPainter
QPainter::Antialiasing
Antialiasing
QPixmap
QPixmap::fill
void fill(const QColor &color)
QPoint
QPointF
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QPolygonF
QRectF
QRectF::adjust
void adjust(qreal dx1, qreal dy1, qreal dx2, qreal dy2)
QRectF::height
qreal height() const const
QRectF::isValid
bool isValid() const const
QRectF::normalized
QRectF normalized() const const
QRectF::setBottom
void setBottom(qreal y)
QRectF::setHeight
void setHeight(qreal height)
QRectF::setLeft
void setLeft(qreal x)
QRectF::setRight
void setRight(qreal x)
QRectF::setTop
void setTop(qreal y)
QRectF::setWidth
void setWidth(qreal width)
QRectF::translate
void translate(qreal dx, qreal dy)
QRectF::width
qreal width() const const
QSettings
Qt::Key_Alt
Key_Alt
Qt::AltModifier
AltModifier
Qt::LeftButton
LeftButton
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39