From f9718cdeb036f0ae07fdfb4f200c3591a97d8528 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:43:44 -0800 Subject: [PATCH 01/23] viewertexteditor: enforce default format on first open --- app/widget/viewer/viewertexteditor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/widget/viewer/viewertexteditor.cpp b/app/widget/viewer/viewertexteditor.cpp index 7c8c27fad..0ecc65740 100644 --- a/app/widget/viewer/viewertexteditor.cpp +++ b/app/widget/viewer/viewertexteditor.cpp @@ -294,6 +294,9 @@ void ViewerTextEditor::DocumentChanged() forced_default_ = true; } } else { + if (default_fmt_.isEmpty()) { + default_fmt_ = document()->firstBlock().charFormat(); + } forced_default_ = false; } From 32c8d08a10ff08d31c1fe27b0c7618158c19925a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:02:13 -0800 Subject: [PATCH 02/23] gizmos: draw shadow so they're still visible on white backdrop Fixes #2142 --- app/node/gizmo/line.cpp | 11 +++++++++-- app/node/gizmo/path.cpp | 9 ++++++++- app/node/gizmo/point.cpp | 2 +- app/node/gizmo/polygon.cpp | 9 ++++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/node/gizmo/line.cpp b/app/node/gizmo/line.cpp index 33f1a45bc..d10cce5f2 100644 --- a/app/node/gizmo/line.cpp +++ b/app/node/gizmo/line.cpp @@ -29,9 +29,16 @@ LineGizmo::LineGizmo(QObject *parent) : void LineGizmo::Draw(QPainter *p) const { - p->setBrush(Qt::NoBrush); - p->setPen(QPen(Qt::white, 0)); + // Draw transposed black + QLineF transposed = p->transform().map(line_); + transposed.translate(1, 1); + transposed = p->transform().inverted().map(transposed); + p->setPen(QPen(Qt::black, 0)); + p->drawLine(transposed); + // Draw normal white polygon + p->setPen(QPen(Qt::white, 0)); + p->setBrush(Qt::NoBrush); p->drawLine(line_); } diff --git a/app/node/gizmo/path.cpp b/app/node/gizmo/path.cpp index cd9642aec..b9733e869 100644 --- a/app/node/gizmo/path.cpp +++ b/app/node/gizmo/path.cpp @@ -30,9 +30,16 @@ PathGizmo::PathGizmo(QObject *parent) : void PathGizmo::Draw(QPainter *p) const { + // Draw transposed black + QPainterPath transposed = p->transform().map(path_); + transposed.translate(1, 1); + transposed = p->transform().inverted().map(transposed); + p->setPen(QPen(Qt::black, 0)); + p->drawPath(transposed); + + // Draw normal white polygon p->setPen(QPen(Qt::white, 0)); p->setBrush(Qt::NoBrush); - p->drawPath(path_); } diff --git a/app/node/gizmo/point.cpp b/app/node/gizmo/point.cpp index d60ccf5bd..213948ba1 100644 --- a/app/node/gizmo/point.cpp +++ b/app/node/gizmo/point.cpp @@ -46,7 +46,7 @@ void PointGizmo::Draw(QPainter *p) const QRectF rect = GetDrawingRect(p->transform(), GetStandardRadius()); if (shape_ != kAnchorPoint) { - p->setPen(Qt::NoPen); + p->setPen(QPen(Qt::black, 0)); p->setBrush(Qt::white); } diff --git a/app/node/gizmo/polygon.cpp b/app/node/gizmo/polygon.cpp index 8279d7fea..602f4a984 100644 --- a/app/node/gizmo/polygon.cpp +++ b/app/node/gizmo/polygon.cpp @@ -29,9 +29,16 @@ PolygonGizmo::PolygonGizmo(QObject *parent) void PolygonGizmo::Draw(QPainter *p) const { + // Draw transposed black + QPolygonF transposed = p->transform().map(polygon_); + transposed.translate(1, 1); + transposed = p->transform().inverted().map(transposed); + p->setPen(QPen(Qt::black, 0)); + p->drawPolyline(transposed); + + // Draw normal white polygon p->setPen(QPen(Qt::white, 0)); p->setBrush(Qt::NoBrush); - p->drawPolyline(polygon_); } From 12aa67b4f7d8f3dee31a6dfc4d92148b9c26cc63 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:36:08 -0800 Subject: [PATCH 03/23] otio: fix compilation --- app/dialog/otioproperties/otiopropertiesdialog.h | 2 +- app/task/project/loadotio/loadotio.h | 2 +- app/task/project/saveotio/saveotio.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/dialog/otioproperties/otiopropertiesdialog.h b/app/dialog/otioproperties/otiopropertiesdialog.h index ad66bbd9f..1e748ad4e 100644 --- a/app/dialog/otioproperties/otiopropertiesdialog.h +++ b/app/dialog/otioproperties/otiopropertiesdialog.h @@ -8,7 +8,7 @@ #include "common/define.h" #include "opentimelineio/timeline.h" #include "node/project/sequence/sequence.h" -#include "node/project/project.h" +#include "node/project.h" namespace olive { diff --git a/app/task/project/loadotio/loadotio.h b/app/task/project/loadotio/loadotio.h index 9ee9d3a2a..4c0785ff9 100644 --- a/app/task/project/loadotio/loadotio.h +++ b/app/task/project/loadotio/loadotio.h @@ -24,7 +24,7 @@ #ifdef USE_OTIO #include "common/otioutils.h" -#include "node/project/project.h" +#include "node/project.h" #include "task/project/load/loadbasetask.h" namespace olive { diff --git a/app/task/project/saveotio/saveotio.h b/app/task/project/saveotio/saveotio.h index 2fcbd1241..44678bd5c 100644 --- a/app/task/project/saveotio/saveotio.h +++ b/app/task/project/saveotio/saveotio.h @@ -27,7 +27,7 @@ #include #include "common/otioutils.h" -#include "node/project/project.h" +#include "node/project.h" #include "task/task.h" namespace olive { From 92af3b5aeca4dcdf64ebbbd66fc775665f9336b5 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 16:09:58 -0800 Subject: [PATCH 04/23] loadotio: corrected includes --- app/task/project/loadotio/loadotio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index d6c2802fb..eeabdc092 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -40,12 +40,12 @@ #include "node/distort/transform/transformdistortnode.h" #include "node/generator/matrix/matrix.h" #include "node/math/math/math.h" +#include "node/nodeundo.h" #include "node/project/folder/folder.h" #include "node/project/footage/footage.h" #include "node/project/sequence/sequence.h" +#include "timeline/timelineundogeneral.h" #include "window/mainwindow/mainwindowundo.h" -#include "widget/nodeview/nodeviewundo.h" -#include "widget/timelinewidget/undo/timelineundogeneral.h" namespace olive { From 48a7172ea3016731ed8d7b8c70562c1bebb2f0dc Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:22:23 -0800 Subject: [PATCH 05/23] timeline: fixed undo issue with slide tool Fixes #2132 --- app/timeline/timelineundopointer.cpp | 22 +++++++++++++++++----- app/timeline/timelineundopointer.h | 4 ++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/timeline/timelineundopointer.cpp b/app/timeline/timelineundopointer.cpp index 864ce468b..c54941704 100644 --- a/app/timeline/timelineundopointer.cpp +++ b/app/timeline/timelineundopointer.cpp @@ -203,6 +203,8 @@ void TrackSlideCommand::redo() in_adjacent_remove_command_->redo_now(); } + + we_removed_in_adjacent_ = true; } else { // Simply resize adjacent in_adjacent_->set_length_and_media_out(in_adjacent_->length() + movement_); @@ -225,6 +227,8 @@ void TrackSlideCommand::redo() out_adjacent_remove_command_->redo_now(); } + + we_removed_out_adjacent_ = true; } else { // Simply resize adjacent out_adjacent_->set_length_and_media_in(out_adjacent_->length() - movement_); @@ -246,9 +250,13 @@ void TrackSlideCommand::undo() // We created this, so we can remove it now track_->RippleRemoveBlock(in_adjacent_); in_adjacent_->setParent(&memory_manager_); - } else if (in_adjacent_remove_command_) { - // We removed this, so we can restore it now - in_adjacent_remove_command_->undo_now(); + } else if (we_removed_in_adjacent_) { + if (in_adjacent_remove_command_) { + // We removed this, so we can restore it now + in_adjacent_remove_command_->undo_now(); + } + + track_->InsertBlockBefore(in_adjacent_, blocks_.first()); } else { // Simply resize adjacent in_adjacent_->set_length_and_media_out(in_adjacent_->length() - movement_); @@ -259,8 +267,12 @@ void TrackSlideCommand::undo() // We created this, so we can remove it now track_->RippleRemoveBlock(out_adjacent_); out_adjacent_->setParent(&memory_manager_); - } else if (out_adjacent_remove_command_) { - out_adjacent_remove_command_->undo_now(); + } else if (we_removed_out_adjacent_) { + if (out_adjacent_remove_command_) { + out_adjacent_remove_command_->undo_now(); + } + + track_->InsertBlockAfter(out_adjacent_, blocks_.last()); } else { out_adjacent_->set_length_and_media_in(out_adjacent_->length() + movement_); } diff --git a/app/timeline/timelineundopointer.h b/app/timeline/timelineundopointer.h index 05adf590e..ad5acf197 100644 --- a/app/timeline/timelineundopointer.h +++ b/app/timeline/timelineundopointer.h @@ -116,8 +116,10 @@ public: track_(track), blocks_(moving_blocks), movement_(movement), + we_removed_in_adjacent_(false), in_adjacent_(in_adjacent), in_adjacent_remove_command_(nullptr), + we_removed_out_adjacent_(false), out_adjacent_(out_adjacent), out_adjacent_remove_command_(nullptr) { @@ -148,9 +150,11 @@ private: rational movement_; bool we_created_in_adjacent_; + bool we_removed_in_adjacent_; Block* in_adjacent_; UndoCommand* in_adjacent_remove_command_; bool we_created_out_adjacent_; + bool we_removed_out_adjacent_; Block* out_adjacent_; UndoCommand* out_adjacent_remove_command_; From 4801891532a973b231df2fe34e25a5caaf276718 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:50:11 -0800 Subject: [PATCH 06/23] timeline: fix issue where transitions wouldn't reconnect on alt+drag --- app/widget/timelinewidget/tool/pointer.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 6c2296ab8..33adebd93 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -718,6 +718,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) if (!relinks.empty()) { for (auto it=relinks.cbegin(); it!=relinks.cend(); it++) { + // Re-connect links on duplicate clips for (auto jt=it.key()->links().cbegin(); jt!=it.key()->links().cend(); jt++) { Node *link = *jt; Node *copy_link = relinks.value(link); @@ -725,6 +726,24 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) command->add_child(new NodeLinkCommand(it.value(), copy_link, true)); } } + + // Re-connect transitions where applicable + if (ClipBlock *og_clip = dynamic_cast(it.key())) { + ClipBlock *cp_clip = static_cast(it.value()); + + TransitionBlock *og_in_transition = og_clip->in_transition(); + TransitionBlock *og_out_transition = og_clip->out_transition(); + + if (og_in_transition && relinks.contains(og_in_transition)) { + TransitionBlock *cp_in_transition = static_cast(relinks.value(og_in_transition)); + command->add_child(new NodeEdgeAddCommand(cp_clip, NodeInput(cp_in_transition, TransitionBlock::kInBlockInput))); + } + + if (og_out_transition && relinks.contains(og_out_transition)) { + TransitionBlock *cp_out_transition = static_cast(relinks.value(og_out_transition)); + command->add_child(new NodeEdgeAddCommand(cp_clip, NodeInput(cp_out_transition, TransitionBlock::kOutBlockInput))); + } + } } } From aa2653a58364d51b88d7abbf713add6d23790c01 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:32:15 -0800 Subject: [PATCH 07/23] mainwindow: implement hack to fix pixel sampler --- app/window/mainwindow/mainwindow.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 6722ba6ae..5354bb313 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -86,6 +86,13 @@ MainWindow::MainWindow(QWidget *parent) : audio_monitor_panel_ = new AudioMonitorPanel(); scope_panel_ = new ScopePanel(); + // HACK: The pixel sampler is closed by default, which signals to Core that + // it's no longer visible. However KDDockWidgets doesn't appear to + // emit the "shown" signal before emitting the "hidden" signals, resulting + // in Core thinking there are -1 pixel samplers open. To mitigate that, + // we force "shown" to emit ourselves here. + emit pixel_sampler_panel_->shown(); + // Make node-related connections connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts, param_panel_, &ParamPanel::SetSelectedNodes); connect(node_panel_, &NodePanel::NodeGroupOpened, this, &MainWindow::NodePanelGroupOpenedOrClosed); From 5e29414dce2773ca508c5de9ae6a311d6405c574 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:44:26 -0800 Subject: [PATCH 08/23] clip: use footage label by default rather than copied label --- app/node/block/clip/clip.cpp | 4 +++- app/widget/timelinewidget/tool/import.cpp | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 9424f2203..d30ec6ac2 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -69,7 +69,9 @@ ClipBlock::ClipBlock() : QString ClipBlock::Name() const { - if (track()) { + if (connected_viewer_ && !connected_viewer_->GetLabel().isEmpty()) { + return connected_viewer_->GetLabel(); + } else if (track()) { if (track()->type() == Track::kVideo) { return tr("Video Clip"); } else if (track()->type() == Track::kAudio) { diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index ae42257da..bc6ff74e3 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -424,7 +424,6 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) ClipBlock* clip = new ClipBlock(); block = clip; clip->set_media_in(ghost->GetMediaIn()); - clip->SetLabel(footage_stream.footage->GetLabel()); command->add_child(new NodeAddCommand(dst_graph, clip)); // Position clip in its own context From 49fd809db76494ae2f19ac13083d23de5b15734b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:49:52 -0800 Subject: [PATCH 09/23] serializer: clear existing clip name if duplicate of footage name --- app/node/project/serializer/serializer220403.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 851ac8d4d..e94780fc9 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -334,6 +334,13 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project // Re-enable caches for (Node *n : project->nodes()) { n->SetCachesEnabled(true); + + // Clear duplicate label (to facilitate #2147) + if (ClipBlock *c = dynamic_cast(n)) { + if (c->connected_viewer() && c->GetLabel() == c->connected_viewer()->GetLabel()) { + c->SetLabel(QString()); + } + } } return load_data; From 1781311e6f029f099b01d35014e5fd7ed9388274 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 19:57:57 -0800 Subject: [PATCH 10/23] tests: fix bad includes --- tests/compositing/compositing-tests.cpp | 2 +- tests/timeline/timeline-tests.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/compositing/compositing-tests.cpp b/tests/compositing/compositing-tests.cpp index 8b831e4fd..ba1257f19 100644 --- a/tests/compositing/compositing-tests.cpp +++ b/tests/compositing/compositing-tests.cpp @@ -24,7 +24,7 @@ #include "node/distort/transform/transformdistortnode.h" #include "node/generator/solid/solid.h" #include "node/math/merge/merge.h" -#include "node/project/project.h" +#include "node/project.h" #include "render/rendermanager.h" namespace olive { diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp index 034349e57..6cb72c387 100644 --- a/tests/timeline/timeline-tests.cpp +++ b/tests/timeline/timeline-tests.cpp @@ -23,12 +23,12 @@ #include "node/block/transition/crossdissolve/crossdissolvetransition.h" #include "node/math/math/math.h" #include "node/math/merge/merge.h" -#include "node/project/project.h" +#include "node/project.h" #include "node/project/sequence/sequence.h" -#include "undo/undocommand.h" -#include "widget/timelinewidget/undo/timelineundogeneral.h" -#include "widget/timelinewidget/undo/timelineundopointer.h" #include "testutil.h" +#include "timeline/timelineundogeneral.h" +#include "timeline/timelineundopointer.h" +#include "undo/undocommand.h" namespace olive { From e859ba51f012a2c6c4bd490b038b6e071e54b98e Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 21:08:35 -0800 Subject: [PATCH 11/23] nodeparamviewitem: fix regression crash on array append command --- app/widget/nodeparamview/nodeparamviewitem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 41d09db57..8cdb5b509 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -444,7 +444,7 @@ void NodeParamViewItemBody::ArrayAppendClicked() for (auto it=array_ui_.cbegin(); it!=array_ui_.cend(); it++) { if (it.value().append_btn == sender()) { NodeInput real_input = NodeGroup::ResolveInput(NodeInput(it.key().node, it.key().input)); - Core::instance()->undo_stack()->push(new NodeArrayInsertCommand(real_input.node(), real_input.input(), real_input.GetArraySize()+1)); + Core::instance()->undo_stack()->push(new NodeArrayInsertCommand(real_input.node(), real_input.input(), real_input.GetArraySize())); break; } } From e2c04f2e7c37e3ac322ddbb25b70341038210630 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 22:17:41 -0800 Subject: [PATCH 12/23] nodeparamview: handle array size changes in keyframe view Fixes #2097 --- app/widget/nodeparamview/nodeparamview.cpp | 39 +++++++++++++++++++ app/widget/nodeparamview/nodeparamview.h | 2 + .../nodeparamview/nodeparamviewitem.cpp | 39 ++++++++++++++----- app/widget/nodeparamview/nodeparamviewitem.h | 13 ++++++- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index cf12c44e5..e931cf209 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -726,6 +726,7 @@ void NodeParamView::AddNode(Node *n, Node *ctx, NodeParamViewContext *context) connect(item, &NodeParamViewItem::ArrayExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::ExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::Moved, this, &NodeParamView::QueueKeyframePositionUpdate); + connect(item, &NodeParamViewItem::InputArraySizeChanged, this, &NodeParamView::InputArraySizeChanged); item->SetKeyframeConnections(keyframe_view_->AddKeyframesOfNode(n)); } @@ -999,4 +1000,42 @@ void NodeParamView::GroupInputPassthroughRemoved(NodeGroup *group, const NodeInp } } +void NodeParamView::InputArraySizeChanged(const QString &input, int, int new_size) +{ + NodeParamViewItem *sender = static_cast(this->sender()); + + KeyframeView::NodeConnections &connections = sender->GetKeyframeConnections(); + KeyframeView::InputConnections &inputs = connections[input]; + + int adj_new_size = new_size + 1; + + if (adj_new_size != inputs.size()) { + if (adj_new_size < inputs.size()) { + // Remove elements from keyframe view + for (int i = adj_new_size; i < inputs.size(); i++) { + const KeyframeView::ElementConnections &ec = inputs.at(i); + for (auto kc : ec) { + keyframe_view_->RemoveKeyframesOfTrack(kc); + } + } + + // Resize vector to match new size + inputs.resize(adj_new_size); + } else { + // Add elements + int old_size = inputs.size(); + + // Resize vector to match + inputs.resize(adj_new_size); + + // Fill in extra elements + for (int i = old_size; i < inputs.size(); i++) { + inputs[i] = keyframe_view_->AddKeyframesOfElement(NodeInput(sender->GetNode(), input, i - 1)); + } + } + } + + QueueKeyframePositionUpdate(); +} + } diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 296abf392..da63e638a 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -190,6 +190,8 @@ private slots: void RequestEditTextInViewer(); + void InputArraySizeChanged(const QString &input, int old_size, int new_size); + }; } diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 8cdb5b509..304d80057 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -50,7 +50,8 @@ NodeParamViewItem::NodeParamViewItem(Node *node, NodeParamViewCheckBoxBehavior c body_(nullptr), node_(node), create_checkboxes_(create_checkboxes), - ctx_(nullptr) + ctx_(nullptr), + time_target_(nullptr) { node_->Retranslate(); @@ -58,6 +59,7 @@ NodeParamViewItem::NodeParamViewItem(Node *node, NodeParamViewCheckBoxBehavior c RecreateBody(); connect(node_, &Node::LabelChanged, this, &NodeParamViewItem::Retranslate); + connect(node_, &Node::InputArraySizeChanged, this, &NodeParamViewItem::InputArraySizeChanged); // FIXME: Implemented to pick up when an input is set to hidden or not - DEFINITELY not a fast // way of doing this, but "fine" for now. @@ -96,6 +98,7 @@ void NodeParamViewItem::RecreateBody() connect(body_, &NodeParamViewItemBody::RequestEditTextInViewer, this, &NodeParamViewItem::RequestEditTextInViewer); body_->Retranslate(); body_->SetTimebase(timebase_); + body_->SetTimeTarget(time_target_); SetBody(body_); } @@ -117,6 +120,7 @@ void NodeParamViewItem::SetInputChecked(const NodeInput &input, bool e) NodeParamViewItemBody::NodeParamViewItemBody(Node* node, NodeParamViewCheckBoxBehavior create_checkboxes, QWidget *parent) : QWidget(parent), node_(node), + time_target_(nullptr), create_checkboxes_(create_checkboxes) { QGridLayout* root_layout = new QGridLayout(this); @@ -265,22 +269,32 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const if (node->IsInputConnectable(input)) { UpdateUIForEdgeConnection(input_ref); } + + SetTimeTargetOnInputUI(ui_objects); + SetTimebaseOnInputUI(ui_objects); } void NodeParamViewItemBody::SetTimeTarget(ViewerOutput *target) { + time_target_ = target; + foreach (const InputUI& ui_obj, input_ui_map_) { - // Only keyframable inputs have a key control widget - if (ui_obj.key_control) { - ui_obj.key_control->SetTimeTarget(target); - } - if (ui_obj.connected_label) { - ui_obj.connected_label->SetViewerNode(target); - } - ui_obj.widget_bridge->SetTimeTarget(target); + SetTimeTargetOnInputUI(ui_obj); } } +void NodeParamViewItemBody::SetTimeTargetOnInputUI(const InputUI &ui_obj) +{ + // Only keyframable inputs have a key control widget + if (ui_obj.key_control) { + ui_obj.key_control->SetTimeTarget(time_target_); + } + if (ui_obj.connected_label) { + ui_obj.connected_label->SetViewerNode(time_target_); + } + ui_obj.widget_bridge->SetTimeTarget(time_target_); +} + void NodeParamViewItemBody::Retranslate() { for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { @@ -492,10 +506,15 @@ void NodeParamViewItemBody::SetTimebase(const rational& timebase) timebase_ = timebase; foreach (const InputUI& ui_obj, input_ui_map_) { - ui_obj.widget_bridge->SetTimebase(timebase); + SetTimebaseOnInputUI(ui_obj); } } +void NodeParamViewItemBody::SetTimebaseOnInputUI(const InputUI& ui_obj) +{ + ui_obj.widget_bridge->SetTimebase(timebase_); +} + void NodeParamViewItemBody::SetInputChecked(const NodeInput &input, bool e) { if (input_ui_map_.contains(input)) { diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index eb52e44d3..56149064c 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -104,6 +104,9 @@ private: NodeParamViewArrayButton* append_btn; }; + void SetTimeTargetOnInputUI(const InputUI &ui); + void SetTimebaseOnInputUI(const InputUI &ui); + Node *node_; QHash array_ui_; @@ -112,6 +115,8 @@ private: rational timebase_; + ViewerOutput *time_target_; + NodeParamViewCheckBoxBehavior create_checkboxes_; QHash input_group_lookup_; @@ -165,6 +170,8 @@ public: void SetTimeTarget(ViewerOutput* target) { + time_target_ = target; + body_->SetTimeTarget(target); } @@ -194,7 +201,7 @@ public: void SetInputChecked(const NodeInput &input, bool e); - const KeyframeView::NodeConnections &GetKeyframeConnections() const + KeyframeView::NodeConnections &GetKeyframeConnections() { return keyframe_connections_; } @@ -213,6 +220,8 @@ signals: void RequestEditTextInViewer(); + void InputArraySizeChanged(const QString &input, int old_size, int new_size); + protected slots: virtual void Retranslate() override; @@ -225,6 +234,8 @@ private: Node *ctx_; + ViewerOutput *time_target_; + rational timebase_; KeyframeView::NodeConnections keyframe_connections_; From 764d9ada36b9ae687c7d98105dd5d4cfa73580c7 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:03:37 -0800 Subject: [PATCH 13/23] timebasedview: assert y scale > 0 --- app/widget/timebased/timebasedview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/widget/timebased/timebasedview.cpp b/app/widget/timebased/timebasedview.cpp index 6576cf4b3..8f83970fb 100644 --- a/app/widget/timebased/timebasedview.cpp +++ b/app/widget/timebased/timebasedview.cpp @@ -133,6 +133,8 @@ void TimeBasedView::ZoomIntoCursorPosition(QWheelEvent *event, double scale_mult void TimeBasedView::SetYScale(const double &y_scale) { + Q_ASSERT(y_scale > 0); + y_scale_ = y_scale; if (y_axis_enabled_) { From 52021a8694445655e62ab925168d8d7f47b84817 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:04:31 -0800 Subject: [PATCH 14/23] curveview/decibel: return -200 rather than infinity While technically less correct, it's mathematically essentially the same and saves a lot of programming headaches. Fixes #2133 --- app/common/decibel.h | 19 +++++++++++++++++-- app/widget/curvewidget/curveview.cpp | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/common/decibel.h b/app/common/decibel.h index 40a88135b..569b0a969 100644 --- a/app/common/decibel.h +++ b/app/common/decibel.h @@ -24,14 +24,25 @@ #include #include +//#define ALLOW_RETURNING_INFINITY + namespace olive { class Decibel { public: + // In basically all circumstances, this should calculate to 0.0 linear + static constexpr double MINIMUM = -200.0; + static double fromLinear(double linear) { - return double(20.0) * std::log10(linear); + double v = double(20.0) * std::log10(linear); +#ifndef ALLOW_RETURNING_INFINITY + if (std::isinf(v)) { + return MINIMUM; + } +#endif + return v; } static double toLinear(double decibel) @@ -49,7 +60,11 @@ public: static double fromLogarithmic(double logarithmic) { if (logarithmic < 0.001) - return -200.0; +#ifdef ALLOW_RETURNING_INFINITY + return std::numeric_limits::infinity(); +#else + return MINIMUM; +#endif else if (logarithmic > 0.99) return 0; else diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index df51b25b3..81d00bf2e 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -27,6 +27,7 @@ #include #include +#include "common/decibel.h" #include "common/qtutils.h" #include "node/nodeundo.h" #include "widget/keyframeview/keyframeviewundo.h" From 7ecabfa68dd5eb03791e12de4bb51d9ba18c9abb Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:14:47 -0800 Subject: [PATCH 15/23] valuenode: add boolean type Fixes #2145 --- app/node/input/value/valuenode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index 1303b91d3..5520d2cf9 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -35,6 +35,7 @@ const QVector ValueNode::kSupportedTypes = { NodeValue::kText, NodeValue::kMatrix, NodeValue::kFont, + NodeValue::kBoolean, }; #define super Node From 1d058ec709ea14601934870f2178065bb3faa598 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:52:56 -0800 Subject: [PATCH 16/23] handmovableview: normalize zoom scroll if scrolling is inverted --- app/widget/handmovableview/handmovableview.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/widget/handmovableview/handmovableview.cpp b/app/widget/handmovableview/handmovableview.cpp index 83052dea3..4a916a0da 100644 --- a/app/widget/handmovableview/handmovableview.cpp +++ b/app/widget/handmovableview/handmovableview.cpp @@ -157,7 +157,11 @@ bool HandMovableView::WheelEventIsAZoomEvent(QWheelEvent *event) qreal HandMovableView::GetScrollZoomMultiplier(QWheelEvent *event) { - return 1.0 + (static_cast(event->angleDelta().x() + event->angleDelta().y()) * 0.001); + qreal v = (static_cast(event->angleDelta().x() + event->angleDelta().y()) * 0.001); + if (event->inverted()) { + v = -v; + } + return 1.0 + v; } void HandMovableView::wheelEvent(QWheelEvent *event) From e44a3d221d45786e4194ba9dd77268c565370ea0 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:01:53 -0800 Subject: [PATCH 17/23] viewersizer: zoom by float and anchor by cursor --- app/widget/viewer/viewersizer.cpp | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/app/widget/viewer/viewersizer.cpp b/app/widget/viewer/viewersizer.cpp index 505cb4eb9..51be32d62 100644 --- a/app/widget/viewer/viewersizer.cpp +++ b/app/widget/viewer/viewersizer.cpp @@ -104,31 +104,28 @@ bool ViewerSizer::eventFilter(QObject *watched, QEvent *event) QWheelEvent *w = static_cast(event); if (HandMovableView::WheelEventIsAZoomEvent(w)) { - int x = w->angleDelta().x() + w->angleDelta().y(); - int current_percent = zoom_; if (current_percent == 0) { // Currently set to "fit" current_percent = current_widget_scale_; } - if (x > 0) { - // Zoom in - for (int i=kZoomLevelCount-2; i>=0; i--) { - if (current_percent >= kZoomLevels[i]) { - SetZoom(kZoomLevels[i+1]); - break; - } - } - } else if (x < 0) { - // Zoom out - for (int i=1; iposition(); + + int anchor_x = qRound(double(cursor_pos.x() + horiz_scrollbar_->value()) / cur_scale * next_scale - cursor_pos.x()); + int anchor_y = qRound(double(cursor_pos.y() + vert_scrollbar_->value()) / cur_scale * next_scale - cursor_pos.y()); + + SetZoom(current_percent); + + horiz_scrollbar_->setValue(anchor_x); + vert_scrollbar_->setValue(anchor_y); } else { // Pass scroll values to scrollbars QPoint p = w->pixelDelta(); From 060c3b088304a20b92b49824cb174316890167de Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:41:17 -0800 Subject: [PATCH 18/23] viewersizer: use float for all calculations, center manual zoom --- app/widget/viewer/viewer.cpp | 9 ++-- app/widget/viewer/viewersizer.cpp | 73 ++++++++++++++++++++----------- app/widget/viewer/viewersizer.h | 15 ++++--- 3 files changed, 62 insertions(+), 35 deletions(-) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 791e0454a..70e973ddd 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -1376,10 +1376,10 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) Menu* zoom_menu = new Menu(tr("Zoom"), &menu); menu.addMenu(zoom_menu); - zoom_menu->addAction(tr("Fit"))->setData(0); + zoom_menu->addAction(tr("Fit"))->setData(-1); for (int i=0;iaddAction(tr("%1%").arg(z))->setData(z); + double z = ViewerSizer::kZoomLevels[i]; + zoom_menu->addAction(tr("%1%").arg(z * 100.0))->setData(z); } connect(zoom_menu, &QMenu::triggered, this, &ViewerWidget::SetZoomFromMenu); @@ -1822,7 +1822,8 @@ void ViewerWidget::UpdateRendererAudioParameters() void ViewerWidget::SetZoomFromMenu(QAction *action) { - sizer_->SetZoom(action->data().toInt()); + auto s = sizer_->GetContainerSize(); + sizer_->SetZoomAnchored(action->data().toDouble(), s.width()/2, s.height()/2); } void ViewerWidget::ViewerInvalidatedVideoRange(const TimeRange &range) diff --git a/app/widget/viewer/viewersizer.cpp b/app/widget/viewer/viewersizer.cpp index 51be32d62..86f4c6079 100644 --- a/app/widget/viewer/viewersizer.cpp +++ b/app/widget/viewer/viewersizer.cpp @@ -35,7 +35,7 @@ ViewerSizer::ViewerSizer(QWidget *parent) : width_(0), height_(0), pixel_aspect_(1), - zoom_(0), + zoom_(-1), current_widget_scale_(0) { horiz_scrollbar_ = new QScrollBar(Qt::Horizontal, this); @@ -64,6 +64,12 @@ void ViewerSizer::SetWidget(QWidget *widget) } } +QSize ViewerSizer::GetContainerSize() const +{ + double s = GetRealCurrentZoom(); + return QSize(std::min(this->width(), int(width_ * s)) - vert_scrollbar_->width(), std::min(int(height_ * s), this->height()) - horiz_scrollbar_->height()); +} + void ViewerSizer::SetChildSize(int width, int height) { width_ = width; @@ -79,13 +85,36 @@ void ViewerSizer::SetPixelAspectRatio(const rational &pixel_aspect) UpdateSize(); } -void ViewerSizer::SetZoom(int percent) +void ViewerSizer::SetZoom(double percent) { zoom_ = percent; UpdateSize(); } +void ViewerSizer::SetZoomAnchored(double next_scale, double cursor_x, double cursor_y) +{ + if (next_scale > 0) { + double cur_scale = GetRealCurrentZoom(); + + // Clamp scale within safe values + next_scale = std::clamp(next_scale, kZoomLevels[0], kZoomLevels[kZoomLevelCount-1]); + + int anchor_x = qRound(double(cursor_x + horiz_scrollbar_->value()) / cur_scale * next_scale - cursor_x); + int anchor_y = qRound(double(cursor_y + vert_scrollbar_->value()) / cur_scale * next_scale - cursor_y); + + SetZoom(next_scale); + + horiz_scrollbar_->setValue(anchor_x); + vert_scrollbar_->setValue(anchor_y); + } else { + SetZoom(-1); + + horiz_scrollbar_->setValue(0); + vert_scrollbar_->setValue(0); + } +} + void ViewerSizer::HandDragMove(int x, int y) { if (horiz_scrollbar_->isVisible()) { @@ -104,28 +133,9 @@ bool ViewerSizer::eventFilter(QObject *watched, QEvent *event) QWheelEvent *w = static_cast(event); if (HandMovableView::WheelEventIsAZoomEvent(w)) { - int current_percent = zoom_; - if (current_percent == 0) { - // Currently set to "fit" - current_percent = current_widget_scale_; - } - - double cur_scale = current_percent * 0.01; - - current_percent *= HandMovableView::GetScrollZoomMultiplier(w); - current_percent = std::clamp(current_percent, kZoomLevels[0], kZoomLevels[kZoomLevelCount-1]); - - double next_scale = current_percent * 0.01; - + double next_scale = GetRealCurrentZoom() * HandMovableView::GetScrollZoomMultiplier(w); QPointF cursor_pos = w->position(); - - int anchor_x = qRound(double(cursor_pos.x() + horiz_scrollbar_->value()) / cur_scale * next_scale - cursor_pos.x()); - int anchor_y = qRound(double(cursor_pos.y() + vert_scrollbar_->value()) / cur_scale * next_scale - cursor_pos.y()); - - SetZoom(current_percent); - - horiz_scrollbar_->setValue(anchor_x); - vert_scrollbar_->setValue(anchor_y); + SetZoomAnchored(next_scale, cursor_pos.x(), cursor_pos.y()); } else { // Pass scroll values to scrollbars QPoint p = w->pixelDelta(); @@ -218,12 +228,12 @@ void ViewerSizer::UpdateSize() } - current_widget_scale_ = current_scale * 100; + current_widget_scale_ = current_scale; if (zoom_ > 0) { // Scale to get to the requested zoom - double zoom_diff = (zoom_ * 0.01) / current_scale; + double zoom_diff = zoom_ / current_scale; child_matrix.scale(zoom_diff, zoom_diff, 1.0); } @@ -235,7 +245,18 @@ void ViewerSizer::UpdateSize() int ViewerSizer::GetZoomedValue(int value) { - return qRound(value * static_cast(zoom_) * 0.01); + return qRound(value * zoom_); +} + +double ViewerSizer::GetRealCurrentZoom() const +{ + if (zoom_ < 0) { + // Currently set to "fit" + return current_widget_scale_; + } else { + // Explicit zoom set + return zoom_; + } } void ViewerSizer::ScrollBarMoved() diff --git a/app/widget/viewer/viewersizer.h b/app/widget/viewer/viewersizer.h index 90157be6d..189e99a27 100644 --- a/app/widget/viewer/viewersizer.h +++ b/app/widget/viewer/viewersizer.h @@ -51,8 +51,10 @@ public: */ void SetWidget(QWidget* widget); - static constexpr int kZoomLevelCount = 8; - static constexpr int kZoomLevels[kZoomLevelCount] = {10, 25, 50, 75, 100, 150, 200, 400}; + QSize GetContainerSize() const; + + static constexpr int kZoomLevelCount = 10; + static constexpr double kZoomLevels[kZoomLevelCount] = {0.05, 0.1, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 4.0, 8.0}; public slots: /** @@ -72,7 +74,8 @@ public slots: * * The number is an integer percentage (100 = 100%). Set to 0 to auto-fit. */ - void SetZoom(int percent); + void SetZoom(double percent); + void SetZoomAnchored(double percent, double cursor_x, double cursor_y); void HandDragMove(int x, int y); @@ -97,6 +100,8 @@ private: int GetZoomedValue(int value); + double GetRealCurrentZoom() const; + /** * @brief Reference to widget * @@ -115,8 +120,8 @@ private: /** * @brief Internal zoom value */ - int zoom_; - int current_widget_scale_; + double zoom_; + double current_widget_scale_; QScrollBar* horiz_scrollbar_; QScrollBar* vert_scrollbar_; From feb7d5762dfd695946d59d47f8f72da0ec6d62de Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:14:40 -0800 Subject: [PATCH 19/23] viewer: allow resizing while text gizmo is active --- app/node/gizmo/text.cpp | 12 +++++ app/node/gizmo/text.h | 15 ++----- app/widget/viewer/viewerdisplay.cpp | 70 +++++++++++++++++++---------- app/widget/viewer/viewerdisplay.h | 2 + 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/app/node/gizmo/text.cpp b/app/node/gizmo/text.cpp index 6d6816948..7b966ee40 100644 --- a/app/node/gizmo/text.cpp +++ b/app/node/gizmo/text.cpp @@ -32,6 +32,12 @@ TextGizmo::TextGizmo(QObject *parent) } +void TextGizmo::SetRect(const QRectF &r) +{ + rect_ = r; + emit RectChanged(rect_); +} + void TextGizmo::UpdateInputHtml(const QString &s, const rational &time) { if (input_.IsValid()) { @@ -41,4 +47,10 @@ void TextGizmo::UpdateInputHtml(const QString &s, const rational &time) } } +void TextGizmo::SetVerticalAlignment(Qt::Alignment va) +{ + valign_ = va; + emit VerticalAlignmentChanged(valign_); +} + } diff --git a/app/node/gizmo/text.h b/app/node/gizmo/text.h index 5c3218671..df2aca92a 100644 --- a/app/node/gizmo/text.h +++ b/app/node/gizmo/text.h @@ -33,7 +33,7 @@ public: explicit TextGizmo(QObject *parent = nullptr); const QRectF &GetRect() const { return rect_; } - void SetRect(const QRectF &r) { rect_ = r; } + void SetRect(const QRectF &r); const QString &GetHtml() const { return text_; } void SetHtml(const QString &t) { text_ = t; } @@ -42,21 +42,14 @@ public: void UpdateInputHtml(const QString &s, const rational &time); - Qt::Alignment GetVerticalAlignment() const - { - return valign_; - } - - void SetVerticalAlignment(Qt::Alignment va) - { - valign_ = va; - emit VerticalAlignmentChanged(valign_); - } + Qt::Alignment GetVerticalAlignment() const { return valign_; } + void SetVerticalAlignment(Qt::Alignment va); signals: void Activated(); void Deactivated(); void VerticalAlignmentChanged(Qt::Alignment va); + void RectChanged(const QRectF &r); private: QRectF rect_; diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 0fcb70e40..399391597 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -677,6 +677,10 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(NodeTraverser >, const NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPointF &p) { + if (!gizmos_) { + return nullptr; + } + for (auto it=gizmos_->GetGizmos().crbegin(); it!=gizmos_->GetGizmos().crend(); it++) { NodeGizmo *gizmo = *it; if (gizmo->IsVisible()) { @@ -705,6 +709,7 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPo void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) { active_text_gizmo_ = text; + connect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); text_transform_ = GenerateGizmoTransform(); text_transform_inverted_ = text_transform_.inverted(); @@ -736,9 +741,7 @@ void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) connect(text_edit_, &ViewerTextEditor::destroyed, this, &ViewerDisplayWidget::TextEditDestroyed); // Set text editor's size to logical size - QRectF text_rect = text->GetRect(); - text_edit_pos_ = text_rect.topLeft(); - text_edit_->setGeometry(text_rect.toRect()); + QRectF text_rect = UpdateActiveTextGizmoSize(); // Emit text gizmo activation signal emit text->Activated(); @@ -808,9 +811,9 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) return true; - } else if (text_edit_) { + } else if (text_edit_ && ForwardMouseEventToTextEdit(event, true)) { - return ForwardMouseEventToTextEdit(event, true); + return true; } else if (event->button() == Qt::LeftButton) { @@ -821,8 +824,7 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) add_band_end_ = add_band_start_; add_band_ = true; - } else if (gizmos_ - && (current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { + } else if ((current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { // Handle gizmo click gizmo_start_drag_ = event->pos(); @@ -856,18 +858,9 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) return true; - } else if (text_edit_) { + } else if (text_edit_ && ForwardMouseEventToTextEdit(event)) { - if (event->buttons() == Qt::NoButton) { - QPointF mapped = text_transform_inverted_.map(event->pos()) - text_edit_pos_; - if (mapped.x() >= 0 && mapped.y() >= 0 && mapped.x() < text_edit_->width() && mapped.y() < text_edit_->height()) { - inner_widget()->setCursor(Qt::IBeamCursor); - } else { - inner_widget()->unsetCursor(); - } - } - - return ForwardMouseEventToTextEdit(event); + return true; } else if (add_band_) { @@ -927,9 +920,9 @@ bool ViewerDisplayWidget::OnMouseRelease(QMouseEvent *e) return true; - } else if (text_edit_) { + } else if (text_edit_ && ForwardMouseEventToTextEdit(e)) { - return ForwardMouseEventToTextEdit(e); + return true; } else if (add_band_) { @@ -964,8 +957,8 @@ bool ViewerDisplayWidget::OnMouseRelease(QMouseEvent *e) bool ViewerDisplayWidget::OnMouseDoubleClick(QMouseEvent *event) { - if (text_edit_) { - return ForwardMouseEventToTextEdit(event); + if (text_edit_ && ForwardMouseEventToTextEdit(event)) { + return true; } else if (event->button() == Qt::LeftButton && gizmos_) { QPointF ptr = TransformViewerSpaceToBufferSpace(event->pos()); foreach (NodeGizmo *g, gizmos_->GetGizmos()) { @@ -1148,13 +1141,31 @@ void ViewerDisplayWidget::ForwardDragEventToTextEdit(T *e) bool ViewerDisplayWidget::ForwardMouseEventToTextEdit(QMouseEvent *event, bool check_if_outside) { + if (current_gizmo_) { + return false; + } + // Transform screen mouse coords to world mouse coords QPointF local_pos = GetVirtualPosForTextEdit(event->pos()); + if (event->type() == QEvent::MouseMove && event->buttons() == Qt::NoButton) { + QPointF mapped = text_transform_inverted_.map(event->pos()) - text_edit_pos_; + if (mapped.x() >= 0 && mapped.y() >= 0 && mapped.x() < text_edit_->width() && mapped.y() < text_edit_->height()) { + inner_widget()->setCursor(Qt::IBeamCursor); + } else { + inner_widget()->unsetCursor(); + } + } + if (check_if_outside) { if (local_pos.x() < 0 || local_pos.x() >= text_edit_->width() || local_pos.y() < 0 || local_pos.y() >= text_edit_->height()) { - CloseTextEditor(); - return true; + // Allow clicking other gizmos so the user can resize while the text editor is active + if ((current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { + return false; + } else { + CloseTextEditor(); + return true; + } } } @@ -1191,6 +1202,9 @@ void ViewerDisplayWidget::CloseTextEditor() { text_edit_->deleteLater(); text_edit_ = nullptr; + + disconnect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); + active_text_gizmo_ = nullptr; } void ViewerDisplayWidget::GenerateGizmoTransforms() @@ -1382,4 +1396,12 @@ void ViewerDisplayWidget::FocusChanged(QWidget *old, QWidget *now) } } +QRectF ViewerDisplayWidget::UpdateActiveTextGizmoSize() +{ + QRectF text_rect = active_text_gizmo_->GetRect(); + text_edit_pos_ = text_rect.topLeft(); + text_edit_->setGeometry(text_rect.toRect()); + return text_rect; +} + } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 2672b1b67..5ffcd2c4f 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -438,6 +438,8 @@ private slots: void FocusChanged(QWidget *old, QWidget *now); + QRectF UpdateActiveTextGizmoSize(); + }; From fbcc4be4ade2dacdc452e0fab5c2bba71c33b3b4 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:33:12 -0800 Subject: [PATCH 20/23] viewerdisplay: force update on keyboard and mouse events For some reason, QTextEdit can be put into states where it no longer emits paint events, which is what we relied on. Now we force repaints for events we expect to change the visuals, e.g. keyboard/mouse events. --- app/widget/viewer/viewerdisplay.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 399391597..8ce5aed92 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -1178,7 +1178,11 @@ bool ViewerDisplayWidget::ForwardMouseEventToTextEdit(QMouseEvent *event, bool c bool ViewerDisplayWidget::ForwardEventToTextEdit(QEvent *event) { qApp->sendEvent(text_edit_->viewport(), event); - return event->isAccepted(); + bool e = event->isAccepted(); + if (e) { + update(); + } + return e; } QPointF ViewerDisplayWidget::AdjustPosByVAlign(QPointF p) From c8908587bbea52f085e7dd597da03b15c7e63d29 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:39:14 -0800 Subject: [PATCH 21/23] viewertexteditor: ensure page size is synced with transparent clone --- app/widget/viewer/viewertexteditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/widget/viewer/viewertexteditor.cpp b/app/widget/viewer/viewertexteditor.cpp index 0ecc65740..1fe8dc4da 100644 --- a/app/widget/viewer/viewertexteditor.cpp +++ b/app/widget/viewer/viewertexteditor.cpp @@ -138,6 +138,7 @@ void ViewerTextEditor::Paint(QPainter *p, Qt::Alignment valign) const bool use_transparent_clone = true; if (transparent_clone_ && use_transparent_clone) { + transparent_clone_->setPageSize(this->document()->pageSize()); transparent_clone_->documentLayout()->draw(p, ctx); } else { document()->documentLayout()->draw(p, ctx); From 8e2db520a7001cf0ea4740726bd952df7156a962 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:18:43 -0800 Subject: [PATCH 22/23] nodeparamview/viewerdisplay: improve opening text gizmo from NPV --- app/widget/nodeparamview/nodeparamview.cpp | 3 +-- app/widget/viewer/viewerdisplay.cpp | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index e931cf209..c29006023 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -311,8 +311,7 @@ void NodeParamView::RequestEditTextInViewer() { NodeParamViewItem *item = static_cast(sender()); - focused_node_ = item; - emit FocusedNodeChanged(item->GetNode()); + SetSelectedNodes({item}); emit RequestViewerToStartEditingText(); } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 8ce5aed92..2a78df02c 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -708,6 +708,9 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPo void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) { + GenerateGizmoTransforms(); + gizmos_->UpdateGizmoPositions(gizmo_db_, NodeGlobals(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff)); + active_text_gizmo_ = text; connect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); text_transform_ = GenerateGizmoTransform(); From 3bc53cef60aba9856753048e66ea2e572a904177 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:50:56 -0800 Subject: [PATCH 23/23] nodegroup: check if an input is actually a passthrough Fixes #2135 --- app/node/group/group.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/node/group/group.cpp b/app/node/group/group.cpp index 9ca495e1f..d4afec050 100644 --- a/app/node/group/group.cpp +++ b/app/node/group/group.cpp @@ -160,6 +160,10 @@ bool NodeGroup::GetInner(NodeInput *input) { if (NodeGroup *g = dynamic_cast(input->node())) { const NodeInput &passthrough = g->GetInputFromID(input->input()); + if (!passthrough.IsValid()) { + return false; + } + input->set_node(passthrough.node()); input->set_input(passthrough.input()); return true;