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) : TransformTool(parent)
30{
31}
32
33void SelectTool::loadSettings()
34{
35 QSettings pencilSettings(PENCIL2D, PENCIL2D);
36
37 QHash<int, PropertyInfo> info;
38
39 mPropertyUsed[TransformToolProperties::SHOWSELECTIONINFO_ENABLED] = { Layer::BITMAP, Layer::VECTOR };
40
41 info[TransformToolProperties::SHOWSELECTIONINFO_ENABLED] = false;
42 toolProperties().insertProperties(info);
43 toolProperties().loadFrom(typeName(), pencilSettings);
44
45 if (toolProperties().requireMigration(pencilSettings, ToolProperties::VERSION_1)) {
46 toolProperties().setBaseValue(TransformToolProperties::SHOWSELECTIONINFO_ENABLED, pencilSettings.value("ShowSelectionInfo", false).toBool());
47 }
48}
49
50QCursor SelectTool::cursor()
51{
52 // Don't update cursor while we're moving the selection
53 if (mScribbleArea->isPointerInUse()) { return QCursor(mCursorPixmap); }
54
55 MoveMode mode = mEditor->select()->getMoveMode();
56
57 mCursorPixmap.fill(QColor(255, 255, 255, 0));
58 QPainter cursorPainter(&mCursorPixmap);
59 cursorPainter.setRenderHint(QPainter::Antialiasing);
60
61 switch(mode)
62 {
63 case MoveMode::TOPLEFT:
64 case MoveMode::BOTTOMRIGHT:
65 {
66 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-left.svg"));
67 break;
68 }
69 case MoveMode::TOPRIGHT:
70 case MoveMode::BOTTOMLEFT:
71 {
72 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-right.svg"));
73 break;
74 }
75 case MoveMode::MIDDLE:
76 {
77 cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-move.svg"));
78 break;
79 }
80 case MoveMode::NONE:
81 cursorPainter.drawPixmap(QPoint(3,3), QPixmap(":icons/general/cross.png"));
82 break;
83 default:
84 Q_UNREACHABLE();
85 break;
86 }
87 return QCursor(mCursorPixmap);
88}
89
90void SelectTool::beginSelection(Layer* currentLayer, const QPointF& pos)
91{
92 auto selectMan = mEditor->select();
93
94 if (selectMan->somethingSelected() && mMoveMode != MoveMode::NONE) // there is something selected
95 {
96 if (currentLayer->type() == Layer::VECTOR)
97 {
98 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
99 if (vectorImage != nullptr) {
100 vectorImage->deselectAll();
101 }
102 }
103 mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect();
104 }
105 else
106 {
107 selectMan->setSelection(QRectF(pos.x(), pos.y(), 0, 0), mEditor->layers()->currentLayer()->type() == Layer::BITMAP);
108 mAnchorOriginPoint = pos;
109 }
110
111 mScribbleArea->updateFrame();
112}
113
114void SelectTool::pointerPressEvent(PointerEvent* event)
115{
116 Layer* currentLayer = mEditor->layers()->currentLayer();
117 if (currentLayer == nullptr) return;
118 if (!currentLayer->isPaintable()) { return; }
119 if (event->button() != Qt::LeftButton) { return; }
120 auto selectMan = mEditor->select();
121
122 mUndoState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY);
123
124 mPressPoint = event->canvasPos();
125
126 if (currentLayer->type() == Layer::BITMAP) {
127 mPressPoint = mPressPoint.toPoint();
128 }
129
130 selectMan->setMoveModeForAnchorInRange(mPressPoint);
131 mMoveMode = selectMan->getMoveMode();
132 mStartMoveMode = mMoveMode;
133
134 beginSelection(currentLayer, mPressPoint);
135}
136
137void SelectTool::pointerMoveEvent(PointerEvent* event)
138{
139 Layer* currentLayer = mEditor->layers()->currentLayer();
140 if (currentLayer == nullptr) { return; }
141 if (!currentLayer->isPaintable()) { return; }
142 auto selectMan = mEditor->select();
143
144 QPointF canvasPos = event->canvasPos();
145
146 if (currentLayer->type() == Layer::BITMAP) {
147 canvasPos = canvasPos.toPoint();
148 }
149
150 selectMan->setMoveModeForAnchorInRange(canvasPos);
151 mMoveMode = selectMan->getMoveMode();
152 mScribbleArea->updateToolCursor();
153
154 if (mScribbleArea->isPointerInUse())
155 {
156 controlOffsetOrigin(canvasPos, mAnchorOriginPoint, currentLayer->type());
157
158 if (currentLayer->type() == Layer::VECTOR)
159 {
160 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
161 if (vectorImage != nullptr) {
162 vectorImage->select(selectMan->mapToSelection(QPolygonF(selectMan->mySelectionRect())).boundingRect());
163 }
164 }
165 }
166
167 mScribbleArea->updateFrame();
168}
169
170void SelectTool::pointerReleaseEvent(PointerEvent* event)
171{
172 Layer* currentLayer = mEditor->layers()->currentLayer();
173 if (currentLayer == nullptr) return;
174 if (event->button() != Qt::LeftButton) return;
175
176 QPointF canvasPos = event->canvasPos();
177 if (currentLayer->type() == Layer::BITMAP) {
178 canvasPos = canvasPos.toPoint();
179 }
180
181 // if there's a small very small distance between current and last point
182 // discard the selection...
183 // TODO: improve by adding a timer to check if the user is deliberately selecting
184 if (QLineF(mAnchorOriginPoint, canvasPos).length() < 1.0)
185 {
186 mEditor->deselectAll();
187 }
188 else if (maybeDeselect(canvasPos))
189 {
190 mEditor->deselectAll();
191 }
192 else
193 {
194 mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect();
195 keepSelection(currentLayer);
196 }
197
198 mEditor->undoRedo()->record(mUndoState, typeName());
199
200 mStartMoveMode = MoveMode::NONE;
201 mMoveMode = MoveMode::NONE;
202
203 mScribbleArea->updateToolCursor();
204 mScribbleArea->updateFrame();
205}
206
207bool SelectTool::maybeDeselect(const QPointF& pos)
208{
209 return ((!isSelectionPointValid(pos) && mEditor->select()->getMoveMode() == MoveMode::NONE)
210 || !mEditor->select()->mySelectionRect().isValid());
211}
212
217void SelectTool::keepSelection(Layer* currentLayer)
218{
219 if (currentLayer->type() == Layer::VECTOR)
220 {
221 VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
222 if (vectorImage == nullptr) { return; }
223 auto selectMan = mEditor->select();
224 selectMan->setSelection(vectorImage->getSelectionRect(), false);
225 }
226}
227
228void SelectTool::controlOffsetOrigin(QPointF currentPoint, QPointF anchorPoint, Layer::LAYER_TYPE layerType)
229{
230 QRectF newSelection;
231 if (mStartMoveMode == MoveMode::NONE) {
232 // When the selection is none, manage the selection Origin
233 newSelection = QRectF(currentPoint, anchorPoint);
234 } else {
235 newSelection = mSelectionRect;
236
237 QPointF offset = offsetFromPressPos(currentPoint);
238 if (mStartMoveMode == MoveMode::TOPLEFT) {
239 newSelection.adjust(offset.x(), offset.y(), 0, 0);
240 } else if (mStartMoveMode == MoveMode::TOPRIGHT) {
241 newSelection.adjust(0, offset.y(), offset.x(), 0);
242 } else if (mStartMoveMode == MoveMode::BOTTOMRIGHT) {
243 newSelection.adjust(0, 0, offset.x(), offset.y());
244 } else if (mStartMoveMode == MoveMode::BOTTOMLEFT) {
245 newSelection.adjust(offset.x(), 0, 0, offset.y());
246 } else {
247 newSelection.translate(offset.x(), offset.y());
248 }
249 }
250 newSelection = newSelection.normalized();
251 mEditor->select()->setSelection(newSelection, layerType == Layer::BITMAP);
252}
253
254bool SelectTool::keyPressEvent(QKeyEvent* event)
255{
256 switch (event->key())
257 {
258 case Qt::Key_Alt:
259 if (mEditor->tools()->setTemporaryTool(MOVE, {}, Qt::AltModifier))
260 {
261 return true;
262 }
263 break;
264 default:
265 break;
266 }
267
268 // Follow the generic behavior anyway
269 return TransformTool::keyPressEvent(event);
270}
271
272QPointF SelectTool::offsetFromPressPos(const QPointF& pos)
273{
274 return pos - mPressPoint;
275}
Layer
Definition: layer.h:33
LayerVector
Definition: layervector.h:26
PointerEvent
Definition: pointerevent.h:8
ScribbleArea::updateFrame
void updateFrame()
Update frame.
Definition: scribblearea.cpp:200
SelectTool::keepSelection
void keepSelection(Layer *currentLayer)
SelectTool::keepSelection Keep selection rect and normalize if invalid.
Definition: selecttool.cpp:217
SelectionManager::mapToSelection
QPointF mapToSelection(const QPointF &point) const
Maps a point from Canvas/Layer space INTO the Selection's transformed space.
Definition: selectionmanager.h:130
SelectionManager::setSelection
void setSelection(QRectF rect, bool roundPixels=false)
Defines the selection area.
Definition: selectionmanager.cpp:273
TransformTool
Definition: transformtool.h:23
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
QHash
QKeyEvent
QLineF
QObject
QObject::event
virtual bool event(QEvent *e)
QPainter
QPainter::Antialiasing
Antialiasing
QPixmap
QPixmap::fill
void fill(const QColor &color)
QPoint
QPointF
QPointF::toPoint
QPoint toPoint() const const
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::isValid
bool isValid() const const
QRectF::normalized
QRectF normalized() const const
QRectF::translate
void translate(qreal dx, qreal dy)
QSettings
QSettings::value
QVariant value(const QString &key, const QVariant &defaultValue) const const
Qt::Key_Alt
Key_Alt
Qt::AltModifier
AltModifier
Qt::LeftButton
LeftButton
QVariant::toBool
bool toBool() const const
Generated on Tue Jan 6 2026 11:38:51 for Pencil2D by doxygen 1.9.6 based on revision f91a96748ec6712509b9b0ff47979db9c34d556f