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 layer->foreachKeyFrame([&](KeyFrame* key)
449 {
450 int framePos = key->pos();
451 int recWidth = standardWidth;
452 int recLeft = getFrameX(framePos) - recWidth;
453
454 if (key->length() > 1)
455 {
456 // This is especially for sound clips.
457 // Sound clips are the only type of KeyFrame with variable frame length.
458 recWidth = frameSize * key->length();
459 }
460
461 // Paint the frame border
462 painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
463
464 // Paint the frame contents
465 if (selected)
466 {
467 if (key->isSelected()) {
468 painter.setBrush(QColor(60, 60, 60));
469 }
470 else
471 {
472 painter.setBrush(QColor(trackCol.red(), trackCol.green(), trackCol.blue(), 150));
473 }
474 }
475
476 if (!key->isSelected()) {
477 painter.drawRect(recLeft, recTop, recWidth, recHeight);
478 }
479 });
480}
481
482void TimeLineCells::paintCurrentFrameBorder(QPainter &painter, int recLeft, int recTop, int recWidth, int recHeight) const
483{
484 painter.save();
485 painter.setBrush(Qt::NoBrush);
486 painter.setPen(Qt::white);
487 painter.drawRect(recLeft, recTop, recWidth, recHeight);
488 painter.restore();
489}
490
491void TimeLineCells::paintFrameCursorOnCurrentLayer(QPainter &painter, int recTop, int recWidth, int recHeight) const
492{
493 int recLeft = getFrameX(mFramePosMoveX) - recWidth;
494
495 painter.save();
496 const QPalette palette = QApplication::palette();
497 // Don't fill
498 painter.setBrush(Qt::NoBrush);
499 // paint border
500 QColor penColor = palette.color(QPalette::WindowText);
501 penColor.setAlpha(127);
502 painter.setPen(penColor);
503 painter.drawRect(recLeft, recTop, recWidth, recHeight);
504 painter.restore();
505}
506
507void TimeLineCells::paintHighlightedFrame(QPainter& painter, int framePos, int recTop, int recWidth, int recHeight) const
508{
509 int recLeft = getFrameX(framePos) - recWidth;
510
511 painter.save();
512 const QPalette palette = QApplication::palette();
513 painter.setBrush(palette.color(QPalette::Window));
514 painter.setPen(palette.color(QPalette::WindowText));
515
516 // Draw a rect slighly smaller than the frame
517 painter.drawRect(recLeft+1, recTop+1, recWidth-1, recHeight-1);
518 painter.restore();
519}
520
521void TimeLineCells::paintSelectedFrames(QPainter& painter, const Layer* layer, const int layerIndex) const
522{
523 int mouseX = mMouseMoveX;
524 int posUnderCursor = getFrameNumber(mMousePressX);
525 int standardWidth = mFrameSize - 2;
526 int recWidth = standardWidth;
527 int recHeight = mLayerHeight - 4;
528 int recTop = getLayerY(layerIndex) + 1;
529
530 painter.save();
531 for (int framePos : layer->getSelectedFramesByPos()) {
532
533 KeyFrame* key = layer->getKeyFrameAt(framePos);
534 if (key->length() > 1)
535 {
536 // This is a special case for sound clip.
537 // Sound clip is the only type of KeyFrame that has variable frame length.
538 recWidth = mFrameSize * key->length();
539 }
540
541 painter.setBrush(QColor(60, 60, 60));
542 painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
543
544 int frameX = getFrameX(framePos);
545 if (mMovingFrames) {
546 int offset = (framePos - posUnderCursor) + mFrameOffset;
547 int newFrameX = getFrameX(getFrameNumber(getFrameX(offset)+mouseX)) - standardWidth;
548 // Paint as frames are hovering
549 painter.drawRect(newFrameX, recTop-4, recWidth, recHeight);
550
551 } else {
552 int currentFrameX = frameX - standardWidth;
553 painter.drawRect(currentFrameX, recTop, recWidth, recHeight);
554 }
555 }
556 painter.restore();
557}
558
559void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer,
560 int x, int y, int width, int height,
561 bool selected, LayerVisibility layerVisibility) const
562{
563 const QPalette palette = QApplication::palette();
564
565 if (selected)
566 {
567 painter.setBrush(palette.color(QPalette::Highlight));
568 }
569 else
570 {
571 painter.setBrush(palette.color(QPalette::Base));
572 }
573 painter.setPen(Qt::NoPen);
574 painter.drawRect(x, y - 1, width, height); // empty rectangle by default
575
576 if (!layer->visible())
577 {
578 painter.setBrush(palette.color(QPalette::Base));
579 }
580 else
581 {
582 if ((layerVisibility == LayerVisibility::ALL) || selected)
583 {
584 painter.setBrush(palette.color(QPalette::Text));
585 }
586 else if (layerVisibility == LayerVisibility::CURRENTONLY)
587 {
588 painter.setBrush(palette.color(QPalette::Base));
589 }
590 else if (layerVisibility == LayerVisibility::RELATED)
591 {
592 QColor color = palette.color(QPalette::Text);
593 color.setAlpha(128);
594 painter.setBrush(color);
595 }
596 }
597 if (selected)
598 {
599 painter.setPen(palette.color(QPalette::HighlightedText));
600 }
601 else
602 {
603 painter.setPen(palette.color(QPalette::Text));
604 }
605 painter.setRenderHint(QPainter::Antialiasing, true);
606 painter.drawEllipse(x + 6, y + 4, 9, 9);
607 painter.setRenderHint(QPainter::Antialiasing, false);
608
609 if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-bitmap.svg"));
610 if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-vector.svg"));
611 if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-sound.svg"));
612 if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-camera.svg"));
613
614 if (selected)
615 {
616 painter.setPen(palette.color(QPalette::HighlightedText));
617 }
618 else
619 {
620 painter.setPen(palette.color(QPalette::Text));
621 }
622 painter.drawText(QPoint(45, y + (2 * height) / 3), layer->name());
623}
624
625void TimeLineCells::paintSelection(QPainter& painter, int x, int y, int width, int height) const
626{
627 QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
628 linearGrad.setColorAt(0, QColor(0, 0, 0, 255));
629 linearGrad.setColorAt(1, QColor(255, 255, 255, 0));
630 painter.save();
631 painter.setCompositionMode(QPainter::CompositionMode_Overlay);
632 painter.setBrush(linearGrad);
633 painter.setPen(Qt::NoPen);
634 painter.drawRect(x, y, width, height - 1);
635 painter.restore();
636}
637
638void TimeLineCells::paintLayerGutter(QPainter& painter) const
639{
640 painter.setPen(QApplication::palette().color(QPalette::Mid));
641 if (mMouseMoveY > mLayerDetachThreshold)
642 {
643 painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY))+mLayerHeight, width(), 2);
644 }
645 else
646 {
647 painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY)), width(), 2);
648 }
649}
650
651void TimeLineCells::paintOnionSkin(QPainter& painter) const
652{
653 Layer* layer = mEditor->layers()->currentLayer();
654 if (layer == nullptr) { return; }
655
656 int frameNumber = mEditor->currentFrame();
657
658 int prevOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_PREV_FRAMES_NUM);
659 int nextOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
660
661 bool isAbsolute = (mEditor->preference()->getString(SETTING::ONION_TYPE) == "absolute");
662
663 if (mEditor->preference()->isOn(SETTING::PREV_ONION) && prevOnionSkinCount > 0)
664 {
665 int onionFrameNumber = frameNumber;
666 if (isAbsolute)
667 {
668 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber+1, true);
669 }
670 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
671 int onionPosition = 0;
672
673 while (onionPosition < prevOnionSkinCount && onionFrameNumber > 0)
674 {
675 painter.setBrush(QColor(128, 128, 128, 128));
676 painter.setPen(Qt::NoPen);
677 QRect onionRect;
678 onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
679 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
680 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
681 painter.drawRect(onionRect);
682
683 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
684 onionPosition++;
685 }
686 }
687
688 if (mEditor->preference()->isOn(SETTING::NEXT_ONION) && nextOnionSkinCount > 0) {
689
690 int onionFrameNumber = layer->getNextFrameNumber(frameNumber, isAbsolute);
691 int onionPosition = 0;
692
693 while (onionPosition < nextOnionSkinCount && onionFrameNumber > 0)
694 {
695 painter.setBrush(QColor(128, 128, 128, 128));
696 painter.setPen(Qt::NoPen);
697 QRect onionRect;
698 onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
699 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
700 onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
701 painter.drawRect(onionRect);
702
703 onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isAbsolute);
704 onionPosition++;
705 }
706 }
707}
708
709void TimeLineCells::paintEvent(QPaintEvent*)
710{
711 const QPalette palette = QApplication::palette();
712 QPainter painter(this);
713
714 bool isPlaying = mEditor->playback()->isPlaying();
715 if (mCache == nullptr || mRedrawContent || trackScrubber())
716 {
717 drawContent();
718 }
719 if (mCache)
720 {
721 painter.drawPixmap(QPoint(0, 0), *mCache);
722 }
723
724 if (mType == TIMELINE_CELL_TYPE::Tracks)
725 {
726 if (!isPlaying) {
727 paintOnionSkin(painter);
728 }
729
730 int currentFrame = mEditor->currentFrame();
731 Layer* currentLayer = mEditor->layers()->currentLayer();
732 KeyFrame* keyFrame = currentLayer->getKeyFrameWhichCovers(currentFrame);
733 if (keyFrame != nullptr && !keyFrame->isSelected())
734 {
735 int recWidth = keyFrame->length() == 1 ? mFrameSize - 2 : mFrameSize * keyFrame->length();
736 int recLeft = getFrameX(keyFrame->pos()) - (mFrameSize - 2);
737 paintCurrentFrameBorder(painter, recLeft, getLayerY(mEditor->currentLayerIndex()) + 1, recWidth, mLayerHeight - 4);
738 }
739
740 if (!mMovingFrames && mLayerPosMoveY != -1 && mLayerPosMoveY == mEditor->currentLayerIndex())
741 {
742 // This is terrible but well...
743 int recTop = getLayerY(mLayerPosMoveY) + 1;
744 int standardWidth = mFrameSize - 2;
745 int recHeight = mLayerHeight - 4;
746
747 if (mHighlightFrameEnabled)
748 {
749 paintHighlightedFrame(painter, mHighlightedFrame, recTop, standardWidth, recHeight);
750 }
751 if (currentLayer->visible())
752 {
753 paintFrameCursorOnCurrentLayer(painter, recTop, standardWidth, recHeight);
754 }
755 }
756
757 // --- draw the position of the current frame
758 if (currentFrame > mFrameOffset)
759 {
760 QColor scrubColor = palette.color(QPalette::Highlight);
761 scrubColor.setAlpha(160);
762 painter.setBrush(scrubColor);
763 painter.setPen(Qt::NoPen);
764
765 int currentFrameStartX = getFrameX(currentFrame - 1) + 1;
766 int currentFrameEndX = getFrameX(currentFrame);
767 QRect scrubRect;
768 scrubRect.setTopLeft(QPoint(currentFrameStartX, 0));
769 scrubRect.setBottomRight(QPoint(currentFrameEndX, height()));
770 if (mbShortScrub)
771 {
772 scrubRect.setBottomRight(QPoint(currentFrameEndX, 19));
773 }
774 painter.save();
775
776 bool mouseUnderScrubber = currentFrame == mFramePosMoveX;
777 if (mouseUnderScrubber) {
778 QRect smallScrub = QRect(QPoint(currentFrameStartX, 0), QPoint(currentFrameEndX,19));
779 QPen pen = scrubColor;
780 pen.setWidth(2);
781 painter.setPen(pen);
782 painter.drawRect(smallScrub);
783 painter.setBrush(Qt::NoBrush);
784 }
785 painter.drawRect(scrubRect);
786 painter.restore();
787
788 painter.setPen(palette.color(QPalette::HighlightedText));
789 int incr = (currentFrame < 10) ? 4 : 0;
790 painter.drawText(QPoint(currentFrameStartX + incr, 15),
791 QString::number(currentFrame));
792 }
793 }
794}
795
796void TimeLineCells::resizeEvent(QResizeEvent* event)
797{
798 clearCache();
799 updateContent();
800 event->accept();
801 emit lengthChanged(getFrameLength());
802}
803
804bool TimeLineCells::event(QEvent* event)
805{
806 if (event->type() == QEvent::Leave) {
807 onDidLeaveWidget();
808 }
809
810 return QWidget::event(event);
811}
812
813void TimeLineCells::mousePressEvent(QMouseEvent* event)
814{
815 int frameNumber = getFrameNumber(event->pos().x());
816 int layerNumber = getLayerNumber(event->pos().y());
817 mCurrentLayerNumber = layerNumber;
818
819 mMousePressX = event->pos().x();
820 mFromLayer = mToLayer = layerNumber;
821
822 mStartY = event->pos().y();
823 mStartLayerNumber = layerNumber;
824 mEndY = event->pos().y();
825
826 mStartFrameNumber = frameNumber;
827 mLastFrameNumber = mStartFrameNumber;
828
829 mCanMoveFrame = false;
830 mMovingFrames = false;
831
832 mCanBoxSelect = false;
833 mBoxSelecting = false;
834
835 mClickSelecting = false;
836
837 primaryButton = event->button();
838
839 switch (mType)
840 {
841 case TIMELINE_CELL_TYPE::Layers:
842 if (layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
843 {
844 if (event->pos().x() < 15)
845 {
846 mEditor->switchVisibilityOfLayer(layerNumber);
847 }
848 else if (mEditor->currentLayerIndex() != layerNumber)
849 {
850 mEditor->layers()->setCurrentLayer(layerNumber);
851 mEditor->layers()->currentLayer()->deselectAll();
852 }
853 }
854 if (layerNumber == -1)
855 {
856 if (event->pos().x() < 15)
857 {
858 if (event->button() == Qt::LeftButton) {
859 mEditor->increaseLayerVisibilityIndex();
860 } else if (event->button() == Qt::RightButton) {
861 mEditor->decreaseLayerVisibilityIndex();
862 }
863 }
864 }
865 break;
866 case TIMELINE_CELL_TYPE::Tracks:
867 if (event->button() == Qt::MiddleButton)
868 {
869 mLastFrameNumber = getFrameNumber(event->pos().x());
870 }
871 else
872 {
873 if (frameNumber == mEditor->currentFrame() && mStartY < 20)
874 {
875 if (mEditor->playback()->isPlaying())
876 {
877 mEditor->playback()->stop();
878 }
879 mTimeLine->scrubbing = true;
880 }
881 else
882 {
883 if ((layerNumber != -1) && layerNumber < mEditor->object()->getLayerCount())
884 {
885 int previousLayerNumber = mEditor->layers()->currentLayerIndex();
886
887 if (previousLayerNumber != layerNumber)
888 {
889 Layer *previousLayer = mEditor->object()->getLayer(previousLayerNumber);
890 previousLayer->deselectAll();
891 emit mEditor->selectedFramesChanged();
892 mEditor->layers()->setCurrentLayer(layerNumber);
893 }
894
895 Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
896
897 // Check if we are using the alt key
898 if (event->modifiers() == Qt::AltModifier)
899 {
900 // If it is the case, we select everything that is after the selected frame
901 mClickSelecting = true;
902 mCanMoveFrame = true;
903
904 currentLayer->selectAllFramesAfter(frameNumber);
905 emit mEditor->selectedFramesChanged();
906 }
907 // Check if we are clicking on a non selected frame
908 else if (!currentLayer->isFrameSelected(frameNumber))
909 {
910 // If it is the case, we select it if it is the left button...
911 mCanBoxSelect = true;
912 mClickSelecting = true;
913 if (event->button() == Qt::LeftButton)
914 {
915
916 if (event->modifiers() == Qt::ControlModifier)
917 {
918 // Add/remove from already selected
919 currentLayer->toggleFrameSelected(frameNumber, true);
920 emit mEditor->selectedFramesChanged();
921 }
922 else if (event->modifiers() == Qt::ShiftModifier)
923 {
924 // Select a range from the last selected
925 currentLayer->extendSelectionTo(frameNumber);
926 emit mEditor->selectedFramesChanged();
927 }
928 else
929 {
930 // Only select if left button clicked
931 currentLayer->toggleFrameSelected(frameNumber, false);
932 emit mEditor->selectedFramesChanged();
933 }
934 }
935
936 // ... or we show the camera context menu, if it is the right button
937 if (event->button() == Qt::RightButton)
938 {
939 showCameraMenu(event->pos());
940 }
941
942 }
943 else
944 {
945 // If selected they can also be interpolated
946 if (event->button() == Qt::RightButton)
947 {
948 showCameraMenu(event->pos());
949 }
950 // We clicked on a selected frame, we can move it
951 mCanMoveFrame = true;
952 }
953
954 if (currentLayer->hasAnySelectedFrames()) {
955 emit selectionChanged();
956 }
957
958 mTimeLine->updateContent();
959 }
960 else
961 {
962 if (frameNumber > 0)
963 {
964 if (mEditor->playback()->isPlaying())
965 {
966 mEditor->playback()->stop();
967 }
968 if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
969 {
970 mEditor->playback()->playScrub(frameNumber);
971 mLastScrubFrame = frameNumber;
972 }
973
974 mEditor->scrubTo(frameNumber);
975
976 mTimeLine->scrubbing = true;
977 qDebug("Scrub to %d frame", frameNumber);
978 }
979 }
980 }
981 }
982 break;
983 }
984}
985
986void TimeLineCells::mouseMoveEvent(QMouseEvent* event)
987{
988 mMouseMoveX = event->pos().x();
989 mFramePosMoveX = getFrameNumber(mMouseMoveX);
990 mLayerPosMoveY = getLayerNumber(event->pos().y());
991
992 if (mType == TIMELINE_CELL_TYPE::Layers)
993 {
994 if (event->buttons() & Qt::LeftButton ) {
995 mEndY = event->pos().y();
996 emit mouseMovedY(mEndY - mStartY);
997 }
998 }
999 else if (mType == TIMELINE_CELL_TYPE::Tracks)
1000 {
1001 if (primaryButton == Qt::MiddleButton)
1002 {
1003 mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - mFramePosMoveX));
1004 update();
1005 emit offsetChanged(mFrameOffset);
1006 }
1007 else
1008 {
1009 if (mTimeLine->scrubbing)
1010 {
1011 if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != mFramePosMoveX)
1012 {
1013 mEditor->playback()->playScrub(mFramePosMoveX);
1014 mLastScrubFrame = mFramePosMoveX;
1015 }
1016 mEditor->scrubTo(mFramePosMoveX);
1017 }
1018 else
1019 {
1020 if (event->buttons() & Qt::LeftButton) {
1021 if (mStartLayerNumber != -1 && mStartLayerNumber < mEditor->object()->getLayerCount())
1022 {
1023 Layer *currentLayer = mEditor->object()->getLayer(mStartLayerNumber);
1024
1025 // Check if the frame we clicked was selected
1026 if (mCanMoveFrame) {
1027
1028 // If it is the case, we move the selected frames in the layer
1029 mMovingFrames = true;
1030 }
1031 else if (mCanBoxSelect)
1032 {
1033 // Otherwise, we do a box select
1034 mBoxSelecting = true;
1035
1036 currentLayer->deselectAll();
1037 currentLayer->setFrameSelected(mStartFrameNumber, true);
1038 currentLayer->extendSelectionTo(mFramePosMoveX);
1039 emit mEditor->selectedFramesChanged();
1040 }
1041 mLastFrameNumber = mFramePosMoveX;
1042 updateContent();
1043 }
1044 }
1045 update();
1046 }
1047 }
1048 }
1049}
1050
1051void TimeLineCells::mouseReleaseEvent(QMouseEvent* event)
1052{
1053 if (event->button() != primaryButton) return;
1054
1055 int frameNumber = getFrameNumber(event->pos().x());
1056 if (frameNumber < 1) frameNumber = 1;
1057 int layerNumber = getLayerNumber(event->pos().y());
1058
1059 if (mType == TIMELINE_CELL_TYPE::Tracks && mCurrentLayerNumber != -1 && primaryButton != Qt::MiddleButton)
1060 {
1061 // We should affect the current layer based on what's selected, not where the mouse currently is.
1062 Layer* currentLayer = mEditor->layers()->getLayer(mCurrentLayerNumber);
1063 Q_ASSERT(currentLayer);
1064
1065 if (mMovingFrames)
1066 {
1067 int posUnderCursor = getFrameNumber(mMousePressX);
1068 int offset = frameNumber - posUnderCursor;
1069
1070 currentLayer->moveSelectedFrames(offset);
1071
1072 mEditor->layers()->notifyAnimationLengthChanged();
1073 emit mEditor->framesModified();
1074 updateContent();
1075 }
1076 else if (!mTimeLine->scrubbing && !mMovingFrames && !mClickSelecting && !mBoxSelecting)
1077 {
1078 // De-selecting if we didn't move, scrub nor select anything
1079 bool multipleSelection = (event->modifiers() == Qt::ControlModifier);
1080
1081 // Add/remove from already selected
1082 currentLayer->toggleFrameSelected(frameNumber, multipleSelection);
1083 emit mEditor->selectedFramesChanged();
1084 updateContent();
1085 }
1086 }
1087 if (mType == TIMELINE_CELL_TYPE::Layers && !mScrollingVertically && layerNumber != mStartLayerNumber && mStartLayerNumber != -1 && layerNumber != -1)
1088 {
1089 mToLayer = getInbetweenLayerNumber(event->pos().y());
1090 if (mToLayer != mFromLayer && mToLayer > -1 && mToLayer < mEditor->layers()->count())
1091 {
1092 // Bubble the from layer up or down to the to layer
1093 if (mToLayer < mFromLayer) // bubble up
1094 {
1095 for (int i = mFromLayer - 1; i >= mToLayer; i--)
1096 mEditor->swapLayers(i, i + 1);
1097 }
1098 else // bubble down
1099 {
1100 for (int i = mFromLayer + 1; i <= mToLayer; i++)
1101 mEditor->swapLayers(i, i - 1);
1102 }
1103 }
1104 }
1105
1106 if (mType == TIMELINE_CELL_TYPE::Layers && event->button() == Qt::LeftButton)
1107 {
1108 emit mouseMovedY(0);
1109 }
1110
1111 primaryButton = Qt::NoButton;
1112 mEndY = mStartY;
1113 mTimeLine->scrubbing = false;
1114 mMovingFrames = false;
1115}
1116
1117void TimeLineCells::mouseDoubleClickEvent(QMouseEvent* event)
1118{
1119 int frameNumber = getFrameNumber(event->pos().x());
1120 int layerNumber = getLayerNumber(event->pos().y());
1121
1122 // -- short scrub --
1123 if (event->pos().y() < 20 && (mType != TIMELINE_CELL_TYPE::Layers || event->pos().x() >= 15))
1124 {
1125 mPrefs->set(SETTING::SHORT_SCRUB, !mbShortScrub);
1126 }
1127
1128 // -- layer --
1129 Layer* layer = mEditor->object()->getLayer(layerNumber);
1130 if (layer && event->buttons() & Qt::LeftButton)
1131 {
1132 if (mType == TIMELINE_CELL_TYPE::Tracks && (layerNumber != -1) && (frameNumber > 0) && layerNumber < mEditor->object()->getLayerCount())
1133 {
1134 if (!layer->keyExistsWhichCovers(frameNumber))
1135 {
1136 mEditor->scrubTo(frameNumber);
1137 emit insertNewKeyFrame();
1138 }
1139
1140 // The release event will toggle the frame on again, so we make sure it gets
1141 // deselected now instead.
1142 layer->setFrameSelected(frameNumber, true);
1143 }
1144 else if (mType == TIMELINE_CELL_TYPE::Layers && event->pos().x() >= 15)
1145 {
1146 editLayerProperties(layer);
1147 }
1148 }
1149 QWidget::mouseDoubleClickEvent(event);
1150}
1151
1152void TimeLineCells::editLayerProperties(Layer *layer) const
1153{
1154 if (layer->type() != Layer::CAMERA)
1155 {
1156 editLayerName(layer);
1157 return;
1158 }
1159
1160 auto cameraLayer = dynamic_cast<LayerCamera*>(layer);
1161 Q_ASSERT(cameraLayer);
1162 editLayerProperties(cameraLayer);
1163}
1164
1165void TimeLineCells::editLayerProperties(LayerCamera* cameraLayer) const
1166{
1167 QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+");
1168
1169 CameraPropertiesDialog dialog(cameraLayer->name(), cameraLayer->getViewRect().width(),
1170 cameraLayer->getViewRect().height());
1171 if (dialog.exec() != QDialog::Accepted)
1172 {
1173 return;
1174 }
1175 QString name = dialog.getName().replace(regex, "");
1176
1177 if (!name.isEmpty())
1178 {
1179 mEditor->layers()->renameLayer(cameraLayer, name);
1180 }
1181 QSettings settings(PENCIL2D, PENCIL2D);
1182 settings.setValue(SETTING_FIELD_W, dialog.getWidth());
1183 settings.setValue(SETTING_FIELD_H, dialog.getHeight());
1184 cameraLayer->setViewRect(QRect(-dialog.getWidth() / 2, -dialog.getHeight() / 2, dialog.getWidth(), dialog.getHeight()));
1185 mEditor->view()->forceUpdateViewTransform();
1186}
1187
1188void TimeLineCells::editLayerName(Layer* layer) const
1189{
1190 QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+");
1191
1192 bool ok;
1193 QString name = QInputDialog::getText(nullptr, tr("Layer Properties"),
1194 tr("Layer name:"), QLineEdit::Normal,
1195 layer->name(), &ok);
1196 name.replace(regex, "");
1197 if (!ok || name.isEmpty())
1198 {
1199 return;
1200 }
1201
1202 mEditor->layers()->renameLayer(layer, name);
1203}
1204
1205void TimeLineCells::hScrollChange(int x)
1206{
1207 mFrameOffset = x;
1208 updateContent();
1209}
1210
1211void TimeLineCells::vScrollChange(int x)
1212{
1213 mLayerOffset = x;
1214 mScrollingVertically = true;
1215 updateContent();
1216}
1217
1218void TimeLineCells::onScrollingVerticallyStopped()
1219{
1220 mScrollingVertically = false;
1221}
1222
1223void TimeLineCells::setMouseMoveY(int x)
1224{
1225 mMouseMoveY = x;
1226 updateContent();
1227}
1228
1229bool TimeLineCells::trackScrubber()
1230{
1231 if (mType != TIMELINE_CELL_TYPE::Tracks ||
1232 (mPrevFrame == mEditor->currentFrame() && !mEditor->playback()->isPlaying()))
1233 {
1234 return false;
1235 }
1236 mPrevFrame = mEditor->currentFrame();
1237
1238 if (mEditor->currentFrame() <= mFrameOffset)
1239 {
1240 // Move the timeline back if the scrubber is offscreen to the left
1241 mFrameOffset = mEditor->currentFrame() - 1;
1242 emit offsetChanged(mFrameOffset);
1243 return true;
1244 }
1245 else if (width() < (mEditor->currentFrame() - mFrameOffset + 1) * mFrameSize)
1246 {
1247 // Move timeline forward if the scrubber is offscreen to the right
1248 if (mEditor->playback()->isPlaying())
1249 mFrameOffset = mFrameOffset + ((mEditor->currentFrame() - mFrameOffset) / 2);
1250 else
1251 mFrameOffset = mEditor->currentFrame() - width() / mFrameSize;
1252 emit offsetChanged(mFrameOffset);
1253 return true;
1254 }
1255 return false;
1256}
1257
1258void TimeLineCells::onDidLeaveWidget()
1259{
1260 // Reset last known frame pos to avoid wrong UI states when leaving the widget
1261 mFramePosMoveX = 0;
1262 update();
1263}
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:403
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
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 May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39