app: migrate playhead/undo/modified events off EventBridge (issues 5-8)
- issue 5 (#53): ExportDialog playhead via PlaybackController - issue 6 (#54): timebasedwidget/timelinewidget playhead via PlaybackController - issue 7 (#55): historywidget via app-internal Core::undo_index_changed - issue 8 (#33): PROJECT_MODIFIED_CHANGED re-broadcast as Core::project_modified_changed
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "historywidget.h"
|
||||
|
||||
#include "oakengine/events.h"
|
||||
#include "core.h"
|
||||
#include "oakengine/undo.h"
|
||||
|
||||
namespace olive
|
||||
@@ -30,22 +30,13 @@ namespace olive
|
||||
HistoryModel::HistoryModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
sub_ = oakengine_event_subscribe(
|
||||
oakengine_undo_handle(), OAKENGINE_EVENT_UNDO_INDEX_CHANGED,
|
||||
[](const oakengine_event *event, void *userdata) {
|
||||
Q_UNUSED(event)
|
||||
auto *self = static_cast<HistoryModel *>(userdata);
|
||||
self->beginResetModel();
|
||||
self->endResetModel();
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
HistoryModel::~HistoryModel()
|
||||
{
|
||||
if (sub_ > 0) {
|
||||
oakengine_event_unsubscribe(sub_);
|
||||
}
|
||||
// Refresh on Core's app-internal undo signal (issue 7 of the EventBridge
|
||||
// elimination plan) instead of a raw C event subscription; the connection
|
||||
// auto-disconnects with `this`.
|
||||
connect(Core::instance(), &Core::undo_index_changed, this, [this](int) {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
}
|
||||
|
||||
QModelIndex HistoryModel::index(int row, int column,
|
||||
@@ -124,26 +115,12 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||
|
||||
this->setModel(model_);
|
||||
this->setRootIsDecorated(false);
|
||||
undo_sub_ = oakengine_event_subscribe(
|
||||
oakengine_undo_handle(), OAKENGINE_EVENT_UNDO_INDEX_CHANGED,
|
||||
[](const oakengine_event *event, void *userdata) {
|
||||
auto *self = static_cast<HistoryWidget *>(userdata);
|
||||
self->index_changed(static_cast<int>(event->a));
|
||||
},
|
||||
this);
|
||||
connect(Core::instance(), &Core::undo_index_changed, this,
|
||||
&HistoryWidget::index_changed);
|
||||
connect(this->selectionModel(), &QItemSelectionModel::currentRowChanged,
|
||||
this, &HistoryWidget::current_row_changed);
|
||||
}
|
||||
|
||||
HistoryWidget::~HistoryWidget()
|
||||
{
|
||||
// Raw subscription carries `this` as userdata; cancel it or the engine
|
||||
// calls back into a dead widget (the undo stack outlives us).
|
||||
if (undo_sub_ > 0) {
|
||||
oakengine_event_unsubscribe(undo_sub_);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::index_changed(int i)
|
||||
{
|
||||
this->selectionModel()->select(this->model()->index(i - 1, 0),
|
||||
|
||||
@@ -36,13 +36,12 @@ namespace olive
|
||||
* Replaces the direct use of the engine's UndoStack as a Qt item model.
|
||||
* Semantics mirror engine/undo/undostack.cpp: two columns (Number, Action),
|
||||
* rows are all commands on the stack (done first, then undone), undone rows
|
||||
* are shown gray. Refreshes itself on OAKENGINE_EVENT_UNDO_INDEX_CHANGED.
|
||||
* are shown gray. Refreshes itself on Core::undo_index_changed.
|
||||
*/
|
||||
class HistoryModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HistoryModel(QObject *parent = nullptr);
|
||||
~HistoryModel() override;
|
||||
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const override;
|
||||
@@ -53,22 +52,16 @@ public:
|
||||
int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
|
||||
private:
|
||||
int64_t sub_ = 0;
|
||||
};
|
||||
|
||||
class HistoryWidget : public QTreeView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HistoryWidget(QWidget *parent = nullptr);
|
||||
~HistoryWidget() override;
|
||||
|
||||
private:
|
||||
HistoryModel *model_;
|
||||
|
||||
int64_t undo_sub_ = 0;
|
||||
|
||||
size_t current_row_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -117,6 +117,7 @@ void TimeBasedWidget::connect_viewer_node(OakEngineNode *node)
|
||||
// Disconnect old bridge subscriptions and connections
|
||||
disconnect(bridge_, nullptr, this, nullptr);
|
||||
bridge_->unsubscribe_all();
|
||||
disconnect(playhead_conn_);
|
||||
|
||||
if (viewer_node_) {
|
||||
oak_video_params vp;
|
||||
@@ -160,17 +161,24 @@ void TimeBasedWidget::connect_viewer_node(OakEngineNode *node)
|
||||
if (viewer_node_) {
|
||||
// Subscribe to viewer events via bridge
|
||||
bridge_->subscribe(node, OAKENGINE_EVENT_VIEWER_LENGTH_CHANGED);
|
||||
bridge_->subscribe(node, OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED);
|
||||
bridge_->subscribe(node, OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH);
|
||||
|
||||
connect(bridge_, &EngineEventBridge::viewer_length_changed, this,
|
||||
[this](OakEngineNode *, qint64, qint64) {
|
||||
update_maximum_scroll();
|
||||
});
|
||||
connect(bridge_, &EngineEventBridge::viewer_playhead_changed, this,
|
||||
[this](OakEngineNode *, qint64 num, qint64 den) {
|
||||
playhead_time_changed(Rational(num, den));
|
||||
});
|
||||
|
||||
// Playhead changes arrive via PlaybackController (issue 6 of the
|
||||
// EventBridge elimination plan) instead of the bridge's
|
||||
// viewer_playhead_changed.
|
||||
playhead_conn_ = connect(
|
||||
PlaybackController::instance(),
|
||||
&PlaybackController::playhead_changed, this,
|
||||
[this](OakEngineNode *n, const Rational &time) {
|
||||
if (n == get_connected_node()) {
|
||||
playhead_time_changed(time);
|
||||
}
|
||||
});
|
||||
|
||||
// Node removed from graph - use the bridge signal
|
||||
connect(bridge_, &EngineEventBridge::node_removed_from_graph, this,
|
||||
|
||||
@@ -169,6 +169,8 @@ protected:
|
||||
|
||||
EngineEventBridge *bridge_ = nullptr;
|
||||
|
||||
QMetaObject::Connection playhead_conn_;
|
||||
|
||||
virtual void ConnectNodeEvent(OakEngineNode *)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -742,7 +742,6 @@ void TimelineWidget::ConnectNodeEvent(OakEngineNode *n)
|
||||
// Subscribe to viewer events via bridge
|
||||
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_FRAME_RATE_CHANGED);
|
||||
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_SAMPLE_RATE_CHANGED);
|
||||
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED);
|
||||
|
||||
connect(bridge_, &EngineEventBridge::viewer_frame_rate_changed, this,
|
||||
[this](OakEngineNode *, qint64, qint64) {
|
||||
@@ -752,10 +751,17 @@ void TimelineWidget::ConnectNodeEvent(OakEngineNode *n)
|
||||
[this](OakEngineNode *, int) {
|
||||
sample_rate_changed();
|
||||
});
|
||||
connect(bridge_, &EngineEventBridge::viewer_playhead_changed, this,
|
||||
[this](OakEngineNode *, qint64 num, qint64 den) {
|
||||
this->timecode_label_->set_value(Rational(num, den));
|
||||
});
|
||||
|
||||
// Keep the timecode label in sync via PlaybackController (issue 6 of the
|
||||
// EventBridge elimination plan) instead of the bridge's
|
||||
// viewer_playhead_changed.
|
||||
timecode_playhead_conn_ = connect(
|
||||
PlaybackController::instance(), &PlaybackController::playhead_changed,
|
||||
this, [this](OakEngineNode *n, const Rational &time) {
|
||||
if (n == get_connected_node()) {
|
||||
this->timecode_label_->set_value(time);
|
||||
}
|
||||
});
|
||||
|
||||
connect(timecode_label_, &RationalSlider::value_changed, this,
|
||||
[handle](const Rational &time) {
|
||||
@@ -806,6 +812,8 @@ void TimelineWidget::DisconnectNodeEvent(OakEngineNode *n)
|
||||
// Bridge subscriptions and connections are cleaned up by
|
||||
// TimeBasedWidget::connect_viewer_node (disconnect(bridge_, nullptr, this, nullptr))
|
||||
|
||||
disconnect(timecode_playhead_conn_);
|
||||
|
||||
deselect_all();
|
||||
|
||||
foreach (OakEngineTrack *track,
|
||||
|
||||
@@ -290,6 +290,7 @@ public slots:
|
||||
void rename_selected_blocks();
|
||||
|
||||
signals:
|
||||
|
||||
void block_selection_changed(const QVector<OakEngineBlock *> &selected_blocks);
|
||||
|
||||
void request_capture_start(const TimeRange &time,
|
||||
@@ -358,6 +359,8 @@ private:
|
||||
|
||||
RationalSlider *timecode_label_;
|
||||
|
||||
QMetaObject::Connection timecode_playhead_conn_;
|
||||
|
||||
QVector<OakEngineBlock *> selected_blocks_;
|
||||
|
||||
QVector<OakEngineBlock *> added_blocks_;
|
||||
|
||||
Reference in New Issue
Block a user