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