Merge branch 'master' into gizmos

This commit is contained in:
itsmattkc
2020-05-02 17:06:49 +10:00
130 changed files with 2310 additions and 981 deletions
+13
View File
@@ -168,6 +168,11 @@ void CurveWidget::SetVerticalScale(const double &vscale)
view_->SetYScale(vscale);
}
void CurveWidget::DeleteSelected()
{
view_->DeleteSelected();
}
void CurveWidget::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
@@ -200,6 +205,8 @@ void CurveWidget::ScaleChangedEvent(const double &scale)
void CurveWidget::TimeTargetChangedEvent(Node *target)
{
ConnectViewerNode(nullptr);
key_control_->SetTimeTarget(target);
view_->SetTimeTarget(target);
@@ -207,6 +214,12 @@ void CurveWidget::TimeTargetChangedEvent(Node *target)
if (bridge_) {
bridge_->SetTimeTarget(target);
}
// FIXME: If a non-viewer node is ever set here, it will fail to update the length
ViewerOutput* viewer = dynamic_cast<ViewerOutput*>(target);
if (viewer) {
ConnectViewerNode(viewer);
}
}
void CurveWidget::UpdateInputLabel()
+2
View File
@@ -47,6 +47,8 @@ public:
const double& GetVerticalScale();
void SetVerticalScale(const double& vscale);
void DeleteSelected();
protected:
virtual void changeEvent(QEvent *) override;
+20 -1
View File
@@ -73,6 +73,25 @@ void KeyframeViewBase::SetYScale(const double &y_scale)
}
}
void KeyframeViewBase::DeleteSelected()
{
QUndoCommand* command = new QUndoCommand();
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator i;
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
if (i.value()->isSelected()) {
NodeInput* input_parent = i.key()->parent();
new NodeParamRemoveKeyframeCommand(input_parent,
input_parent->get_keyframe_shared_ptr_from_raw(i.key()),
command);
}
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key)
{
KeyframeAboutToBeRemoved(key.get());
@@ -391,7 +410,7 @@ void KeyframeViewBase::ShowContextMenu()
{
Menu m;
MenuShared::instance()->AddItemsForEditMenu(&m);
MenuShared::instance()->AddItemsForEditMenu(&m, false);
QAction* linear_key_action = nullptr;
QAction* bezier_key_action = nullptr;
@@ -40,6 +40,8 @@ public:
const double& GetYScale() const;
void SetYScale(const double& y_scale);
void DeleteSelected();
public slots:
void RemoveKeyframe(NodeKeyframePtr key);
+9 -2
View File
@@ -32,6 +32,11 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) :
setContextMenuPolicy(Qt::CustomContextMenu);
}
ManagedDisplayWidget::~ManagedDisplayWidget()
{
ContextCleanup();
}
void ManagedDisplayWidget::ConnectColorManager(ColorManager *color_manager)
{
if (color_manager_ == color_manager) {
@@ -142,9 +147,13 @@ void ManagedDisplayWidget::MenuLookSelect(QAction *action)
void ManagedDisplayWidget::SetColorTransform(const ColorTransform &transform)
{
makeCurrent();
color_transform_ = transform;
SetupColorProcessor();
ColorProcessorChangedEvent();
doneCurrent();
}
void ManagedDisplayWidget::initializeGL()
@@ -248,9 +257,7 @@ void ManagedDisplayWidget::SetupColorProcessor()
color_manager_->GetReferenceColorSpace(),
color_transform_);
makeCurrent();
color_service_->Enable(context(), true);
doneCurrent();
} catch (OCIO::Exception& e) {
@@ -35,6 +35,8 @@ class ManagedDisplayWidget : public QOpenGLWidget
public:
ManagedDisplayWidget(QWidget* parent = nullptr);
virtual ~ManagedDisplayWidget() override;
/**
* @brief Disconnect a ColorManager (equivalent to ConnectColorManager(nullptr))
*/
+11
View File
@@ -50,6 +50,17 @@ Menu::Menu(const QString &s, QWidget *parent) :
Init();
}
QAction *Menu::AddActionWithData(const QString &text, const QVariant &data, const QVariant &compare)
{
QAction* a = addAction(text);
a->setData(data);
a->setCheckable(true);
a->setChecked(data == compare);
return a;
}
QAction* Menu::InsertAlphabetically(const QString &s)
{
QAction* action = new QAction(s, this);
+4
View File
@@ -131,6 +131,10 @@ public:
return a;
}
QAction* AddActionWithData(const QString& text,
const QVariant& data,
const QVariant& compare);
QAction *InsertAlphabetically(const QString& s);
void InsertAlphabetically(QAction* entry);
void InsertAlphabetically(Menu* menu);
+7 -4
View File
@@ -79,7 +79,7 @@ void MenuShared::AddItemsForNewMenu(Menu *m)
m->addAction(new_folder_item_);
}
void MenuShared::AddItemsForEditMenu(Menu *m)
void MenuShared::AddItemsForEditMenu(Menu *m, bool for_clips)
{
m->addAction(edit_cut_item_);
m->addAction(edit_copy_item_);
@@ -87,8 +87,11 @@ void MenuShared::AddItemsForEditMenu(Menu *m)
m->addAction(edit_paste_insert_item_);
m->addAction(edit_duplicate_item_);
m->addAction(edit_delete_item_);
m->addAction(edit_ripple_delete_item_);
m->addAction(edit_split_item_);
if (for_clips) {
m->addAction(edit_ripple_delete_item_);
m->addAction(edit_split_item_);
}
}
void MenuShared::AddItemsForInOutMenu(Menu *m)
@@ -185,7 +188,7 @@ void MenuShared::PasteInsertTriggered()
void MenuShared::DuplicateTriggered()
{
qDebug() << "FIXME: Stub";
PanelManager::instance()->CurrentlyFocused()->Duplicate();
}
void MenuShared::EnableDisableTriggered()
+1 -1
View File
@@ -39,7 +39,7 @@ public:
void Retranslate();
void AddItemsForNewMenu(Menu* m);
void AddItemsForEditMenu(Menu* m);
void AddItemsForEditMenu(Menu* m, bool for_clips);
void AddItemsForInOutMenu(Menu* m);
void AddItemsForClipEditMenu(Menu* m);
+13 -1
View File
@@ -158,7 +158,9 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
items_.append(item);
QMetaObject::invokeMethod(item, "SignalAllKeyframes", Qt::QueuedConnection);
QMetaObject::invokeMethod(item,
"SignalAllKeyframes",
Qt::QueuedConnection);
emit OpenedNode(node);
@@ -225,6 +227,16 @@ const QList<Node *> &NodeParamView::nodes()
return nodes_;
}
Node *NodeParamView::GetTimeTarget() const
{
return keyframe_view_->GetTimeTarget();
}
void NodeParamView::DeleteSelected()
{
keyframe_view_->DeleteSelected();
}
void NodeParamView::UpdateItemTime(const int64_t &timestamp)
{
rational time = Timecode::timestamp_to_time(timestamp, keyframe_view_->timebase());
+4
View File
@@ -40,6 +40,10 @@ public:
void SetNodes(QList<Node*> nodes);
const QList<Node*>& nodes();
Node* GetTimeTarget() const;
void DeleteSelected();
signals:
void InputDoubleClicked(NodeInput* input);
@@ -73,6 +73,8 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
connect(title_bar_collapse_btn_, &QPushButton::toggled, body_, &NodeParamViewItemBody::setVisible);
main_layout->addWidget(body_);
connect(node_, &Node::LabelChanged, this, &NodeParamViewItem::Retranslate);
Retranslate();
}
@@ -111,7 +113,11 @@ void NodeParamViewItem::Retranslate()
{
node_->Retranslate();
title_bar_lbl_->setText(node_->Name());
if (node_->GetLabel().isEmpty()) {
title_bar_lbl_->setText(node_->Name());
} else {
title_bar_lbl_->setText(tr("%1 (%2)").arg(node_->GetLabel(), node_->Name()));
}
body_->Retranslate();
}
+3 -2
View File
@@ -129,8 +129,6 @@ protected:
virtual void changeEvent(QEvent *e) override;
private:
void Retranslate();
NodeParamViewItemTitleBar* title_bar_;
QLabel* title_bar_lbl_;
@@ -143,6 +141,9 @@ private:
rational time_;
private slots:
void Retranslate();
};
OLIVE_NAMESPACE_EXIT
+1
View File
@@ -18,6 +18,7 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/nodeview/nodeview.h
widget/nodeview/nodeview.cpp
widget/nodeview/nodeviewcommon.h
widget/nodeview/nodeviewedge.h
widget/nodeview/nodeviewedge.cpp
widget/nodeview/nodeviewitem.h
+286 -78
View File
@@ -20,30 +20,34 @@
#include "nodeview.h"
#include <QInputDialog>
#include <QMouseEvent>
#include "core.h"
#include "nodeviewundo.h"
#include "node/factory.h"
#include "widget/menu/menushared.h"
#define super HandMovableView
OLIVE_NAMESPACE_ENTER
NodeView::NodeView(QWidget *parent) :
QGraphicsView(parent),
HandMovableView(parent),
graph_(nullptr),
attached_item_(nullptr),
drop_edge_(nullptr)
{
setScene(&scene_);
setDragMode(RubberBandDrag);
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
setRenderHint(QPainter::Antialiasing);
connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged);
connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot);
connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu);
setMouseTracking(true);
setRenderHint(QPainter::Antialiasing);
SetFlowDirection(NodeViewCommon::kTopToBottom);
}
NodeView::~NodeView()
@@ -93,21 +97,33 @@ void NodeView::DeleteSelected()
return;
}
QList<Node*> selected_nodes = scene_.GetSelectedNodes();
QUndoCommand* command = new QUndoCommand();
// Ensure no nodes are "undeletable"
for (int i=0;i<selected_nodes.size();i++) {
if (!selected_nodes.at(i)->CanBeDeleted()) {
selected_nodes.removeAt(i);
i--;
{
QList<NodeEdge*> selected_edges = scene_.GetSelectedEdges();
foreach (NodeEdge* edge, selected_edges) {
new NodeEdgeRemoveCommand(edge->output(), edge->input(), command);
}
}
if (selected_nodes.isEmpty()) {
return;
{
QList<Node*> selected_nodes = scene_.GetSelectedNodes();
// Ensure no nodes are "undeletable"
for (int i=0;i<selected_nodes.size();i++) {
if (!selected_nodes.at(i)->CanBeDeleted()) {
selected_nodes.removeAt(i);
i--;
}
}
if (!selected_nodes.isEmpty()) {
new NodeRemoveCommand(graph_, selected_nodes, command);
}
}
Core::instance()->undo_stack()->push(new NodeRemoveCommand(graph_, selected_nodes));
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void NodeView::SelectAll()
@@ -181,7 +197,60 @@ void NodeView::Paste()
Core::instance()->undo_stack()->pushIfHasChildren(command);
if (!pasted_nodes.isEmpty()) {
// FIXME: Attach to cursor so user can drop in place
AttachNodesToCursor(pasted_nodes);
}
}
void NodeView::Duplicate()
{
if (!graph_) {
return;
}
QList<Node*> selected = scene_.GetSelectedNodes();
if (selected.isEmpty()) {
return;
}
QUndoCommand* command = new QUndoCommand();
QList<Node*> duplicated_nodes;
foreach (Node* n, selected) {
Node* copy = n->copy();
Node::CopyInputs(n, copy, false);
duplicated_nodes.append(copy);
new NodeAddCommand(graph_, copy, command);
}
for (int i=0;i<selected.size();i++) {
Node* src = selected.at(i);
for (int j=0;j<selected.size();j++) {
if (i == j) {
continue;
}
Node* dst = selected.at(j);
foreach (NodeEdgePtr edge, src->output()->edges()) {
if (edge->input()->parentNode() == dst) {
new NodeEdgeAddCommand(duplicated_nodes.at(i)->output(),
duplicated_nodes.at(j)->GetInputWithID(edge->input()->id()),
command);
}
}
}
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
if (!duplicated_nodes.isEmpty()) {
AttachNodesToCursor(duplicated_nodes);
}
}
@@ -196,10 +265,10 @@ void NodeView::ItemsChanged()
void NodeView::keyPressEvent(QKeyEvent *event)
{
QGraphicsView::keyPressEvent(event);
super::keyPressEvent(event);
if (event->key() == Qt::Key_Escape && attached_item_) {
DetachItemFromCursor();
if (event->key() == Qt::Key_Escape && !attached_items_.isEmpty()) {
DetachItemsFromCursor();
// We undo the last action which SHOULD be adding the node
// FIXME: Possible danger of this not being the case?
@@ -209,93 +278,111 @@ void NodeView::keyPressEvent(QKeyEvent *event)
void NodeView::mousePressEvent(QMouseEvent *event)
{
if (attached_item_) {
Node* dropping_node = attached_item_->GetNode();
if (HandPress(event)) return;
DetachItemFromCursor();
if (!attached_items_.isEmpty()) {
if (attached_items_.size() == 1) {
Node* dropping_node = attached_items_.first().item->GetNode();
if (drop_edge_) {
NodeEdgePtr old_edge = drop_edge_->edge();
if (drop_edge_) {
NodeEdgePtr old_edge = drop_edge_->edge();
// We have everything we need to place the node in between
QUndoCommand* command = new QUndoCommand();
// We have everything we need to place the node in between
QUndoCommand* command = new QUndoCommand();
// Remove old edge
new NodeEdgeRemoveCommand(old_edge, command);
// Remove old edge
new NodeEdgeRemoveCommand(old_edge, command);
// Place new edges
new NodeEdgeAddCommand(old_edge->output(), drop_compatible_input_, command);
new NodeEdgeAddCommand(dropping_node->output(), old_edge->input(), command);
// Place new edges
new NodeEdgeAddCommand(old_edge->output(), drop_input_, command);
new NodeEdgeAddCommand(dropping_node->output(), old_edge->input(), command);
Core::instance()->undo_stack()->push(command);
Core::instance()->undo_stack()->push(command);
}
drop_edge_ = nullptr;
}
drop_edge_ = nullptr;
DetachItemsFromCursor();
}
QGraphicsView::mousePressEvent(event);
super::mousePressEvent(event);
}
void NodeView::mouseMoveEvent(QMouseEvent *event)
{
QGraphicsView::mouseMoveEvent(event);
if (HandMove(event)) return;
if (attached_item_) {
attached_item_->setPos(mapToScene(event->pos()));
super::mouseMoveEvent(event);
// See if the user clicked on an edge
QRect edge_detect_rect(event->pos(), event->pos());
if (!attached_items_.isEmpty()) {
MoveAttachedNodesToCursor(event->pos());
// FIXME: Hardcoded numbers
edge_detect_rect.adjust(-20, -20, 20, 20);
// See if the user clicked on an edge (only when dropping single nodes)
if (attached_items_.size() == 1) {
Node* attached_node = attached_items_.first().item->GetNode();
QList<QGraphicsItem*> items = this->items(edge_detect_rect);
QRect edge_detect_rect(event->pos(), event->pos());
NodeViewEdge* new_drop_edge = nullptr;
// FIXME: Hardcoded numbers
edge_detect_rect.adjust(-20, -20, 20, 20);
foreach (QGraphicsItem* item, items) {
NodeViewEdge* edge = dynamic_cast<NodeViewEdge*>(item);
QList<QGraphicsItem*> items = this->items(edge_detect_rect);
if (edge) {
// Try to place this node inside this edge
NodeViewEdge* new_drop_edge = nullptr;
// See if the node we're dropping has an input of a compatible data type
NodeInput* edges_input = edge->edge()->input();
NodeParam::DataType input_type = edges_input->data_type();
// See if there is an edge here
foreach (QGraphicsItem* item, items) {
new_drop_edge = dynamic_cast<NodeViewEdge*>(item);
NodeInput* compatible_input = nullptr;
if (new_drop_edge) {
drop_input_ = nullptr;
foreach (NodeParam* drop_node_param, attached_item_->GetNode()->parameters()) {
if (drop_node_param->type() == NodeParam::kInput
&& static_cast<NodeInput*>(drop_node_param)->data_type() & input_type) {
compatible_input = static_cast<NodeInput*>(drop_node_param);
foreach (NodeParam* param, attached_node->parameters()) {
if (param->type() == NodeParam::kInput) {
NodeInput* input = static_cast<NodeInput*>(param);
if (input->IsConnectable()) {
if (input->data_type() & new_drop_edge->edge()->input()->data_type()) {
drop_input_ = input;
break;
} else if (!drop_input_) {
drop_input_ = input;
}
}
}
}
if (drop_input_) {
break;
} else {
new_drop_edge = nullptr;
}
}
}
if (compatible_input) {
new_drop_edge = edge;
drop_compatible_input_ = compatible_input;
break;
if (drop_edge_ != new_drop_edge) {
if (drop_edge_) {
drop_edge_->SetHighlighted(false);
}
}
}
if (drop_edge_ != new_drop_edge) {
if (drop_edge_) {
drop_edge_->SetHighlighted(false);
}
drop_edge_ = new_drop_edge;
drop_edge_ = new_drop_edge;
if (drop_edge_) {
drop_edge_->SetHighlighted(true);
if (drop_edge_) {
drop_edge_->SetHighlighted(true);
}
}
}
}
}
void NodeView::mouseReleaseEvent(QMouseEvent *event)
{
if (HandRelease(event)) return;
super::mouseReleaseEvent(event);
}
void NodeView::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::ControlModifier) {
@@ -321,10 +408,59 @@ void NodeView::ShowContextMenu(const QPoint &pos)
Menu m;
Menu* add_menu = NodeFactory::CreateMenu(&m);
add_menu->setTitle(tr("Add"));
connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot);
m.addMenu(add_menu);
MenuShared::instance()->AddItemsForEditMenu(&m, false);
m.addSeparator();
QList<NodeViewItem*> selected = scene_.GetSelectedItems();
if (itemAt(pos) && !selected.isEmpty()) {
if (selected.size() == 1) {
// Label node action
QAction* label_action = m.addAction(tr("Label"));
connect(label_action, &QAction::triggered, this, &NodeView::ContextMenuLabelNode);
m.addSeparator();
}
// Auto-position action
QAction* autopos = m.addAction(tr("Auto-Position"));
connect(autopos, &QAction::triggered, this, &NodeView::AutoPositionDescendents);
} else {
Menu* direction_menu = new Menu(tr("Direction"), &m);
m.addMenu(direction_menu);
direction_menu->AddActionWithData(tr("Top to Bottom"),
NodeViewCommon::kTopToBottom,
scene_.GetFlowDirection());
direction_menu->AddActionWithData(tr("Bottom to Top"),
NodeViewCommon::kBottomToTop,
scene_.GetFlowDirection());
direction_menu->AddActionWithData(tr("Left to Right"),
NodeViewCommon::kLeftToRight,
scene_.GetFlowDirection());
direction_menu->AddActionWithData(tr("Right to Left"),
NodeViewCommon::kRightToLeft,
scene_.GetFlowDirection());
connect(direction_menu, &Menu::triggered, this, &NodeView::ContextMenuSetDirection);
m.addSeparator();
Menu* add_menu = NodeFactory::CreateMenu(&m);
add_menu->setTitle(tr("Add"));
connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot);
m.addMenu(add_menu);
}
m.exec(mapToGlobal(pos));
}
@@ -337,7 +473,45 @@ void NodeView::CreateNodeSlot(QAction *action)
Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node));
NodeViewItem* item = scene_.NodeToUIObject(new_node);
AttachItemToCursor(item);
AttachItemsToCursor({item});
}
}
void NodeView::ContextMenuSetDirection(QAction *action)
{
SetFlowDirection(static_cast<NodeViewCommon::FlowDirection>(action->data().toInt()));
}
void NodeView::AutoPositionDescendents()
{
QList<Node*> selected = scene_.GetSelectedNodes();
foreach (Node* n, selected) {
scene_.ReorganizeFrom(n);
}
}
void NodeView::ContextMenuLabelNode()
{
QList<Node*> nodes = scene_.GetSelectedNodes();
if (nodes.isEmpty()) {
return;
}
Node* n = nodes.first();
bool ok;
QString s = QInputDialog::getText(this,
tr("Label Node"),
tr("Set node label"),
QLineEdit::Normal,
n->GetLabel(),
&ok);
if (ok) {
n->SetLabel(s);
}
}
@@ -475,16 +649,50 @@ void NodeView::PlaceNode(NodeViewItem *n, const QPointF &pos)
}
}
void NodeView::AttachItemToCursor(NodeViewItem *item)
void NodeView::AttachNodesToCursor(const QList<Node *> &nodes)
{
attached_item_ = item;
QList<NodeViewItem*> items;
setMouseTracking(attached_item_);
foreach (Node* p, nodes) {
items.append(scene_.NodeToUIObject(p));
}
AttachItemsToCursor(items);
}
void NodeView::DetachItemFromCursor()
void NodeView::AttachItemsToCursor(const QList<NodeViewItem*>& items)
{
AttachItemToCursor(nullptr);
DetachItemsFromCursor();
if (!items.isEmpty()) {
foreach (NodeViewItem* i, items) {
attached_items_.append({i, i->pos() - items.first()->pos()});
}
setMouseTracking(true);
MoveAttachedNodesToCursor(mapFromGlobal(QCursor::pos()));
}
}
void NodeView::DetachItemsFromCursor()
{
attached_items_.clear();
setMouseTracking(false);
}
void NodeView::SetFlowDirection(NodeViewCommon::FlowDirection dir)
{
scene_.SetFlowDirection(dir);
}
void NodeView::MoveAttachedNodesToCursor(const QPoint& p)
{
QPointF item_pos = mapToScene(p);
foreach (const AttachedItem& i, attached_items_) {
i.item->setPos(item_pos + i.original_pos);
}
}
OLIVE_NAMESPACE_EXIT
+35 -6
View File
@@ -26,6 +26,7 @@
#include "node/graph.h"
#include "nodeviewscene.h"
#include "widget/timelinewidget/view/handmovableview.h"
#include "widget/nodecopypaste/nodecopypaste.h"
OLIVE_NAMESPACE_ENTER
@@ -36,7 +37,7 @@ OLIVE_NAMESPACE_ENTER
* This widget takes a NodeGraph object and constructs a QGraphicsScene representing its data, viewing and allowing
* the user to make modifications to it.
*/
class NodeView : public QGraphicsView, public NodeCopyPasteWidget
class NodeView : public HandMovableView, public NodeCopyPasteWidget
{
Q_OBJECT
public:
@@ -63,6 +64,8 @@ public:
void CopySelected(bool cut);
void Paste();
void Duplicate();
signals:
/**
* @brief Signal emitted when the selected nodes have changed
@@ -73,24 +76,35 @@ protected:
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override;
private:
void PlaceNode(NodeViewItem* n, const QPointF& pos);
void AttachItemToCursor(NodeViewItem* item);
void AttachNodesToCursor(const QList<Node*>& nodes);
void DetachItemFromCursor();
void AttachItemsToCursor(const QList<NodeViewItem*>& items);
void DetachItemsFromCursor();
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
void MoveAttachedNodesToCursor(const QPoint &p);
NodeGraph* graph_;
NodeViewItem* attached_item_;
struct AttachedItem {
NodeViewItem* item;
QPointF original_pos;
};
QList<AttachedItem> attached_items_;
NodeViewEdge* drop_edge_;
NodeInput* drop_compatible_input_;
NodeInput* drop_input_;
NodeViewScene scene_;
@@ -117,6 +131,21 @@ private slots:
*/
void CreateNodeSlot(QAction* action);
/**
* @brief Receiver for setting the direction from the context menu
*/
void ContextMenuSetDirection(QAction* action);
/**
* @brief Receiver for auto-position descendents menu action
*/
void AutoPositionDescendents();
/**
* @brief Receiver for labelling a node from the context menu
*/
void ContextMenuLabelNode();
};
OLIVE_NAMESPACE_EXIT
+58
View File
@@ -0,0 +1,58 @@
/***
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/>.
***/
#ifndef NODEVIEWCOMMON_H
#define NODEVIEWCOMMON_H
#include <QtGlobal>
#include "common/define.h"
OLIVE_NAMESPACE_ENTER
class NodeViewCommon {
public:
enum FlowDirection {
kTopToBottom,
kBottomToTop,
kLeftToRight,
kRightToLeft
};
static Qt::Orientation GetFlowOrientation(FlowDirection dir) {
if (dir == kTopToBottom || dir == kBottomToTop) {
return Qt::Vertical;
} else {
return Qt::Horizontal;
}
}
static bool DirectionsAreOpposing(FlowDirection a, FlowDirection b) {
return ((a == NodeViewCommon::kLeftToRight && b == NodeViewCommon::kRightToLeft)
|| (a == NodeViewCommon::kRightToLeft && b == NodeViewCommon::kLeftToRight)
|| (a == NodeViewCommon::kTopToBottom && b == NodeViewCommon::kBottomToTop)
|| (a == NodeViewCommon::kBottomToTop && b == NodeViewCommon::kTopToBottom));
}
};
OLIVE_NAMESPACE_EXIT
#endif // NODEVIEWCOMMON_H
+59 -21
View File
@@ -23,6 +23,7 @@
#include <QApplication>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QStyleOptionGraphicsItem>
#include "common/clamp.h"
#include "common/lerp.h"
@@ -34,9 +35,12 @@ OLIVE_NAMESPACE_ENTER
NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) :
QGraphicsPathItem(parent),
edge_(nullptr),
color_group_(QPalette::Active),
color_role_(QPalette::Text)
connected_(false),
highlighted_(false),
flow_dir_(NodeViewCommon::kLeftToRight)
{
setFlag(QGraphicsItem::ItemIsSelectable);
// Ensures this UI object is drawn behind other objects
setZValue(-1);
@@ -75,45 +79,79 @@ void NodeViewEdge::Adjust()
}
// Draw a line between the two
SetPoints(output->GetParamPoint(edge_->output()), input->GetParamPoint(edge_->input()));
SetPoints(output->GetParamPoint(edge_->output(), output->pos()),
input->GetParamPoint(edge_->input(), output->pos()),
input->IsExpanded());
}
void NodeViewEdge::SetConnected(bool c)
{
if (c) {
color_group_ = QPalette::Active;
} else {
color_group_ = QPalette::Disabled;
}
connected_ = c;
UpdatePen();
update();
}
void NodeViewEdge::SetHighlighted(bool e)
{
if (e) {
color_role_ = QPalette::Highlight;
} else {
color_role_ = QPalette::Text;
}
highlighted_ = e;
UpdatePen();
update();
}
void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end)
void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool input_is_expanded)
{
QPainterPath path;
double half_x = lerp(start.x(), end.x(), 0.5);
path.moveTo(start);
path.cubicTo(QPointF(half_x, start.y()), QPointF(half_x, end.y()), end);
double half_x = lerp(start.x(), end.x(), 0.5);
double half_y = lerp(start.y(), end.y(), 0.5);
QPointF cp1, cp2;
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
cp1 = QPointF(half_x, start.y());
} else {
cp1 = QPointF(start.x(), half_y);
}
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal || input_is_expanded) {
cp2 = QPointF(half_x, end.y());
} else {
cp2 = QPointF(end.x(), half_y);
}
path.cubicTo(cp1, cp2, end);
setPath(path);
}
void NodeViewEdge::UpdatePen()
void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir)
{
setPen(QPen(qApp->palette().color(color_group_, color_role_), edge_width_));
flow_dir_ = dir;
//update();
Adjust();
}
void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
{
QPalette::ColorGroup group;
QPalette::ColorRole role;
if (connected_) {
group = QPalette::Active;
} else {
group = QPalette::Disabled;
}
if (highlighted_ != bool(option->state & QStyle::State_Selected)) {
role = QPalette::Highlight;
} else {
role = QPalette::Text;
}
painter->setPen(QPen(qApp->palette().color(group, role), edge_width_));
painter->setBrush(Qt::NoBrush);
painter->drawPath(path());
}
OLIVE_NAMESPACE_EXIT
+14 -5
View File
@@ -25,6 +25,7 @@
#include <QPalette>
#include "node/edge.h"
#include "nodeviewcommon.h"
OLIVE_NAMESPACE_ENTER
@@ -81,18 +82,26 @@ public:
/**
* @brief Set points to create curve from
*/
void SetPoints(const QPointF& start, const QPointF& end);
void SetPoints(const QPointF& start, const QPointF& end, bool input_is_expanded);
/**
* @brief Sets the direction nodes are flowing
*/
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
void UpdatePen();
NodeEdgePtr edge_;
int edge_width_;
QPalette::ColorGroup color_group_;
bool connected_;
QPalette::ColorRole color_role_;
bool highlighted_;
NodeViewCommon::FlowDirection flow_dir_;
};
+244 -59
View File
@@ -44,9 +44,11 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
cached_drop_item_(nullptr),
cached_drop_item_expanded_(false),
expanded_(false),
hide_titlebar_(false),
standard_click_(false),
highlighted_index_(-1),
node_edge_change_command_(nullptr)
node_edge_change_command_(nullptr),
flow_dir_(NodeViewCommon::kLeftToRight)
{
// Set flags for this widget
setFlag(QGraphicsItem::ItemIsMovable);
@@ -57,26 +59,110 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
// We use font metrics to set all the UI measurements for DPI-awareness
//
QFont default_font;
QFontMetrics font_metrics(default_font);
// Set border width
node_border_width_ = font_metrics.height() / 12;
node_border_width_ = DefaultItemBorder();
// Set text and icon padding
int node_text_padding = font_metrics.height() / 4;
// Not particularly great way of using text scaling to set the width (DPI-awareness, etc.)
int widget_width = QFontMetricsWidth(font_metrics, "HHHHHHHHHHHHHH");
// Use the current default font height to size this widget
// Set default "collapsed" size
int widget_height = font_metrics.height() + node_text_padding * 2;
int widget_width = DefaultItemWidth();
int widget_height = DefaultItemHeight();
title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height);
setRect(title_bar_rect_);
}
QPointF NodeViewItem::GetNodePosition() const
{
QPointF node_pos;
qreal adjusted_x = pos().x() / DefaultItemHorizontalPadding();
qreal adjusted_y = pos().y() / DefaultItemVerticalPadding();
switch (flow_dir_) {
case NodeViewCommon::kLeftToRight:
node_pos.setX(adjusted_x);
node_pos.setY(adjusted_y);
break;
case NodeViewCommon::kRightToLeft:
node_pos.setX(-adjusted_x);
node_pos.setY(adjusted_y);
break;
case NodeViewCommon::kTopToBottom:
node_pos.setX(adjusted_y);
node_pos.setY(adjusted_x);
break;
case NodeViewCommon::kBottomToTop:
node_pos.setX(-adjusted_y);
node_pos.setY(adjusted_x);
break;
}
return node_pos;
}
void NodeViewItem::SetNodePosition(const QPointF &pos)
{
switch (flow_dir_) {
case NodeViewCommon::kLeftToRight:
setPos(pos.x() * DefaultItemHorizontalPadding(),
pos.y() * DefaultItemVerticalPadding());
break;
case NodeViewCommon::kRightToLeft:
setPos(-pos.x() * DefaultItemHorizontalPadding(),
pos.y() * DefaultItemVerticalPadding());
break;
case NodeViewCommon::kTopToBottom:
setPos(pos.y() * DefaultItemHorizontalPadding(),
pos.x() * DefaultItemVerticalPadding());
break;
case NodeViewCommon::kBottomToTop:
setPos(pos.y() * DefaultItemHorizontalPadding(),
-pos.x() * DefaultItemVerticalPadding());
break;
}
}
int NodeViewItem::DefaultTextPadding()
{
return QFontMetrics(QFont()).height() / 4;
}
int NodeViewItem::DefaultItemHeight()
{
return QFontMetrics(QFont()).height() + DefaultTextPadding() * 2;
}
int NodeViewItem::DefaultItemWidth()
{
return QFontMetricsWidth(QFontMetrics(QFont()), "HHHHHHHHHH");;
}
int NodeViewItem::DefaultMaximumTextWidth()
{
return QFontMetricsWidth(QFontMetrics(QFont()), "HHHHHHHH");;
}
int NodeViewItem::DefaultItemBorder()
{
return QFontMetrics(QFont()).height() / 12;
}
qreal NodeViewItem::DefaultItemHorizontalPadding() const
{
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
return DefaultItemWidth() * 1.5;
} else {
return DefaultItemWidth() * 1.25;
}
}
qreal NodeViewItem::DefaultItemVerticalPadding() const
{
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
return DefaultItemHeight() * 1.5;
} else {
return DefaultItemHeight() * 2.0;
}
}
void NodeViewItem::SetNode(Node *n)
{
node_ = n;
@@ -96,7 +182,7 @@ void NodeViewItem::SetNode(Node *n)
}
}
setPos(node_->GetPosition());
SetNodePosition(node_->GetPosition());
}
update();
@@ -112,18 +198,26 @@ bool NodeViewItem::IsExpanded() const
return expanded_;
}
void NodeViewItem::SetExpanded(bool e)
void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
{
if (expanded_ == e) {
if (node_inputs_.isEmpty()
|| (expanded_ == e && hide_titlebar_ == hide_titlebar)) {
return;
}
expanded_ = e;
hide_titlebar_ = hide_titlebar;
if (expanded_ && !node_inputs_.isEmpty()) {
// Create new rect
QRectF new_rect = title_bar_rect_;
new_rect.setHeight(new_rect.height() * node_inputs_.size());
if (hide_titlebar_) {
new_rect.setHeight(new_rect.height() * node_inputs_.size());
} else {
new_rect.setHeight(new_rect.height() * (node_inputs_.size() + 1));
}
setRect(new_rect);
} else {
setRect(title_bar_rect_);
@@ -143,33 +237,14 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
// don't want here)
QPalette app_pal = Core::instance()->main_window()->palette();
{
QPen border_pen;
border_pen.setWidth(node_border_width_);
QBrush bkg_color;
if (option->state & QStyle::State_Selected) {
border_pen.setColor(app_pal.color(QPalette::Highlight));
} else {
border_pen.setColor(css_proxy_.BorderColor());
}
if (IsExpanded()) {
bkg_color = app_pal.color(QPalette::Window);
} else {
bkg_color = css_proxy_.TitleBarColor();
}
painter->setPen(border_pen);
painter->setBrush(bkg_color);
// Draw background rect if expanded
if (IsExpanded()) {
painter->setPen(Qt::NoPen);
painter->setBrush(app_pal.color(QPalette::Window));
painter->drawRect(rect());
}
painter->setPen(app_pal.color(QPalette::Text));
if (IsExpanded()) {
painter->setPen(app_pal.color(QPalette::Text));
for (int i=0;i<node_inputs_.size();i++) {
QRectF input_rect = GetInputRect(i);
@@ -180,13 +255,65 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name());
}
}
} else if (node_) {
// Draw the titlebar
if (!hide_titlebar_ && node_) {
painter->setPen(Qt::black);
painter->setBrush(css_proxy_.TitleBarColor());
painter->drawRect(title_bar_rect_);
painter->setPen(app_pal.color(QPalette::Text));
QString node_label;
if (node_->GetLabel().isEmpty()) {
node_label = node_->ShortName();
} else {
node_label = node_->GetLabel();
}
{
QFont f;
QFontMetrics fm(f);
int max_text_width = DefaultMaximumTextWidth();
if (QFontMetricsWidth(fm, node_label) > max_text_width) {
QString concatenated;
do {
node_label.chop(1);
concatenated = QCoreApplication::translate("NodeViewItem", "%1...").arg(node_label);
} while (QFontMetricsWidth(fm, concatenated) > max_text_width);
node_label = concatenated;
}
}
// Draw the text in a rect (the rect is sized around text already in the constructor)
painter->drawText(title_bar_rect_, Qt::AlignCenter, node_->Name());
painter->drawText(title_bar_rect_,
Qt::AlignCenter,
node_label);
}
// Draw final border
QPen border_pen;
border_pen.setWidth(node_border_width_);
if (option->state & QStyle::State_Selected) {
border_pen.setColor(app_pal.color(QPalette::Highlight));
} else {
border_pen.setColor(css_proxy_.BorderColor());
}
painter->setPen(border_pen);
painter->setBrush(Qt::NoBrush);
painter->drawRect(rect());
}
void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
@@ -203,6 +330,7 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
// Create draggable object
dragging_edge_ = new NodeViewEdge();
dragging_edge_->SetFlowDirection(flow_dir_);
// Set up a QUndoCommand to make this action undoable
node_edge_change_command_ = new QUndoCommand();
@@ -213,7 +341,7 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
drag_src_param_ = param;
// Set the starting position to the current param's connector
dragging_edge_start_ = GetParamPoint(param);
dragging_edge_start_ = GetParamPoint(param, QPointF());
} else if (param->type() == NodeParam::kInput) {
// For an input param, we default to moving an existing edge
@@ -229,8 +357,8 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
drag_source_ = static_cast<NodeViewScene*>(scene())->NodeToUIObject(drag_src_param_->parentNode());
// Get the opposing parameter's rect center using the line's current coordinates
// (we use the current coordinates because a complex formula is used for the line's coords if the opposing
// node is collapsed, therefore it's easier to just retrieve it from line itself)
// (we use the current coordinates because a complex formula is used for the line's coords if
// the opposing node is collapsed, therefore it's easier to just retrieve it from line itself)
NodeViewEdge* existing_edge_ui = static_cast<NodeViewScene*>(scene())->EdgeToUIObject(edge);
QPainterPath existing_edge_line = existing_edge_ui->path();
QPointF edge_start = existing_edge_line.pointAtPercent(0);
@@ -288,6 +416,8 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
cached_drop_item_->SetExpanded(false);
}
cached_drop_item_->SetHighlightedIndex(-1);
cached_drop_item_->setZValue(0);
cached_drop_item_ = nullptr;
}
@@ -303,7 +433,7 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
cached_drop_item_expanded_ = !cached_drop_item_->IsExpanded();
if (cached_drop_item_expanded_) {
cached_drop_item_->SetExpanded(true);
cached_drop_item_->SetExpanded(true, true);
}
cached_drop_item_->setZValue(1);
@@ -330,7 +460,9 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (!cached_drop_item_->GetNode()->OutputsTo(node_)) {
drag_dest_param_ = comp_param;
highlight_their_index = i;
end_point = cached_drop_item_->mapToScene(cached_drop_item_->GetInputPoint(i));
QPointF end_point_local = cached_drop_item_->GetInputPoint(i, pos());
end_point = cached_drop_item_->mapToScene(end_point_local);
}
break;
@@ -340,9 +472,11 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
cached_drop_item_->SetHighlightedIndex(highlight_their_index);
}
dragging_edge_->SetConnected(drag_dest_param_ != nullptr);
dragging_edge_->SetConnected(drag_dest_param_);
dragging_edge_->SetPoints(dragging_edge_start_, end_point);
dragging_edge_->SetPoints(dragging_edge_start_,
end_point,
cached_drop_item_ ? cached_drop_item_->IsExpanded() : false);
return;
}
@@ -363,8 +497,14 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
scene()->removeItem(dragging_edge_);
// If we expanded an item in the drag, re-collapse it now
if (cached_drop_item_ != nullptr) {
cached_drop_item_->SetExpanded(false);
if (cached_drop_item_) {
if (cached_drop_item_expanded_) {
cached_drop_item_->SetExpanded(false);
}
cached_drop_item_->SetHighlightedIndex(-1);
cached_drop_item_->setZValue(0);
cached_drop_item_ = nullptr;
}
@@ -405,10 +545,19 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
}
}
void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsRectItem::mouseDoubleClickEvent(event);
SetExpanded(!IsExpanded());
}
QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionHasChanged && node_) {
node_->SetPosition(value.toPointF());
node_->blockSignals(true);
node_->SetPosition(GetNodePosition());
node_->blockSignals(false);
}
return QGraphicsItem::itemChange(change, value);
@@ -429,6 +578,10 @@ QRectF NodeViewItem::GetInputRect(int index) const
{
QRectF r = title_bar_rect_;
if (!hide_titlebar_) {
index++;
}
if (IsExpanded()) {
r.translate(0, r.height() * index);
}
@@ -436,10 +589,22 @@ QRectF NodeViewItem::GetInputRect(int index) const
return r;
}
QPointF NodeViewItem::GetParamPoint(NodeParam *param) const
QPointF NodeViewItem::GetParamPoint(NodeParam *param, const QPointF& source_pos) const
{
if (param->type() == NodeParam::kOutput) {
return pos() + QPointF(rect().right(), rect().center().y());
switch (flow_dir_) {
case NodeViewCommon::kLeftToRight:
default:
return pos() + QPointF(rect().right(), rect().center().y());
case NodeViewCommon::kRightToLeft:
return pos() + QPointF(rect().left(), rect().center().y());
case NodeViewCommon::kTopToBottom:
return pos() + QPointF(rect().center().x(), rect().bottom());
case NodeViewCommon::kBottomToTop:
return pos() + QPointF(rect().center().x(), rect().top());
}
} else {
NodeInput* input = static_cast<NodeInput*>(param);
@@ -448,15 +613,35 @@ QPointF NodeViewItem::GetParamPoint(NodeParam *param) const
input = static_cast<NodeInput*>(input->parent());
}
return pos() + GetInputPoint(node_inputs_.indexOf(input));
return pos() + GetInputPoint(node_inputs_.indexOf(input), source_pos);
}
}
QPointF NodeViewItem::GetInputPoint(int index) const
void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir)
{
flow_dir_ = dir;
}
QPointF NodeViewItem::GetInputPoint(int index, const QPointF& source_pos) const
{
QRectF input_rect = GetInputRect(index);
return QPointF(input_rect.left(), input_rect.center().y());
Qt::Orientation flow_orientation = NodeViewCommon::GetFlowOrientation(flow_dir_);
if (flow_orientation == Qt::Horizontal || IsExpanded()) {
if (flow_dir_ == NodeViewCommon::kLeftToRight
|| (flow_orientation == Qt::Vertical && source_pos.x() < pos().x())) {
return QPointF(input_rect.left(), input_rect.center().y());
} else {
return QPointF(input_rect.right(), input_rect.center().y());
}
} else {
if (flow_dir_ == NodeViewCommon::kTopToBottom) {
return QPointF(input_rect.center().x(), input_rect.top());
} else {
return QPointF(input_rect.center().x(), input_rect.bottom());
}
}
}
OLIVE_NAMESPACE_EXIT
+31 -3
View File
@@ -28,6 +28,7 @@
#include <QWidget>
#include "node/node.h"
#include "nodeviewcommon.h"
#include "nodeviewedge.h"
#include "nodeviewitemwidgetproxy.h"
@@ -45,6 +46,9 @@ class NodeViewItem : public QGraphicsRectItem
public:
NodeViewItem(QGraphicsItem* parent = nullptr);
QPointF GetNodePosition() const;
void SetNodePosition(const QPointF& pos);
/**
* @brief Set the Node to correspond to this widget
*/
@@ -63,13 +67,32 @@ public:
/**
* @brief Set expanded state
*/
void SetExpanded(bool e);
void SetExpanded(bool e, bool hide_titlebar = false);
void ToggleExpanded();
/**
* @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object
*/
QPointF GetParamPoint(NodeParam* param) const;
QPointF GetParamPoint(NodeParam* param, const QPointF &source_pos) const;
/**
* @brief Sets the direction nodes are flowing
*/
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
static int DefaultTextPadding();
static int DefaultItemHeight();
static int DefaultItemWidth();
static int DefaultMaximumTextWidth();
static int DefaultItemBorder();
qreal DefaultItemHorizontalPadding() const;
qreal DefaultItemVerticalPadding() const;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
@@ -77,6 +100,7 @@ protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
@@ -94,7 +118,7 @@ private:
/**
* @brief Returns local point that edges should connect to for a NodeInput in array node_inputs_[index]
*/
QPointF GetInputPoint(int index) const;
QPointF GetInputPoint(int index, const QPointF &source_pos) const;
/**
* @brief Reference to attached Node
@@ -136,6 +160,8 @@ private:
*/
bool expanded_;
bool hide_titlebar_;
/**
* @brief Current click mode
*
@@ -153,6 +179,8 @@ private:
*/
QUndoCommand* node_edge_change_command_;
NodeViewCommon::FlowDirection flow_dir_;
};
OLIVE_NAMESPACE_EXIT
+74 -128
View File
@@ -20,14 +20,40 @@
#include "nodeviewscene.h"
#include "nodeviewedge.h"
#include "nodeviewitem.h"
OLIVE_NAMESPACE_ENTER
NodeViewScene::NodeViewScene(QObject *parent) :
QGraphicsScene(parent),
graph_(nullptr)
graph_(nullptr),
direction_(NodeViewCommon::kLeftToRight)
{
connect(&reorganize_timer_, &QTimer::timeout, &reorganize_timer_, &QTimer::stop);
connect(&reorganize_timer_, &QTimer::timeout, this, &NodeViewScene::Reorganize);
}
void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction)
{
direction_ = direction;
{
// Iterate over node items setting direction
QHash<Node*, NodeViewItem*>::const_iterator i;
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
i.value()->SetFlowDirection(direction_);
// Update position too
i.value()->SetNodePosition(i.key()->GetPosition());
}
}
{
// Iterate over edge items setting direction
QHash<NodeEdge*, NodeViewEdge*>::const_iterator i;
for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) {
i.value()->SetFlowDirection(direction_);
}
}
}
void NodeViewScene::clear()
@@ -35,7 +61,7 @@ void NodeViewScene::clear()
// Deselect everything (prevents signals that a selection has changed after deleting an object)
DeselectAll();
// HACK: QGraphicsScene contains some sort of internal hashing of the selected items which doesn't update unless
// HACK: QGraphicsScene contains some sort of internal caching of the selected items which doesn't update unless
// we call a function like this. That means even though we deselect all items above, QGraphicsScene will
// continue to incorrectly signal selectionChanged() when items that were selected (but are now not) get
// deleted. Calling this function appears to update the internal cache and prevent this.
@@ -119,6 +145,21 @@ QList<NodeViewItem *> NodeViewScene::GetSelectedItems() const
return selected;
}
QList<NodeEdge*> NodeViewScene::GetSelectedEdges() const
{
QList<NodeEdge*> edges;
QHash<NodeEdge*, NodeViewEdge*>::const_iterator i;
for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) {
if (i.value()->isSelected()) {
edges.append(i.key());
}
}
return edges;
}
const QHash<Node *, NodeViewItem *> &NodeViewScene::item_map() const
{
return item_map_;
@@ -133,6 +174,7 @@ void NodeViewScene::AddNode(Node* node)
{
NodeViewItem* item = new NodeViewItem();
item->SetFlowDirection(direction_);
item->SetNode(node);
addItem(item);
@@ -152,11 +194,15 @@ void NodeViewScene::AddNode(Node* node)
}
}
QueueReorganize();
connect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged);
connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged);
}
void NodeViewScene::RemoveNode(Node *node)
{
disconnect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged);
disconnect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged);
delete item_map_.take(node);
}
@@ -165,11 +211,10 @@ void NodeViewScene::AddEdge(NodeEdgePtr edge)
NodeViewEdge* edge_ui = new NodeViewEdge();
edge_ui->SetEdge(edge);
edge_ui->SetFlowDirection(direction_);
addItem(edge_ui);
edge_map_.insert(edge.get(), edge_ui);
QueueReorganize();
}
void NodeViewScene::RemoveEdge(NodeEdgePtr edge)
@@ -177,146 +222,47 @@ void NodeViewScene::RemoveEdge(NodeEdgePtr edge)
delete edge_map_.take(edge.get());
}
void NodeViewScene::QueueReorganize()
Qt::Orientation NodeViewScene::GetFlowOrientation() const
{
// Avoids the fairly complex Reorganize() function every single time a connection or node is added
reorganize_timer_.stop();
reorganize_timer_.start(20);
return NodeViewCommon::GetFlowOrientation(direction_);
}
QList<Node *> NodeViewScene::GetNodeDirectDescendants(Node* n, const QList<Node*> connected_nodes, QList<Node*>& processed_nodes)
NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const
{
QList<Node*> direct_descendants = connected_nodes;
processed_nodes.append(n);
// Remove any nodes that aren't necessarily attached directly
for (int i=0;i<direct_descendants.size();i++) {
Node* connected = direct_descendants.at(i);
for (int j=1;j<connected->output()->edges().size();j++) {
Node* this_output_connection = connected->output()->edges().at(j)->input()->parentNode();
if (!processed_nodes.contains(this_output_connection)) {
direct_descendants.removeAt(i);
i--;
break;
}
}
}
return direct_descendants;
return direction_;
}
int NodeViewScene::FindWeightsInternal(Node *node, QHash<Node *, int> &weights, QList<Node*>& weighted_nodes)
void NodeViewScene::ReorganizeFrom(Node* n)
{
QList<Node*> connected_nodes = node->GetImmediateDependencies();
QList<Node*> immediates = n->GetImmediateDependencies();
int weight = 0;
if (!connected_nodes.isEmpty()) {
QList<Node*> direct_descendants = GetNodeDirectDescendants(node, connected_nodes, weighted_nodes);
foreach (Node* dep, direct_descendants) {
weight += FindWeightsInternal(dep, weights, weighted_nodes);
}
}
weight = qMax(weight, 1);
weights.insert(node, weight);
return weight;
}
void NodeViewScene::ReorganizeInternal(NodeViewItem* src_item, QHash<Node*, int>& weights, QList<Node*>& positioned_nodes)
{
if (!src_item) {
if (immediates.isEmpty()) {
// Nothing to do
return;
}
Node* n = src_item->GetNode();
QPointF parent_pos = n->GetPosition();
QList<Node*> connected_nodes = n->GetImmediateDependencies();
qreal child_x = parent_pos.x() - 1.0;
qreal children_height = immediates.size()-1;
qreal children_y = parent_pos.y() - children_height * 0.5;
if (connected_nodes.isEmpty()) {
return;
}
for (int i=0;i<immediates.size();i++) {
immediates.at(i)->SetPosition(QPointF(child_x,
children_y + i));
QList<Node*> direct_descendants = GetNodeDirectDescendants(n, connected_nodes, positioned_nodes);
int descendant_weight = 0;
foreach (Node* dep, direct_descendants) {
descendant_weight += weights.value(dep);
}
qreal center_y = src_item->y();
qreal total_height = descendant_weight * src_item->rect().height() + (direct_descendants.size()-1) * src_item->rect().height()/2;
double item_top = center_y - (total_height/2) + src_item->rect().height()/2;
// Set each node's position
int weight_index = 0;
for (int i=0;i<direct_descendants.size();i++) {
Node* connected = direct_descendants.at(i);
NodeViewItem* item = NodeToUIObject(connected);
if (!item) {
continue;
}
double item_y = item_top;
// Multiply the index by the item height (with 1.5 for padding)
item_y += weight_index * src_item->rect().height() * 1.5;
QPointF item_pos(src_item->pos().x() - item->rect().width() * 3 / 2,
item_y);
item->setPos(item_pos);
weight_index += weights.value(connected);
}
// Recursively work on each node
foreach (Node* connected, connected_nodes) {
NodeViewItem* item = NodeToUIObject(connected);
if (!item) {
continue;
}
ReorganizeInternal(item, weights, positioned_nodes);
ReorganizeFrom(immediates.at(i));
}
}
void NodeViewScene::Reorganize()
void NodeViewScene::NodePositionChanged(const QPointF &pos)
{
if (!graph_) {
return;
}
item_map_.value(static_cast<Node*>(sender()))->SetNodePosition(pos);
}
QList<Node*> end_nodes;
// Calculate the nodes that don't output to anything, they'll be our anchors
foreach (Node* node, graph_->nodes()) {
if (!node->HasConnectedOutputs()) {
end_nodes.append(node);
}
}
QList<Node*> processed_nodes;
QHash<Node*, int> node_weights;
foreach (Node* end_node, end_nodes) {
FindWeightsInternal(end_node, node_weights, processed_nodes);
}
processed_nodes.clear();
foreach (Node* end_node, end_nodes) {
ReorganizeInternal(NodeToUIObject(end_node), node_weights, processed_nodes);
}
void NodeViewScene::NodeLabelChanged()
{
item_map_.value(static_cast<Node*>(sender()))->update();
}
OLIVE_NAMESPACE_EXIT
+22 -14
View File
@@ -25,8 +25,8 @@
#include <QTimer>
#include "node/graph.h"
#include "widget/nodeview/nodeviewedge.h"
#include "widget/nodeview/nodeviewitem.h"
#include "nodeviewedge.h"
#include "nodeviewitem.h"
OLIVE_NAMESPACE_ENTER
@@ -65,10 +65,21 @@ public:
QList<Node*> GetSelectedNodes() const;
QList<NodeViewItem*> GetSelectedItems() const;
QList<NodeEdge *> GetSelectedEdges() const;
const QHash<Node*, NodeViewItem*>& item_map() const;
const QHash<NodeEdge*, NodeViewEdge*>& edge_map() const;
Qt::Orientation GetFlowOrientation() const;
NodeViewCommon::FlowDirection GetFlowDirection() const;
void SetFlowDirection(NodeViewCommon::FlowDirection direction);
/**
* @brief Automatically reposition the nodes based on their connections
*/
void ReorganizeFrom(Node* n);
public slots:
/**
* @brief Slot when a Node is added to a graph (SetGraph() connects this)
@@ -103,27 +114,24 @@ public slots:
void RemoveEdge(NodeEdgePtr edge);
private:
void QueueReorganize();
QList<Node*> GetNodeDirectDescendants(Node* n, const QList<Node*> connected_nodes, QList<Node*>& processed_nodes);
int FindWeightsInternal(Node* node, QHash<Node*, int>& weights, QList<Node*>& weighted_nodes);
void ReorganizeInternal(NodeViewItem *src_item, QHash<Node*, int>& weights, QList<Node *> &positioned_nodes);
QHash<Node*, NodeViewItem*> item_map_;
QHash<NodeEdge*, NodeViewEdge*> edge_map_;
QTimer reorganize_timer_;
NodeGraph* graph_;
NodeViewCommon::FlowDirection direction_;
private slots:
/**
* @brief Automatically reposition the nodes based on their connections
* @brief Receiver for whenever a node position changes
*/
void Reorganize();
void NodePositionChanged(const QPointF& pos);
/**
* @brief Receiver for when a node's label has changed
*/
void NodeLabelChanged();
};
+2
View File
@@ -165,6 +165,8 @@ public:
virtual void ToggleSelectedEnabled(){}
virtual void Duplicate(){}
signals:
void CloseRequested();
+10 -1
View File
@@ -49,7 +49,9 @@ void HistogramScope::SetBuffer(Frame* frame)
{
buffer_ = frame;
StartUpdate();
if (isVisible()) {
StartUpdate();
}
}
void HistogramScope::FinishedProcessing(QVector<double> red, QVector<double> green, QVector<double> blue)
@@ -111,6 +113,13 @@ void HistogramScope::StartUpdate()
}
}
void HistogramScope::showEvent(QShowEvent* e)
{
ManagedDisplayWidget::showEvent(e);
StartUpdate();
}
HistogramScopeWorker::HistogramScopeWorker() :
cancelled_(false)
{
+2
View File
@@ -78,6 +78,8 @@ protected:
virtual void ColorProcessorChangedEvent() override;
virtual void showEvent(QShowEvent* e) override;
private:
void StartUpdate();
+155 -18
View File
@@ -21,6 +21,11 @@
#include "waveform.h"
#include <QPainter>
#include <QtMath>
#include <QDebug>
#include "common/qtutils.h"
#include "node/node.h"
#include "render/backend/opengl/openglrenderfunctions.h"
@@ -28,16 +33,32 @@ OLIVE_NAMESPACE_ENTER
WaveformScope::WaveformScope(QWidget* parent) :
ManagedDisplayWidget(parent),
texture_(nullptr)
buffer_(nullptr)
{
EnableDefaultContextMenu();
}
void WaveformScope::SetTexture(OpenGLTexture *texture)
WaveformScope::~WaveformScope()
{
texture_ = texture;
CleanUp();
update();
if (context()) {
disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp);
}
}
void WaveformScope::SetBuffer(Frame *frame)
{
buffer_ = frame;
UploadTextureFromBuffer();
}
void WaveformScope::showEvent(QShowEvent* e)
{
ManagedDisplayWidget::showEvent(e);
UploadTextureFromBuffer();
}
void WaveformScope::initializeGL()
@@ -49,37 +70,153 @@ void WaveformScope::initializeGL()
pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex());
pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag"));
pipeline_->link();
framebuffer_.Create(context());
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection);
UploadTextureFromBuffer();
}
void WaveformScope::paintGL()
{
context()->functions()->glClearColor(0, 0, 0, 0);
context()->functions()->glClear(GL_COLOR_BUFFER_BIT);
QOpenGLFunctions* f = context()->functions();
if (!pipeline_ || !texture_) {
f->glClearColor(0, 0, 0, 0);
f->glClear(GL_COLOR_BUFFER_BIT);
float waveform_scale = 0.80f;
float waveform_dim_x = width() * waveform_scale;
float waveform_dim_y = height() * waveform_scale;
float waveform_start_dim_x = (width() - waveform_dim_x) / 2.0f;
float waveform_start_dim_y = (height() - waveform_dim_y) / 2.0f;
float waveform_end_dim_x = width() - waveform_start_dim_x;
float waveform_end_dim_y = height() - waveform_start_dim_y;
if (buffer_ && pipeline_ && texture_.IsCreated()) {
// Convert reference frame to display space
framebuffer_.Attach(&managed_tex_);
framebuffer_.Bind();
texture_.Bind();
f->glViewport(0, 0, texture_.width(), texture_.height());
color_service()->ProcessOpenGL();
texture_.Release();
framebuffer_.Release();
framebuffer_.Detach();
// Draw waveform through shader
pipeline_->bind();
pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height());
pipeline_->setUniformValue("ove_viewport", width(), height());
GLfloat luma[3] = {0.0, 0.0, 0.0};
color_manager()->GetDefaultLumaCoefs(luma);
pipeline_->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]);
// Scale of the waveform relative to the viewport surface.
pipeline_->setUniformValue("waveform_scale", waveform_scale);
pipeline_->setUniformValue(
"waveform_dims", waveform_dim_x, waveform_dim_y);
pipeline_->setUniformValue(
"waveform_region",
waveform_start_dim_x, waveform_start_dim_y,
waveform_end_dim_x, waveform_end_dim_y);
float waveform_start_uv_x = waveform_start_dim_x / width();
float waveform_start_uv_y = waveform_start_dim_y / height();
float waveform_end_uv_x = waveform_end_dim_x / width();
float waveform_end_uv_y = waveform_end_dim_y / height();
pipeline_->setUniformValue(
"waveform_uv",
waveform_start_uv_x, waveform_start_uv_y,
waveform_end_uv_x, waveform_end_uv_y);
pipeline_->release();
f->glViewport(0, 0, width(), height());
managed_tex_.Bind();
OpenGLRenderFunctions::Blit(pipeline_);
managed_tex_.Release();
}
// Draw line overlays
QPainter p(this);
QFontMetrics font_metrics = QFontMetrics(QFont());
QString label;
float ire_increment = 0.1f;
float ire_steps = int(1.0 / ire_increment);
QVector<QLine> ire_lines(ire_steps + 1);
int font_x_offset = 0;
int font_y_offset = font_metrics.capHeight() / 2.0f;
p.setCompositionMode(QPainter::CompositionMode_Plus);
p.setPen(QColor(0.0, 0.6 * 255.0, 0.0));
p.setFont(QFont());
for (int i=0; i <= ire_steps; i++) {
ire_lines[i].setLine(
waveform_start_dim_x,
(waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y,
waveform_end_dim_x,
(waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y);
label = QString::number(1.0 - (i * ire_increment), 'f', 1);
font_x_offset = QFontMetricsWidth(font_metrics, label) + 4;
p.drawText(
waveform_start_dim_x - font_x_offset,
(waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y + font_y_offset,
label);
}
p.drawLines(ire_lines);
}
void WaveformScope::UploadTextureFromBuffer()
{
if (!isVisible()) {
return;
}
pipeline_->bind();
pipeline_->setUniformValue("ove_resolution", texture_->width(), texture_->height());
pipeline_->setUniformValue("ove_viewport", width(), height());
if (buffer_) {
makeCurrent();
// The general size of a pixel
pipeline_->setUniformValue("threshold", 2.0f / static_cast<float>(height()));
if (!texture_.IsCreated()
|| texture_.width() != buffer_->width()
|| texture_.height() != buffer_->height()
|| texture_.format() != buffer_->format()) {
texture_.Destroy();
managed_tex_.Destroy();
pipeline_->release();
texture_.Create(context(), buffer_);
managed_tex_.Create(context(), buffer_->width(), buffer_->height(), buffer_->format());
} else {
texture_.Upload(buffer_);
}
texture_->Bind();
doneCurrent();
}
OpenGLRenderFunctions::Blit(pipeline_);
texture_->Release();
update();
}
void WaveformScope::CleanUp()
{
makeCurrent();
pipeline_ = nullptr;
texture_ = nullptr;
texture_.Destroy();
managed_tex_.Destroy();
framebuffer_.Destroy();
doneCurrent();
}
OLIVE_NAMESPACE_EXIT
+15 -2
View File
@@ -23,6 +23,7 @@
#include "codec/frame.h"
#include "render/backend/opengl/openglcolorprocessor.h"
#include "render/backend/opengl/openglframebuffer.h"
#include "render/backend/opengl/openglshader.h"
#include "render/backend/opengl/opengltexture.h"
#include "widget/manageddisplay/manageddisplay.h"
@@ -35,18 +36,30 @@ class WaveformScope : public ManagedDisplayWidget
public:
WaveformScope(QWidget* parent = nullptr);
virtual ~WaveformScope() override;
public slots:
void SetTexture(OpenGLTexture* texture);
void SetBuffer(Frame* frame);
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void showEvent(QShowEvent* e) override;
private:
void UploadTextureFromBuffer();
OpenGLShaderPtr pipeline_;
OpenGLTexture* texture_;
OpenGLTexture texture_;
OpenGLTexture managed_tex_;
OpenGLFramebuffer framebuffer_;
Frame* buffer_;
private slots:
void CleanUp();
+1 -1
View File
@@ -91,7 +91,7 @@ void TimelineWidget::PointerTool::MousePress(TimelineViewMouseEvent *event)
if (!(event->GetModifiers() & Qt::AltModifier)) {
parent()->SetBlockLinksSelected(item->block(), true);
}
} else {
} else if (event->GetButton() == Qt::LeftButton) {
// Start rubberband drag
parent()->StartRubberBandSelect(true, !(event->GetModifiers() & Qt::AltModifier));
@@ -16,6 +16,8 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/timelinewidget/view/handmovableview.h
widget/timelinewidget/view/handmovableview.cpp
widget/timelinewidget/view/timelineplayhead.h
widget/timelinewidget/view/timelineplayhead.cpp
widget/timelinewidget/view/timelineview.h
@@ -0,0 +1,118 @@
/***
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/>.
***/
#include "handmovableview.h"
#include <QMouseEvent>
#include "core.h"
OLIVE_NAMESPACE_ENTER
HandMovableView::HandMovableView(QWidget* parent) :
QGraphicsView(parent),
dragging_hand_(false)
{
connect(Core::instance(), &Core::ToolChanged, this, &HandMovableView::ApplicationToolChanged);
}
void HandMovableView::ApplicationToolChanged(Tool::Item tool)
{
if (tool == Tool::kHand) {
setDragMode(ScrollHandDrag);
} else {
setDragMode(default_drag_mode_);
}
ToolChangedEvent(tool);
}
bool HandMovableView::HandPress(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton) {
pre_hand_drag_mode_ = dragMode();
dragging_hand_ = true;
setDragMode(ScrollHandDrag);
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mousePressEvent(&transformed);
return true;
}
return false;
}
bool HandMovableView::HandMove(QMouseEvent *event)
{
if (dragging_hand_) {
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mouseMoveEvent(&transformed);
}
return dragging_hand_;
}
bool HandMovableView::HandRelease(QMouseEvent *event)
{
if (dragging_hand_) {
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mouseReleaseEvent(&transformed);
setDragMode(pre_hand_drag_mode_);
dragging_hand_ = false;
return true;
}
return false;
}
void HandMovableView::SetDefaultDragMode(QGraphicsView::DragMode mode)
{
default_drag_mode_ = mode;
setDragMode(default_drag_mode_);
}
const QGraphicsView::DragMode &HandMovableView::GetDefaultDragMode() const
{
return default_drag_mode_;
}
OLIVE_NAMESPACE_EXIT
@@ -0,0 +1,59 @@
/***
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/>.
***/
#ifndef HANDMOVABLEVIEW_H
#define HANDMOVABLEVIEW_H
#include <QGraphicsView>
#include "tool/tool.h"
OLIVE_NAMESPACE_ENTER
class HandMovableView : public QGraphicsView
{
Q_OBJECT
public:
HandMovableView(QWidget* parent = nullptr);
protected:
virtual void ToolChangedEvent(Tool::Item tool){Q_UNUSED(tool)}
bool HandPress(QMouseEvent* event);
bool HandMove(QMouseEvent* event);
bool HandRelease(QMouseEvent* event);
void SetDefaultDragMode(DragMode mode);
const DragMode& GetDefaultDragMode() const;
private:
bool dragging_hand_;
DragMode pre_hand_drag_mode_;
DragMode default_drag_mode_;
private slots:
void ApplicationToolChanged(Tool::Item tool);
};
OLIVE_NAMESPACE_EXIT
#endif // HANDMOVABLEVIEW_H
+19 -15
View File
@@ -81,7 +81,7 @@ void TimelineView::mousePressEvent(QMouseEvent *event)
return;
}
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->modifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event);
emit MousePressed(&timeline_event);
}
@@ -98,7 +98,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event)
return;
}
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->modifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event);
emit MouseMoved(&timeline_event);
}
@@ -115,14 +115,14 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event)
return;
}
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->modifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event);
emit MouseReleased(&timeline_event);
}
void TimelineView::mouseDoubleClickEvent(QMouseEvent *event)
{
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->modifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event);
emit MouseDoubleClicked(&timeline_event);
}
@@ -146,7 +146,7 @@ void TimelineView::wheelEvent(QWheelEvent *event)
void TimelineView::dragEnterEvent(QDragEnterEvent *event)
{
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->keyboardModifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), Qt::NoButton, event->keyboardModifiers());
timeline_event.SetMimeData(event->mimeData());
timeline_event.SetEvent(event);
@@ -156,7 +156,7 @@ void TimelineView::dragEnterEvent(QDragEnterEvent *event)
void TimelineView::dragMoveEvent(QDragMoveEvent *event)
{
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->keyboardModifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), Qt::NoButton, event->keyboardModifiers());
timeline_event.SetMimeData(event->mimeData());
timeline_event.SetEvent(event);
@@ -171,7 +171,7 @@ void TimelineView::dragLeaveEvent(QDragLeaveEvent *event)
void TimelineView::dropEvent(QDropEvent *event)
{
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), event->keyboardModifiers());
TimelineViewMouseEvent timeline_event = CreateMouseEvent(event->pos(), Qt::NoButton, event->keyboardModifiers());
timeline_event.SetMimeData(event->mimeData());
timeline_event.SetEvent(event);
@@ -273,17 +273,21 @@ TimelineCoordinate TimelineView::SceneToCoordinate(const QPointF& pt)
return TimelineCoordinate(SceneToTime(pt.x()), TrackReference(ConnectedTrackType(), SceneToTrack(pt.y())));
}
TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::KeyboardModifiers modifiers)
TimelineViewMouseEvent TimelineView::CreateMouseEvent(QMouseEvent *event)
{
return CreateMouseEvent(event->pos(), event->button(), event->modifiers());
}
TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
{
QPointF scene_pt = mapToScene(pos);
TimelineViewMouseEvent timeline_event(scene_pt.x(),
GetScale(),
timebase(),
TrackReference(ConnectedTrackType(), SceneToTrack(scene_pt.y())),
modifiers);
return timeline_event;
return TimelineViewMouseEvent(scene_pt.x(),
GetScale(),
timebase(),
TrackReference(ConnectedTrackType(), SceneToTrack(scene_pt.y())),
button,
modifiers);
}
int TimelineView::GetHeightOfAllTracks() const
@@ -100,7 +100,8 @@ private:
TimelineCoordinate ScreenToCoordinate(const QPoint& pt);
TimelineCoordinate SceneToCoordinate(const QPointF& pt);
TimelineViewMouseEvent CreateMouseEvent(const QPoint &pos, Qt::KeyboardModifiers modifiers);
TimelineViewMouseEvent CreateMouseEvent(QMouseEvent* event);
TimelineViewMouseEvent CreateMouseEvent(const QPoint &pos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
int GetHeightOfAllTracks() const;
@@ -34,12 +34,11 @@ OLIVE_NAMESPACE_ENTER
const double TimelineViewBase::kMaximumScale = 8192;
TimelineViewBase::TimelineViewBase(QWidget *parent) :
QGraphicsView(parent),
HandMovableView(parent),
playhead_(0),
playhead_scene_left_(-1),
playhead_scene_right_(-1),
dragging_playhead_(false),
dragging_hand_(false),
limit_y_axis_(false)
{
setScene(&scene_);
@@ -50,7 +49,6 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) :
SetDefaultDragMode(NoDrag);
connect(&scene_, SIGNAL(changed(const QList<QRectF>&)), this, SLOT(UpdateSceneRect()));
connect(Core::instance(), &Core::ToolChanged, this, &TimelineViewBase::ApplicationToolChanged);
SetMaximumScale(kMaximumScale);
}
@@ -100,17 +98,6 @@ rational TimelineViewBase::GetPlayheadTime() const
return Timecode::timestamp_to_time(playhead_, timebase());
}
void TimelineViewBase::SetDefaultDragMode(QGraphicsView::DragMode mode)
{
default_drag_mode_ = mode;
setDragMode(default_drag_mode_);
}
const QGraphicsView::DragMode &TimelineViewBase::GetDefaultDragMode() const
{
return default_drag_mode_;
}
bool TimelineViewBase::PlayheadPress(QMouseEvent *event)
{
QPointF scene_pos = mapToScene(event->pos());
@@ -148,70 +135,6 @@ bool TimelineViewBase::PlayheadRelease(QMouseEvent*)
return false;
}
bool TimelineViewBase::HandPress(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton) {
pre_hand_drag_mode_ = dragMode();
dragging_hand_ = true;
setDragMode(ScrollHandDrag);
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mousePressEvent(&transformed);
return true;
}
return false;
}
bool TimelineViewBase::HandMove(QMouseEvent *event)
{
if (dragging_hand_) {
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mouseMoveEvent(&transformed);
}
return dragging_hand_;
}
bool TimelineViewBase::HandRelease(QMouseEvent *event)
{
if (dragging_hand_) {
// Transform mouse event to act like the left button is pressed
QMouseEvent transformed(event->type(),
event->localPos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
QGraphicsView::mouseReleaseEvent(&transformed);
setDragMode(pre_hand_drag_mode_);
dragging_hand_ = false;
return true;
}
return false;
}
void TimelineViewBase::ToolChangedEvent(Tool::Item)
{
}
qreal TimelineViewBase::GetPlayheadX()
{
return TimeToScene(Timecode::timestamp_to_time(playhead_, timebase()));
@@ -305,15 +228,4 @@ void TimelineViewBase::SetLimitYAxis(bool)
UpdateSceneRect();
}
void TimelineViewBase::ApplicationToolChanged(Tool::Item tool)
{
if (tool == Tool::kHand) {
setDragMode(ScrollHandDrag);
} else {
setDragMode(default_drag_mode_);
}
ToolChangedEvent(tool);
}
OLIVE_NAMESPACE_EXIT
@@ -24,12 +24,13 @@
#include <QGraphicsView>
#include "core.h"
#include "handmovableview.h"
#include "timelineplayhead.h"
#include "widget/timelinewidget/timelinescaledobject.h"
OLIVE_NAMESPACE_ENTER
class TimelineViewBase : public QGraphicsView, public TimelineScaledObject
class TimelineViewBase : public HandMovableView, public TimelineScaledObject
{
Q_OBJECT
public:
@@ -66,19 +67,10 @@ protected:
rational GetPlayheadTime() const;
void SetDefaultDragMode(DragMode mode);
const DragMode& GetDefaultDragMode() const;
bool PlayheadPress(QMouseEvent* event);
bool PlayheadMove(QMouseEvent* event);
bool PlayheadRelease(QMouseEvent* event);
bool HandPress(QMouseEvent* event);
bool HandMove(QMouseEvent* event);
bool HandRelease(QMouseEvent* event);
virtual void ToolChangedEvent(Tool::Item tool);
virtual void TimebaseChangedEvent(const rational &) override;
private:
@@ -93,15 +85,10 @@ private:
bool dragging_playhead_;
bool dragging_hand_;
DragMode pre_hand_drag_mode_;
QGraphicsScene scene_;
bool limit_y_axis_;
DragMode default_drag_mode_;
rational end_time_;
private slots:
@@ -118,8 +105,6 @@ private slots:
*/
void PageScrollToPlayhead();
void ApplicationToolChanged(Tool::Item tool);
};
OLIVE_NAMESPACE_EXIT
@@ -145,7 +145,7 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
QFontMetrics fm = painter->fontMetrics();
int text_width = qMin(qRound(rect().width()), QFontMetricsWidth(fm, block_->block_name()));
QPointF underline_start = rect().topLeft() + QPointF(0, fm.height());
QPointF underline_start = rect().topLeft() + QPointF(0, text_top + fm.height());
QPointF underline_end = underline_start + QPointF(text_width, 0);
painter->drawLine(underline_start, underline_end);
@@ -30,11 +30,13 @@ TimelineViewMouseEvent::TimelineViewMouseEvent(const qreal &scene_x,
const double &scale_x,
const rational &timebase,
const TrackReference &track,
const Qt::MouseButton &button,
const Qt::KeyboardModifiers &modifiers) :
scene_x_(scene_x),
scale_x_(scale_x),
timebase_(timebase),
track_(track),
button_(button),
modifiers_(modifiers),
source_event_(nullptr),
mime_data_(nullptr)
@@ -81,6 +83,11 @@ const qreal &TimelineViewMouseEvent::GetSceneX() const
return scene_x_;
}
const Qt::MouseButton &TimelineViewMouseEvent::GetButton() const
{
return button_;
}
void TimelineViewMouseEvent::accept()
{
if (source_event_ != nullptr)
@@ -36,6 +36,7 @@ public:
const double& scale_x,
const rational& timebase,
const TrackReference &track,
const Qt::MouseButton &button,
const Qt::KeyboardModifiers& modifiers = Qt::NoModifier);
TimelineCoordinate GetCoordinates(bool round_time = false) const;
@@ -61,6 +62,8 @@ public:
const qreal& GetSceneX() const;
const Qt::MouseButton& GetButton() const;
void accept();
void ignore();
@@ -71,6 +74,8 @@ private:
TrackReference track_;
Qt::MouseButton button_;
Qt::KeyboardModifiers modifiers_;
QEvent* source_event_;
@@ -1,6 +0,0 @@
#include "manageddisplayobject.h"
ManagedDisplayObject::ManagedDisplayObject()
{
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef MANAGEDDISPLAYOBJECT_H
#define MANAGEDDISPLAYOBJECT_H
class ManagedDisplayObject
{
public:
ManagedDisplayObject();
};
#endif // MANAGEDDISPLAYOBJECT_H
-7
View File
@@ -66,8 +66,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer);
connect(main_widget, &ViewerDisplayWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture);
connect(main_widget, &ViewerDisplayWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture);
connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix);
@@ -750,11 +748,6 @@ void ViewerWidget::SetSignalCursorColorEnabled(bool e)
}
}
void ViewerWidget::SetEmitDrewManagedTextureEnabled(bool e)
{
main_gl_widget()->SetEmitDrewManagedTextureEnabled(e);
}
void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
{
TimeBasedWidget::TimebaseChangedEvent(timebase);
-15
View File
@@ -109,11 +109,6 @@ public slots:
*/
void SetSignalCursorColorEnabled(bool e);
/**
* @brief Wrapper for ViewerGLWidget::SetEmitDrewManagedTextureEnabled()
*/
void SetEmitDrewManagedTextureEnabled(bool e);
signals:
/**
* @brief Wrapper for ViewerGLWidget::CursorColor()
@@ -125,16 +120,6 @@ signals:
*/
void LoadedBuffer(Frame* load_buffer);
/**
* @brief Wrapper for ViewerGLWidget::LoadedTexture()
*/
void LoadedTexture(OpenGLTexture* texture);
/**
* @brief Wrapper for ViewerGLWidget::DrewManagedTexture()
*/
void DrewManagedTexture(OpenGLTexture* texture);
/**
* @brief Request a scope panel
*
+4 -68
View File
@@ -42,10 +42,8 @@ bool ViewerDisplayWidget::nouveau_check_done_ = false;
ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
ManagedDisplayWidget(parent),
managed_copy_pipeline_(nullptr),
has_image_(false),
signal_cursor_color_(false),
enable_display_referred_signal_(false),
gizmos_(nullptr)
{
}
@@ -92,14 +90,12 @@ void ViewerDisplayWidget::SetImage(const QString &fn)
input->read_image(input->spec().format, load_buffer_.data(), OIIO::AutoStride, load_buffer_.linesize_bytes());
input->close();
emit LoadedBuffer(&load_buffer_);
texture_.Upload(load_buffer_.data(), load_buffer_.linesize_pixels());
emit LoadedTexture(&texture_);
texture_.Upload(&load_buffer_);
doneCurrent();
emit LoadedBuffer(&load_buffer_);
has_image_ = true;
#if OIIO_VERSION < 10903
@@ -139,7 +135,7 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|| texture_.format() != in_buffer->format()) {
texture_.Create(context(), in_buffer->width(), in_buffer->height(), in_buffer->format(), in_buffer->data(), load_buffer_.linesize_pixels());
} else {
texture_.Upload(in_buffer->data(), load_buffer_.linesize_pixels());
texture_.Upload(in_buffer);
}
doneCurrent();
@@ -148,18 +144,6 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer)
update();
}
void ViewerDisplayWidget::SetEmitDrewManagedTextureEnabled(bool e)
{
enable_display_referred_signal_ = e;
if (!enable_display_referred_signal_) {
// Destroy the texture now
managed_texture_.Destroy();
managed_copy_pipeline_ = nullptr;
framebuffer_.Destroy();
}
}
void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling)
{
connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection);
@@ -251,33 +235,6 @@ void ViewerDisplayWidget::paintGL()
// We only draw if we have a pipeline
if (has_image_ && color_service() && texture_.IsCreated()) {
// If we're distributing our display-referred final buffer, we'll have to make a copy of it
if (enable_display_referred_signal_) {
if (!managed_texture_.IsCreated()
|| managed_texture_.width() != texture_.width()
|| managed_texture_.height() != texture_.height()
|| managed_texture_.format() != texture_.format()) {
managed_texture_.Destroy();
managed_texture_.Create(context(), texture_.width(), texture_.height(), texture_.format());
}
if (!managed_copy_pipeline_) {
managed_copy_pipeline_ = OpenGLShader::CreateDefault();
}
if (!framebuffer_.IsCreated()) {
framebuffer_.Create(context());
}
framebuffer_.Attach(&managed_texture_);
framebuffer_.Bind();
f->glViewport(0, 0, managed_texture_.width(), managed_texture_.height());
}
// Bind retrieved texture
f->glBindTexture(GL_TEXTURE_2D, texture_.texture());
@@ -287,24 +244,6 @@ void ViewerDisplayWidget::paintGL()
// Release retrieved texture
f->glBindTexture(GL_TEXTURE_2D, 0);
if (enable_display_referred_signal_) {
framebuffer_.Release();
framebuffer_.Detach();
emit DrewManagedTexture(&managed_texture_);
// Bind retrieved texture
managed_texture_.Bind();
f->glViewport(0, 0, width(), height());
OpenGLRenderFunctions::Blit(managed_copy_pipeline_);
// Bind retrieved texture
managed_texture_.Release();
}
}
// Draw gizmos if we have any
@@ -362,10 +301,7 @@ void ViewerDisplayWidget::ContextCleanup()
{
makeCurrent();
managed_copy_pipeline_ = nullptr;
texture_.Destroy();
managed_texture_.Destroy();
framebuffer_.Destroy();
doneCurrent();
}
-40
View File
@@ -104,15 +104,6 @@ public slots:
*/
void SetImageFromLoadBuffer(Frame* in_buffer);
/**
* @brief Enables or disables DrewManagedTexture()
*
* To emit a display referred texture, it needs to be copied after the color transform is complete. This naturally
* adds extra GPU cycles that are wasted if there's nothing receiving the signal. Therefore, the signal is disabled
* by default.
*/
void SetEmitDrewManagedTextureEnabled(bool e);
signals:
/**
* @brief Signal emitted when the user starts dragging from the viewer
@@ -133,18 +124,6 @@ signals:
*/
void LoadedBuffer(Frame* load_buffer);
/**
* @brief Signal emitted when a buffer is loaded into a texture
*
* This texture will be the direct output of the renderer in reference space in GPU VRAM.
*/
void LoadedTexture(OpenGLTexture* texture);
/**
* @brief Emitted when the a texture has been transformed to display
*/
void DrewManagedTexture(OpenGLTexture* texture);
protected:
/**
* @brief Override the mouse press event simply to emit the DragStarted() signal
@@ -176,23 +155,6 @@ private:
*/
OpenGLTexture texture_;
/**
* @brief Internal framebuffer used to draw to managed_texture_
*/
OpenGLFramebuffer framebuffer_;
/**
* @brief Internal referenceto the OpenGL texture that's been managed
*
* Kept so that scopes can use the display-referred buffer without having to transform again.
*/
OpenGLTexture managed_texture_;
/**
* @brief Pipeline used to draw to managed_texture_
*/
OpenGLShaderPtr managed_copy_pipeline_;
/**
* @brief Drawing matrix (defaults to identity)
*/
@@ -213,8 +175,6 @@ private:
ViewerSafeMarginInfo safe_margin_;
bool enable_display_referred_signal_;
Node* gizmos_;
private slots: