18#include "stroketool.h"
21#include "scribblearea.h"
22#include "viewmanager.h"
23#include "preferencemanager.h"
25#include "toolmanager.h"
28#include "canvascursorpainter.h"
32 void detectWhichOSX();
33 void disableCoalescing();
34 void enableCoalescing();
38 void detectWhichOSX() {}
39 void disableCoalescing() {}
40 void enableCoalescing() {}
44const qreal StrokeTool::FEATHER_MIN = 1.;
45const qreal StrokeTool::FEATHER_MAX = 99.;
46const qreal StrokeTool::WIDTH_MIN = 1.;
47const qreal StrokeTool::WIDTH_MAX = 200.;
50bool StrokeTool::msIsAdjusting =
false;
51bool StrokeTool::mQuickSizingEnabled =
false;
60 mQuickSizingEnabled = mEditor->preference()->isOn(SETTING::QUICK_SIZING);
61 mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR);
65 connect(mEditor->preference(), &PreferenceManager::optionChanged,
this, &StrokeTool::onPreferenceChanged);
70 mActiveConnections.
append(
connect(mEditor->view(), &ViewManager::viewChanged,
this, &StrokeTool::onViewUpdated));
79void StrokeTool::onPreferenceChanged(SETTING setting)
81 if (setting == SETTING::QUICK_SIZING) {
82 mQuickSizingEnabled = mEditor->preference()->isOn(setting);
83 }
else if (setting == SETTING::CANVAS_CURSOR) {
84 mCanvasCursorEnabled = mEditor->preference()->isOn(setting);
88void StrokeTool::onViewUpdated()
93QPointF StrokeTool::getCurrentPressPixel()
const
95 return mInterpolator.getCurrentPressPixel();
98QPointF StrokeTool::getCurrentPressPoint()
const
100 return mEditor->view()->mapScreenToCanvas(mInterpolator.getCurrentPressPixel());
103QPointF StrokeTool::getCurrentPixel()
const
105 return mInterpolator.getCurrentPixel();
108QPointF StrokeTool::getCurrentPoint()
const
110 return mEditor->view()->mapScreenToCanvas(getCurrentPixel());
113QPointF StrokeTool::getLastPixel()
const
115 return mInterpolator.getLastPixel();
118QPointF StrokeTool::getLastPoint()
const
120 return mEditor->view()->mapScreenToCanvas(getLastPixel());
123void StrokeTool::startStroke(PointerEvent::InputType inputType)
131 mLastPixel = getCurrentPixel();
133 mStrokePoints.
clear();
136 QPointF startStrokes = mInterpolator.interpolateStart(mLastPixel);
137 mStrokePoints << mEditor->view()->mapScreenToCanvas(startStrokes);
139 mStrokePressures.
clear();
140 mStrokePressures << mInterpolator.getPressure();
142 mCurrentInputType = inputType;
143 mUndoSaveState = mEditor->undoRedo()->
state(UndoRedoRecordType::KEYFRAME_MODIFY);
148bool StrokeTool::keyPressEvent(
QKeyEvent *event)
150 switch (
event->key()) {
152 if (mEditor->tools()->setTemporaryTool(EYEDROPPER, {},
Qt::AltModifier))
164 return BaseTool::keyPressEvent(
event);
172void StrokeTool::endStroke()
174 mInterpolator.interpolateEnd();
175 mStrokePressures << mInterpolator.getPressure();
176 mStrokePoints.
clear();
177 mStrokePressures.
clear();
181 mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame());
182 mScribbleArea->endStroke();
184 mEditor->undoRedo()->
record(mUndoSaveState, typeName());
187void StrokeTool::drawStroke()
189 QPointF pixel = getCurrentPixel();
190 if (pixel != mLastPixel || !mFirstDraw)
193 QPointF startStrokes = mInterpolator.interpolateStart(getLastPixel());
194 mStrokePoints << mEditor->view()->mapScreenToCanvas(startStrokes);
195 mStrokePressures << mInterpolator.getPressure();
205 if (
event->eventType() == PointerEvent::Press) {
206 if (mQuickSizingEnabled) {
207 return startAdjusting(
event->modifiers());
209 }
else if (
event->eventType() == PointerEvent::Move) {
211 adjustCursor(
event->modifiers());
214 }
else if (
event->eventType() == PointerEvent::Release) {
225 updateCanvasCursor();
230 updateCanvasCursor();
235 updateCanvasCursor();
240 mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR);
244bool StrokeTool::leaveEvent(
QEvent*)
251 mCanvasCursorEnabled =
false;
252 updateCanvasCursor();
256void StrokeTool::updateCanvasCursor()
258 const qreal brushWidth = properties.width;
259 const qreal brushFeather = properties.feather;
261 const QPointF& cursorPos = msIsAdjusting ? mAdjustPosition : getCurrentPoint();
262 const qreal cursorRad = brushWidth * 0.5;
263 const QPointF& cursorOffset =
QPointF(cursorPos.
x() - cursorRad, cursorPos.
y() - cursorRad);
266 options.widthRect =
QRectF(cursorOffset,
QSizeF(brushWidth, brushWidth));
268 const qreal featherWidthFactor = MathUtils::normalize(brushFeather, 0.0, FEATHER_MAX);
269 options.featherRect =
QRectF(options.widthRect.
center().
x() - (cursorRad * featherWidthFactor),
270 options.widthRect.
center().
y() - (cursorRad * featherWidthFactor),
271 brushWidth * featherWidthFactor,
272 brushWidth * featherWidthFactor);
273 options.showCursor = mCanvasCursorEnabled;
274 options.isAdjusting = msIsAdjusting && mQuickSizingEnabled;
275 options.useFeather = mPropertyEnabled[FEATHER];
277 mCanvasCursorPainter.preparePainter(options, mEditor->view()->getView());
279 const QRect& dirtyRect = mCanvasCursorPainter.dirtyRect();
280 const QRect& updateRect = mEditor->view()->getView().
mapRect(
QRectF(cursorOffset,
QSizeF(brushWidth, brushWidth))).toAlignedRect();
282 if (!msIsAdjusting && !mCanvasCursorEnabled) {
283 if (mCanvasCursorPainter.isDirty()) {
285 mScribbleArea->
update(mCanvasCursorPainter.dirtyRect().
adjusted(-2, -2, 2, 2));
286 mCanvasCursorPainter.clearDirty();
297 if (!mQuickSizingProperties.
contains(modifiers))
302 const QPointF& currentPressPoint = getCurrentPressPoint();
303 const QPointF& currentPoint = getCurrentPoint();
304 auto propertyType = mQuickSizingProperties.
value(modifiers);
305 switch (propertyType) {
307 const qreal factor = 0.5;
308 const qreal rad = properties.width * factor;
309 const qreal distance =
QLineF(currentPressPoint -
QPointF(rad, rad), currentPoint).
length();
310 mAdjustPosition = currentPressPoint -
QPointF(distance * factor, distance * factor);
314 const qreal factor = 0.5;
315 const qreal cursorRad = properties.width * factor;
316 const qreal featherWidthFactor = MathUtils::normalize(properties.feather, 0.0, FEATHER_MAX);
317 const qreal offset = (cursorRad * featherWidthFactor) * factor;
318 const qreal distance =
QLineF(currentPressPoint -
QPointF(offset, offset), currentPoint).
length();
319 mAdjustPosition = currentPressPoint -
QPointF(distance, distance);
324 qWarning() <<
"Unhandled quick sizing property for tool" << typeName();
328 msIsAdjusting =
true;
329 updateCanvasCursor();
333void StrokeTool::stopAdjusting()
335 msIsAdjusting =
false;
338 mEditor->tools()->setWidth(properties.width);
339 mEditor->tools()->setFeather(properties.feather);
341 updateCanvasCursor();
346 switch (mQuickSizingProperties.
value(modifiers))
351 const qreal newValue =
QLineF(mAdjustPosition, getCurrentPoint()).
length() * 2.0;
358 const qreal inputMin = 0.0;
359 const qreal inputMax = properties.width * 0.5;
360 const qreal distance =
QLineF(mAdjustPosition, getCurrentPoint()).
length();
361 const qreal outputMax = FEATHER_MAX;
362 const qreal outputMin = 0.0;
365 const qreal mappedValue = MathUtils::map(distance, inputMin, inputMax, outputMax, outputMin);
372 qWarning() <<
"Unhandled quick sizing property for tool" << typeName();
374 updateCanvasCursor();
377void StrokeTool::paint(
QPainter& painter,
const QRect& blitRect)
379 mCanvasCursorPainter.paint(painter, blitRect);
384 if (std::isnan(width) || width < 0)
389 properties.width = width;
390 emit mEditor->tools()->toolPropertyChanged(this->type(), WIDTH);
395 if (std::isnan(feather) || feather < 0)
400 properties.feather = feather;
401 emit mEditor->tools()->toolPropertyChanged(this->type(), FEATHER);
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
void record(const UndoSaveState *&undoState, const QString &description)
Records the given save state.
const UndoSaveState * state(UndoRedoRecordType recordType) const
Prepares and returns a save state with the given scope.
bool contains(const Key &key) const const
const T value(const Key &key) const const
qreal length() const const
void append(const T &value)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual bool event(QEvent *e)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
QRect united(const QRect &rectangle) const const
QPointF center() const const