Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAnimCurvesDocker.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Jouni Pentikäinen <joupent@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include <QTreeView>
8#include <QSplitter>
9#include <QToolBar>
10#include <QScroller>
11#include <QHBoxLayout>
12#include <QFormLayout>
13#include <QLabel>
14#include <QMenu>
15#include <QToolButton>
16#include <QSizePolicy>
17
18#include "KisAnimCurvesDocker.h"
19#include "KisAnimCurvesModel.h"
20#include "KisAnimCurvesView.h"
23
27
29#include "KisAnimUtils.h"
30#include "kis_image_config.h"
31
32#include "KisDocument.h"
33#include "kis_canvas2.h"
36#include "KisViewManager.h"
37#include "kis_node_manager.h"
39#include "klocalizedstring.h"
40#include "kis_icon_utils.h"
41#include "kis_action_manager.h"
42#include "kis_action.h"
45#include "kis_slider_spin_box.h"
47#include "kis_zoom_button.h"
49#include "kis_signals_blocker.h"
50#include "kis_time_span.h"
52#include "KisMainWindow.h"
53#include "KisPart.h"
54#include "KisPlaybackEngine.h"
55#include <QItemSelection>
58
59
61 KisUtilityTitleBar(new QLabel(i18n("Animation Curves"), parent), parent)
62{
63 // Transport Controls...
65 widgetAreaLayout->addWidget(transport);
66 widgetAreaLayout->addSpacing(SPACING_UNIT);
67
68 // Frame Register...
70 sbFrameRegister->setToolTip(i18n("Frame register"));
71 sbFrameRegister->setPrefix("# ");
72 sbFrameRegister->setRange(0, MAX_FRAMES);
74 widgetAreaLayout->addSpacing(SPACING_UNIT);
75
76 { // Drop Frames..
77 btnDropFrames = new QToolButton(this);
78 btnDropFrames->setAutoRaise(true);
80
81 // Playback Speed..
82 sbSpeed = new KisSliderSpinBox(this);
83 sbSpeed->setRange(25, 200);
84 sbSpeed->setSingleStep(5);
85 sbSpeed->setValue(100);
86 sbSpeed->setPrefix(i18nc("preview playback speed percentage prefix", "Speed: "));
87 sbSpeed->setSuffix(" %");
88 sbSpeed->setToolTip(i18n("Preview playback speed"));
89 widgetAreaLayout->addWidget(sbSpeed);
90 }
91
92 widgetAreaLayout->addSpacing(SPACING_UNIT);
93
94 { // Frame ops...
95 QWidget *widget = new QWidget(this);
96 QHBoxLayout *layout = new QHBoxLayout(widget);
97 layout->setSpacing(0);
98 layout->setContentsMargins(0,0,0,0);
99
100 // Add/Remove Key..
101 btnAddKey = new QToolButton(this);
102 btnAddKey->setAutoRaise(true);
103 layout->addWidget(btnAddKey);
104
105 btnRemoveKey = new QToolButton(this);
106 btnRemoveKey->setAutoRaise(true);
107 layout->addWidget(btnRemoveKey);
108
109 layout->addSpacing(SPACING_UNIT);
110
111 // Interpolation Modes..
114 btnGroupInterpolation->setIconSize(QSize(22, 22));
115 layout->addWidget(btnGroupInterpolation);
116
117 layout->addSpacing(SPACING_UNIT);
118
119 // Tangent Modes..
122 btnGroupTangents->setIconSize(QSize(22, 22));
123 layout->addWidget(btnGroupTangents);
124
125 widgetAreaLayout->addWidget(widget);
126 }
127
128 widgetAreaLayout->addSpacing(SPACING_UNIT);
129
131 sbValueRegister->setPrefix(i18nc("Value (Keep short!)", "Val:"));
132 sbValueRegister->setToolTip(i18n("Value register"));
133 sbValueRegister->setRange(-99000.f, 99000.f);
135
136 widgetAreaLayout->addSpacing(SPACING_UNIT);
137
138 // Zoom buttons..
141 btnGroupZoomFit->setIconSize(QSize(22,22));
143
144 btnZoomHori = new KisZoomButton(this);
145 btnZoomHori->setAutoRaise(true);
146 btnZoomHori->setIcon(KisIconUtils::loadIcon("zoom-horizontal"));
147 btnZoomHori->setToolTip(i18n("Zoom horizontally"));
148 btnZoomHori->setIconSize(QSize(22, 22));
149 widgetAreaLayout->addWidget(btnZoomHori);
150
151 btnZoomVert = new KisZoomButton(this);
152 btnZoomVert->setAutoRaise(true);
153 btnZoomVert->setIcon(KisIconUtils::loadIcon("zoom-vertical"));
154 btnZoomVert->setToolTip(i18n("Zoom vertically"));
155 btnZoomVert->setIconSize(QSize(22, 22));
156 widgetAreaLayout->addWidget(btnZoomVert);
157
158 widgetAreaLayout->addStretch();
159
160 { // Menus..
161 QWidget *widget = new QWidget(this);
162
163 QHBoxLayout *layout = new QHBoxLayout(widget);
164 layout->setSpacing(0);
165 layout->setContentsMargins(SPACING_UNIT,0,0,0);
166
167 // Onion skins menu.
168 btnOnionSkinsMenu = new QToolButton(this);
169 btnOnionSkinsMenu->setAutoRaise(true);
170 btnOnionSkinsMenu->setIcon(KisIconUtils::loadIcon("onion_skin_options"));
171 btnOnionSkinsMenu->setToolTip(i18n("Onion skins menu"));
172 btnOnionSkinsMenu->setIconSize(QSize(22, 22));
173 layout->addWidget(btnOnionSkinsMenu);
174
175 // Audio menu..
176 btnAudioMenu = new QToolButton(this);
177 btnAudioMenu->setAutoRaise(true);
178 btnAudioMenu->setIcon(KisIconUtils::loadIcon("audio-none"));
179 btnAudioMenu->setToolTip(i18n("Audio menu"));
180 btnAudioMenu->setIconSize(QSize(22, 22));
181 btnAudioMenu->hide(); // (NOTE: Hidden for now while audio features develop.)
182 layout->addWidget(btnAudioMenu);
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(180);
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 settingsMenuLayout->addWidget(fields);
215 settingsMenuLayout->addWidget(buttons);
216
217 layout->addWidget(btnSettingsMenu);
218
219 QMenu *settingsPopMenu = new QMenu(this);
220 QWidgetAction *settingsMenuAction = new QWidgetAction(this);
221 settingsMenuAction->setDefaultWidget(settingsMenuWidget);
222 settingsPopMenu->addAction(settingsMenuAction);
223
224 btnSettingsMenu->setPopupMode(QToolButton::InstantPopup);
225 btnSettingsMenu->setMenu(settingsPopMenu);
226 }
227
228 widgetAreaLayout->addWidget(widget);
229 }
230}
231
262
264 : QDockWidget(i18n("Animation Curves"))
265 , m_d(new Private(this))
266{
267 QWidget *mainWidget = new QWidget(0);
268 mainWidget->setLayout(new QVBoxLayout());
269 setWidget(mainWidget);
270
271 QSplitter *mainSplitter = new QSplitter(this);
272 mainWidget->layout()->addWidget(mainSplitter);
273
274 { // Channel Tree..
275 m_d->channelTreeView->setModel(m_d->channelTreeModel);
276 m_d->channelTreeView->setHeaderHidden(true);
278 m_d->channelTreeView->setItemDelegate(listDelegate);
279
280 //Right click menu configuration for Channel Tree
281 m_d->channelTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
282 connect(m_d->channelTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestChannelMenuAt(QPoint)));
283
284 m_d->channelTreeMenuChannels->addSection(i18n("Channel Operations"));
285 m_d->channelTreeMenuLayers->addSection(i18n("Layer Operations"));
286
287 { //Channels Menu
288 QAction* action = new QAction(i18n("Reset Channel"), this);
289 connect(action, SIGNAL(triggered(bool)), this, SLOT(resetChannelTreeSelection()));
290 m_d->channelTreeMenuChannels->addAction(action);
291 }
292
293 { //Layers Menu
294 QAction* action = new QAction(i18n("Reset All Channels"), this);
295 connect(action, SIGNAL(triggered(bool)), this, SLOT(resetChannelTreeSelection()));
296 m_d->channelTreeMenuLayers->addAction(action);
297 }
298 }
299
300 { // Curves View..
301 m_d->curvesView->setModel(m_d->curvesModel);
302 }
303
304 mainSplitter->addWidget(m_d->channelTreeView);
305 mainSplitter->setStretchFactor(0, 1);
306 mainSplitter->addWidget(m_d->curvesView);
307 mainSplitter->setStretchFactor(1, 10);
308
309 // Kinetic Scrolling..
310 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_d->channelTreeView);
311 if (scroller){
312 connect(scroller, SIGNAL(stateChanged(QScroller::State)),
313 this, SLOT(slotScrollerStateChanged(QScroller::State)));
314 }
315
316 connect(m_d->channelTreeModel, &KisAnimCurvesChannelsModel::rowsInserted,
318
319 // Titlebar Widget..
320 setTitleBarWidget(m_d->titlebar);
321 setEnabled(false);
322
323 connect(m_d->titlebar->btnOnionSkinsMenu, &QToolButton::released, [this](){
324 if (m_d->mainWindow) {
325 QDockWidget *docker = m_d->mainWindow->dockWidget("OnionSkinsDocker");
326 if (docker) {
327 docker->setVisible(!docker->isVisible());
328 }
329 }
330 });
331
332 connect(m_d->titlebar->btnZoomHori, &KisZoomButton::zoom, [this](qreal zoomDelta){
333 if (m_d->curvesView) {
334 m_d->curvesView->changeZoom(Qt::Horizontal, zoomDelta);
335 }
336 });
337
338 connect(m_d->titlebar->btnZoomVert, &KisZoomButton::zoom, [this](qreal zoomDelta){
339 if (m_d->curvesView) {
340 m_d->curvesView->changeZoom(Qt::Vertical, zoomDelta);
341 }
342 });
343
344 connect(m_d->curvesView, SIGNAL(activated(QModelIndex)), this, SLOT(slotActiveNodeUpdate(QModelIndex)));
345 connect(m_d->curvesView, SIGNAL(activeDataChanged(QModelIndex)), this, SLOT(slotActiveNodeUpdate(QModelIndex)));
346 connect(m_d->titlebar->sbValueRegister, SIGNAL(valueChanged(double)), this, SLOT(slotValueRegisterChanged(double)));
347
348 {
349 using namespace KisWidgetConnectionUtils;
350 connectControl(m_d->titlebar->sbSpeed, &m_d->controlsModel, "playbackSpeedDenorm");
351 }
352
353 // Watch for KisPlaybackEngine changes and initialize current one..
355 setPlaybackEngine(KisPart::instance()->playbackEngine());
356}
357
360
362{
363 if (canvas && m_d->canvas == canvas) return;
364
365 if (m_d->canvas) {
366 m_d->canvasConnections.clear();
367 m_d->canvas->disconnectCanvasObserver(this);
368 m_d->channelTreeModel->selectedNodesChanged(KisNodeList());
369 m_d->canvas->animationState()->disconnect(this);
370 m_d->titlebar->transport->disconnect(m_d->canvas->animationState());
371 m_d->titlebar->transport->setPlaying(false);
372 m_d->titlebar->sbFrameRegister->disconnect(m_d->canvas->animationState());
373
374 if (m_d->canvas->image()) {
375 m_d->canvas->image()->animationInterface()->disconnect(this);
376 m_d->titlebar->sbStartFrame->disconnect(m_d->canvas->image()->animationInterface());
377 m_d->titlebar->sbEndFrame->disconnect(m_d->canvas->image()->animationInterface());
378 m_d->titlebar->sbFrameRate->disconnect(m_d->canvas->image()->animationInterface());
379 }
380
381 m_d->curvesModel->setImage(0);
382 }
383
384 m_d->canvas = dynamic_cast<KisCanvas2*>(canvas);
385 setEnabled(m_d->canvas != 0);
386
387 if (m_d->canvas) {
388 KisDocument *doc = static_cast<KisDocument*>(m_d->canvas->imageView()->document());
389 KisShapeController *kritaShapeController = dynamic_cast<KisShapeController*>(doc->shapeController());
390 m_d->channelTreeModel->setDummiesFacade(kritaShapeController);
391
392 m_d->curvesModel->setImage(m_d->canvas->image());
393 m_d->curvesModel->setFrameCache(m_d->canvas->frameCache());
394 m_d->curvesModel->setAnimationPlayer(m_d->canvas->animationState());
395
396 m_d->canvasConnections.addConnection(
397 m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)),
398 m_d->channelTreeModel, SLOT(selectedNodesChanged(KisNodeList))
399 );
400
401 m_d->canvasConnections.addConnection(
402 m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigNodeActivated(KisNodeSP)),
403 this, SLOT(slotNodeActivated(KisNodeSP))
404 );
405
406 m_d->channelTreeModel->clear();
407 m_d->channelTreeModel->selectedNodesChanged(m_d->canvas->viewManager()->nodeManager()->selectedNodes());
408
409 { // Reinitialize titlebar widgets..
410 KisSignalsBlocker blocker(m_d->titlebar->sbStartFrame,
411 m_d->titlebar->sbEndFrame,
412 m_d->titlebar->sbFrameRate,
413 m_d->titlebar->sbFrameRegister,
414 m_d->titlebar->sbValueRegister);
415
416 KisImageAnimationInterface *animinterface = m_d->canvas->image()->animationInterface();
417 m_d->titlebar->sbStartFrame->setValue(animinterface->documentPlaybackRange().start());
418 m_d->titlebar->sbEndFrame->setValue(animinterface->documentPlaybackRange().end());
419 m_d->titlebar->sbFrameRate->setValue(animinterface->framerate());
420 m_d->titlebar->sbFrameRegister->setValue(animinterface->currentTime());
421
422 QModelIndex activeIndex = m_d->curvesView->currentIndex();
423 m_d->titlebar->sbValueRegister->setEnabled(activeIndex.isValid());
424 m_d->titlebar->sbValueRegister->setValue( activeIndex.isValid() ?
425 activeIndex.data(KisAnimCurvesModel::ScalarValueRole).toReal() : 0.0f);
426 }
427
428 m_d->titlebar->transport->setPlaying(m_d->canvas->animationState()->playbackState() == PlaybackState::PLAYING);
429
430 connect(m_d->titlebar->sbFrameRate, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setFramerate(int)));
431 connect(m_d->titlebar->sbStartFrame, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setDocumentRangeStartFrame(int)));
432 connect(m_d->titlebar->sbEndFrame, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setDocumentRangeEndFrame(int)));
433
434 connect(m_d->canvas->animationState(), &KisCanvasAnimationState::sigPlaybackStateChanged, m_d->titlebar->transport, [this](PlaybackState p_state){
435 m_d->titlebar->transport->setPlaying(p_state == PlaybackState::PLAYING);
436 m_d->titlebar->sbFrameRegister->setDisabled(p_state == PlaybackState::PLAYING);
437
438 if (p_state == PlaybackState::STOPPED) {
439 updateFrameRegister();
440 }
441 });
442
443 connect(m_d->canvas->animationState(), SIGNAL(sigFrameChanged()), this, SLOT(updateFrameRegister()));
444
445 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigUiTimeChanged(int)), this, SLOT(updateFrameRegister()));
446
447 connect(m_d->canvas->image()->animationInterface(), &KisImageAnimationInterface::sigPlaybackRangeChanged, this, [this]() {
448 if (!m_d->canvas || !m_d->canvas->image()) return;
449
450 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
451
452 m_d->titlebar->sbStartFrame->setValue(animInterface->documentPlaybackRange().start());
453 m_d->titlebar->sbEndFrame->setValue(animInterface->documentPlaybackRange().end());
454 });
455
456 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), this, SLOT(handleFrameRateChange()));
457
458 m_d->controlsModel.connectAnimationState(m_d->canvas->animationState());
459 }
460}
461
466
468{
469 m_d->mainWindow = view->mainWindow();
470
471 connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons()));
473
474 KisActionManager* actionManager = view->actionManager();
475
476 KisAction* action = actionManager->createAction("add_scalar_keyframes");
477 action->setIcon(KisIconUtils::loadIcon("keyframe-add"));
478 connect(action, SIGNAL(triggered(bool)),
479 this, SLOT(slotAddAllEnabledKeys()));
480 m_d->titlebar->btnAddKey->setDefaultAction(action);
481 m_d->titlebar->btnAddKey->setIconSize(QSize(22, 22));
482
483 action = actionManager->createAction("remove_scalar_keyframe");
484 action->setIcon(KisIconUtils::loadIcon("keyframe-remove"));
485 connect(action, SIGNAL(triggered(bool)),
486 this, SLOT(slotRemoveSelectedKeys()));
487 m_d->titlebar->btnRemoveKey->setDefaultAction(action);
488 m_d->titlebar->btnRemoveKey->setIconSize(QSize(22, 22));
489
490 action = actionManager->createAction("interpolation_constant");
491 action->setIcon(KisIconUtils::loadIcon("interpolation_constant"));
492 action->setToolTip(i18n("Hold constant value. No interpolation."));
493 connect(action, &KisAction::triggered,
495 m_d->titlebar->btnGroupInterpolation->addAction(action);
496
497 action = actionManager->createAction("interpolation_linear");
498 action->setIcon(KisIconUtils::loadIcon("interpolation_linear"));
499 action->setToolTip(i18n("Linear interpolation."));
500 connect(action, &KisAction::triggered,
502 m_d->titlebar->btnGroupInterpolation->addAction(action);
503
504 action = actionManager->createAction("interpolation_bezier");
505 action->setIcon(KisIconUtils::loadIcon("interpolation_bezier"));
506 action->setToolTip(i18n("Bezier curve interpolation."));
507 connect(action, &KisAction::triggered,
509 m_d->titlebar->btnGroupInterpolation->addAction(action);
510
511 action = actionManager->createAction("tangents_sharp");
512 action->setIcon(KisIconUtils::loadIcon("interpolation_sharp"));
513 action->setToolTip(i18n("Sharp interpolation tangents."));
514 connect(action, &KisAction::triggered,
516 m_d->titlebar->btnGroupTangents->addAction(action);
517
518 action = actionManager->createAction("tangents_smooth");
519 action->setIcon(KisIconUtils::loadIcon("interpolation_smooth"));
520 action->setToolTip(i18n("Smooth interpolation tangents."));
521 connect(action, &KisAction::triggered,
523 m_d->titlebar->btnGroupTangents->addAction(action);
524
525 action = actionManager->createAction("zoom_to_fit_range");
526 action->setIcon(KisIconUtils::loadIcon("zoom-fit"));
527 action->setToolTip(i18n("Zoom view to fit channel range."));
528 connect(action, &KisAction::triggered,
530 m_d->titlebar->btnGroupZoomFit->addAction(action);
531
532 action = actionManager->createAction("zoom_to_fit_curve");
533 action->setIcon(KisIconUtils::loadIcon("zoom-fit-curve"));
534 action->setToolTip(i18n("Zoom view to fit curve."));
535 connect(action, &KisAction::triggered,
537 m_d->titlebar->btnGroupZoomFit->addAction(action);
538
539 {
540 action = actionManager->createAction("drop_frames");
541 m_d->titlebar->btnDropFrames->setDefaultAction(action);
542 m_d->titlebar->btnDropFrames->setIconSize(QSize(22, 22));
543
544 using namespace KisWidgetConnectionUtils;
545 connectControl(action, &m_d->controlsModel, "dropFramesMode");
546 }
547}
548
550{
551 if (!playbackEngine) return;
552
553 // Connect transport controls..
554 connect(m_d->titlebar->transport, SIGNAL(skipBack()), playbackEngine, SLOT(previousKeyframe()));
555 connect(m_d->titlebar->transport, SIGNAL(back()), playbackEngine, SLOT(previousFrame()));
556 connect(m_d->titlebar->transport, SIGNAL(stop()), playbackEngine, SLOT(stop()));
557 connect(m_d->titlebar->transport, SIGNAL(playPause()), playbackEngine, SLOT(playPause()));
558 connect(m_d->titlebar->transport, SIGNAL(forward()), playbackEngine, SLOT(nextFrame()));
559 connect(m_d->titlebar->transport, SIGNAL(skipForward()), playbackEngine, SLOT(nextKeyframe()));
560
561 connect(m_d->titlebar->sbFrameRegister, SIGNAL(valueChanged(int)), playbackEngine, SLOT(seek(int)));
562
563 m_d->controlsModel.connectPlaybackEngine(playbackEngine);
564}
565
566void KisAnimCurvesDocker::addKeyframeCommandToParent(const QString &channelIdentity, KUndo2Command* parentCMD)
567{
568 if (!m_d->canvas) return;
569
571 if (!node) return;
572
573 const int time = m_d->canvas->image()->animationInterface()->currentTime();
574 KisAnimUtils::createKeyframeCommand(m_d->canvas->image(), node, channelIdentity, time, false, parentCMD);
575}
576
577void KisAnimCurvesDocker::addKeyframeQuick(const QString &channelIdentity)
578{
579 if (!m_d->canvas) return;
580
582 if (!node) return;
583
584 const int time = m_d->canvas->image()->animationInterface()->currentTime();
585 KisAnimUtils::createKeyframeLazy(m_d->canvas->image(), node, channelIdentity, time, false);
586}
587
588void KisAnimCurvesDocker::removeKeyframe(const QString &channel)
589{
590 if (!m_d->canvas) return;
591
593 if (!node) return;
594
595 QItemSelectionModel* selectionModel = m_d->curvesView->selectionModel();
596 QModelIndexList selected = selectionModel ? selectionModel->selectedIndexes() : QModelIndexList();
597
598 if (selected.count() > 0) {
599 Q_FOREACH(const QModelIndex& selection, selected) {
600 KisAnimUtils::removeKeyframe(m_d->canvas->image(), node, channel, selection.column());
601 }
602 } else {
603 const int time = m_d->canvas->image()->animationInterface()->currentTime();
604 KisAnimUtils::removeKeyframe(m_d->canvas->image(), node, channel, time);
605 }
606}
607
609{
610 KisKineticScroller::updateCursor(m_d->channelTreeView, state);
611}
612
628
630 if (!m_d->canvas && !m_d->canvas->image()) {
631 return;
632 }
633
634 const int frame = m_d->canvas->animationState()->displayProxy()->activeFrame();
635
636 QSignalBlocker blocker(m_d->titlebar->sbFrameRegister);
637 m_d->titlebar->sbFrameRegister->setValue(frame);
638}
639
641{
642 if (!m_d->canvas || !m_d->canvas->image()) return;
643
644 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface();
645 m_d->titlebar->sbFrameRate->setValue(animInterface->framerate());
646}
647
651
653{
654 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager());
655 // remember current node's opacity and set it once we create a new opacity keyframe
658
659 /* Once we start dealing with more scalar values,
660 * we should add a dropdown check-box set of actions that can
661 * enable and disable keys. For now, we will just consider all channels
662 * enabled. */
663 KUndo2Command* parentCMD = new KUndo2Command(kundo2_i18n("Add Scalar Keyframes"));
664
665 //This should eventually be a list of all currently enabled channels.
666 QList<KoID> ids = {
677 };
678
679 Q_FOREACH( const KoID& koid, ids ) {
680 if (node->supportsKeyframeChannel(koid.id())) {
681 addKeyframeCommandToParent(koid.id(), parentCMD);
682 }
683 }
684
685 if (m_d->canvas && m_d->canvas->image()) {
686 KisProcessingApplicator::runSingleCommandStroke(m_d->canvas->image(), parentCMD,
689 }
690}
691
703
704
706{
707 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager());
708
711
712
713 QItemSelectionModel* selectionModel = m_d->curvesView->selectionModel();
714 QModelIndexList selected = selectionModel ? selectionModel->selectedIndexes() : QModelIndexList();
715
717
718 if (selected.count() > 0) {
719 Q_FOREACH(const QModelIndex& selection, selected) {
720 QVariant data = selection.data(KisAnimCurvesModel::ChannelIdentifier);
721
722 if (!data.isValid())
723 continue;
724
725 const QString identifier = data.toString();
726 const int time = selection.column();
727 framesToRemove.push_back(KisAnimUtils::FrameItem(node, identifier, time));
728 }
729 } else {
730 const int time = m_d->canvas->image()->animationInterface()->currentTime();
731 for(int channelIndex = 0; channelIndex < m_d->curvesModel->rowCount(); channelIndex++) {
732 QModelIndex chanIndex = m_d->curvesModel->index(channelIndex, time);
733 if (!chanIndex.isValid())
734 continue;
735
736 QVariant data = chanIndex.data(KisAnimCurvesModel::ChannelIdentifier);
737 if (!data.isValid())
738 continue;
739
740 const QString identifier = data.toString();
741 framesToRemove.push_back(KisAnimUtils::FrameItem(node, identifier, time));
742 }
743 }
744
745 if (m_d->canvas && m_d->canvas->image()) {
746 KisAnimUtils::removeKeyframes(m_d->canvas->image(), framesToRemove);
747 }
748}
749
761
762void KisAnimCurvesDocker::slotListRowsInserted(const QModelIndex &parentIndex, int first, int last)
763{
764 // Auto-expand nodes on the tree
765 for (int r=first; r<=last; r++) {
766 QModelIndex index = m_d->channelTreeModel->index(r, 0, parentIndex);
767 m_d->channelTreeView->expand(index);
768 }
769}
770
772 if (!m_d->curvesModel)
773 return;
774
775 QModelIndex current = m_d->curvesView->currentIndex();
776
777 if (current.isValid() && m_d->curvesView->indexHasKey(current)) {
778 m_d->curvesModel->setData(current, value, KisAnimCurvesModel::ScalarValueRole);
779 }
780}
781
782void KisAnimCurvesDocker::slotActiveNodeUpdate(const QModelIndex index)
783{
784 KisSignalsBlocker blockSignal(m_d->titlebar->sbValueRegister);
785
786 if (index.isValid() && m_d->curvesView->indexHasKey(index)) {
787 QVariant variant = m_d->curvesModel->data(index, KisAnimCurvesModel::ScalarValueRole);
788 m_d->titlebar->sbValueRegister->setEnabled(variant.isValid());
789 m_d->titlebar->sbValueRegister->setValue(variant.isValid() ? variant.toReal() : 0.0);
790 } else {
791 m_d->titlebar->sbValueRegister->setEnabled(false);
792 }
793}
794
796{
797 if (m_d->channelTreeView->selectionModel()->selectedIndexes().size() == 0) {
798 return;
799 }
800 QModelIndex selected = m_d->channelTreeView->selectionModel()->selectedIndexes().first();
801
802 if (selected.data(KisAnimCurvesChannelsModel::CurveRole).toBool()) {
803 m_d->channelTreeMenuChannels->popup(m_d->channelTreeView->mapToGlobal(point));
804 } else {
805 m_d->channelTreeMenuLayers->popup(m_d->channelTreeView->mapToGlobal(point));
806 }
807}
808
810{
811 QList<QModelIndex> selected = m_d->channelTreeView->selectionModel()->selectedIndexes();
812 Q_FOREACH( const QModelIndex& index, selected) {
813 m_d->channelTreeModel->reset(index);
814 }
815}
float value(const T *src, size_t ch)
QList< QModelIndex > QModelIndexList
Definition LayerBox.h:41
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
A customized titlebar for the Animation Curves Docker that's packed with useful widgets and menus.
KisIntParseSpinBox * sbStartFrame
KisIntParseSpinBox * sbFrameRate
KisCollapsibleButtonGroup * btnGroupTangents
KisCollapsibleButtonGroup * btnGroupZoomFit
KisIntParseSpinBox * sbFrameRegister
KisTransportControls * transport
KisDoubleParseSpinBox * sbValueRegister
KisAnimCurvesDockerTitlebar(QWidget *parent=nullptr)
KisIntParseSpinBox * sbEndFrame
KisCollapsibleButtonGroup * btnGroupInterpolation
void setCanvas(KoCanvasBase *canvas) override
void slotListRowsInserted(const QModelIndex &parentIndex, int first, int last)
const QScopedPointer< Private > m_d
void setViewManager(KisViewManager *kisview) override
void slotActiveNodeUpdate(const QModelIndex index)
void addKeyframeQuick(const QString &channelIdentity)
void setPlaybackEngine(KisPlaybackEngine *playbackEngine)
void removeKeyframe(const QString &channel)
void requestChannelMenuAt(const QPoint &point)
void slotNodeActivated(KisNodeSP node)
void addKeyframeCommandToParent(const QString &channelIdentity, KUndo2Command *parentCMD)
void slotScrollerStateChanged(QScroller::State state)
void slotValueRegisterChanged(double value)
KisImageWSP image() const
KisViewManager * viewManager() const
void sigPlaybackStateChanged(PlaybackState state)
KisShapeController * shapeController
The KisDoubleParseSpinBox class is a cleverer doubleSpinBox, able to parse arithmetic expressions.
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
KisImageAnimationInterface * animationInterface() const
The KisIntParseSpinBox class is a cleverer SpinBox, able to parse arithmetic expressions.
static const KoID RotationY
static const KoID ScaleY
static const KoID RotationZ
static const KoID Opacity
static const KoID ScaleX
static const KoID RotationX
static const KoID PositionX
static const KoID PositionY
static const KoID ShearX
static const KoID ShearY
Main window for Krita.
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.
static void runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
runSingleCommandStroke creates a stroke and runs cmd in it. The text() field of cmd is used as a titl...
void setImage(KisImageWSP image, KisNodeSP activeNode) override
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 ...
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
KisNodeSP activeNode()
void zoom(qreal delta)
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
QString buttons(const T &ev)
QList< KisNodeSP > KisNodeList
Definition kis_types.h:264
KUndo2MagicString kundo2_i18n(const char *text)
void removeKeyframes(KisImageSP image, const FrameItemList &frames)
void createKeyframeLazy(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy)
void removeKeyframe(KisImageSP image, KisNodeSP node, const QString &channel, int time)
KUndo2Command * createKeyframeCommand(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy, KUndo2Command *parentCommand)
QIcon loadIcon(const QString &name)
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)
void connectControl(KisCompositeOpListWidget *widget, QObject *source, const char *property)
KisAnimCurvesChannelsModel * channelTreeModel
KisAnimationPlaybackControlsModel controlsModel
KisSignalAutoConnectionsStore canvasConnections
KisAnimCurvesDockerTitlebar * titlebar
virtual bool supportsKeyframeChannel(const QString &id)
KisCanvas2 * canvas