/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 .
***/
#include "nodeparamview.h"
#include
#include
#include
#include
#include "common/functiontimer.h"
#include "common/timecodefunctions.h"
#include "node/output/viewer/viewer.h"
namespace olive {
#define super TimeBasedWidget
NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) :
super(true, false, parent),
last_scroll_val_(0),
focused_node_(nullptr),
create_checkboxes_(kNoCheckBoxes),
time_target_(nullptr),
ignore_flags_(false)
{
// Create horizontal layout to place scroll area in (and keyframe editing eventually)
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setSpacing(0);
layout->setMargin(0);
QSplitter* splitter = new QSplitter(Qt::Horizontal);
layout->addWidget(splitter);
// Set up scroll area for params
param_scroll_area_ = new QScrollArea();
param_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
param_scroll_area_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
param_scroll_area_->setWidgetResizable(true);
splitter->addWidget(param_scroll_area_);
// Param widget
param_widget_container_ = new QWidget();
param_scroll_area_->setWidget(param_widget_container_);
param_widget_area_ = new NodeParamViewDockArea();
QVBoxLayout* param_widget_container_layout = new QVBoxLayout(param_widget_container_);
QMargins param_widget_margin = param_widget_container_layout->contentsMargins();
param_widget_margin.setTop(ruler()->height());
param_widget_container_layout->setContentsMargins(param_widget_margin);
param_widget_container_layout->setSpacing(0);
param_widget_container_layout->addWidget(param_widget_area_);
param_widget_container_layout->addStretch(INT_MAX);
// Create contexts for three different types
context_items_.resize(Track::kCount + 1);
for (int i=0; isetVisible(false);
NodeParamViewItemTitleBar *title_bar = static_cast(c->titleBarWidget());
if (i == Track::kVideo || i == Track::kAudio) {
title_bar->SetAddEffectButtonVisible(true);
title_bar->SetText(tr("%1 Nodes").arg(Footage::GetStreamTypeName(static_cast(i))));
} else {
title_bar->SetText(tr("Other"));
}
context_items_[i] = c;
param_widget_area_->AddItem(c);
}
// Disable collapsing param view (but collapsing keyframe view is permitted)
splitter->setCollapsible(0, false);
// Create global vertical scrollbar on the right
vertical_scrollbar_ = new QScrollBar();
vertical_scrollbar_->setMaximum(0);
layout->addWidget(vertical_scrollbar_);
// Connect scrollbars together
connect(param_scroll_area_->verticalScrollBar(), &QScrollBar::valueChanged, vertical_scrollbar_, &QScrollBar::setValue);
connect(param_scroll_area_->verticalScrollBar(), &QScrollBar::rangeChanged, vertical_scrollbar_, &QScrollBar::setRange);
connect(param_scroll_area_->verticalScrollBar(), &QScrollBar::rangeChanged, this, &NodeParamView::UpdateGlobalScrollBar);
connect(vertical_scrollbar_, &QScrollBar::valueChanged, param_scroll_area_->verticalScrollBar(), &QScrollBar::setValue);
if (create_keyframe_view) {
// Set up keyframe view
QWidget* keyframe_area = new QWidget();
QVBoxLayout* keyframe_area_layout = new QVBoxLayout(keyframe_area);
keyframe_area_layout->setSpacing(0);
keyframe_area_layout->setMargin(0);
// Create ruler object
keyframe_area_layout->addWidget(ruler());
// Create keyframe view
keyframe_view_ = new KeyframeView();
keyframe_view_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ConnectTimelineView(keyframe_view_);
keyframe_area_layout->addWidget(keyframe_view_);
// Connect ruler and keyframe view together
connect(ruler(), &TimeRuler::TimeChanged, keyframe_view_, &KeyframeView::SetTime);
connect(keyframe_view_, &KeyframeView::TimeChanged, ruler(), &TimeRuler::SetTime);
connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTime);
connect(keyframe_view_, &KeyframeView::Dragged, this, &NodeParamView::KeyframeViewDragged);
// Connect keyframe view scaling to this
connect(keyframe_view_, &KeyframeView::ScaleChanged, this, &NodeParamView::SetScale);
splitter->addWidget(keyframe_area);
// Set both widgets to 50/50
splitter->setSizes({INT_MAX, INT_MAX});
connect(keyframe_view_->verticalScrollBar(), &QScrollBar::valueChanged, vertical_scrollbar_, &QScrollBar::setValue);
connect(keyframe_view_->verticalScrollBar(), &QScrollBar::valueChanged, param_scroll_area_->verticalScrollBar(), &QScrollBar::setValue);
connect(param_scroll_area_->verticalScrollBar(), &QScrollBar::valueChanged, keyframe_view_->verticalScrollBar(), &QScrollBar::setValue);
connect(vertical_scrollbar_, &QScrollBar::valueChanged, keyframe_view_->verticalScrollBar(), &QScrollBar::setValue);
// TimeBasedWidget's scrollbar has extra functionality that we can take advantage of
keyframe_view_->setHorizontalScrollBar(scrollbar());
keyframe_view_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
connect(keyframe_view_->horizontalScrollBar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll);
} else {
keyframe_view_ = nullptr;
}
// Set a default scale - FIXME: Hardcoded
SetScale(120);
// Pickup on widget focus changes
connect(qApp,
&QApplication::focusChanged,
this,
&NodeParamView::FocusChanged);
}
NodeParamView::~NodeParamView()
{
qDeleteAll(context_items_);
}
/*void NodeParamView::SelectNodes(const QVector &nodes)
{
return;
int original_node_count = items_.size();
foreach (Node* n, nodes) {
// If we've already added this node (either a duplicate or a pinned node), don't add another
if (items_.contains(n)) {
continue;
}
// Add to "active" list to represent currently selected node
active_nodes_.append(n);
// Create node UI
AddNode(n, param_widget_area_);
}
if (items_.size() > original_node_count ) {
UpdateItemTime(GetTime());
// Re-arrange keyframes
QueueKeyframePositionUpdate();
SignalNodeOrder();
}
}
void NodeParamView::DeselectNodes(const QVector &nodes)
{
return;
// Remove item from map and delete the widget
int original_node_count = items_.size();
foreach (Node* n, nodes) {
// Filter out duplicates
if (!items_.contains(n)) {
continue;
}
if (!pinned_nodes_.contains(n)) {
// Store expanded state
node_expanded_state_.insert(n, items_.value(n)->IsExpanded());
// Remove all keyframes from this node
RemoveNode(n);
}
active_nodes_.removeOne(n);
}
if (items_.size() < original_node_count) {
// Re-arrange keyframes
QueueKeyframePositionUpdate();
SignalNodeOrder();
}
}*/
void NodeParamView::SetInputChecked(const NodeInput &input, bool e)
{
input_checked_.insert(input, e);
foreach (NodeParamViewContext *ctx, context_items_) {
ctx->SetInputChecked(input, e);
}
}
void NodeParamView::SetContexts(const QVector &contexts)
{
TIME_THIS_FUNCTION;
foreach (NodeParamViewContext *ctx, context_items_) {
ctx->Clear();
ctx->setVisible(false);
}
if (keyframe_view_) {
keyframe_view_->Clear();
}
if (focused_node_) {
focused_node_ = nullptr;
emit FocusedNodeChanged(nullptr);
}
foreach (Node *ctx, contexts) {
Track::Type ctx_type = Track::kCount;
if (ClipBlock *clip = dynamic_cast(ctx)) {
if (clip->track()) {
if (clip->track()->type() != Track::kNone) {
ctx_type = clip->track()->type();
}
}
} else if (Track *track = dynamic_cast