File indexing completed on 2024-05-26 04:32:21
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Jouni Pentikäinen <joupent@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <QTreeView> 0008 #include <QSplitter> 0009 #include <QToolBar> 0010 #include <QScroller> 0011 #include <QHBoxLayout> 0012 #include <QFormLayout> 0013 #include <QLabel> 0014 #include <QMenu> 0015 #include <QToolButton> 0016 #include <QSizePolicy> 0017 0018 #include "KisAnimCurvesDocker.h" 0019 #include "KisAnimCurvesModel.h" 0020 #include "KisAnimCurvesView.h" 0021 #include "KisAnimCurvesChannelsModel.h" 0022 #include "KisAnimCurvesChannelDelegate.h" 0023 0024 #include "KisCanvasAnimationState.h" 0025 #include "animation/KisFrameDisplayProxy.h" 0026 #include "kis_keyframe_channel.h" 0027 0028 #include "kis_image_animation_interface.h" 0029 #include "KisAnimUtils.h" 0030 #include "kis_image_config.h" 0031 0032 #include "KisDocument.h" 0033 #include "kis_canvas2.h" 0034 #include "kis_shape_controller.h" 0035 #include "kis_signal_auto_connection.h" 0036 #include "KisViewManager.h" 0037 #include "kis_node_manager.h" 0038 #include "kis_animation_frame_cache.h" 0039 #include "klocalizedstring.h" 0040 #include "kis_icon_utils.h" 0041 #include "kis_action_manager.h" 0042 #include "kis_action.h" 0043 #include "kis_transport_controls.h" 0044 #include "kis_int_parse_spin_box.h" 0045 #include "kis_slider_spin_box.h" 0046 #include "kis_double_parse_spin_box.h" 0047 #include "kis_zoom_button.h" 0048 #include "kis_collapsible_button_group.h" 0049 #include "kis_signals_blocker.h" 0050 #include "kis_time_span.h" 0051 #include "kis_processing_applicator.h" 0052 #include "KisMainWindow.h" 0053 #include "KisPart.h" 0054 #include "KisPlaybackEngine.h" 0055 #include <QItemSelection> 0056 #include "KisAnimationPlaybackControlsModel.h" 0057 #include "KisWidgetConnectionUtils.h" 0058 0059 0060 KisAnimCurvesDockerTitlebar::KisAnimCurvesDockerTitlebar(QWidget* parent) : 0061 KisUtilityTitleBar(new QLabel(i18n("Animation Curves"), parent), parent) 0062 { 0063 // Transport Controls... 0064 transport = new KisTransportControls(this); 0065 widgetAreaLayout->addWidget(transport); 0066 widgetAreaLayout->addSpacing(SPACING_UNIT); 0067 0068 // Frame Register... 0069 sbFrameRegister = new KisIntParseSpinBox(this); 0070 sbFrameRegister->setToolTip(i18n("Frame register")); 0071 sbFrameRegister->setPrefix("# "); 0072 sbFrameRegister->setRange(0, MAX_FRAMES); 0073 widgetAreaLayout->addWidget(sbFrameRegister); 0074 widgetAreaLayout->addSpacing(SPACING_UNIT); 0075 0076 { // Drop Frames.. 0077 btnDropFrames = new QToolButton(this); 0078 btnDropFrames->setAutoRaise(true); 0079 widgetAreaLayout->addWidget(btnDropFrames); 0080 0081 // Playback Speed.. 0082 sbSpeed = new KisSliderSpinBox(this); 0083 sbSpeed->setRange(25, 200); 0084 sbSpeed->setSingleStep(5); 0085 sbSpeed->setValue(100); 0086 sbSpeed->setPrefix(i18nc("preview playback speed percentage prefix", "Speed: ")); 0087 sbSpeed->setSuffix(" %"); 0088 sbSpeed->setToolTip(i18n("Preview playback speed")); 0089 widgetAreaLayout->addWidget(sbSpeed); 0090 } 0091 0092 widgetAreaLayout->addSpacing(SPACING_UNIT); 0093 0094 { // Frame ops... 0095 QWidget *widget = new QWidget(this); 0096 QHBoxLayout *layout = new QHBoxLayout(widget); 0097 layout->setSpacing(0); 0098 layout->setContentsMargins(0,0,0,0); 0099 0100 // Add/Remove Key.. 0101 btnAddKey = new QToolButton(this); 0102 btnAddKey->setAutoRaise(true); 0103 layout->addWidget(btnAddKey); 0104 0105 btnRemoveKey = new QToolButton(this); 0106 btnRemoveKey->setAutoRaise(true); 0107 layout->addWidget(btnRemoveKey); 0108 0109 layout->addSpacing(SPACING_UNIT); 0110 0111 // Interpolation Modes.. 0112 btnGroupInterpolation = new KisCollapsibleButtonGroup(this); 0113 btnGroupInterpolation->setAutoRaise(true); 0114 btnGroupInterpolation->setIconSize(QSize(22, 22)); 0115 layout->addWidget(btnGroupInterpolation); 0116 0117 layout->addSpacing(SPACING_UNIT); 0118 0119 // Tangent Modes.. 0120 btnGroupTangents = new KisCollapsibleButtonGroup(this); 0121 btnGroupTangents->setAutoRaise(true); 0122 btnGroupTangents->setIconSize(QSize(22, 22)); 0123 layout->addWidget(btnGroupTangents); 0124 0125 widgetAreaLayout->addWidget(widget); 0126 } 0127 0128 widgetAreaLayout->addSpacing(SPACING_UNIT); 0129 0130 sbValueRegister = new KisDoubleParseSpinBox(this); 0131 sbValueRegister->setPrefix(i18nc("Value (Keep short!)", "Val:")); 0132 sbValueRegister->setRange(-99000.f, 99000.f); 0133 widgetAreaLayout->addWidget(sbValueRegister); 0134 0135 widgetAreaLayout->addSpacing(SPACING_UNIT); 0136 0137 // Zoom buttons.. 0138 btnGroupZoomFit = new KisCollapsibleButtonGroup(this); 0139 btnGroupZoomFit->setAutoRaise(true); 0140 btnGroupZoomFit->setIconSize(QSize(22,22)); 0141 widgetAreaLayout->addWidget(btnGroupZoomFit); 0142 0143 btnZoomHori = new KisZoomButton(this); 0144 btnZoomHori->setAutoRaise(true); 0145 btnZoomHori->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); 0146 btnZoomHori->setIconSize(QSize(22, 22)); 0147 widgetAreaLayout->addWidget(btnZoomHori); 0148 0149 btnZoomVert = new KisZoomButton(this); 0150 btnZoomVert->setAutoRaise(true); 0151 btnZoomVert->setIcon(KisIconUtils::loadIcon("zoom-vertical")); 0152 btnZoomVert->setIconSize(QSize(22, 22)); 0153 widgetAreaLayout->addWidget(btnZoomVert); 0154 0155 widgetAreaLayout->addStretch(); 0156 0157 { // Menus.. 0158 QWidget *widget = new QWidget(this); 0159 0160 QHBoxLayout *layout = new QHBoxLayout(widget); 0161 layout->setSpacing(0); 0162 layout->setContentsMargins(SPACING_UNIT,0,0,0); 0163 0164 // Onion skins menu. 0165 btnOnionSkinsMenu = new QToolButton(this); 0166 btnOnionSkinsMenu->setAutoRaise(true); 0167 btnOnionSkinsMenu->setIcon(KisIconUtils::loadIcon("onion_skin_options")); 0168 btnOnionSkinsMenu->setToolTip(i18n("Onion skins menu")); 0169 btnOnionSkinsMenu->setIconSize(QSize(22, 22)); 0170 layout->addWidget(btnOnionSkinsMenu); 0171 0172 // Audio menu.. 0173 btnAudioMenu = new QToolButton(this); 0174 btnAudioMenu->setAutoRaise(true); 0175 btnAudioMenu->setIcon(KisIconUtils::loadIcon("audio-none")); 0176 btnAudioMenu->setToolTip(i18n("Audio menu")); 0177 btnAudioMenu->setIconSize(QSize(22, 22)); 0178 btnAudioMenu->hide(); // (NOTE: Hidden for now while audio features develop.) 0179 layout->addWidget(btnAudioMenu); 0180 0181 { // Settings menu.. 0182 btnSettingsMenu = new QToolButton(this); 0183 btnSettingsMenu->setIcon(KisIconUtils::loadIcon("view-choose-22")); 0184 btnSettingsMenu->setToolTip(i18n("Animation settings menu")); 0185 btnSettingsMenu->setIconSize(QSize(22, 22)); 0186 btnSettingsMenu->setAutoRaise(true); 0187 0188 QWidget *settingsMenuWidget = new QWidget(this); 0189 QHBoxLayout *settingsMenuLayout = new QHBoxLayout(settingsMenuWidget); 0190 0191 QWidget *fields = new QWidget(settingsMenuWidget); 0192 QFormLayout *fieldsLayout = new QFormLayout(fields); 0193 0194 sbStartFrame = new KisIntParseSpinBox(settingsMenuWidget); 0195 sbStartFrame->setMaximum(10000); 0196 fieldsLayout->addRow(i18n("Clip Start: "), sbStartFrame); 0197 0198 sbEndFrame = new KisIntParseSpinBox(settingsMenuWidget); 0199 sbEndFrame->setMaximum(10000); 0200 fieldsLayout->addRow(i18n("Clip End: "), sbEndFrame); 0201 0202 sbFrameRate = new KisIntParseSpinBox(settingsMenuWidget); 0203 sbFrameRate->setMinimum(0); 0204 sbFrameRate->setMaximum(180); 0205 fieldsLayout->addRow(i18n("Frame Rate: "), sbFrameRate); 0206 0207 QWidget *buttons = new QWidget(settingsMenuWidget); 0208 QVBoxLayout *buttonsLayout = new QVBoxLayout(buttons); 0209 buttonsLayout->setAlignment(Qt::AlignTop); 0210 0211 settingsMenuLayout->addWidget(fields); 0212 settingsMenuLayout->addWidget(buttons); 0213 0214 layout->addWidget(btnSettingsMenu); 0215 0216 QMenu *settingsPopMenu = new QMenu(this); 0217 QWidgetAction *settingsMenuAction = new QWidgetAction(this); 0218 settingsMenuAction->setDefaultWidget(settingsMenuWidget); 0219 settingsPopMenu->addAction(settingsMenuAction); 0220 0221 btnSettingsMenu->setPopupMode(QToolButton::InstantPopup); 0222 btnSettingsMenu->setMenu(settingsPopMenu); 0223 } 0224 0225 widgetAreaLayout->addWidget(widget); 0226 } 0227 } 0228 0229 struct KisAnimCurvesDocker::Private 0230 { 0231 Private(QWidget *parent) 0232 : titlebar(new KisAnimCurvesDockerTitlebar(parent)) 0233 , curvesModel(new KisAnimCurvesModel(parent)) 0234 , curvesView(new KisAnimCurvesView(parent)) 0235 , channelTreeModel(new KisAnimCurvesChannelsModel(curvesModel, parent)) 0236 , channelTreeView(new QTreeView(parent)) 0237 , channelTreeMenuChannels(new QMenu(parent)) 0238 , channelTreeMenuLayers(new QMenu(parent)) 0239 , mainWindow(nullptr) 0240 { 0241 } 0242 0243 KisAnimCurvesDockerTitlebar *titlebar; 0244 0245 KisAnimCurvesModel *curvesModel; 0246 KisAnimCurvesView *curvesView; 0247 0248 KisAnimCurvesChannelsModel *channelTreeModel; 0249 QTreeView *channelTreeView; 0250 0251 QMenu *channelTreeMenuChannels; //Menu for channels 0252 QMenu *channelTreeMenuLayers; //Menu for layers 0253 0254 KisMainWindow *mainWindow; 0255 QPointer<KisCanvas2> canvas; 0256 KisSignalAutoConnectionsStore canvasConnections; 0257 KisAnimationPlaybackControlsModel controlsModel; 0258 }; 0259 0260 KisAnimCurvesDocker::KisAnimCurvesDocker() 0261 : QDockWidget(i18n("Animation Curves")) 0262 , m_d(new Private(this)) 0263 { 0264 QWidget *mainWidget = new QWidget(0); 0265 mainWidget->setLayout(new QVBoxLayout()); 0266 setWidget(mainWidget); 0267 0268 QSplitter *mainSplitter = new QSplitter(this); 0269 mainWidget->layout()->addWidget(mainSplitter); 0270 0271 { // Channel Tree.. 0272 m_d->channelTreeView->setModel(m_d->channelTreeModel); 0273 m_d->channelTreeView->setHeaderHidden(true); 0274 KisAnimCurvesChannelDelegate *listDelegate = new KisAnimCurvesChannelDelegate(this); 0275 m_d->channelTreeView->setItemDelegate(listDelegate); 0276 0277 //Right click menu configuration for Channel Tree 0278 m_d->channelTreeView->setContextMenuPolicy(Qt::CustomContextMenu); 0279 connect(m_d->channelTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestChannelMenuAt(QPoint))); 0280 0281 m_d->channelTreeMenuChannels->addSection(i18n("Channel Operations")); 0282 m_d->channelTreeMenuLayers->addSection(i18n("Layer Operations")); 0283 0284 { //Channels Menu 0285 QAction* action = new QAction(i18n("Reset Channel"), this); 0286 connect(action, SIGNAL(triggered(bool)), this, SLOT(resetChannelTreeSelection())); 0287 m_d->channelTreeMenuChannels->addAction(action); 0288 } 0289 0290 { //Layers Menu 0291 QAction* action = new QAction(i18n("Reset All Channels"), this); 0292 connect(action, SIGNAL(triggered(bool)), this, SLOT(resetChannelTreeSelection())); 0293 m_d->channelTreeMenuLayers->addAction(action); 0294 } 0295 } 0296 0297 { // Curves View.. 0298 m_d->curvesView->setModel(m_d->curvesModel); 0299 } 0300 0301 mainSplitter->addWidget(m_d->channelTreeView); 0302 mainSplitter->setStretchFactor(0, 1); 0303 mainSplitter->addWidget(m_d->curvesView); 0304 mainSplitter->setStretchFactor(1, 10); 0305 0306 // Kinetic Scrolling.. 0307 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_d->channelTreeView); 0308 if (scroller){ 0309 connect(scroller, SIGNAL(stateChanged(QScroller::State)), 0310 this, SLOT(slotScrollerStateChanged(QScroller::State))); 0311 } 0312 0313 connect(m_d->channelTreeModel, &KisAnimCurvesChannelsModel::rowsInserted, 0314 this, &KisAnimCurvesDocker::slotListRowsInserted); 0315 0316 // Titlebar Widget.. 0317 setTitleBarWidget(m_d->titlebar); 0318 setEnabled(false); 0319 0320 connect(m_d->titlebar->btnOnionSkinsMenu, &QToolButton::released, [this](){ 0321 if (m_d->mainWindow) { 0322 QDockWidget *docker = m_d->mainWindow->dockWidget("OnionSkinsDocker"); 0323 if (docker) { 0324 docker->setVisible(!docker->isVisible()); 0325 } 0326 } 0327 }); 0328 0329 connect(m_d->titlebar->btnZoomHori, &KisZoomButton::zoom, [this](qreal zoomDelta){ 0330 if (m_d->curvesView) { 0331 m_d->curvesView->changeZoom(Qt::Horizontal, zoomDelta); 0332 } 0333 }); 0334 0335 connect(m_d->titlebar->btnZoomVert, &KisZoomButton::zoom, [this](qreal zoomDelta){ 0336 if (m_d->curvesView) { 0337 m_d->curvesView->changeZoom(Qt::Vertical, zoomDelta); 0338 } 0339 }); 0340 0341 connect(m_d->curvesView, SIGNAL(activated(QModelIndex)), this, SLOT(slotActiveNodeUpdate(QModelIndex))); 0342 connect(m_d->curvesView, SIGNAL(activeDataChanged(QModelIndex)), this, SLOT(slotActiveNodeUpdate(QModelIndex))); 0343 connect(m_d->titlebar->sbValueRegister, SIGNAL(valueChanged(double)), this, SLOT(slotValueRegisterChanged(double))); 0344 0345 { 0346 using namespace KisWidgetConnectionUtils; 0347 connectControl(m_d->titlebar->sbSpeed, &m_d->controlsModel, "playbackSpeedDenorm"); 0348 } 0349 0350 // Watch for KisPlaybackEngine changes and initialize current one.. 0351 connect(KisPart::instance(), &KisPart::playbackEngineChanged, this, &KisAnimCurvesDocker::setPlaybackEngine); 0352 setPlaybackEngine(KisPart::instance()->playbackEngine()); 0353 } 0354 0355 KisAnimCurvesDocker::~KisAnimCurvesDocker() 0356 {} 0357 0358 void KisAnimCurvesDocker::setCanvas(KoCanvasBase *canvas) 0359 { 0360 if (canvas && m_d->canvas == canvas) return; 0361 0362 if (m_d->canvas) { 0363 m_d->canvasConnections.clear(); 0364 m_d->canvas->disconnectCanvasObserver(this); 0365 m_d->channelTreeModel->selectedNodesChanged(KisNodeList()); 0366 m_d->canvas->animationState()->disconnect(this); 0367 m_d->titlebar->transport->disconnect(m_d->canvas->animationState()); 0368 m_d->titlebar->transport->setPlaying(false); 0369 m_d->titlebar->sbFrameRegister->disconnect(m_d->canvas->animationState()); 0370 0371 if (m_d->canvas->image()) { 0372 m_d->canvas->image()->animationInterface()->disconnect(this); 0373 m_d->titlebar->sbStartFrame->disconnect(m_d->canvas->image()->animationInterface()); 0374 m_d->titlebar->sbEndFrame->disconnect(m_d->canvas->image()->animationInterface()); 0375 m_d->titlebar->sbFrameRate->disconnect(m_d->canvas->image()->animationInterface()); 0376 } 0377 0378 m_d->curvesModel->setImage(0); 0379 } 0380 0381 m_d->canvas = dynamic_cast<KisCanvas2*>(canvas); 0382 setEnabled(m_d->canvas != 0); 0383 0384 if (m_d->canvas) { 0385 KisDocument *doc = static_cast<KisDocument*>(m_d->canvas->imageView()->document()); 0386 KisShapeController *kritaShapeController = dynamic_cast<KisShapeController*>(doc->shapeController()); 0387 m_d->channelTreeModel->setDummiesFacade(kritaShapeController); 0388 0389 m_d->curvesModel->setImage(m_d->canvas->image()); 0390 m_d->curvesModel->setFrameCache(m_d->canvas->frameCache()); 0391 m_d->curvesModel->setAnimationPlayer(m_d->canvas->animationState()); 0392 0393 m_d->canvasConnections.addConnection( 0394 m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), 0395 m_d->channelTreeModel, SLOT(selectedNodesChanged(KisNodeList)) 0396 ); 0397 0398 m_d->canvasConnections.addConnection( 0399 m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigNodeActivated(KisNodeSP)), 0400 this, SLOT(slotNodeActivated(KisNodeSP)) 0401 ); 0402 0403 m_d->channelTreeModel->clear(); 0404 m_d->channelTreeModel->selectedNodesChanged(m_d->canvas->viewManager()->nodeManager()->selectedNodes()); 0405 0406 { // Reinitialize titlebar widgets.. 0407 KisSignalsBlocker blocker(m_d->titlebar->sbStartFrame, 0408 m_d->titlebar->sbEndFrame, 0409 m_d->titlebar->sbFrameRate, 0410 m_d->titlebar->sbFrameRegister, 0411 m_d->titlebar->sbValueRegister); 0412 0413 KisImageAnimationInterface *animinterface = m_d->canvas->image()->animationInterface(); 0414 m_d->titlebar->sbStartFrame->setValue(animinterface->documentPlaybackRange().start()); 0415 m_d->titlebar->sbEndFrame->setValue(animinterface->documentPlaybackRange().end()); 0416 m_d->titlebar->sbFrameRate->setValue(animinterface->framerate()); 0417 m_d->titlebar->sbFrameRegister->setValue(animinterface->currentTime()); 0418 0419 QModelIndex activeIndex = m_d->curvesView->currentIndex(); 0420 m_d->titlebar->sbValueRegister->setEnabled(activeIndex.isValid()); 0421 m_d->titlebar->sbValueRegister->setValue( activeIndex.isValid() ? 0422 activeIndex.data(KisAnimCurvesModel::ScalarValueRole).toReal() : 0.0f); 0423 } 0424 0425 m_d->titlebar->transport->setPlaying(m_d->canvas->animationState()->playbackState() == PlaybackState::PLAYING); 0426 0427 connect(m_d->titlebar->sbFrameRate, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setFramerate(int))); 0428 connect(m_d->titlebar->sbStartFrame, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setDocumentRangeStartFrame(int))); 0429 connect(m_d->titlebar->sbEndFrame, SIGNAL(valueChanged(int)), m_d->canvas->image()->animationInterface(), SLOT(setDocumentRangeEndFrame(int))); 0430 0431 connect(m_d->canvas->animationState(), &KisCanvasAnimationState::sigPlaybackStateChanged, m_d->titlebar->transport, [this](PlaybackState p_state){ 0432 m_d->titlebar->transport->setPlaying(p_state == PlaybackState::PLAYING); 0433 m_d->titlebar->sbFrameRegister->setDisabled(p_state == PlaybackState::PLAYING); 0434 0435 if (p_state == PlaybackState::STOPPED) { 0436 updateFrameRegister(); 0437 } 0438 }); 0439 0440 connect(m_d->canvas->animationState(), SIGNAL(sigFrameChanged()), this, SLOT(updateFrameRegister())); 0441 0442 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigUiTimeChanged(int)), this, SLOT(updateFrameRegister())); 0443 0444 connect(m_d->canvas->image()->animationInterface(), &KisImageAnimationInterface::sigPlaybackRangeChanged, this, [this]() { 0445 if (!m_d->canvas || !m_d->canvas->image()) return; 0446 0447 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface(); 0448 0449 m_d->titlebar->sbStartFrame->setValue(animInterface->documentPlaybackRange().start()); 0450 m_d->titlebar->sbEndFrame->setValue(animInterface->documentPlaybackRange().end()); 0451 }); 0452 0453 connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), this, SLOT(handleFrameRateChange())); 0454 0455 m_d->controlsModel.connectAnimationState(m_d->canvas->animationState()); 0456 } 0457 } 0458 0459 void KisAnimCurvesDocker::unsetCanvas() 0460 { 0461 setCanvas(0); 0462 } 0463 0464 void KisAnimCurvesDocker::setViewManager(KisViewManager *view) 0465 { 0466 m_d->mainWindow = view->mainWindow(); 0467 0468 connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons())); 0469 slotUpdateIcons(); 0470 0471 KisActionManager* actionManager = view->actionManager(); 0472 0473 KisAction* action = actionManager->createAction("add_scalar_keyframes"); 0474 action->setIcon(KisIconUtils::loadIcon("keyframe-add")); 0475 connect(action, SIGNAL(triggered(bool)), 0476 this, SLOT(slotAddAllEnabledKeys())); 0477 m_d->titlebar->btnAddKey->setDefaultAction(action); 0478 m_d->titlebar->btnAddKey->setIconSize(QSize(22, 22)); 0479 0480 action = actionManager->createAction("remove_scalar_keyframe"); 0481 action->setIcon(KisIconUtils::loadIcon("keyframe-remove")); 0482 connect(action, SIGNAL(triggered(bool)), 0483 this, SLOT(slotRemoveSelectedKeys())); 0484 m_d->titlebar->btnRemoveKey->setDefaultAction(action); 0485 m_d->titlebar->btnRemoveKey->setIconSize(QSize(22, 22)); 0486 0487 action = actionManager->createAction("interpolation_constant"); 0488 action->setIcon(KisIconUtils::loadIcon("interpolation_constant")); 0489 action->setToolTip(i18n("Hold constant value. No interpolation.")); 0490 connect(action, &KisAction::triggered, 0491 m_d->curvesView, &KisAnimCurvesView::applyConstantMode); 0492 m_d->titlebar->btnGroupInterpolation->addAction(action); 0493 0494 action = actionManager->createAction("interpolation_linear"); 0495 action->setIcon(KisIconUtils::loadIcon("interpolation_linear")); 0496 action->setToolTip(i18n("Linear interpolation.")); 0497 connect(action, &KisAction::triggered, 0498 m_d->curvesView, &KisAnimCurvesView::applyLinearMode); 0499 m_d->titlebar->btnGroupInterpolation->addAction(action); 0500 0501 action = actionManager->createAction("interpolation_bezier"); 0502 action->setIcon(KisIconUtils::loadIcon("interpolation_bezier")); 0503 action->setToolTip(i18n("Bezier curve interpolation.")); 0504 connect(action, &KisAction::triggered, 0505 m_d->curvesView, &KisAnimCurvesView::applyBezierMode); 0506 m_d->titlebar->btnGroupInterpolation->addAction(action); 0507 0508 action = actionManager->createAction("tangents_sharp"); 0509 action->setIcon(KisIconUtils::loadIcon("interpolation_sharp")); 0510 action->setToolTip(i18n("Sharp interpolation tangents.")); 0511 connect(action, &KisAction::triggered, 0512 m_d->curvesView, &KisAnimCurvesView::applySharpMode); 0513 m_d->titlebar->btnGroupTangents->addAction(action); 0514 0515 action = actionManager->createAction("tangents_smooth"); 0516 action->setIcon(KisIconUtils::loadIcon("interpolation_smooth")); 0517 action->setToolTip(i18n("Smooth interpolation tangents.")); 0518 connect(action, &KisAction::triggered, 0519 m_d->curvesView, &KisAnimCurvesView::applySmoothMode); 0520 m_d->titlebar->btnGroupTangents->addAction(action); 0521 0522 action = actionManager->createAction("zoom_to_fit_range"); 0523 action->setIcon(KisIconUtils::loadIcon("zoom-fit")); 0524 action->setToolTip(i18n("Zoom view to fit channel range.")); 0525 connect(action, &KisAction::triggered, 0526 m_d->curvesView, &KisAnimCurvesView::zoomToFitChannel); 0527 m_d->titlebar->btnGroupZoomFit->addAction(action); 0528 0529 action = actionManager->createAction("zoom_to_fit_curve"); 0530 action->setIcon(KisIconUtils::loadIcon("zoom-fit-curve")); 0531 action->setToolTip(i18n("Zoom view to fit curve.")); 0532 connect(action, &KisAction::triggered, 0533 m_d->curvesView, &KisAnimCurvesView::zoomToFitCurve); 0534 m_d->titlebar->btnGroupZoomFit->addAction(action); 0535 0536 { 0537 action = actionManager->createAction("drop_frames"); 0538 m_d->titlebar->btnDropFrames->setDefaultAction(action); 0539 m_d->titlebar->btnDropFrames->setIconSize(QSize(22, 22)); 0540 0541 using namespace KisWidgetConnectionUtils; 0542 connectControl(action, &m_d->controlsModel, "dropFramesMode"); 0543 } 0544 } 0545 0546 void KisAnimCurvesDocker::setPlaybackEngine(KisPlaybackEngine *playbackEngine) 0547 { 0548 if (!playbackEngine) return; 0549 0550 // Connect transport controls.. 0551 connect(m_d->titlebar->transport, SIGNAL(skipBack()), playbackEngine, SLOT(previousKeyframe())); 0552 connect(m_d->titlebar->transport, SIGNAL(back()), playbackEngine, SLOT(previousFrame())); 0553 connect(m_d->titlebar->transport, SIGNAL(stop()), playbackEngine, SLOT(stop())); 0554 connect(m_d->titlebar->transport, SIGNAL(playPause()), playbackEngine, SLOT(playPause())); 0555 connect(m_d->titlebar->transport, SIGNAL(forward()), playbackEngine, SLOT(nextFrame())); 0556 connect(m_d->titlebar->transport, SIGNAL(skipForward()), playbackEngine, SLOT(nextKeyframe())); 0557 0558 connect(m_d->titlebar->sbFrameRegister, SIGNAL(valueChanged(int)), playbackEngine, SLOT(seek(int))); 0559 0560 m_d->controlsModel.connectPlaybackEngine(playbackEngine); 0561 } 0562 0563 void KisAnimCurvesDocker::addKeyframeCommandToParent(const QString &channelIdentity, KUndo2Command* parentCMD) 0564 { 0565 if (!m_d->canvas) return; 0566 0567 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0568 if (!node) return; 0569 0570 const int time = m_d->canvas->image()->animationInterface()->currentTime(); 0571 KisAnimUtils::createKeyframeCommand(m_d->canvas->image(), node, channelIdentity, time, false, parentCMD); 0572 } 0573 0574 void KisAnimCurvesDocker::addKeyframeQuick(const QString &channelIdentity) 0575 { 0576 if (!m_d->canvas) return; 0577 0578 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0579 if (!node) return; 0580 0581 const int time = m_d->canvas->image()->animationInterface()->currentTime(); 0582 KisAnimUtils::createKeyframeLazy(m_d->canvas->image(), node, channelIdentity, time, false); 0583 } 0584 0585 void KisAnimCurvesDocker::removeKeyframe(const QString &channel) 0586 { 0587 if (!m_d->canvas) return; 0588 0589 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0590 if (!node) return; 0591 0592 QItemSelectionModel* selectionModel = m_d->curvesView->selectionModel(); 0593 QModelIndexList selected = selectionModel ? selectionModel->selectedIndexes() : QModelIndexList(); 0594 0595 if (selected.count() > 0) { 0596 Q_FOREACH(const QModelIndex& selection, selected) { 0597 KisAnimUtils::removeKeyframe(m_d->canvas->image(), node, channel, selection.column()); 0598 } 0599 } else { 0600 const int time = m_d->canvas->image()->animationInterface()->currentTime(); 0601 KisAnimUtils::removeKeyframe(m_d->canvas->image(), node, channel, time); 0602 } 0603 } 0604 0605 void KisAnimCurvesDocker::slotScrollerStateChanged(QScroller::State state) 0606 { 0607 KisKineticScroller::updateCursor(m_d->channelTreeView, state); 0608 } 0609 0610 void KisAnimCurvesDocker::slotNodeActivated(KisNodeSP node) 0611 { 0612 if (!node) return; 0613 bool supported = node->supportsKeyframeChannel(KisKeyframeChannel::Opacity.id()) || 0614 node->supportsKeyframeChannel(KisKeyframeChannel::PositionX.id()) || 0615 node->supportsKeyframeChannel(KisKeyframeChannel::PositionY.id()) || 0616 node->supportsKeyframeChannel(KisKeyframeChannel::ScaleX.id()) || 0617 node->supportsKeyframeChannel(KisKeyframeChannel::ScaleY.id()) || 0618 node->supportsKeyframeChannel(KisKeyframeChannel::ShearX.id()) || 0619 node->supportsKeyframeChannel(KisKeyframeChannel::ShearY.id()) || 0620 node->supportsKeyframeChannel(KisKeyframeChannel::RotationX.id()) || 0621 node->supportsKeyframeChannel(KisKeyframeChannel::RotationY.id()) || 0622 node->supportsKeyframeChannel(KisKeyframeChannel::RotationZ.id()); 0623 m_d->titlebar->btnAddKey->setEnabled(supported); 0624 } 0625 0626 void KisAnimCurvesDocker::updateFrameRegister(){ 0627 if (!m_d->canvas && !m_d->canvas->image()) { 0628 return; 0629 } 0630 0631 const int frame = m_d->canvas->animationState()->displayProxy()->activeFrame(); 0632 0633 QSignalBlocker blocker(m_d->titlebar->sbFrameRegister); 0634 m_d->titlebar->sbFrameRegister->setValue(frame); 0635 } 0636 0637 void KisAnimCurvesDocker::handleFrameRateChange() 0638 { 0639 if (!m_d->canvas || !m_d->canvas->image()) return; 0640 0641 KisImageAnimationInterface *animInterface = m_d->canvas->image()->animationInterface(); 0642 m_d->titlebar->sbFrameRate->setValue(animInterface->framerate()); 0643 } 0644 0645 void KisAnimCurvesDocker::slotUpdateIcons() 0646 { 0647 } 0648 0649 void KisAnimCurvesDocker::slotAddAllEnabledKeys() 0650 { 0651 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager()); 0652 // remember current node's opacity and set it once we create a new opacity keyframe 0653 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0654 KIS_SAFE_ASSERT_RECOVER_RETURN(node); 0655 0656 /* Once we start dealing with more scalar values, 0657 * we should add a dropdown check-box set of actions that can 0658 * enable and disable keys. For now, we will just consider all channels 0659 * enabled. */ 0660 KUndo2Command* parentCMD = new KUndo2Command(kundo2_i18n("Add Scalar Keyframes")); 0661 0662 //This should eventually be a list of all currently enabled channels. 0663 QList<KoID> ids = { 0664 KisKeyframeChannel::Opacity, 0665 KisKeyframeChannel::PositionX, 0666 KisKeyframeChannel::PositionY, 0667 KisKeyframeChannel::ScaleX, 0668 KisKeyframeChannel::ScaleY, 0669 KisKeyframeChannel::ShearX, 0670 KisKeyframeChannel::ShearY, 0671 KisKeyframeChannel::RotationX, 0672 KisKeyframeChannel::RotationY, 0673 KisKeyframeChannel::RotationZ 0674 }; 0675 0676 Q_FOREACH( const KoID& koid, ids ) { 0677 if (node->supportsKeyframeChannel(koid.id())) { 0678 addKeyframeCommandToParent(koid.id(), parentCMD); 0679 } 0680 } 0681 0682 if (m_d->canvas && m_d->canvas->image()) { 0683 KisProcessingApplicator::runSingleCommandStroke(m_d->canvas->image(), parentCMD, 0684 KisStrokeJobData::BARRIER, 0685 KisStrokeJobData::EXCLUSIVE); 0686 } 0687 } 0688 0689 void KisAnimCurvesDocker::slotAddOpacityKey() 0690 { 0691 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager()); 0692 0693 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0694 KIS_SAFE_ASSERT_RECOVER_RETURN(node); 0695 0696 if (node->supportsKeyframeChannel(KisKeyframeChannel::Opacity.id())) { 0697 addKeyframeQuick(KisKeyframeChannel::Opacity.id()); 0698 } 0699 } 0700 0701 0702 void KisAnimCurvesDocker::slotRemoveSelectedKeys() 0703 { 0704 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager()); 0705 0706 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0707 KIS_SAFE_ASSERT_RECOVER_RETURN(node); 0708 0709 0710 QItemSelectionModel* selectionModel = m_d->curvesView->selectionModel(); 0711 QModelIndexList selected = selectionModel ? selectionModel->selectedIndexes() : QModelIndexList(); 0712 0713 QVector<KisAnimUtils::FrameItem> framesToRemove; 0714 0715 if (selected.count() > 0) { 0716 Q_FOREACH(const QModelIndex& selection, selected) { 0717 QVariant data = selection.data(KisAnimCurvesModel::ChannelIdentifier); 0718 0719 if (!data.isValid()) 0720 continue; 0721 0722 const QString identifier = data.toString(); 0723 const int time = selection.column(); 0724 framesToRemove.push_back(KisAnimUtils::FrameItem(node, identifier, time)); 0725 } 0726 } else { 0727 const int time = m_d->canvas->image()->animationInterface()->currentTime(); 0728 for(int channelIndex = 0; channelIndex < m_d->curvesModel->rowCount(); channelIndex++) { 0729 QModelIndex chanIndex = m_d->curvesModel->index(channelIndex, time); 0730 if (!chanIndex.isValid()) 0731 continue; 0732 0733 QVariant data = chanIndex.data(KisAnimCurvesModel::ChannelIdentifier); 0734 if (!data.isValid()) 0735 continue; 0736 0737 const QString identifier = data.toString(); 0738 framesToRemove.push_back(KisAnimUtils::FrameItem(node, identifier, time)); 0739 } 0740 } 0741 0742 if (m_d->canvas && m_d->canvas->image()) { 0743 KisAnimUtils::removeKeyframes(m_d->canvas->image(), framesToRemove); 0744 } 0745 } 0746 0747 void KisAnimCurvesDocker::slotRemoveOpacityKey() 0748 { 0749 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->canvas && m_d->canvas->viewManager()); 0750 0751 KisNodeSP node = m_d->canvas->viewManager()->activeNode(); 0752 KIS_SAFE_ASSERT_RECOVER_RETURN(node); 0753 0754 if (node->supportsKeyframeChannel(KisKeyframeChannel::Opacity.id())) { 0755 removeKeyframe(KisKeyframeChannel::Opacity.id()); 0756 } 0757 } 0758 0759 void KisAnimCurvesDocker::slotListRowsInserted(const QModelIndex &parentIndex, int first, int last) 0760 { 0761 // Auto-expand nodes on the tree 0762 for (int r=first; r<=last; r++) { 0763 QModelIndex index = m_d->channelTreeModel->index(r, 0, parentIndex); 0764 m_d->channelTreeView->expand(index); 0765 } 0766 } 0767 0768 void KisAnimCurvesDocker::slotValueRegisterChanged(double value){ 0769 if (!m_d->curvesModel) 0770 return; 0771 0772 QModelIndex current = m_d->curvesView->currentIndex(); 0773 0774 if (current.isValid() && m_d->curvesView->indexHasKey(current)) { 0775 m_d->curvesModel->setData(current, value, KisAnimCurvesModel::ScalarValueRole); 0776 } 0777 } 0778 0779 void KisAnimCurvesDocker::slotActiveNodeUpdate(const QModelIndex index) 0780 { 0781 KisSignalsBlocker blockSignal(m_d->titlebar->sbValueRegister); 0782 0783 if (index.isValid() && m_d->curvesView->indexHasKey(index)) { 0784 QVariant variant = m_d->curvesModel->data(index, KisAnimCurvesModel::ScalarValueRole); 0785 m_d->titlebar->sbValueRegister->setEnabled(variant.isValid()); 0786 m_d->titlebar->sbValueRegister->setValue(variant.isValid() ? variant.toReal() : 0.0); 0787 } else { 0788 m_d->titlebar->sbValueRegister->setEnabled(false); 0789 } 0790 } 0791 0792 void KisAnimCurvesDocker::requestChannelMenuAt(const QPoint &point) 0793 { 0794 if (m_d->channelTreeView->selectionModel()->selectedIndexes().size() == 0) { 0795 return; 0796 } 0797 QModelIndex selected = m_d->channelTreeView->selectionModel()->selectedIndexes().first(); 0798 0799 if (selected.data(KisAnimCurvesChannelsModel::CurveRole).toBool()) { 0800 m_d->channelTreeMenuChannels->popup(m_d->channelTreeView->mapToGlobal(point)); 0801 } else { 0802 m_d->channelTreeMenuLayers->popup(m_d->channelTreeView->mapToGlobal(point)); 0803 } 0804 } 0805 0806 void KisAnimCurvesDocker::resetChannelTreeSelection() 0807 { 0808 QList<QModelIndex> selected = m_d->channelTreeView->selectionModel()->selectedIndexes(); 0809 Q_FOREACH( const QModelIndex& index, selected) { 0810 m_d->channelTreeModel->reset(index); 0811 } 0812 }