Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAnimTimelineDocker.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com>
3 * SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
4 * SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
10
11#include <QPointer>
12#include "QHBoxLayout"
13#include "QVBoxLayout"
14#include "QFormLayout"
15#include "QLabel"
16#include "QToolButton"
17#include "QMenu"
18#include "QWidgetAction"
19#include <QActionGroup>
20
21#include "krita_utils.h"
22#include "kis_canvas2.h"
23#include "kis_image.h"
24#include <KoIcon.h>
25#include "KisViewManager.h"
26#include "kis_paint_layer.h"
27#include "KisDocument.h"
28#include "kis_dummies_facade.h"
30#include "kis_action.h"
31#include "kis_action_manager.h"
34#include "KisAnimUtils.h"
35#include "kis_image_config.h"
38#include "kis_image.h"
39#include "KisPart.h"
40#include "KisPlaybackEngine.h"
42
45#include "kis_time_span.h"
49#include "kis_node_manager.h"
52#include "kis_slider_spin_box.h"
53#include "kis_signals_blocker.h"
54#include "KisMainWindow.h"
59
60
62 KisUtilityTitleBar(new QLabel(i18n("Animation Timeline"), parent), parent)
63{
64 setFocusPolicy(Qt::ClickFocus);
65
66 // Transport Controls...
69 widgetAreaLayout->addWidget(transport);
70
71 widgetAreaLayout->addSpacing(SPACING_UNIT);
72
73 // Frame Register...
75 frameRegister->setToolTip(i18n("Frame register"));
76 frameRegister->setPrefix("# ");
77 frameRegister->setRange(0, MAX_FRAMES);
79
80 widgetAreaLayout->addSpacing(SPACING_UNIT);
81
82 { // Drop Frames..
83 btnDropFrames = new QToolButton(this);
84 btnDropFrames->setAutoRaise(true);
86
87 // Playback Speed..
88 sbSpeed = new KisSliderSpinBox(this);
89 sbSpeed->setRange(25, 200);
90 sbSpeed->setSingleStep(5);
91 sbSpeed->setValue(100);
92 sbSpeed->setPrefix(i18nc("preview playback speed percentage prefix", "Speed: "));
93 sbSpeed->setSuffix(" %");
94 sbSpeed->setToolTip(i18n("Preview playback speed"));
95
96 widgetAreaLayout->addWidget(sbSpeed);
97 }
98
99 widgetAreaLayout->addSpacing(SPACING_UNIT);
100
101 { // Frame ops...
102 QWidget *widget = new QWidget(this);
103 QHBoxLayout *layout = new QHBoxLayout(widget);
104 layout->setSpacing(0);
105 layout->setContentsMargins(0,0,0,0);
106
107 btnAddKeyframe = new QToolButton(this);
108 btnAddKeyframe->setAutoRaise(true);
109 layout->addWidget(btnAddKeyframe);
110
111 btnDuplicateKeyframe = new QToolButton(this);
112 btnDuplicateKeyframe->setAutoRaise(true);
113 layout->addWidget(btnDuplicateKeyframe);
114
115 btnRemoveKeyframe = new QToolButton(this);
116 btnRemoveKeyframe->setAutoRaise(true);
117 layout->addWidget(btnRemoveKeyframe);
118
119 widgetAreaLayout->addWidget(widget);
120 }
121
122 widgetAreaLayout->addStretch();
123
124 { // Menus..
125 QWidget *widget = new QWidget(this);
126
127 QHBoxLayout *layout = new QHBoxLayout(widget);
128 layout->setSpacing(0);
129 layout->setContentsMargins(SPACING_UNIT,0,0,0);
130
131 // Onion skins menu.
132 btnOnionSkinsMenu = new QToolButton(this);
133 btnOnionSkinsMenu->setIcon(KisIconUtils::loadIcon("onion_skin_options"));
134 btnOnionSkinsMenu->setToolTip(i18n("Onion skins menu"));
135 btnOnionSkinsMenu->setIconSize(QSize(22, 22));
136 btnOnionSkinsMenu->setAutoRaise(true);
137 layout->addWidget(btnOnionSkinsMenu);
138
139 { // Audio menu..
140 QMenu *audioMenu = new QMenu(this);
141
142 strImportAudio = QString(i18nc("@item:inmenu Load audio file into Krita from disk.", "Import Audio..."));
143 importAudioAction = new QAction(strImportAudio, audioMenu);
144 removeAudioAction = new QAction(i18nc("@item:inmenu", "Remove audio"), audioMenu);
145
146 muteAudioAction = new QAction(i18nc("@item:inmenu Mute audio playback.", "Mute"), audioMenu);
147 muteAudioAction->setCheckable(true);
148
149 volumeSlider = new KisSliderSpinBox(audioMenu);
150 volumeSlider->setRange(0, 100);
153 i18nc("@item:inmenu Volume slider; {n} is the number value, % is the percent sign", "Volume: {n}%"));
154 volumeSlider->setSingleStep(1);
156 volumeSlider->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
157
158 QWidgetAction *volumeAction = new QWidgetAction(audioMenu);
159 volumeAction->setDefaultWidget(volumeSlider);
160
161 audioMenu->addSeparator();
162
163 audioMenu->addAction(importAudioAction);
164 audioMenu->addAction(removeAudioAction);
165
166 audioMenu->addSeparator();
167
168 audioMenu->addAction(volumeAction);
169 audioMenu->addAction(muteAudioAction);
170
171 btnAudioMenu = new QToolButton(this);
172 btnAudioMenu->setIcon(KisIconUtils::loadIcon("audio-none"));
173 btnAudioMenu->setToolTip(i18n("Animation audio menu"));
174 btnAudioMenu->setIconSize(QSize(22, 22));
175 btnAudioMenu->setAutoRaise(true);
176
177 btnAudioMenu->setPopupMode(QToolButton::InstantPopup);
178 btnAudioMenu->setMenu(audioMenu);
179 btnAudioMenu->setEnabled(false); // To be enabled on set canvas...
180
181 layout->addWidget(btnAudioMenu);
182 }
183
184 { // Settings menu..
185 btnSettingsMenu = new QToolButton(this);
186 btnSettingsMenu->setIcon(KisIconUtils::loadIcon("view-choose-22"));
187 btnSettingsMenu->setToolTip(i18n("Animation settings menu"));
188 btnSettingsMenu->setIconSize(QSize(22, 22));
189 btnSettingsMenu->setAutoRaise(true);
190
191 QWidget *settingsMenuWidget = new QWidget(this);
192 QHBoxLayout *settingsMenuLayout = new QHBoxLayout(settingsMenuWidget);
193
194 QWidget *fields = new QWidget(settingsMenuWidget);
195 QFormLayout *fieldsLayout = new QFormLayout(fields);
196
197 sbStartFrame = new KisIntParseSpinBox(settingsMenuWidget);
198 sbStartFrame->setMaximum(10000);
199 fieldsLayout->addRow(i18n("Clip Start: "), sbStartFrame);
200
201 sbEndFrame = new KisIntParseSpinBox(settingsMenuWidget);
202 sbEndFrame->setMaximum(10000);
203 fieldsLayout->addRow(i18n("Clip End: "), sbEndFrame);
204
205 sbFrameRate = new KisIntParseSpinBox(settingsMenuWidget);
206 sbFrameRate->setMinimum(0);
207 sbFrameRate->setMaximum(120);
208 fieldsLayout->addRow(i18n("Frame Rate: "), sbFrameRate);
209
210 QWidget *buttons = new QWidget(settingsMenuWidget);
211 QVBoxLayout *buttonsLayout = new QVBoxLayout(buttons);
212 buttonsLayout->setAlignment(Qt::AlignTop);
213
214 { // AutoKey..
215 // AutoKey Actions & Action Group..
216 autoKeyBlank = new QAction(i18n("AutoKey Blank"), this);
217 autoKeyBlank->setCheckable(true);
218 autoKeyDuplicate = new QAction(i18n("AutoKey Duplicate"), this);
219 autoKeyDuplicate->setCheckable(true);
220 QActionGroup *autoKeyModes = new QActionGroup(this);
221 autoKeyModes->addAction(autoKeyBlank);
222 autoKeyModes->addAction(autoKeyDuplicate);
223 autoKeyModes->setExclusive(true);
224
225 connect(autoKeyModes, &QActionGroup::triggered, this, [this](QAction* modeAction){
226 if (!modeAction) return;
227 {
228 KisImageConfig imageCfg(false);
229 if (modeAction == autoKeyBlank) {
230 imageCfg.setAutoKeyModeDuplicate(false);
231 } else if (modeAction == autoKeyDuplicate) {
232 imageCfg.setAutoKeyModeDuplicate(true);
233 }
234 }
236 });
237
238 // AutoKey Mode Menu..
239 QMenu *autoKeyModeMenu = new QMenu(settingsMenuWidget);
240 autoKeyModeMenu->addActions(autoKeyModes->actions());
241
242 // AutoKey Button..
243 btnAutoKey = new QToolButton(settingsMenuWidget);
244 btnAutoKey->setMenu(autoKeyModeMenu);
245 btnAutoKey->setPopupMode(QToolButton::MenuButtonPopup);
246 buttonsLayout->addWidget(btnAutoKey);
247 }
248
249 settingsMenuLayout->addWidget(fields);
250 settingsMenuLayout->addWidget(buttons);
251
252 layout->addWidget(btnSettingsMenu);
253
254 QMenu *settingsPopMenu = new QMenu(this);
255 QWidgetAction *settingsMenuAction = new QWidgetAction(this);
256 settingsMenuAction->setDefaultWidget(settingsMenuWidget);
257 settingsPopMenu->addAction(settingsMenuAction);
258
259 btnSettingsMenu->setPopupMode(QToolButton::InstantPopup);
260 btnSettingsMenu->setMenu(settingsPopMenu);
261 }
262
263 widgetAreaLayout->addWidget(widget);
264 }
265}
266
267
268
297
298
299
301 : QDockWidget(i18n("Animation Timeline"))
302 , m_d(new Private(this))
303{
304 setWidget(m_d->framesView);
305
306 // Titlebar Widget..
307 setTitleBarWidget(m_d->titlebar);
308
309 connect(m_d->titlebar->btnOnionSkinsMenu, &QToolButton::released, [this](){
310 if (m_d->mainWindow) {
311 QDockWidget *docker = m_d->mainWindow->dockWidget("OnionSkinsDocker");
312 if (docker) {
313 docker->setVisible(!docker->isVisible());
314 }
315 }
316 });
317
318 {
319 using namespace KisWidgetConnectionUtils;
320 connectControl(m_d->titlebar->sbSpeed, &m_d->controlsModel, "playbackSpeedDenorm");
321 }
322
323 // Watch for KisPlaybackEngine changes and initialize current one..
325 setPlaybackEngine(KisPart::instance()->playbackEngine());
326
327 setEnabled(false);
328}
329
333
335{
337
338 KisLayerSP addPaintLayer() const override {
339 return m_manager->createPaintLayer();
340 }
341
342 void removeNode(KisNodeSP node) const override {
344 }
345
346 bool setNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const override
347 {
348 return m_manager->trySetNodeProperties(node, image, properties);
349 }
350
351private:
353};
354
356{
357 if (m_d->canvas == canvas) return;
358
359 if (m_d->framesModel->hasConnectionToCanvas()) {
360 m_d->canvasConnections.clear();
361 m_d->framesModel->setDummiesFacade(0, 0, 0);
362 m_d->framesModel->setFrameCache(0);
363 m_d->framesModel->setAnimationPlayer(0);
364 m_d->framesModel->setDocument(0);
365 m_d->framesModel->setNodeManipulationInterface(0);
366 }
367
368 // Deinitialize from previous canvas...
369 if (m_d->canvas) {
370 m_d->canvas->disconnectCanvasObserver(this);
371 m_d->canvas->animationState()->disconnect(this);
372 m_d->titlebar->transport->setPlaying(false);
373
374 m_d->titlebar->btnAudioMenu->setEnabled(false);
375
376 if(m_d->canvas->image()) {
377 m_d->canvas->image()->animationInterface()->disconnect(this);
378 m_d->titlebar->sbStartFrame->disconnect(m_d->canvas->image()->animationInterface());
379 m_d->titlebar->sbEndFrame->disconnect(m_d->canvas->image()->animationInterface());
380 m_d->titlebar->sbFrameRate->disconnect(m_d->canvas->image()->animationInterface());
381 }
382 }
383
384 m_d->canvas = dynamic_cast<KisCanvas2*>(canvas);
385 setEnabled(m_d->canvas != 0);
386 m_d->framesView->slotCanvasUpdate(m_d->canvas);
387
388 // Reinitialize new canvas..
389 if (m_d->canvas) {
390 KisDocument *doc = static_cast<KisDocument*>(m_d->canvas->imageView()->document());
391 KisShapeController *kritaShapeController = dynamic_cast<KisShapeController*>(doc->shapeController());
392 m_d->framesModel->setDummiesFacade(kritaShapeController,
393 m_d->canvas->image(),
395
396 m_d->framesModel->setDocument(doc);
397
399
400 { // Titlebar widgets...
401 KisSignalsBlocker blocker(m_d->titlebar->sbStartFrame,
402 m_d->titlebar->sbEndFrame,
403 m_d->titlebar->sbFrameRate,
404 m_d->titlebar->frameRegister);
405
406 KisImageAnimationInterface *animinterface = m_d->canvas->image()->animationInterface();
407 m_d->titlebar->sbStartFrame->setValue(animinterface->documentPlaybackRange().start());
408 m_d->titlebar->sbEndFrame->setValue(animinterface->documentPlaybackRange().end());
409 m_d->titlebar->sbFrameRate->setValue(animinterface->framerate());
410 m_d->titlebar->frameRegister->setValue(animinterface->currentTime());
411
412 m_d->titlebar->btnAudioMenu->setEnabled(true); // Menu is disabled until a canvas is loaded.
413 }
414
415 m_d->framesModel->setAnimationPlayer(m_d->canvas->animationState());
416
417 m_d->framesModel->setNodeManipulationInterface(
418 new NodeManagerInterface(m_d->canvas->viewManager()->nodeManager()));
419
420 m_d->canvasConnections.addConnection(
421 m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigNodeActivated(KisNodeSP)),
422 m_d->framesModel, SLOT(slotCurrentNodeChanged(KisNodeSP)));
423
424 m_d->canvasConnections.addConnection(
425 m_d->framesModel, SIGNAL(requestCurrentNodeChanged(KisNodeSP)),
426 m_d->canvas->viewManager()->nodeManager(), SLOT(slotNonUiActivatedNode(KisNodeSP)));
427
428 m_d->framesModel->slotCurrentNodeChanged(m_d->canvas->viewManager()->activeNode());
429
430 m_d->canvasConnections.addConnection(
431 m_d->canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()),
432 this, SLOT(handleThemeChange()));
433
434 m_d->canvasConnections.addConnection(
435 m_d->canvas, SIGNAL(sigCanvasEngineChanged()),
436 this, SLOT(updateFrameCache()));
437
438 m_d->titlebar->transport->setPlaying(m_d->canvas->animationState()->playbackState() == PlaybackState::PLAYING);
439
440 m_d->titlebar->volumeSlider->setValue(m_d->framesModel->audioVolume() * 100.0);
441
442 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), SLOT(handleFrameRateChange()));
443
444 connect(m_d->titlebar->sbFrameRate, SIGNAL(valueChanged(int)), this, SLOT(setImageAnimSettings()));
445 connect(m_d->titlebar->sbStartFrame, SIGNAL(valueChanged(int)), this, SLOT(setImageAnimSettings()));
446 connect(m_d->titlebar->sbEndFrame, SIGNAL(valueChanged(int)), this, SLOT(setImageAnimSettings()));
447
448 connect(m_d->canvas->image()->animationInterface(), &KisImageAnimationInterface::sigDocumentRangeChanged, this, [this]() {
449 if (!m_d->canvas || !m_d->canvas->image()) return;
450
451 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
452
453 int start = animInterface->documentPlaybackRange().start();
454 int end = animInterface->documentPlaybackRange().end();
455
456 m_d->titlebar->sbStartFrame->setValue(start);
457 m_d->titlebar->sbEndFrame->setValue(end);
458
459 //Auto zoom Timeline to new document playback range..
460 KisConfig cfg(true);
461 if (cfg.autoZoomTimelineToPlaybackRange()) {
462 m_d->framesView->slotFitViewToFrameRange(0, end); // TODO: fit from start to end, instead of 0 to end.
463 }
464 });
465
466 connect(m_d->canvas->animationState(), SIGNAL(sigFrameChanged()), this, SLOT(updateFrameRegister()));
467 connect(m_d->canvas->animationState(), &KisCanvasAnimationState::sigPlaybackStateChanged, this, [this](PlaybackState state){
468 m_d->titlebar->frameRegister->setDisabled(state == PlaybackState::PLAYING);
469 if (state == PlaybackState::STOPPED) {
470 updateFrameRegister();
471 }
472 });
473 connect(m_d->canvas->animationState(), &KisCanvasAnimationState::sigPlaybackStateChanged, this, [this](PlaybackState state){
474 m_d->titlebar->transport->setPlaying(state == PlaybackState::PLAYING);
475 });
476
479
480 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigUiTimeChanged(int)), this, SLOT(updateFrameRegister()));
481
482 m_d->controlsModel.connectAnimationState(m_d->canvas->animationState());
483 }
484}
485
487{
488 if (m_d->framesView) {
489 m_d->framesView->slotUpdateIcons();
490 }
491}
492
494{
495 if (!m_d->canvas || !m_d->canvas->image()) return;
496
498 m_d->titlebar->sbFrameRate->value(),
499 m_d->titlebar->sbStartFrame->value(),
500 m_d->titlebar->sbEndFrame->value()
501 };
502
503 KisImageAnimSettingCommand *undoCommand = new KisImageAnimSettingCommand(m_d->canvas->image()->animationInterface(), settings);
504
505 //KisProcessingApplicator::applyCommand(undoCommand);
506 QScopedPointer<KisProcessingApplicator> applicator(
509 undoCommand->text(),
510 0, undoCommand->id()));
511 applicator->applyCommand(undoCommand);
512 applicator->end();
513}
514
516{
517 m_d->framesModel->setFrameCache(m_d->canvas->frameCache());
518}
519
521{
522 if (!m_d->canvas && !m_d->canvas->image()) {
523 return;
524 }
525
526 const int frame = m_d->canvas->animationState()->displayProxy()->activeFrame();
527
528 QSignalBlocker blocker(m_d->titlebar->frameRegister);
529 m_d->titlebar->frameRegister->setValue(frame);
530}
531
533{
534 if (!m_d->playbackEngine) return;
535
536 QString playbackEngineClass = m_d->playbackEngine->metaObject()->className();
537
538 qreal effectiveFps = 0.0;
539 qreal realFps = 0.0;
540 qreal framesDropped = 0.0;
541 bool isPlaying = false;
542
543 {
544 KisPlaybackEngine::PlaybackStats stats = m_d->playbackEngine->playbackStatistics();
545 effectiveFps = stats.expectedFps;
546 realFps = stats.realFps;
547 framesDropped = stats.droppedFramesPortion;
548 isPlaying = effectiveFps > 0.0;
549 }
550
551 KisConfig cfg(true);
552 const bool shouldDropFrames = cfg.animationDropFrames();
553
554 QAction *action = m_d->titlebar->btnDropFrames->defaultAction();
555 const bool droppingFrames = framesDropped > 0.05;
556 action->setIcon(KisIconUtils::loadIcon(droppingFrames ? "droppedframes" : "dropframe"));
557
558 QString actionText;
559 if (!isPlaying) {
560 actionText = QString("%1 (%2)\n"
561 "%3\n"
562 "[PlaybackEngine: %4]")
564 .arg(KritaUtils::toLocalizedOnOff(shouldDropFrames))
565 .arg(i18n("Enable to preserve playback timing."))
566 .arg(playbackEngineClass);
567 } else {
568 actionText = QString("%1 (%2)\n"
569 "%3\n"
570 "%4\n"
571 "%5\n"
572 "[PlaybackEngine: %6]")
574 .arg(KritaUtils::toLocalizedOnOff(shouldDropFrames))
575 .arg(i18n("Effective FPS:\t%1", QString::number(effectiveFps, 'f', 1)))
576 .arg(i18n("Real FPS:\t%1", QString::number(realFps, 'f', 1)))
577 .arg(i18n("Frames dropped:\t%1\%", QString::number(framesDropped * 100, 'f', 1)))
578 .arg(playbackEngineClass);
579 }
580
587 action->setToolTip(actionText);
588}
589
594
596{
597 m_d->mainWindow = view->mainWindow();
598 KisActionManager *actionManager = view->actionManager();
599 m_d->framesView->setActionManager(actionManager);
600
601 KisAction *action = 0;
602
603 KisAnimTimelineDockerTitlebar* titleBar = static_cast<KisAnimTimelineDockerTitlebar*>(titleBarWidget());
604
605 action = actionManager->actionByName("add_blank_frame");
606 titleBar->btnAddKeyframe->setDefaultAction(action);
607 titleBar->btnAddKeyframe->setIconSize(QSize(22, 22));
608
609 action = actionManager->actionByName("add_duplicate_frame");
610 titleBar->btnDuplicateKeyframe->setDefaultAction(action);
611 titleBar->btnDuplicateKeyframe->setIconSize(QSize(22, 22));
612
613 action = actionManager->actionByName("remove_frames");
614 titleBar->btnRemoveKeyframe->setDefaultAction(action);
615 titleBar->btnRemoveKeyframe->setIconSize(QSize(22, 22));
616
617 // Connect playback-related actions..
618 action = actionManager->createAction("toggle_playback");
620 connect(action, SIGNAL(triggered(bool)), this, SLOT(togglePlayback()));
621
622 action = actionManager->createAction("stop_playback");
624 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(stop()));
625
626 action = actionManager->createAction("previous_frame");
628 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(previousFrame()));
629
630 action = actionManager->createAction("next_frame");
632 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(nextFrame()));
633
634 action = actionManager->createAction("previous_keyframe");
636 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(previousKeyframe()));
637
638 action = actionManager->createAction("next_keyframe");
640 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(nextKeyframe()));
641
642 action = actionManager->createAction("previous_matching_keyframe");
644 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(previousMatchingKeyframe()));
645
646 action = actionManager->createAction("next_matching_keyframe");
648 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(nextMatchingKeyframe()));
649
650 action = actionManager->createAction("previous_unfiltered_keyframe");
652 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(previousUnfilteredKeyframe()));
653
654 action = actionManager->createAction("next_unfiltered_keyframe");
656 connect(action, SIGNAL(triggered(bool)), m_d->playbackEngine, SLOT(nextUnfilteredKeyframe()));
657
658 action = actionManager->createAction("first_frame");
660 connect(action, &KisAction::triggered, this, [this](bool){
661 if (m_d->canvas) {
662 m_d->playbackEngine->firstFrame();
663 }
664 });
665
666 action = actionManager->createAction("last_frame");
668 connect(action, &KisAction::triggered, this, [this](bool){
669 if (m_d->canvas) {
670 m_d->playbackEngine->lastFrame();
671 }
672 });
673
674 {
675 action = actionManager->createAction("auto_key");
676 m_d->titlebar->btnAutoKey->setDefaultAction(action);
677 m_d->titlebar->btnAutoKey->setIconSize(QSize(22, 22));
678 connect(action, SIGNAL(triggered(bool)), SLOT(setAutoKey(bool)));
679
680 KisImageConfig config(true);
681 action->setChecked(config.autoKeyEnabled());
682 action->setIcon(config.autoKeyEnabled() ? KisIconUtils::loadIcon("auto-key-on") : KisIconUtils::loadIcon("auto-key-off"));
683
684 const bool autoKeyModeDuplicate = config.autoKeyModeDuplicate();
685 m_d->titlebar->autoKeyBlank->setChecked(!autoKeyModeDuplicate);
686 m_d->titlebar->autoKeyDuplicate->setChecked(autoKeyModeDuplicate);
687 }
688
689 {
690 action = actionManager->createAction("drop_frames");
691 m_d->titlebar->btnDropFrames->setDefaultAction(action);
692 m_d->titlebar->btnDropFrames->setIconSize(QSize(22, 22));
693
694 using namespace KisWidgetConnectionUtils;
695 connectControl(action, &m_d->controlsModel, "dropFramesMode");
696 }
697}
698
700{
701 if (!playbackEngine) return;
702
703 // Connect transport controls..
704 connect(m_d->titlebar->transport, SIGNAL(skipBack()), playbackEngine, SLOT(previousKeyframe()), Qt::UniqueConnection);
705 connect(m_d->titlebar->transport, SIGNAL(back()), playbackEngine, SLOT(previousFrame()), Qt::UniqueConnection);
706 connect(m_d->titlebar->transport, SIGNAL(stop()), playbackEngine, SLOT(stop()), Qt::UniqueConnection);
707 connect(m_d->titlebar->transport, SIGNAL(playPause()), this, SLOT(togglePlayback()), Qt::UniqueConnection);
708 connect(m_d->titlebar->transport, SIGNAL(forward()), playbackEngine, SLOT(nextFrame()), Qt::UniqueConnection);
709 connect(m_d->titlebar->transport, SIGNAL(skipForward()), playbackEngine, SLOT(nextKeyframe()), Qt::UniqueConnection);
710
711 connect(m_d->titlebar->frameRegister, SIGNAL(valueChanged(int)), playbackEngine, SLOT(seek(int)), Qt::UniqueConnection);
712
713 m_d->controlsModel.connectPlaybackEngine(playbackEngine);
714
715 m_d->playbackEngine = playbackEngine;
716}
717
719{
720 m_d->playbackEngine->playPause();
721
722 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
723 KisCanvasAnimationState *animState = m_d->canvas->animationState();
724
725 if (!animInterface || !animState) return;
726
727 if (animState->playbackState() == PlaybackState::PAUSED) {
728 QModelIndexList selectedIndices = m_d->framesView->selectionModel()->selectedIndexes();
729 QModelIndex currentIndex = m_d->framesView->currentIndex();
730 const int pausedTime = animInterface->currentTime();
731
732 QModelIndex pauseIndex = m_d->framesModel->index(currentIndex.row(), pausedTime);
733
734 if (!pauseIndex.isValid()) return;
735
736 if (!selectedIndices.contains(pauseIndex)) {
737 m_d->framesView->setCurrentIndex(pauseIndex);
738 }
739 }
740}
741
743{
744 {
745 KisImageConfig cfg(false);
746 if (value != cfg.autoKeyEnabled()) {
748 const QIcon icon = cfg.autoKeyEnabled() ? KisIconUtils::loadIcon("auto-key-on") : KisIconUtils::loadIcon("auto-key-off");
749 QAction* action = m_d->titlebar->btnAutoKey->defaultAction();
750 action->setIcon(icon);
751 }
752 }
754}
755
757{
758 if (!m_d->canvas || !m_d->canvas->image()) return;
759
760 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
761
762 m_d->titlebar->sbFrameRate->setValue(animInterface->framerate());
763}
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KUndo2MagicString text() const
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
KisAction * actionByName(const QString &name) const
void setActivationFlags(ActivationFlags flags)
@ ACTIVE_IMAGE
Activate if there is at least one image.
Definition kis_action.h:43
A customized titlebar for the Animation Timeline Docker that's packed with useful widgets and menus.
KisAnimTimelineDockerTitlebar(QWidget *parent=nullptr)
void setPlaybackEngine(KisPlaybackEngine *playbackEngine)
void setViewManager(KisViewManager *kisview) override
void setCanvas(KoCanvasBase *canvas) override
const QScopedPointer< Private > m_d
void setModel(QAbstractItemModel *model) override
KisViewManager * viewManager() const
The KisCanvasAnimationState class stores all of the canvas-specific animation state.
void sigPlaybackStateChanged(PlaybackState state)
bool animationDropFrames(bool defaultValue=false) const
KisShapeController * shapeController
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
static KisImageConfigNotifier * instance()
bool autoKeyEnabled(bool requestDefault=false) const
void setAutoKeyEnabled(bool value)
void setAutoKeyModeDuplicate(bool value)
bool autoKeyModeDuplicate(bool requestDefault=false) const
KisImageAnimationInterface * animationInterface() const
The KisIntParseSpinBox class is a cleverer SpinBox, able to parse arithmetic expressions.
Main window for Krita.
KisNodeDisplayModeAdapter * nodeDisplayModeAdapter() const
bool trySetNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const
void removeSingleNode(KisNodeSP node)
KisLayerSP createPaintLayer()
static KisPart * instance()
Definition KisPart.cpp:131
void playbackEngineChanged(KisPlaybackEngine *newPlaybackEngine)
Krita's base animation playback engine for producing image frame changes and associated audio.
virtual void playPause()
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(int newValue)
void setRange(int newMinimum, int newMaximum, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range, computing a new "fast slider step" based on the ...
void setPageStep(int newPageStep)
Does nothing currently.
int start() const
int end() const
The KisTransportControls class provides a simple, reusable widget for common transport controls,...
A special utility titlebar with a title and controls, as well as a central area for adding frequently...
KisMainWindow * mainWindow() const
KisActionManager * actionManager() const
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
QString buttons(const T &ev)
const QString dropFramesActionName
QIcon loadIcon(const QString &name)
void setText(QSpinBox *spinBox, const QStringView textTemplate)
QString KRITAIMAGE_EXPORT toLocalizedOnOff(bool value)
KisAnimTimelineDockerTitlebar * titlebar
KisSignalAutoConnectionsStore canvasConnections
KisAnimationPlaybackControlsModel controlsModel
KisAnimTimelineFramesModel * framesModel
KisAnimTimelineFramesView * framesView
bool setNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const override
void removeNode(KisNodeSP node) const override
KisLayerSP addPaintLayer() const override
NodeManagerInterface(KisNodeManager *manager)
KisCanvas2 * canvas