Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • app
  • src
timelinecells.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
18#include "timelinecells.h"
19
20#include <QApplication>
21#include <QResizeEvent>
22#include <QInputDialog>
23#include <QPainter>
24#include <QRegularExpression>
25#include <QSettings>
26#include <QDebug>
27
28#include "camerapropertiesdialog.h"
29#include "editor.h"
30#include "keyframe.h"
31#include "layermanager.h"
32#include "viewmanager.h"
33#include "object.h"
34#include "playbackmanager.h"
35#include "preferencemanager.h"
36#include "undoredomanager.h"
37#include "timeline.h"
38
39#include "cameracontextmenu.h"
40
41TimeLineCells::TimeLineCells(TimeLine* parent, Editor* editor, TIMELINE_CELL_TYPE type) : QWidget(parent)
42{
43 mTimeLine = parent;
44 mEditor = editor;
45 mPrefs = editor->preference();
46 mType = type;
47
48 mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
49 mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
50 mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
51 mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
52 mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
53
54 setMinimumSize(500, 4 * mLayerHeight);
55 setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
56 setAttribute(Qt::WA_OpaquePaintEvent, false);
57 setMouseTracking(true);
58
59 connect(mPrefs, &PreferenceManager::optionChanged, this, &TimeLineCells::loadSetting);
60}
61
62TimeLineCells::~TimeLineCells()
63{
64 delete mCache;
65}
66
67void TimeLineCells::loadSetting(SETTING setting)
68{
69 switch (setting)
70 {
71 case SETTING::TIMELINE_SIZE:
72 mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
73 mTimeLine->updateLength();
74 break;
75 case SETTING::LABEL_FONT_SIZE:
76 mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
77 break;
78 case SETTING::FRAME_SIZE:
79 mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
80 mTimeLine->updateLength();
81 break;
82 case SETTING::SHORT_SCRUB:
83 mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
84 break;
85 case SETTING::DRAW_LABEL:
86 mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
87 break;
88 default:
89 break;
90 }
91
92 updateContent();
93}
94
95int TimeLineCells::getFrameNumber(int x) const
96{
97 return mFrameOffset + 1 + (x - mOffsetX) / mFrameSize;
98}
99
100int TimeLineCells::getFrameX(int frameNumber) const
101{
102 return mOffsetX + (frameNumber - mFrameOffset) * mFrameSize;
103}
104
105void TimeLineCells::setFrameSize(int size)
106{
107 mFrameSize = size;
108 mPrefs->set(SETTING::FRAME_SIZE, mFrameSize);
109 updateContent();
110}
111
112int TimeLineCells::getLayerNumber(int y) const
113{
114 int layerNumber = mLayerOffset + (y - mOffsetY) / mLayerHeight;
115
116 int totalLayerCount = mEditor->object()->getLayerCount();
117
118 // Layers numbers are displayed in descending order
119 // The last row is layer 0
120 if (layerNumber <= totalLayerCount)
121 layerNumber = (totalLayerCount - 1) - layerNumber;
122 else
123 layerNumber = 0;
124
125 if (y < mOffsetY)
126 {
127 layerNumber = -1;
128 }
129
130 if (layerNumber >= totalLayerCount)
131 {
132 layerNumber = totalLayerCount;
133 }
134
135 //If the mouse release event if fired with mouse off the frame of the application
136 // mEditor->object()->getLayerCount() doesn't return the correct value.
137 if (layerNumber < -1)
138 {
139 layerNumber = -1;
140 }
141 return layerNumber;
142}
143
144int TimeLineCells::getInbetweenLayerNumber(int y) const {
145 int layerNumber = getLayerNumber(y);
146 // Round the layer number towards the drag start
147 if(layerNumber != mFromLayer) {
148 if(mMouseMoveY > 0 && y < getLayerY(layerNumber) + mLayerHeight / 2) {
149 layerNumber++;
150 }
151 else if(mMouseMoveY < 0 && y > getLayerY(layerNumber) + mLayerHeight / 2) {
152 layerNumber--;
153 }
154 }
155 return layerNumber;
156}
157
158int TimeLineCells::getLayerY(int layerNumber) const
159{
160 return mOffsetY + (mEditor->object()->getLayerCount() - 1 - layerNumber - mLayerOffset)*mLayerHeight;
161}
162
163void TimeLineCells::updateFrame(int frameNumber)
164{
165 int x = getFrameX(frameNumber);
166 update(x - mFrameSize, 0, mFrameSize + 1, height());
167}
168
169void TimeLineCells::updateContent()
170{
171 mRedrawContent = true;
172 update();
173}
174
175bool TimeLineCells::didDetachLayer() const {
176 return abs(mMouseMoveY) > mLayerDetachThreshold;
177}
178
179void TimeLineCells::showCameraMenu(QPoint pos)
180{
181 int frameNumber = getFrameNumber(pos.x());
182
183 const Layer* curLayer = mEditor->layers()->currentLayer();
184 Q_ASSERT(curLayer);
185
186 // only show menu if on camera layer and key exists
187 if (curLayer->type() != Layer::CAMERA || !curLayer->keyExists(frameNumber))
188 {
189 return;
190 }
191
192 mHighlightFrameEnabled = true;
193 mHighlightedFrame = frameNumber;
194
195 CameraContextMenu menu(frameNumber, static_cast<const LayerCamera*>(curLayer));
196
197 menu.connect(&menu, &CameraContextMenu::aboutToHide, this, [=] {
198 mHighlightFrameEnabled = false;
199 mHighlightedFrame = -1;
200 update();
201
202 KeyFrame* key = curLayer->getKeyFrameAt(frameNumber);
203 if (key->isModified()) {
204 emit mEditor->frameModified(frameNumber);
205 }
206 });
207
208 // Update needs to happen before executing menu, otherwise paint event might be postponed
209 update();
210
211 menu.exec(mapToGlobal(pos));
212}
213
214void TimeLineCells::drawContent()
215{
216 if (mCache == nullptr)
217 {
218 mCache = new QPixmap(size());
219 if (mCache->isNull())
220 {
221 // fail to create cache
222 return;
223 }
224 }
225
226 QPainter painter(mCache);
227
228 // grey background of the view
229 const QPalette palette = QApplication::palette();
230 painter.setPen(Qt::NoPen);
231 painter.setBrush(palette.color(QPalette::Base));
232 painter.drawRect(QRect(0, 0, width(), height()));
233
234 const int widgetWidth = width();
235
236 // Draw non-current layers
237 const Object* object = mEditor->object();
238 Q_ASSERT(object != nullptr);
239 for (int i = 0; i < object->getLayerCount(); i++)
240 {
241 if (i == mEditor->layers()->currentLayerIndex())
242 {
243 continue;
244 }
245 const Layer* layeri = object->getLayer(i);
246
247 if (layeri != nullptr)
248 {
249 const int layerY = getLayerY(i);
250 switch (mType)
251 {
252 case TIMELINE_CELL_TYPE::Tracks:
253 paintTrack(painter, layeri, mOffsetX,
254 layerY, widgetWidth - mOffsetX,
255 mLayerHeight, false, mFrameSize);
256 break;
257
258 case TIMELINE_CELL_TYPE::Layers:
259 paintLabel(painter, layeri, 0,
260 layerY, widgetWidth - 1,
261 mLayerHeight, false, mEditor->layerVisibility());
262 break;
263 }
264 }
265 }
266
267 // Draw current layer
268 const Layer* currentLayer = mEditor->layers()->currentLayer();
269 if (didDetachLayer())
270 {
271 int layerYMouseMove = getLayerY(mEditor->layers()->currentLayerIndex()) + mMouseMoveY;
272 if (mType == TIMELINE_CELL_TYPE::Tracks)
273 {
274 paintTrack(painter, currentLayer,
275 mOffsetX, layerYMouseMove,
276 widgetWidth - mOffsetX, mLayerHeight,
277 true, mFrameSize);
278 }
279 else if (mType == TIMELINE_CELL_TYPE::Layers)
280 {
281 paintLabel(painter, currentLayer,
282 0, layerYMouseMove,
283 widgetWidth - 1, mLayerHeight, true, mEditor->layerVisibility());
284
285 paintLayerGutter(painter);
286 }
287 }
288 else
289 {
290 if (mType == TIMELINE_CELL_TYPE::Tracks)
291 {
292 paintTrack(painter,
293 currentLayer,
294 mOffsetX,
295 getLayerY(mEditor->layers()->currentLayerIndex()),
296 widgetWidth - mOffsetX,
297 mLayerHeight,
298 true,
299 mFrameSize);
300 }
301 else if (mType == TIMELINE_CELL_TYPE::Layers)
302 {
303 paintLabel(painter,
304 currentLayer,
305 0,
306 getLayerY(mEditor->layers()->currentLayerIndex()),
307 widgetWidth - 1,
308 mLayerHeight,
309 true,
310 mEditor->layerVisibility());
311 }
312 }
313
314 // --- draw track bar background
315 painter.setPen(Qt::NoPen);
316 painter.setBrush(palette.color(QPalette::Base));
317 painter.drawRect(QRect(0, 0, width() - 1, mOffsetY - 1));
318
319 // --- draw bottom line splitter for track bar
320 painter.setPen(palette.color(QPalette::Mid));
321 painter.drawLine(0, mOffsetY - 2, width() - 1, mOffsetY - 2);
322
323 if (mType == TIMELINE_CELL_TYPE::Layers)
324 {
325 // --- draw circle
326 painter.setPen(palette.color(QPalette::Text));
327 if (mEditor->layerVisibility() == LayerVisibility::CURRENTONLY)
328 {
329 painter.setBrush(palette.color(QPalette::Base));
330 }
331 else if (mEditor->layerVisibility() == LayerVisibility::RELATED)
332 {
333 QColor color = palette.color(QPalette::Text);
334 color.setAlpha(128);
335 painter.setBrush(color);
336 }
337 else if (mEditor->layerVisibility() == LayerVisibility::ALL)
338 {
339 painter.setBrush(palette.brush(QPalette::Text));
340 }
341 painter.setRenderHint(QPainter::Antialiasing, true);
342 painter.drawEllipse(6, 4, 9, 9);
343 painter.setRenderHint(QPainter::Antialiasing, false);
344 }
345 else if (mType == TIMELINE_CELL_TYPE::Tracks)
346 {
347 paintTicks(painter, palette);
348
349 for (int i = 0; i < object->getLayerCount(); i++) {
350 paintSelectedFrames(painter, object->getLayer(i), i);
351 }
352 }
353 mRedrawContent = false;
354}
355
356void TimeLineCells::paintTicks(QPainter& painter, const QPalette& palette) const
357{
358 painter.setPen(palette.color(QPalette::Text));
359 painter.setBrush(palette.brush(QPalette::Text));
360 int fps = mEditor->playback()->fps();
361 for (int i = mFrameOffset; i < mFrameOffset + (width() - mOffsetX) / mFrameSize; i++)
362 {
363 // line x pos + some offset
364 const int lineX = getFrameX(i) + 1;
365 if (i + 1 >= mTimeLine->getRangeLower() && i < mTimeLine->getRangeUpper())
366 {
367 painter.setPen(Qt::NoPen);
368 painter.setBrush(palette.color(QPalette::Highlight));
369
370 painter.drawRect(lineX, 1, mFrameSize + 1, 2);
371
372 painter.setPen(palette.color(QPalette::Text));
373 painter.setBrush(palette.brush(QPalette::Text));
374 }
375
376 // Draw large tick at fps mark
377 if (i % fps == 0 || i % fps == fps / 2)
378 {
379 painter.drawLine(lineX, 1, lineX, 5);
380 }
381 else // draw small tick
382 {
383 painter.drawLine(lineX, 1, lineX, 3);
384 }
385 if (i == 0 || i % fps == fps - 1)
386 {
387 int incr = (i < 9) ? 4 : 0; // poor man’s text centering
388 painter.drawText(QPoint(lineX + incr, 15), QString::number(i + 1));
389 }
390 }
391}
392
393void TimeLineCells::paintTrack(QPainter& painter, const Layer* layer,
394 int x, int y, int width, int height,
395 bool selected, int frameSize) const
396{
397 const QPalette palette = QApplication::palette();
398 QColor col;
399 // Color each track according to the layer type
400 if (layer->type() == Layer::BITMAP) col = QColor(51, 155, 252);
401 if (layer->type() == Layer::VECTOR) col = QColor(70, 205, 123);
402 if (layer->type() == Layer::SOUND) col = QColor(255, 141, 112);
403 if (layer->type() == Layer::CAMERA) col = QColor(253, 202, 92);
404 // Dim invisible layers
405 if (!layer->visible()) col.setAlpha(64);
406
407 painter.save();
408 painter.setBrush(col);
409 painter.setPen(QPen(QBrush(palette.color(QPalette::Mid)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
410 painter.drawRect(x, y - 1, width, height);
411
412 if (!layer->visible())
413 {
414 painter.restore();
415 return;
416 }
417
418 // Changes the appearance if selected
419 if (selected)
420 {
421 paintSelection(painter, x, y, width, height);
422 }
423 else
424 {
425 painter.save();
426 QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
427 linearGrad.setColorAt(0, QColor(255,255,255,150));
428 linearGrad.setColorAt(1, QColor(0,0,0,0));
429 painter.setCompositionMode(QPainter::CompositionMode_Overlay);
430 painter.setBrush(linearGrad);
431 painter.drawRect(x, y - 1, width, height);
432 painter.restore();
433 }
434
435 paintFrames(painter, col, layer, y, height, selected, frameSize);
436
437 painter.restore();
438}
439
440void TimeLineCells::paintFrames(QPainter& painter, QColor trackCol, const Layer* layer, int y, int height, bool selected, int frameSize) const
441{
442 painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
443
444 int recTop = y + 1;
445 int standardWidth = frameSize - 2;
446
447 int recHeight = height - 4;
448
449 const QList<int> selectedFrames = layer->getSelectedFramesByPos();
450 layer->foreachKeyFrame([&](KeyFrame* key)
451 {
452 int framePos = key->pos();
453 int recWidth = standardWidth;
454 int recLeft = getFrameX(framePos) - recWidth;
455
456 // Selected frames are painted separately
457 if (selectedFrames.contains(framePos)) {
458 return;
459 }
460
461 if (key->length() > 1)
462 {
463 // This is especially for sound clips.
464 // Sound clips are the only type of KeyFrame with variable frame length.
465 recWidth = frameSize * key->length();
466 }
467
468 // Paint the frame border
469 painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
470
471 // Paint the frame contents
472 if (selected)
473 {
474 painter.setBrush(QColor(trackCol.red(), trackCol.green(), trackCol.blue(), 150));
475 }
476
477 painter.drawRect(recLeft, recTop, recWidth, recHeight);
478 });
479}
480
481void TimeLineCells::paintCurrentFrameBorder(QPainter &painter, int recLeft, int recTop, int recWidth, int recHeight) const
482{
483 painter.save();
484 painter.setBrush(Qt::NoBrush);
485 painter.setPen(Qt::white);
486 painter.drawRect(recLeft, recTop, recWidth, recHeight);
487 painter.restore();
488}
489
490void TimeLineCells::paintFrameCursorOnCurrentLayer(QPainter &painter, int recTop, int recWidth, int recHeight) const
491{
492 int recLeft = getFrameX(mFramePosMoveX) - recWidth;
493
494 painter.save();
495 const QPalette palette = QApplication::palette();
496 // Don't fill
497 painter.setBrush(Qt::NoBrush);
498 // paint border
499 QColor penColor = palette.color(QPalette::WindowText);
500 penColor.setAlpha(127);
501 painter.setPen(penColor);
502 painter.drawRect(recLeft, recTop, recWidth, recHeight);
503 painter.restore();
504}
505
506void TimeLineCells::paintHighlightedFrame(QPainter& painter, int framePos, int recTop, int recWidth, int recHeight) const
507{
508 int recLeft = getFrameX(framePos) - recWidth;
509
510 painter.save();
511 const QPalette palette = QApplication::palette();
512 painter.setBrush(palette.color(QPalette::Window));
513 painter.setPen(palette.color(QPalette::WindowText));
514
515 // Draw a rect slighly smaller than the frame
516 painter.drawRect(recLeft+1, recTop+1, recWidth-1, recHeight-1);
517 painter.restore();
518}
519
520void TimeLineCells::paintSelectedFrames(QPainter& painter, const Layer* layer, const int layerIndex) const
521{
522 int mouseX = mMouseMoveX;
523 int posUnderCursor = getFrameNumber(mMousePressX);
524 int standardWidth = mFrameSize - 2;
525 int recWidth = standardWidth;
526 int recHeight = mLayerHeight - 4;
527 int recTop = getLayerY(layerIndex) + 1;
528
529 painter.save();
530 for (int framePos : layer->getSelectedFramesByPos()) {
531
532 KeyFrame* key = layer->getKeyFrameAt(framePos);
533 if (key->length() > 1)
534 {
535 // This is a special case for sound clip.
536 // Sound clip is the only type of KeyFrame that has variable frame length.
537 recWidth = mFrameSize * key->length();
538 }
539
540 painter.setBrush(QColor(60, 60, 60));
541 painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
542
543 int frameX = getFrameX(framePos);
544 if (mMovingFrames) {
545 int offset = (framePos - posUnderCursor) + mFrameOffset;
546 int newFrameX = getFrameX(getFrameNumber(getFrameX(offset)+mouseX)) - standardWidth;
547 // Paint as frames are hovering
548 painter.drawRect(newFrameX, recTop-4, recWidth, recHeight);
549
550 } else {
551 int currentFrameX = frameX - standardWidth;
552 painter.drawRect(currentFrameX, recTop, recWidth, recHeight);
553 }
554 }
555 painter.restore();
556}
557
558void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer,
559 int x, int y, int width, int height,
560 bool selected, LayerVisibility layerVisibility) const
561{
562 const QPalette palette = QApplication::palette();
563
564 if (selected)
565 {
566 painter.setBrush(palette.color(QPalette::Highlight));
567 }
568 else
569 {
570 painter.setBrush(palette.color(QPalette::Base));
571 }
572 painter.setPen(Qt::NoPen);
573 painter.drawRect(x, y - 1, width, height); // empty rectangle by default
574
575 if (!layer->visible())
576 {
577 painter.setBrush(palette.color(QPalette::Base));
578 }
579 else
580 {
581 if ((layerVisibility == LayerVisibility::ALL) || selected)
582 {
583 painter.setBrush(palette.color(QPalette::Text));
584 }
585 else if (layerVisibility == LayerVisibility::CURRENTONLY)
586 {
587 painter.setBrush(palette.color(QPalette::Base));
588 }
589 else if (layerVisibility == LayerVisibility::RELATED)
590 {
591 QColor color = palette.color(QPalette::Text);
592 color.setAlpha(128);
593 painter.setBrush(color);
594 }
595 }
596 if (selected)
597 {
598 painter.setPen(palette.color(QPalette::HighlightedText));
599 }
600 else
601 {
602 painter.setPen(palette.color(QPalette::Text));
603 }
604 painter.setRenderHint(QPainter::Antialiasing, true);
605 painter.drawEllipse(x + 6, y + 4, 9, 9);
606 painter.setRenderHint(QPainter::Antialiasing, false);
607
608 if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-bitmap.svg"));
609 if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-vector.svg"));
610 if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-sound.svg"));
611 if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-camera.svg"));
612
613 if (selected)
614 {
615 painter.setPen(palette.color(QPalette::HighlightedText));
616 }
617 else
618 {
619 painter.setPen(palette.color(QPalette::Text));
620 }
621 painter.drawText(QPoint(45, y + (2 * height) / 3), layer->name());
622}
623
624void TimeLineCells::paintSelection(QPainter& painter, int x, int y, int width, int height) const
625{
626 QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
627 linearGrad.setColorAt(0, QColor(0, 0, 0, 255));
628 linearGrad.setColorAt(1, QColor(255, 255, 255, 0));
629 painter.save();
630 painter.setCompositionMode(QPainter::CompositionMode_Overlay);
631 painter.setBrush(linearGrad);
632 painter.setPen(Qt::NoPen);
633 painter.drawRect(x, y, width, height - 1);
634 painter.restore();
635}
636
637void TimeLineCells::paintLayerGutter(QPainter& painter) const
638{
639 painter.setPen(QApplication::palette().color(QPalette::Mid));
640 if (mMouseMoveY > mLayerDetachThreshold)
641 {
642 painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY))+mLayerHeight, width(), 2);
643 }
644 else
645 {
646 painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY)), width(), 2);
647 }
648}
649
650void TimeLineCells::paintOnionSkin(QPainter& painter) const
651{
652 Layer* layer = mEditor->layers()->currentLayer();
653 if (layer == nullptr) { return; }
654
655 int frameNumber = mEditor->currentFrame();
656
657 int prevOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_PREV_FRAMES_NUM);
658 int nextOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
659
660 bool isAbsolute = (mEditor->preference()->getString(SETTING::ONION_TYPE) == "absolute");
661
662 if (mEditor->preference()->isOn(SETTING::PREV_ONION) && prevOnionSkinCount > 0)
663 {
664 int onionFrameNumber = frameNumber;
665 if (isAbsolute)
666 {
667 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber+1, true);
668 }
669 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
670 int onionPosition = 0;
671
672 while (onionPosition < prevOnionSkinCount && onionFrameNumber > 0)
673 {
674 painter.setBrush(QColor(128, 128, 128, 128));
675 painter.setPen(Qt::NoPen);
676 QRect onionRect;
677 onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
678 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
679 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
680 painter.drawRect(onionRect);
681
682 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
683 onionPosition++;
684 }
685 }
686
687 if (mEditor->preference()->isOn(SETTING::NEXT_ONION) && nextOnionSkinCount > 0) {
688
689 int onionFrameNumber = layer->getNextFrameNumber(frameNumber, isAbsolute);
690 int onionPosition = 0;
691
692 while (onionPosition < nextOnionSkinCount && onionFrameNumber > 0)
693 {
694 painter.setBrush(QColor(128, 128, 128, 128));
695 painter.setPen(Qt::NoPen);
696 QRect onionRect;
697 onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
698 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
699 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
700 painter.drawRect(onionRect);
701
702 onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isAbsolute);
703 onionPosition++;
704 }
705 }
706}
707
708void TimeLineCells::paintEvent(QPaintEvent*)
709{
710 const QPalette palette = QApplication::palette();
711 QPainter painter(this);
712
713 bool isPlaying = mEditor->playback()->isPlaying();
714 if (mCache == nullptr || mRedrawContent || trackScrubber())
715 {
716 drawContent();
717 }
718 if (mCache)
719 {
720 painter.drawPixmap(QPoint(0, 0), *mCache);
721 }
722
723 if (mType == TIMELINE_CELL_TYPE::Tracks)
724 {
725 if (!isPlaying) {
726 paintOnionSkin(painter);
727 }
728
729 int currentFrame = mEditor->currentFrame();
730 Layer* currentLayer = mEditor->layers()->currentLayer();
731 KeyFrame* keyFrame = currentLayer->getKeyFrameWhichCovers(currentFrame);
732 if (keyFrame != nullptr)
733 {
734 int recWidth = keyFrame->length() == 1 ? mFrameSize - 2 : mFrameSize * keyFrame->length();
735 int recLeft = getFrameX(keyFrame->pos()) - (mFrameSize - 2);
736 paintCurrentFrameBorder(painter, recLeft, getLayerY(mEditor->currentLayerIndex()) + 1, recWidth, mLayerHeight - 4);
737 }
738
739 if (!mMovingFrames && mLayerPosMoveY != -1 && mLayerPosMoveY == mEditor->currentLayerIndex())
740 {
741 // This is terrible but well...
742 int recTop = getLayerY(mLayerPosMoveY) + 1;
743 int standardWidth = mFrameSize - 2;
744 int recHeight = mLayerHeight - 4;
745
746 if (mHighlightFrameEnabled)
747 {
748 paintHighlightedFrame(painter, mHighlightedFrame, recTop, standardWidth, recHeight);
749 }
750 if (currentLayer->visible())
751 {
752 paintFrameCursorOnCurrentLayer(painter, recTop, standardWidth, recHeight);
753 }
754 }
755
756 // --- draw the position of the current frame
757 if (currentFrame > mFrameOffset)
758 {
759 QColor scrubColor = palette.color(QPalette::Highlight);
760 scrubColor.setAlpha(160);
761 painter.setBrush(scrubColor);
762 painter.setPen(Qt::NoPen);
763
764 int currentFrameStartX = getFrameX(currentFrame - 1) + 1;
765 int currentFrameEndX = getFrameX(currentFrame);
766 QRect scrubRect;
767 scrubRect.setTopLeft(QPoint(currentFrameStartX, 0));
768 scrubRect.setBottomRight(QPoint(currentFrameEndX, height()));
769 if (mbShortScrub)
770 {
771 scrubRect.setBottomRight(QPoint(currentFrameEndX, 19));
772 }
773 painter.save();
774
775 bool mouseUnderScrubber = currentFrame == mFramePosMoveX;
776 if (mouseUnderScrubber) {
777 QRect smallScrub = QRect(QPoint(currentFrameStartX, 0), QPoint(currentFrameEndX,19));
778 QPen pen = scrubColor;
779 pen.setWidth(2);
780 painter.setPen(pen);
781 painter.drawRect(smallScrub);
782 painter.setBrush(Qt::NoBrush);
783 }
784 painter.drawRect(scrubRect);
785 painter.restore();
786
787 painter.setPen(palette.color(QPalette::HighlightedText));
788 int incr = (currentFrame < 10) ? 4 : 0;
789 painter.drawText(QPoint(currentFrameStartX + incr, 15),
790 QString::number(currentFrame));
791 }
792 }
793}
794
795void TimeLineCells::resizeEvent(QResizeEvent* event)
796{
797 clearCache();
798 updateContent();
799 event->accept();
800 emit lengthChanged(getFrameLength());
801}
802
803bool TimeLineCells::event(QEvent* event)
804{
805 if (event->type() == QEvent::Leave) {
806 onDidLeaveWidget();
807 }
808
809 return QWidget::event(event);
810}
811
812void TimeLineCells::mousePressEvent(QMouseEvent* event)
813{
814 int frameNumber = getFrameNumber(event->pos().x());
815 int layerNumber = getLayerNumber(event->pos().y());
816 mCurrentLayerNumber = layerNumber;
817
818 mMousePressX = event->pos().x();
819 mFromLayer = mToLayer = layerNumber;
820
821 mStartY = event->pos().y();
822 mStartLayerNumber = layerNumber;
823 mEndY = event->pos().y();
824
825 mStartFrameNumber = frameNumber;
826 mLastFrameNumber = mStartFrameNumber;
827
828 mCanMoveFrame = false;
829 mMovingFrames = false;
830
831 mCanBoxSelect = false;
832 mBoxSelecting = false;
833
834 mClickSelecting = false;
835
836 primaryButton = event->button();
837
838 switch (mType)
839 {
840 case TIMELINE_CELL_TYPE::Layers:
841 if (layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
842 {
843 if (event->pos().x() < 15)
844 {
845 mEditor->switchVisibilityOfLayer(layerNumber);
846 }
847 else if (mEditor->currentLayerIndex() != layerNumber)
848 {
849 mEditor->layers()->setCurrentLayer(layerNumber);
850 mEditor->layers()->currentLayer()->deselectAll();
851 }
852 }
853 if (layerNumber == -1)
854 {
855 if (event->pos().x() < 15)
856 {
857 if (event->button() == Qt::LeftButton) {
858 mEditor->increaseLayerVisibilityIndex();
859 } else if (event->button() == Qt::RightButton) {
860 mEditor->decreaseLayerVisibilityIndex();
861 }
862 }
863 }
864 break;
865 case TIMELINE_CELL_TYPE::Tracks:
866 if (event->button() == Qt::MiddleButton)
867 {
868 mLastFrameNumber = getFrameNumber(event->pos().x());
869 }
870 else
871 {
872 if (frameNumber == mEditor->currentFrame() && mStartY < 20)
873 {
874 if (mEditor->playback()->isPlaying())
875 {
876 mEditor->playback()->stop();
877 }
878 mTimeLine->scrubbing = true;
879 }
880 else
881 {
882 if ((layerNumber != -1) && layerNumber < mEditor->object()->getLayerCount())
883 {
884 int previousLayerNumber = mEditor->layers()->currentLayerIndex();
885
886 if (previousLayerNumber != layerNumber)
887 {
888 Layer *previousLayer = mEditor->object()->getLayer(previousLayerNumber);
889 previousLayer->deselectAll();
890 emit mEditor->selectedFramesChanged();
891 mEditor->layers()->setCurrentLayer(layerNumber);
892 }
893
894 Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
895
896 // Check if we are using the alt key
897 if (event->modifiers() == Qt::AltModifier)
898 {
899 // If it is the case, we select everything that is after the selected frame
900 mClickSelecting = true;
901 mCanMoveFrame = true;
902
903 currentLayer->selectAllFramesAfter(frameNumber);
904 emit mEditor->selectedFramesChanged();
905 }
906 // Check if we are clicking on a non selected frame
907 else if (!currentLayer->isFrameSelected(frameNumber))
908 {
909 // If it is the case, we select it if it is the left button...
910 mCanBoxSelect = true;
911 mClickSelecting = true;
912 if (event->button() == Qt::LeftButton)
913 {
914
915 if (event->modifiers() == Qt::ControlModifier)
916 {
917 // Add/remove from already selected
918 currentLayer->toggleFrameSelected(frameNumber, true);
919 emit mEditor->selectedFramesChanged();
920 }
921 else if (event->modifiers() == Qt::ShiftModifier)
922 {
923 // Select a range from the last selected
924 currentLayer->extendSelectionTo(frameNumber);
925 emit mEditor->selectedFramesChanged();
926 }
927 else
928 {
929 // Only select if left button clicked
930 currentLayer->toggleFrameSelected(frameNumber, false);
931 emit mEditor->selectedFramesChanged();
932 }
933 }
934
935 // ... or we show the camera context menu, if it is the right button
936 if (event->button() == Qt::RightButton)
937 {
938 showCameraMenu(event->pos());
939 }
940
941 }
942 else
943 {
944 // If selected they can also be interpolated
945 if (event->button() == Qt::RightButton)
946 {
947 showCameraMenu(event->pos());
948 }
949 // We clicked on a selected frame, we can move it
950 mCanMoveFrame = true;
951 }
952
953 if (currentLayer->hasAnySelectedFrames()) {
954 emit selectionChanged();
955 }
956
957 mTimeLine->updateContent();
958 }
959 else
960 {
961 if (frameNumber > 0)
962 {
963 if (mEditor->playback()->isPlaying())
964 {
965 mEditor->playback()->stop();
966 }
967 if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
968 {
969 mEditor->playback()->playScrub(frameNumber);
970 mLastScrubFrame = frameNumber;
971 }
972
973 mEditor->scrubTo(frameNumber);
974
975 mTimeLine->scrubbing = true;
976 qDebug("Scrub to %d frame", frameNumber);
977 }
978 }
979 }
980 }
981 break;
982 }
983}
984
985void TimeLineCells::mouseMoveEvent(QMouseEvent* event)
986{
987 mMouseMoveX = event->pos().x();
988 mFramePosMoveX = getFrameNumber(mMouseMoveX);
989 mLayerPosMoveY = getLayerNumber(event->pos().y());
990
991 if (mType == TIMELINE_CELL_TYPE::Layers)
992 {
993 if (event->buttons() & Qt::LeftButton ) {
994 mEndY = event->pos().y();
995 emit mouseMovedY(mEndY - mStartY);
996 }
997 }
998 else if (mType == TIMELINE_CELL_TYPE::Tracks)
999 {
1000 if (primaryButton == Qt::MiddleButton)
1001 {
1002 mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - mFramePosMoveX));
1003 update();
1004 emit offsetChanged(mFrameOffset);
1005 }
1006 else
1007 {
1008 if (mTimeLine->scrubbing)
1009 {
1010 if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != mFramePosMoveX)
1011 {
1012 mEditor->playback()->playScrub(mFramePosMoveX);
1013 mLastScrubFrame = mFramePosMoveX;
1014 }
1015 mEditor->scrubTo(mFramePosMoveX);
1016 }
1017 else
1018 {
1019 if (event->buttons() & Qt::LeftButton) {
1020 if (mStartLayerNumber != -1 && mStartLayerNumber < mEditor->object()->getLayerCount())
1021 {
1022 Layer *currentLayer = mEditor->object()->getLayer(mStartLayerNumber);
1023
1024 // Check if the frame we clicked was selected
1025 if (mCanMoveFrame) {
1026
1027 // If it is the case, we move the selected frames in the layer
1028 mMovingFrames = true;
1029 }
1030 else if (mCanBoxSelect)
1031 {
1032 // Otherwise, we do a box select
1033 mBoxSelecting = true;
1034
1035 currentLayer->deselectAll();
1036 currentLayer->setFrameSelected(mStartFrameNumber, true);
1037 currentLayer->extendSelectionTo(mFramePosMoveX);
1038 emit mEditor->selectedFramesChanged();
1039 }
1040 mLastFrameNumber = mFramePosMoveX;
1041 updateContent();
1042 }
1043 }
1044 update();
1045 }
1046 }
1047 }
1048}
1049
1050void TimeLineCells::mouseReleaseEvent(QMouseEvent* event)
1051{
1052 if (event->button() != primaryButton) return;
1053
1054 int frameNumber = getFrameNumber(event->pos().x());
1055 if (frameNumber < 1) frameNumber = 1;
1056 int layerNumber = getLayerNumber(event->pos().y());
1057
1058 if (mType == TIMELINE_CELL_TYPE::Tracks && mCurrentLayerNumber != -1 && primaryButton != Qt::MiddleButton)
1059 {
1060 // We should affect the current layer based on what's selected, not where the mouse currently is.
1061 Layer* currentLayer = mEditor->layers()->getLayer(mCurrentLayerNumber);
1062 Q_ASSERT(currentLayer);
1063
1064 if (mMovingFrames)
1065 {
1066 int posUnderCursor = getFrameNumber(mMousePressX);
1067 int offset = frameNumber - posUnderCursor;
1068
1069 if (currentLayer->canMoveSelectedFramesToOffset(offset)) {
1070 SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MOVE);
1071 UserSaveState userState;
1072 userState.moveFramesState = MoveFramesSaveState(offset, currentLayer->selectedKeyFramesPositions());
1073 mEditor->undoRedo()->addUserState(saveStateId, userState);
1074
1075 currentLayer->moveSelectedFrames(offset);
1076 mEditor->undoRedo()->record(saveStateId, tr("Move Frames"));
1077 }
1078 mEditor->layers()->notifyAnimationLengthChanged();
1079 emit mEditor->framesModified();
1080 updateContent();
1081 }
1082 else if (!mTimeLine->scrubbing && !mMovingFrames && !mClickSelecting && !mBoxSelecting)
1083 {
1084 // De-selecting if we didn't move, scrub nor select anything
1085 bool multipleSelection = (event->modifiers() == Qt::ControlModifier);
1086
1087 // Add/remove from already selected
1088 currentLayer->toggleFrameSelected(frameNumber, multipleSelection);
1089 emit mEditor->selectedFramesChanged();
1090 updateContent();
1091 }
1092 }
1093 if (mType == TIMELINE_CELL_TYPE::Layers && !mScrollingVertically && layerNumber != mStartLayerNumber && mStartLayerNumber != -1 && layerNumber != -1)
1094 {
1095 mToLayer = getInbetweenLayerNumber(event->pos().y());
1096 if (mToLayer != mFromLayer && mToLayer > -1 && mToLayer < mEditor->layers()->count())
1097 {
1098 // Bubble the from layer up or down to the to layer
1099 if (mToLayer < mFromLayer) // bubble up
1100 {
1101 for (int i = mFromLayer - 1; i >= mToLayer; i--)
1102 mEditor->swapLayers(i, i + 1);
1103 }
1104 else // bubble down
1105 {
1106 for (int i = mFromLayer + 1; i <= mToLayer; i++)
1107 mEditor->swapLayers(i, i - 1);
1108 }
1109 }
1110 }
1111
1112 if (mType == TIMELINE_CELL_TYPE::Layers && event->button() == Qt::LeftButton)
1113 {
1114 emit mouseMovedY(0);
1115 }
1116
1117 primaryButton = Qt::NoButton;
1118 mEndY = mStartY;
1119 mTimeLine->scrubbing = false;
1120 mMovingFrames = false;
1121}
1122
1123void TimeLineCells::mouseDoubleClickEvent(QMouseEvent* event)
1124{
1125 int frameNumber = getFrameNumber(event->pos().x());
1126 int layerNumber = getLayerNumber(event->pos().y());
1127
1128 // -- short scrub --
1129 if (event->pos().y() < 20 && (mType != TIMELINE_CELL_TYPE::Layers || event->pos().x() >= 15))
1130 {
1131 mPrefs->set(SETTING::SHORT_SCRUB, !mbShortScrub);
1132 }
1133
1134 // -- layer --
1135 Layer* layer = mEditor->object()->getLayer(layerNumber);
1136 if (layer && event->buttons() & Qt::LeftButton)
1137 {
1138 if (mType == TIMELINE_CELL_TYPE::Tracks && (layerNumber != -1) && (frameNumber > 0) && layerNumber < mEditor->object()->getLayerCount())
1139 {
1140 if (!layer->keyExistsWhichCovers(frameNumber))
1141 {
1142 mEditor->scrubTo(frameNumber);
1143 }
1144
1145 // The release event will toggle the frame on again, so we make sure it gets
1146 // deselected now instead.
1147 layer->setFrameSelected(frameNumber, true);
1148 }
1149 else if (mType == TIMELINE_CELL_TYPE::Layers && event->pos().x() >= 15)
1150 {
1151 editLayerProperties(layer);
1152 }
1153 }
1154 QWidget::mouseDoubleClickEvent(event);
1155}
1156
1157void TimeLineCells::editLayerProperties(Layer *layer) const
1158{
1159 if (layer->type() != Layer::CAMERA)
1160 {
1161 editLayerName(layer);
1162 return;
1163 }
1164
1165 auto cameraLayer = dynamic_cast<LayerCamera*>(layer);
1166 Q_ASSERT(cameraLayer);
1167 editLayerProperties(cameraLayer);
1168}
1169
1170void TimeLineCells::editLayerProperties(LayerCamera* cameraLayer) const
1171{
1172 QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+");
1173
1174 CameraPropertiesDialog dialog(cameraLayer->name(), cameraLayer->getViewRect().width(),
1175 cameraLayer->getViewRect().height());
1176 if (dialog.exec() != QDialog::Accepted)
1177 {
1178 return;
1179 }
1180 QString name = dialog.getName().replace(regex, "");
1181
1182 if (!name.isEmpty())
1183 {
1184 mEditor->layers()->renameLayer(cameraLayer, name);
1185 }
1186 QSettings settings(PENCIL2D, PENCIL2D);
1187 settings.setValue(SETTING_FIELD_W, dialog.getWidth());
1188 settings.setValue(SETTING_FIELD_H, dialog.getHeight());
1189 cameraLayer->setViewRect(QRect(-dialog.getWidth() / 2, -dialog.getHeight() / 2, dialog.getWidth(), dialog.getHeight()));
1190 mEditor->view()->forceUpdateViewTransform();
1191}
1192
1193void TimeLineCells::editLayerName(Layer* layer) const
1194{
1195 QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+");
1196
1197 bool ok;
1198 QString name = QInputDialog::getText(nullptr, tr("Layer Properties"),
1199 tr("Layer name:"), QLineEdit::Normal,
1200 layer->name(), &ok);
1201 name.replace(regex, "");
1202 if (!ok || name.isEmpty())
1203 {
1204 return;
1205 }
1206
1207 mEditor->layers()->renameLayer(layer, name);
1208}
1209
1210void TimeLineCells::hScrollChange(int x)
1211{
1212 mFrameOffset = x;
1213 updateContent();
1214}
1215
1216void TimeLineCells::vScrollChange(int x)
1217{
1218 mLayerOffset = x;
1219 mScrollingVertically = true;
1220 updateContent();
1221}
1222
1223void TimeLineCells::onScrollingVerticallyStopped()
1224{
1225 mScrollingVertically = false;
1226}
1227
1228void TimeLineCells::setMouseMoveY(int x)
1229{
1230 mMouseMoveY = x;
1231 updateContent();
1232}
1233
1234bool TimeLineCells::trackScrubber()
1235{
1236 if (mType != TIMELINE_CELL_TYPE::Tracks ||
1237 (mPrevFrame == mEditor->currentFrame() && !mEditor->playback()->isPlaying()))
1238 {
1239 return false;
1240 }
1241 mPrevFrame = mEditor->currentFrame();
1242
1243 if (mEditor->currentFrame() <= mFrameOffset)
1244 {
1245 // Move the timeline back if the scrubber is offscreen to the left
1246 mFrameOffset = mEditor->currentFrame() - 1;
1247 emit offsetChanged(mFrameOffset);
1248 return true;
1249 }
1250 else if (width() < (mEditor->currentFrame() - mFrameOffset + 1) * mFrameSize)
1251 {
1252 // Move timeline forward if the scrubber is offscreen to the right
1253 if (mEditor->playback()->isPlaying())
1254 mFrameOffset = mFrameOffset + ((mEditor->currentFrame() - mFrameOffset) / 2);
1255 else
1256 mFrameOffset = mEditor->currentFrame() - width() / mFrameSize;
1257 emit offsetChanged(mFrameOffset);
1258 return true;
1259 }
1260 return false;
1261}
1262
1263void TimeLineCells::onDidLeaveWidget()
1264{
1265 // Reset last known frame pos to avoid wrong UI states when leaving the widget
1266 mFramePosMoveX = 0;
1267 update();
1268}
CameraContextMenu
Definition: cameracontextmenu.h:29
CameraPropertiesDialog
Definition: camerapropertiesdialog.h:28
Editor
Definition: editor.h:71
Editor::framesModified
void framesModified()
This should be emitted after modifying multiple frames.
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
KeyFrame
Definition: keyframe.h:30
LayerCamera
Definition: layercamera.h:30
Layer
Definition: layer.h:33
Layer::selectedKeyFramesPositions
QList< int > selectedKeyFramesPositions() const
Get selected keyframe positions sorted by position.
Definition: layer.h:62
Layer::canMoveSelectedFramesToOffset
bool canMoveSelectedFramesToOffset(int offset) const
Predetermines whether the frames can be moved to a new position depending on the offset.
Definition: layer.cpp:497
LayerManager::notifyAnimationLengthChanged
void notifyAnimationLengthChanged()
This should be emitted whenever the animation length frames, eg.
Definition: layermanager.cpp:403
Object
Definition: object.h:42
TimeLine
Definition: timeline.h:32
UndoRedoManager::addUserState
void addUserState(SAVESTATE_ID SaveStateId, const UserSaveState &userState)
Adds userState to the saveState found at SaveStateId If no record is found matching the id,...
Definition: undoredomanager.cpp:274
UndoRedoManager::record
void record(SAVESTATE_ID SaveStateId, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:96
UndoRedoManager::createState
SAVESTATE_ID createState(UndoRedoRecordType recordType)
Prepares and returns an save state with common data.
Definition: undoredomanager.cpp:261
QBrush
QColor
QColor::blue
int blue() const const
QColor::green
int green() const const
QColor::red
int red() const const
QColor::setAlpha
void setAlpha(int alpha)
QDialog::Accepted
Accepted
QEvent
QEvent::Leave
Leave
QEvent::type
QEvent::Type type() const const
QGuiApplication::palette
QPalette palette()
QInputDialog::getText
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
QInputEvent::modifiers
Qt::KeyboardModifiers modifiers() const const
QLinearGradient
QLineEdit::Normal
Normal
QList
QList::contains
bool contains(const T &value) const const
QMenu::aboutToHide
void aboutToHide()
QMouseEvent
QMouseEvent::button
Qt::MouseButton button() const const
QMouseEvent::buttons
Qt::MouseButtons buttons() const const
QMouseEvent::pos
QPoint pos() const const
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QPainter
QPainter::CompositionMode_Overlay
CompositionMode_Overlay
QPainter::Antialiasing
Antialiasing
QPainter::drawEllipse
void drawEllipse(const QRectF &rectangle)
QPainter::drawLine
void drawLine(const QLineF &line)
QPainter::drawPixmap
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QPainter::drawRect
void drawRect(const QRectF &rectangle)
QPainter::drawText
void drawText(const QPointF &position, const QString &text)
QPainter::restore
void restore()
QPainter::save
void save()
QPainter::setBrush
void setBrush(const QBrush &brush)
QPainter::setCompositionMode
void setCompositionMode(QPainter::CompositionMode mode)
QPainter::setPen
void setPen(const QColor &color)
QPainter::setRenderHint
void setRenderHint(QPainter::RenderHint hint, bool on)
QPaintEvent
QPalette
QPalette::Base
Base
QPen
QPen::setWidth
void setWidth(int width)
QPixmap
QPixmap::isNull
bool isNull() const const
QPoint
QPoint::x
int x() const const
QPoint::y
int y() const const
QPointF
QRect
QRect::height
int height() const const
QRect::setBottomRight
void setBottomRight(const QPoint &position)
QRect::setTopLeft
void setTopLeft(const QPoint &position)
QRect::width
int width() const const
QRegularExpression
QResizeEvent
QSettings
QSizePolicy
QSizePolicy::MinimumExpanding
MinimumExpanding
QString
QString::isEmpty
bool isEmpty() const const
QString::number
QString number(int n, int base)
QString::replace
QString & replace(int position, int n, QChar after)
Qt::NoBrush
NoBrush
Qt::white
white
Qt::AltModifier
AltModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::NoPen
NoPen
Qt::WA_OpaquePaintEvent
WA_OpaquePaintEvent
QWidget
QWidget::event
virtual bool event(QEvent *event) override
QWidget::frameSize
frameSize
QWidget::height
height
QWidget::mapToGlobal
QPoint mapToGlobal(const QPoint &pos) const const
QWidget::mouseDoubleClickEvent
virtual void mouseDoubleClickEvent(QMouseEvent *event)
QWidget::palette
palette
QWidget::pos
pos
QWidget::size
size
QWidget::update
void update()
QWidget::width
width
QWidget::x
x
QWidget::y
y
MoveFramesSaveState
Definition: undoredomanager.h:81
UserSaveState
Use this struct to store user related data that will later be added to the backup This struct is mean...
Definition: undoredomanager.h:100
Generated on Mon May 4 2026 07:50:47 for Pencil2D by doxygen 1.9.6 based on revision 3ed50cdd696e72315cedf30508d3572536c3876e