began UI for keyframing

Largely extending the existing NodeParamView with functionality from the
previous release.
This commit is contained in:
itsmattkc
2019-12-25 01:20:46 +11:00
parent aac8c5ec17
commit dc6d498da8
19 changed files with 454 additions and 2 deletions
+11
View File
@@ -27,6 +27,7 @@
NodeInput::NodeInput(const QString& id) :
NodeParam(id),
keyframable_(true),
keyframing_(false),
dependent_(true),
has_minimum_(false),
@@ -241,6 +242,16 @@ void NodeInput::set_is_keyframing(bool k)
keyframing_ = k;
}
bool NodeInput::is_keyframable() const
{
return keyframable_;
}
void NodeInput::set_is_keyframable(bool k)
{
keyframable_ = k;
}
const QVariant &NodeInput::minimum() const
{
return minimum_;
+15
View File
@@ -98,6 +98,16 @@ public:
*/
void set_is_keyframing(bool k);
/**
* @brief Return whether this input can be keyframed or not
*/
bool is_keyframable() const;
/**
* @brief Set whether this input can be keyframed or not
*/
void set_is_keyframable(bool k);
const QVariant& minimum() const;
bool has_minimum() const;
void set_minimum(const QVariant& min);
@@ -122,6 +132,11 @@ private:
*/
DataType data_type_;
/**
* @brief Internal keyframable value
*/
bool keyframable_;
/**
* @brief Internal keyframe array
*
+4
View File
@@ -69,6 +69,8 @@ QIcon olive::icon::Record;
QIcon olive::icon::Add;
QIcon olive::icon::Error;
QIcon olive::icon::DirUp;
QIcon olive::icon::Clock;
QIcon olive::icon::Diamond;
void olive::icon::LoadAll(const QString& theme)
{
@@ -116,6 +118,8 @@ void olive::icon::LoadAll(const QString& theme)
Add = Create(theme, "add-button");
Error = Create(theme, "error");
DirUp = Create(theme, "dirup");
Clock = Create(theme, "clock");
Diamond = Create(theme, "diamond");
}
QIcon olive::icon::Create(const QString& theme, const QString &name)
+2
View File
@@ -76,6 +76,8 @@ extern QIcon Record;
extern QIcon Add;
extern QIcon Error;
extern QIcon DirUp;
extern QIcon Clock;
extern QIcon Diamond;
/**
* @brief Create an icon object loaded from file
+1
View File
@@ -20,6 +20,7 @@ add_subdirectory(columnedgridlayout)
add_subdirectory(flowlayout)
add_subdirectory(focusablelineedit)
add_subdirectory(footagecombobox)
add_subdirectory(keyframeview)
add_subdirectory(menu)
add_subdirectory(nodeview)
add_subdirectory(nodeparamview)
+24
View File
@@ -0,0 +1,24 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2019 Olive Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/keyframeview/keyframeview.h
widget/keyframeview/keyframeview.cpp
widget/keyframeview/keyframeviewitem.h
widget/keyframeview/keyframeviewitem.cpp
PARENT_SCOPE
)
+27
View File
@@ -0,0 +1,27 @@
#include "keyframeview.h"
#include <QVBoxLayout>
KeyframeView::KeyframeView(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setMargin(0);
ruler_ = new TimeRuler();
ruler_->SetTextVisible(true);
layout->addWidget(ruler_);
view_ = new QGraphicsView();
view_->setScene(&scene_);
view_->setBackgroundRole(QPalette::Base);
view_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
view_->setDragMode(QGraphicsView::RubberBandDrag);
layout->addWidget(view_);
}
void KeyframeView::SetTimebase(const rational &timebase)
{
ruler_->SetTimebase(timebase);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef KEYFRAMEVIEW_H
#define KEYFRAMEVIEW_H
#include <QGraphicsView>
#include "common/rational.h"
#include "keyframeviewitem.h"
#include "widget/timeruler/timeruler.h"
class KeyframeView : public QWidget
{
public:
KeyframeView(QWidget* parent = nullptr);
void AddKeyframe(const rational& time, int y);
void SetTimebase(const rational& timebase);
private:
TimeRuler* ruler_;
QGraphicsView* view_;
QGraphicsScene scene_;
};
#endif // KEYFRAMEVIEW_H
@@ -0,0 +1,83 @@
#include "keyframeviewitem.h"
#include <QApplication>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QWidget>
#include "common/qtversionabstraction.h"
KeyframeViewItem::KeyframeViewItem(QGraphicsItem *parent) :
QGraphicsRectItem(parent),
key_(nullptr),
scale_(1.0)
{
keyframe_size_ = QFontMetricsWidth(qApp->fontMetrics(), "Oi");
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
}
void KeyframeViewItem::SetKeyframe(NodeKeyframe *key)
{
key_ = key;
UpdateRect();
}
void KeyframeViewItem::SetVerticalCenter(int middle)
{
middle_ = middle;
UpdateRect();
}
void KeyframeViewItem::SetScale(double scale)
{
scale_ = scale;
UpdateRect();
}
void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if (!key_) {
return;
}
painter->setPen(Qt::black);
if (option->state & QStyle::State_Selected) {
painter->setBrush(widget->palette().highlight());
} else {
painter->setBrush(widget->palette().text());
}
switch (key_->type()) {
case NodeKeyframe::kLinear:
{
QPointF points[] = {
QPointF(rect().center().x(), rect().top()),
QPointF(rect().right(), rect().center().y()),
QPointF(rect().center().x(), rect().bottom()),
QPointF(rect().left(), rect().center().y())
};
painter->drawPolygon(points, 4);
break;
}
case NodeKeyframe::kBezier:
painter->drawEllipse(rect());
break;
case NodeKeyframe::kHold:
painter->drawRect(rect());
break;
}
}
void KeyframeViewItem::UpdateRect()
{
if (!key_) {
return;
}
double x_center = key_->time().toDouble() * scale_;
setRect(x_center - keyframe_size_/2, middle_ - keyframe_size_/2, keyframe_size_, keyframe_size_);
}
@@ -0,0 +1,34 @@
#ifndef KEYFRAMEVIEWITEM_H
#define KEYFRAMEVIEWITEM_H
#include <QGraphicsRectItem>
#include "node/keyframe.h"
class KeyframeViewItem : public QGraphicsRectItem
{
public:
KeyframeViewItem(QGraphicsItem *parent = nullptr);
void SetKeyframe(NodeKeyframe* key);
void SetVerticalCenter(int middle);
void SetScale(double scale);
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
void UpdateRect();
NodeKeyframe* key_;
double scale_;
int middle_;
int keyframe_size_;
};
#endif // KEYFRAMEVIEWITEM_H
+4
View File
@@ -20,6 +20,10 @@ set(OLIVE_SOURCES
widget/nodeparamview/nodeparamview.cpp
widget/nodeparamview/nodeparamviewitem.h
widget/nodeparamview/nodeparamviewitem.cpp
widget/nodeparamview/nodeparamviewkeyframecontrol.h
widget/nodeparamview/nodeparamviewkeyframecontrol.cpp
widget/nodeparamview/nodeparamviewundo.h
widget/nodeparamview/nodeparamviewundo.cpp
widget/nodeparamview/nodeparamviewwidgetbridge.h
widget/nodeparamview/nodeparamviewwidgetbridge.cpp
PARENT_SCOPE
+12 -1
View File
@@ -21,6 +21,7 @@
#include "nodeparamview.h"
#include <QScrollArea>
#include <QSplitter>
NodeParamView::NodeParamView(QWidget *parent) :
QWidget(parent)
@@ -30,10 +31,13 @@ NodeParamView::NodeParamView(QWidget *parent) :
widget_layout->setSpacing(0);
widget_layout->setMargin(0);
QSplitter* splitter = new QSplitter(Qt::Horizontal);
widget_layout->addWidget(splitter);
// Set up scroll area for params
QScrollArea* scroll_area = new QScrollArea();
scroll_area->setWidgetResizable(true);
widget_layout->addWidget(scroll_area);
splitter->addWidget(scroll_area);
// Param widget
QWidget* param_widget_area = new QWidget();
@@ -46,6 +50,13 @@ NodeParamView::NodeParamView(QWidget *parent) :
// Add a stretch to allow empty space at the bottom of the layout
param_layout_->addStretch();
// Set up keyframe view
keyframe_view_ = new KeyframeView();
splitter->addWidget(keyframe_view_);
// Disable collapsing param view (but collapsing keyframe view is permitted)
splitter->setCollapsible(0, false);
}
void NodeParamView::SetNodes(QList<Node *> nodes)
+3
View File
@@ -26,6 +26,7 @@
#include "node/node.h"
#include "nodeparamviewitem.h"
#include "widget/keyframeview/keyframeview.h"
class NodeParamView : public QWidget
{
@@ -38,6 +39,8 @@ public:
private:
QVBoxLayout* param_layout_;
KeyframeView* keyframe_view_;
QList<Node*> nodes_;
QList<NodeParamViewItem*> items_;
+58 -1
View File
@@ -23,10 +23,14 @@
#include <QCheckBox>
#include <QDebug>
#include <QEvent>
#include <QMessageBox>
#include <QPainter>
#include "nodeparamviewkeyframecontrol.h"
#include "nodeparamviewundo.h"
#include "project/item/sequence/sequence.h"
#include "ui/icons/icons.h"
#include "undo/undostack.h"
NodeParamViewItem::NodeParamViewItem(QWidget *parent) :
QWidget(parent)
@@ -110,6 +114,7 @@ void NodeParamViewItem::SetupUI()
foreach (NodeParam* param, first_node->parameters()) {
// This widget only needs to show input parameters
if (param->type() == NodeParam::kInput) {
NodeInput* input = static_cast<NodeInput*>(param);
// Add descriptor label
QLabel* param_label = new QLabel();
@@ -119,7 +124,7 @@ void NodeParamViewItem::SetupUI()
// Create a widget/input bridge for this input
NodeParamViewWidgetBridge* bridge = new NodeParamViewWidgetBridge(this);
bridge->AddInput(static_cast<NodeInput*>(param));
bridge->AddInput(input);
bridges_.append(bridge);
// Add widgets for this parameter ot the layout
@@ -128,6 +133,15 @@ void NodeParamViewItem::SetupUI()
content_layout_->addWidget(widgets_for_param.at(i), row_count, i + 1);
}
// Add keyframe control to this layout if parameter is keyframable
if (input->is_keyframable()) {
// Hacky but effective way to make sure this widget is always as far right as possible
int control_column = 10;
NodeParamViewKeyframeControl* key_control = new NodeParamViewKeyframeControl(input);
content_layout_->addWidget(key_control, row_count, control_column);
}
row_count++;
}
}
@@ -181,6 +195,49 @@ void NodeParamViewItem::SetExpanded(bool e)
}
}
void NodeParamViewItem::KeyframeEnableChanged(bool e)
{
NodeParamViewKeyframeControl* control = static_cast<NodeParamViewKeyframeControl*>(sender());
NodeInput* input = control->GetConnectedInput();
if (e == input->is_keyframing()) {
// No-op
return;
}
if (e) {
QUndoCommand* command = new QUndoCommand();
// Enable keyframing
new NodeParamSetKeyframing(input, true, command);
// FIXME: Create a keyframe at this time
olive::undo_stack.push(command);
} else {
// Confirm the user wants to clear all keyframes
if (QMessageBox::warning(this,
tr("Warning"),
tr("Are you sure you want to disable keyframing on this value? This will clear all existing keyframes."),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
// Disable keyframing
QUndoCommand* command = new QUndoCommand();
// FIXME: Delete all keyframes
// Disable keyframing
new NodeParamSetKeyframing(input, false, command);
olive::undo_stack.push(command);
} else {
// Disable action has effectively been ignored
control->SetKeyframeEnabled(true);
}
}
}
NodeParamViewItemTitleBar::NodeParamViewItemTitleBar(QWidget *parent) :
QWidget(parent)
{
@@ -78,6 +78,8 @@ private:
private slots:
void SetExpanded(bool e);
void KeyframeEnableChanged(bool e);
};
#endif // NODEPARAMVIEWITEM_H
@@ -0,0 +1,72 @@
#include "nodeparamviewkeyframecontrol.h"
#include <QHBoxLayout>
#include "ui/icons/icons.h"
NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(NodeInput* input, QWidget *parent) :
QWidget(parent),
input_(input)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(0);
// Automatically right aligns all buttons
layout->addStretch();
prev_key_btn_ = CreateNewToolButton(olive::icon::TriLeft);
prev_key_btn_->setIconSize(prev_key_btn_->iconSize() / 2);
layout->addWidget(prev_key_btn_);
toggle_key_btn_ = CreateNewToolButton(olive::icon::Diamond);
toggle_key_btn_->setCheckable(true);
toggle_key_btn_->setIconSize(toggle_key_btn_->iconSize() / 2);
layout->addWidget(toggle_key_btn_);
next_key_btn_ = CreateNewToolButton(olive::icon::TriRight);
next_key_btn_->setIconSize(next_key_btn_->iconSize() / 2);
layout->addWidget(next_key_btn_);
enable_key_btn_ = CreateNewToolButton(olive::icon::Clock);
enable_key_btn_->setCheckable(true);
enable_key_btn_->setIconSize(enable_key_btn_->iconSize() / 4 * 3);
layout->addWidget(enable_key_btn_);
connect(enable_key_btn_, SIGNAL(toggled(bool)), this, SLOT(ShowButtonsFromKeyframeEnable(bool)));
connect(enable_key_btn_, SIGNAL(toggled(bool)), this, SIGNAL(KeyframeEnableChanged(bool)));
// Pick up keyframing value
ShowButtonsFromKeyframeEnable(input_->is_keyframing());
}
NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const
{
return input_;
}
void NodeParamViewKeyframeControl::SetKeyframeEnabled(bool e)
{
// Suppress KeyframeEnableChanged() signal from this object
blockSignals(true);
enable_key_btn_->setChecked(e);
blockSignals(false);
}
QPushButton *NodeParamViewKeyframeControl::CreateNewToolButton(const QIcon& icon) const
{
QPushButton* btn = new QPushButton();
btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
btn->setIcon(icon);
return btn;
}
void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e)
{
prev_key_btn_->setVisible(e);
toggle_key_btn_->setVisible(e);
next_key_btn_->setVisible(e);
}
@@ -0,0 +1,37 @@
#ifndef NODEPARAMVIEWKEYFRAMECONTROL_H
#define NODEPARAMVIEWKEYFRAMECONTROL_H
#include <QPushButton>
#include <QWidget>
#include "node/input.h"
class NodeParamViewKeyframeControl : public QWidget
{
Q_OBJECT
public:
NodeParamViewKeyframeControl(NodeInput* input, QWidget* parent = nullptr);
NodeInput* GetConnectedInput() const;
public slots:
void SetKeyframeEnabled(bool e);
signals:
void KeyframeEnableChanged(bool);
private:
QPushButton* CreateNewToolButton(const QIcon &icon) const;
QPushButton* prev_key_btn_;
QPushButton* toggle_key_btn_;
QPushButton* next_key_btn_;
QPushButton* enable_key_btn_;
NodeInput* input_;
private slots:
void ShowButtonsFromKeyframeEnable(bool e);
};
#endif // NODEPARAMVIEWKEYFRAMECONTROL_H
@@ -0,0 +1,19 @@
#include "nodeparamviewundo.h"
NodeParamSetKeyframing::NodeParamSetKeyframing(NodeInput *input, bool setting, QUndoCommand *parent) :
QUndoCommand(parent),
input_(input),
setting_(setting)
{
Q_ASSERT(setting != input_->is_keyframing());
}
void NodeParamSetKeyframing::redo()
{
input_->set_is_keyframing(setting_);
}
void NodeParamSetKeyframing::undo()
{
input_->set_is_keyframing(!setting_);
}
@@ -0,0 +1,20 @@
#ifndef NODEPARAMVIEWUNDO_H
#define NODEPARAMVIEWUNDO_H
#include <QUndoCommand>
#include "node/input.h"
class NodeParamSetKeyframing : public QUndoCommand {
public:
NodeParamSetKeyframing(NodeInput* input, bool setting, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
NodeInput* input_;
bool setting_;
};
#endif // NODEPARAMVIEWUNDO_H