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
477 connect(m_d->canvas->animationState(), &KisCanvasAnimationState::sigPlaybackStatisticsUpdated,
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 if (actionManager) {
518 // Connect actions..
519 KisAction *action = nullptr;
520
521 action = actionManager->createAction("stop_playback");
523 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(stop()));
524
525 action = actionManager->createAction("previous_frame");
527 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(previousFrame()));
528
529 action = actionManager->createAction("next_frame");
531 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(nextFrame()));
532
533 action = actionManager->createAction("previous_keyframe");
535 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(previousKeyframe()));
536
537 action = actionManager->createAction("next_keyframe");
539 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(nextKeyframe()));
540
541 action = actionManager->createAction("previous_matching_keyframe");
543 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(previousMatchingKeyframe()));
544
545 action = actionManager->createAction("next_matching_keyframe");
547 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(nextMatchingKeyframe()));
548
549 action = actionManager->createAction("previous_unfiltered_keyframe");
551 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(previousUnfilteredKeyframe()));
552
553 action = actionManager->createAction("next_unfiltered_keyframe");
555 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(nextUnfilteredKeyframe()));
556
557 action = actionManager->createAction("first_frame");
559 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(firstFrame()));
560
561 action = actionManager->createAction("last_frame");
563 connect(action, SIGNAL(triggered(bool)), playbackEngine, SLOT(lastFrame()));
564 }
565
566 // Connect transport controls gui..
567 connect(m_d->titlebar->transport, SIGNAL(skipBack()), playbackEngine, SLOT(previousKeyframe()), Qt::UniqueConnection);
568 connect(m_d->titlebar->transport, SIGNAL(back()), playbackEngine, SLOT(previousFrame()), Qt::UniqueConnection);
569 connect(m_d->titlebar->transport, SIGNAL(stop()), playbackEngine, SLOT(stop()), Qt::UniqueConnection);
570 connect(m_d->titlebar->transport, SIGNAL(playPause()), this, SLOT(togglePlayback()), Qt::UniqueConnection);
571 connect(m_d->titlebar->transport, SIGNAL(forward()), playbackEngine, SLOT(nextFrame()), Qt::UniqueConnection);
572 connect(m_d->titlebar->transport, SIGNAL(skipForward()), playbackEngine, SLOT(nextKeyframe()), Qt::UniqueConnection);
573
574 connect(m_d->titlebar->frameRegister, SIGNAL(valueChanged(int)), playbackEngine, SLOT(seek(int)), Qt::UniqueConnection);
575}
576
578{
579 m_d->framesModel->setFrameCache(m_d->canvas->frameCache());
580}
581
583{
584 if (!m_d->canvas && !m_d->canvas->image()) {
585 return;
586 }
587
588 const int frame = m_d->canvas->animationState()->displayProxy()->activeFrame();
589
590 QSignalBlocker blocker(m_d->titlebar->frameRegister);
591 m_d->titlebar->frameRegister->setValue(frame);
592}
593
595{
596 if (!m_d->playbackEngine) return;
597
598 QString playbackEngineClass = m_d->playbackEngine->metaObject()->className();
599
600 qreal effectiveFps = 0.0;
601 qreal realFps = 0.0;
602 qreal framesDropped = 0.0;
603 bool isPlaying = false;
604
605 {
606 KisPlaybackEngine::PlaybackStats stats = m_d->playbackEngine->playbackStatistics();
607 effectiveFps = stats.expectedFps;
608 realFps = stats.realFps;
609 framesDropped = stats.droppedFramesPortion;
610 isPlaying = effectiveFps > 0.0;
611 }
612
613 KisConfig cfg(true);
614 const bool shouldDropFrames = cfg.animationDropFrames();
615
616 QAction *action = m_d->titlebar->btnDropFrames->defaultAction();
617 const bool droppingFrames = framesDropped > 0.05;
618 action->setIcon(KisIconUtils::loadIcon(droppingFrames ? "droppedframes" : "dropframe"));
619
620 QString actionText;
621 if (!isPlaying) {
622 actionText = QString("%1 (%2)\n"
623 "%3\n"
624 "[PlaybackEngine: %4]")
626 .arg(KritaUtils::toLocalizedOnOff(shouldDropFrames))
627 .arg(i18n("Enable to preserve playback timing."))
628 .arg(playbackEngineClass);
629 } else {
630 actionText = QString("%1 (%2)\n"
631 "%3\n"
632 "%4\n"
633 "%5\n"
634 "[PlaybackEngine: %6]")
636 .arg(KritaUtils::toLocalizedOnOff(shouldDropFrames))
637 .arg(i18n("Effective FPS:\t%1", QString::number(effectiveFps, 'f', 1)))
638 .arg(i18n("Real FPS:\t%1", QString::number(realFps, 'f', 1)))
639 .arg(i18n("Frames dropped:\t%1\%", QString::number(framesDropped * 100, 'f', 1)))
640 .arg(playbackEngineClass);
641 }
642
649 action->setToolTip(actionText);
650}
651
656
658{
659 m_d->mainWindow = view->mainWindow();
660 KisActionManager *actionManager = view->actionManager();
661 m_d->framesView->setActionManager(actionManager);
662
663 KisAction *action = 0;
664
665 KisAnimTimelineDockerTitlebar* titleBar = static_cast<KisAnimTimelineDockerTitlebar*>(titleBarWidget());
666
667 action = actionManager->actionByName("add_blank_frame");
668 titleBar->btnAddKeyframe->setDefaultAction(action);
669 titleBar->btnAddKeyframe->setIconSize(QSize(22, 22));
670
671 action = actionManager->actionByName("add_duplicate_frame");
672 titleBar->btnDuplicateKeyframe->setDefaultAction(action);
673 titleBar->btnDuplicateKeyframe->setIconSize(QSize(22, 22));
674
675 action = actionManager->actionByName("remove_frames");
676 titleBar->btnRemoveKeyframe->setDefaultAction(action);
677 titleBar->btnRemoveKeyframe->setIconSize(QSize(22, 22));
678
679 // Connect playback-related actions..
680 action = actionManager->createAction("toggle_playback");
682 connect(action, SIGNAL(triggered(bool)), this, SLOT(togglePlayback()));
683
684 establishPlaybackEngineConnections(m_d->playbackEngine, actionManager);
685
686 {
687 action = actionManager->createAction("auto_key");
688 m_d->titlebar->btnAutoKey->setDefaultAction(action);
689 m_d->titlebar->btnAutoKey->setIconSize(QSize(22, 22));
690 connect(action, SIGNAL(triggered(bool)), SLOT(setAutoKey(bool)));
691
692 KisImageConfig config(true);
693 action->setChecked(config.autoKeyEnabled());
694 action->setIcon(config.autoKeyEnabled() ? KisIconUtils::loadIcon("auto-key-on") : KisIconUtils::loadIcon("auto-key-off"));
695
696 const bool autoKeyModeDuplicate = config.autoKeyModeDuplicate();
697 m_d->titlebar->autoKeyBlank->setChecked(!autoKeyModeDuplicate);
698 m_d->titlebar->autoKeyDuplicate->setChecked(autoKeyModeDuplicate);
699 }
700
701 {
702 action = actionManager->createAction("drop_frames");
703 m_d->titlebar->btnDropFrames->setDefaultAction(action);
704 m_d->titlebar->btnDropFrames->setIconSize(QSize(22, 22));
705
706 using namespace KisWidgetConnectionUtils;
707 connectControl(action, &m_d->controlsModel, "dropFramesMode");
708 }
709}
710
712{
713 if (!playbackEngine) return;
714
715 m_d->controlsModel.connectPlaybackEngine(playbackEngine);
716
717 m_d->playbackEngine = playbackEngine;
718
719 KisActionManager *actionManager = nullptr;
720 if (m_d->canvas && m_d->canvas->viewManager()) {
721 actionManager = m_d->canvas->viewManager()->actionManager();
722 }
723 establishPlaybackEngineConnections(m_d->playbackEngine, actionManager);
724}
725
727{
728 m_d->playbackEngine->playPause();
729
730 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
731 KisCanvasAnimationState *animState = m_d->canvas->animationState();
732
733 if (!animInterface || !animState) return;
734
735 if (animState->playbackState() == PlaybackState::PAUSED) {
736 QModelIndexList selectedIndices = m_d->framesView->selectionModel()->selectedIndexes();
737 QModelIndex currentIndex = m_d->framesView->currentIndex();
738 const int pausedTime = animInterface->currentTime();
739
740 QModelIndex pauseIndex = m_d->framesModel->index(currentIndex.row(), pausedTime);
741
742 if (!pauseIndex.isValid()) return;
743
744 if (!selectedIndices.contains(pauseIndex)) {
745 m_d->framesView->setCurrentIndex(pauseIndex);
746 }
747 }
748}
749
751{
752 {
753 KisImageConfig cfg(false);
754 if (value != cfg.autoKeyEnabled()) {
756 const QIcon icon = cfg.autoKeyEnabled() ? KisIconUtils::loadIcon("auto-key-on") : KisIconUtils::loadIcon("auto-key-off");
757 QAction* action = m_d->titlebar->btnAutoKey->defaultAction();
758 action->setIcon(icon);
759 }
760 }
762}
763
765{
766 if (!m_d->canvas || !m_d->canvas->image()) return;
767
768 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
769
770 m_d->titlebar->sbFrameRate->setValue(animInterface->framerate());
771}
float value(const T *src, size_t ch)
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 establishPlaybackEngineConnections(KisPlaybackEngine *playbackEngine, KisActionManager *actionManager=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:130
void playbackEngineChanged(KisPlaybackEngine *newPlaybackEngine)
Krita's base animation playback engine for producing image frame changes and associated audio.
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