R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types: engine access goes through the oakengine C ABI plus C++ wrappers (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes, subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp) - engine: new C ABI functions for block/track/clip/transition navigation and predicates, links, caches, waveform/playback, disk folder, sequence_track_list, node_free, footage_is_valid, block_get_track, get_brush; loadotio/saveotio ported to the current engine API - OTIO is now a required dependency: CI and CD build it on every platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps include requirement silently disabled OTIO everywhere), runtime libraries are bundled into packages and copied next to macOS binaries (oak_copy_otio_runtime) - fix ProjectViewModel drag&drop mime read/write size mismatch (segfault) - unify color label naming (k_olive -> "Oak") in the app-side mirror - docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh) - gtest suite: 1925 passed, 0 failed
This commit is contained in:
@@ -164,15 +164,15 @@ MainWindow::~MainWindow()
|
||||
|
||||
void MainWindow::load_layout(const SerializedLayoutInfo &info)
|
||||
{
|
||||
foreach (Folder *folder, info.open_folders) {
|
||||
foreach (OakEngineNode *folder, info.open_folders) {
|
||||
open_folder(folder, true);
|
||||
}
|
||||
|
||||
foreach (Sequence *sequence, info.open_sequences) {
|
||||
foreach (OakEngineNode *sequence, info.open_sequences) {
|
||||
open_sequence(sequence, info.open_sequences.size() == 1);
|
||||
}
|
||||
|
||||
foreach (ViewerOutput *viewer, info.open_viewers) {
|
||||
foreach (OakEngineNode *viewer, info.open_viewers) {
|
||||
open_node_in_viewer(viewer);
|
||||
}
|
||||
|
||||
@@ -219,19 +219,21 @@ SerializedLayoutInfo MainWindow::save_layout() const
|
||||
|
||||
for (int i = 0; i < folder_panels_.size(); i++) {
|
||||
auto panel = folder_panels_.at(i);
|
||||
info.open_folders.push_back(panel->get_root());
|
||||
info.open_folders.push_back(panel->get_root().handle());
|
||||
correct_panel_data_if_necessary(panel->uniqueName(), i, info, layout);
|
||||
}
|
||||
|
||||
for (int i = 0; i < timeline_panels_.size(); i++) {
|
||||
auto panel = timeline_panels_.at(i);
|
||||
info.open_sequences.push_back(panel->get_sequence());
|
||||
info.open_sequences.push_back(
|
||||
reinterpret_cast<OakEngineNode *>(panel->get_sequence()));
|
||||
correct_panel_data_if_necessary(panel->uniqueName(), i, info, layout);
|
||||
}
|
||||
|
||||
for (int i = 0; i < viewer_panels_.size(); i++) {
|
||||
auto panel = viewer_panels_.at(i);
|
||||
info.open_viewers.push_back(panel->get_connected_viewer());
|
||||
info.open_viewers.push_back(
|
||||
reinterpret_cast<OakEngineNode *>(panel->get_connected_viewer()));
|
||||
correct_panel_data_if_necessary(panel->uniqueName(), i, info, layout);
|
||||
}
|
||||
|
||||
@@ -240,11 +242,11 @@ SerializedLayoutInfo MainWindow::save_layout() const
|
||||
return info;
|
||||
}
|
||||
|
||||
TimelinePanel *MainWindow::open_sequence(Sequence *sequence, bool enable_focus)
|
||||
TimelinePanel *MainWindow::open_sequence(OakEngineNode *sequence, bool enable_focus)
|
||||
{
|
||||
// See if this sequence is already open, and switch to it if so
|
||||
foreach (TimelinePanel *tl, timeline_panels_) {
|
||||
if (tl->get_connected_viewer() == sequence) {
|
||||
if (reinterpret_cast<OakEngineNode *>(tl->get_connected_viewer()) == sequence) {
|
||||
tl->raise();
|
||||
return tl;
|
||||
}
|
||||
@@ -270,23 +272,23 @@ TimelinePanel *MainWindow::open_sequence(Sequence *sequence, bool enable_focus)
|
||||
return panel;
|
||||
}
|
||||
|
||||
void MainWindow::close_sequence(Sequence *sequence)
|
||||
void MainWindow::close_sequence(OakEngineNode *sequence)
|
||||
{
|
||||
// We defer to RemoveTimelinePanel() to close the panels, which may delete and remove indices from timeline_panels_.
|
||||
// We make a copy so that our array here doesn't get ruined by what RemoveTimelinePanel() does
|
||||
QList<TimelinePanel *> copy = timeline_panels_;
|
||||
|
||||
foreach (TimelinePanel *tp, copy) {
|
||||
if (tp->get_connected_viewer() == sequence) {
|
||||
if (reinterpret_cast<OakEngineNode *>(tp->get_connected_viewer()) == sequence) {
|
||||
remove_timeline_panel(tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::is_sequence_open(Sequence *sequence) const
|
||||
bool MainWindow::is_sequence_open(OakEngineNode *sequence) const
|
||||
{
|
||||
foreach (TimelinePanel *tp, timeline_panels_) {
|
||||
if (tp->get_connected_viewer() == sequence) {
|
||||
if (reinterpret_cast<OakEngineNode *>(tp->get_connected_viewer()) == sequence) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -294,13 +296,14 @@ bool MainWindow::is_sequence_open(Sequence *sequence) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::open_folder(Folder *i, bool floating)
|
||||
void MainWindow::open_folder(OakEngineNode *i, bool floating)
|
||||
{
|
||||
ProjectPanel *panel =
|
||||
append_panel_internal(QStringLiteral("FolderPanel"), folder_panels_);
|
||||
|
||||
panel->set_project(i->project());
|
||||
panel->set_root(i);
|
||||
oak::Node folder(i);
|
||||
panel->set_project(folder.project());
|
||||
panel->set_root(folder);
|
||||
|
||||
if (floating) {
|
||||
panel->setFloating(floating);
|
||||
@@ -313,13 +316,13 @@ void MainWindow::open_folder(Folder *i, bool floating)
|
||||
&MainWindow::folder_panel_close_requested);
|
||||
}
|
||||
|
||||
void MainWindow::open_node_in_viewer(ViewerOutput *node)
|
||||
void MainWindow::open_node_in_viewer(OakEngineNode *node)
|
||||
{
|
||||
ViewerPanel *existing = nullptr;
|
||||
|
||||
for (auto it = viewer_panels_.cbegin(); it != viewer_panels_.cend(); it++) {
|
||||
ViewerPanel *it2 = (*it);
|
||||
if (it2->get_connected_viewer() == node) {
|
||||
if (reinterpret_cast<OakEngineNode *>(it2->get_connected_viewer()) == node) {
|
||||
existing = it2;
|
||||
break;
|
||||
}
|
||||
@@ -338,7 +341,7 @@ void MainWindow::open_node_in_viewer(ViewerOutput *node)
|
||||
connect(viewer, &ViewerPanel::close_requested, this,
|
||||
&MainWindow::viewer_close_requested);
|
||||
auto sub = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(node),
|
||||
node,
|
||||
OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH);
|
||||
removed_from_graph_subs_[node] = sub;
|
||||
}
|
||||
@@ -405,7 +408,7 @@ void MainWindow::toggle_maximized_panel()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::set_project(Project *p)
|
||||
void MainWindow::set_project(OakEngineProject *p)
|
||||
{
|
||||
if (project_ == p) {
|
||||
return;
|
||||
@@ -413,19 +416,22 @@ void MainWindow::set_project(Project *p)
|
||||
|
||||
if (project_) {
|
||||
// Clear all data
|
||||
param_panel_->set_contexts(QVector<Node *>());
|
||||
node_panel_->set_contexts(QVector<Node *>());
|
||||
param_panel_->set_contexts(QVector<oak::Node>());
|
||||
node_panel_->set_contexts(QVector<oak::Node>());
|
||||
|
||||
// Close any nodes open in TimeBasedWidgets
|
||||
foreach (PanelWidget *panel, PanelManager::instance()->panels()) {
|
||||
TimeBasedPanel *tbp = dynamic_cast<TimeBasedPanel *>(panel);
|
||||
|
||||
if (tbp && tbp->get_connected_viewer() &&
|
||||
tbp->get_connected_viewer()->project() == project_) {
|
||||
oak::Node(reinterpret_cast<OakEngineNode *>(
|
||||
tbp->get_connected_viewer()))
|
||||
.project()
|
||||
.handle() == project_) {
|
||||
if (dynamic_cast<TimelinePanel *>(tbp)) {
|
||||
// Prefer our CloseSequence function which will delete any unnecessary timeline panels
|
||||
close_sequence(
|
||||
static_cast<Sequence *>(tbp->get_connected_viewer()));
|
||||
reinterpret_cast<OakEngineNode *>(tbp->get_connected_viewer()));
|
||||
} else {
|
||||
tbp->disconnect_viewer_node();
|
||||
}
|
||||
@@ -444,7 +450,7 @@ void MainWindow::set_project(Project *p)
|
||||
}
|
||||
|
||||
project_ = p;
|
||||
project_panel_->set_project(p);
|
||||
project_panel_->set_project(oak::Project(p));
|
||||
|
||||
if (project_) {
|
||||
project_panel_->setFocus(Qt::OtherFocusReason);
|
||||
@@ -486,7 +492,7 @@ void MainWindow::set_application_progress_value(int value)
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::select_footage(const QVector<Footage *> &e)
|
||||
void MainWindow::select_footage(const QVector<OakEngineNode *> &e)
|
||||
{
|
||||
select_footage_for_project_panel(e, project_panel_);
|
||||
for (ProjectPanel *p : folder_panels_) {
|
||||
@@ -558,12 +564,7 @@ void MainWindow::timeline_panel_selection_changed(const QVector<OakEngineBlock *
|
||||
|
||||
if (PanelManager::instance()->currently_focused(false) == panel) {
|
||||
update_node_panel_context_from_timeline_panel(panel);
|
||||
QVector<Block *> native_blocks;
|
||||
native_blocks.reserve(blocks.size());
|
||||
for (OakEngineBlock *b : blocks) {
|
||||
native_blocks.append(reinterpret_cast<Block *>(b));
|
||||
}
|
||||
sequence_viewer_panel_->set_timeline_selected_blocks(native_blocks);
|
||||
sequence_viewer_panel_->set_timeline_selected_blocks(blocks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,9 +581,9 @@ void MainWindow::reveal_viewer_in_project(OakEngineNode *r)
|
||||
// Rather than just using the resident ProjectPanel, find the most recently focused one since
|
||||
// that's probably the one people will want
|
||||
auto panels = PanelManager::instance()->get_panels_of_type<ProjectPanel>();
|
||||
ViewerOutput *viewer = reinterpret_cast<ViewerOutput *>(r);
|
||||
oak::Node node(r);
|
||||
foreach (ProjectPanel *p, panels) {
|
||||
if (p->select_item(viewer)) {
|
||||
if (p->select_item(node)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -591,20 +592,18 @@ void MainWindow::reveal_viewer_in_project(OakEngineNode *r)
|
||||
void MainWindow::reveal_viewer_in_footage_viewer(OakEngineNode *r,
|
||||
const TimeRange &range)
|
||||
{
|
||||
ViewerOutput *viewer = reinterpret_cast<ViewerOutput *>(r);
|
||||
|
||||
footage_viewer_panel_->connect_viewer_node(viewer);
|
||||
footage_viewer_panel_->connect_viewer_node(r);
|
||||
|
||||
auto command = oakengine_undo_command_create_multi();
|
||||
OakEngineWorkarea *wa = reinterpret_cast<OakEngineWorkarea *>(viewer->get_work_area());
|
||||
if (!viewer->get_work_area()->enabled()) {
|
||||
OakEngineWorkarea *wa = oakengine_viewer_get_workarea_handle(r);
|
||||
int64_t old_in_num, old_in_den, old_out_num, old_out_den;
|
||||
int old_enabled;
|
||||
oakengine_workarea_get(wa, &old_in_num, &old_in_den,
|
||||
&old_out_num, &old_out_den, &old_enabled);
|
||||
if (!old_enabled) {
|
||||
oakengine_workarea_set_enabled_undoable(wa, 1, command);
|
||||
}
|
||||
{
|
||||
int64_t old_in_num, old_in_den, old_out_num, old_out_den;
|
||||
int old_enabled;
|
||||
oakengine_workarea_get(wa, &old_in_num, &old_in_den,
|
||||
&old_out_num, &old_out_den, &old_enabled);
|
||||
oakengine_workarea_set_range_undoable(wa,
|
||||
range.in().numerator(), range.in().denominator(),
|
||||
range.out().numerator(), range.out().denominator(),
|
||||
@@ -669,11 +668,10 @@ void MainWindow::viewer_close_requested()
|
||||
|
||||
void MainWindow::viewer_with_panel_removed_from_graph(OakEngineNode *source)
|
||||
{
|
||||
ViewerOutput *vo = reinterpret_cast<ViewerOutput *>(source);
|
||||
ViewerPanel *panel = nullptr;
|
||||
|
||||
foreach (ViewerPanel *p, viewer_panels_) {
|
||||
if (p->get_connected_viewer() == vo) {
|
||||
if (p->get_connected_viewer() == source) {
|
||||
panel = p;
|
||||
break;
|
||||
}
|
||||
@@ -682,7 +680,7 @@ void MainWindow::viewer_with_panel_removed_from_graph(OakEngineNode *source)
|
||||
if (panel) {
|
||||
remove_panel_internal(viewer_panels_, panel);
|
||||
panel->deleteLater();
|
||||
auto it = removed_from_graph_subs_.find(vo);
|
||||
auto it = removed_from_graph_subs_.find(source);
|
||||
if (it != removed_from_graph_subs_.end()) {
|
||||
bridge_->unsubscribe(it.value());
|
||||
removed_from_graph_subs_.erase(it);
|
||||
@@ -741,7 +739,7 @@ void MainWindow::remove_timeline_panel(TimelinePanel *panel)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::timeline_focused(ViewerOutput *viewer)
|
||||
void MainWindow::timeline_focused(OakEngineNode *viewer)
|
||||
{
|
||||
sequence_viewer_panel_->connect_viewer_node(viewer);
|
||||
multicam_panel_->connect_viewer_node(viewer);
|
||||
@@ -750,7 +748,7 @@ void MainWindow::timeline_focused(ViewerOutput *viewer)
|
||||
|
||||
// Update the global status bar info chips (resolution + frame rate)
|
||||
if (viewer) {
|
||||
VideoParams vp = viewer_output_video_params(viewer);
|
||||
oak::VideoParams vp = viewer_output_video_params(viewer);
|
||||
status_bar_->set_sequence_info(vp.width(), vp.height(),
|
||||
vp.frame_rate().to_double());
|
||||
} else {
|
||||
@@ -861,7 +859,7 @@ void MainWindow::save_custom_shortcuts()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::update_audio_monitor_params(ViewerOutput *viewer)
|
||||
void MainWindow::update_audio_monitor_params(OakEngineNode *viewer)
|
||||
{
|
||||
if (!audio_monitor_panel_->is_playing()) {
|
||||
audio_monitor_panel_->set_params(viewer ? viewer_output_audio_params(viewer) :
|
||||
@@ -872,29 +870,30 @@ void MainWindow::update_audio_monitor_params(ViewerOutput *viewer)
|
||||
void MainWindow::update_node_panel_context_from_timeline_panel(TimelinePanel *panel)
|
||||
{
|
||||
// Add selected blocks (if any)
|
||||
const QVector<Block *> &blocks = panel->get_selected_blocks();
|
||||
QVector<Node *> context(blocks.size());
|
||||
const QVector<OakEngineBlock *> blocks = panel->get_selected_blocks();
|
||||
QVector<oak::Node> context(blocks.size());
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
context[i] = blocks.at(i);
|
||||
context[i] = oak::Node(reinterpret_cast<OakEngineNode *>(blocks.at(i)));
|
||||
}
|
||||
|
||||
// If no selected blocks, set the context to the sequence
|
||||
ViewerOutput *viewer = panel->get_connected_viewer();
|
||||
OakEngineNode *viewer = panel->get_connected_viewer();
|
||||
if (viewer && context.isEmpty()) {
|
||||
context.append(viewer);
|
||||
context.append(oak::Node(viewer));
|
||||
}
|
||||
|
||||
node_panel_->set_contexts(context);
|
||||
param_panel_->set_contexts(context);
|
||||
}
|
||||
|
||||
void MainWindow::select_footage_for_project_panel(const QVector<Footage *> &e,
|
||||
void MainWindow::select_footage_for_project_panel(const QVector<OakEngineNode *> &e,
|
||||
ProjectPanel *p)
|
||||
{
|
||||
p->deselect_all();
|
||||
for (Footage *f : e) {
|
||||
if (p->get_root()->has_child_recursive(f)) {
|
||||
p->select_item(f, false);
|
||||
for (OakEngineNode *f : e) {
|
||||
oak::Node node(f);
|
||||
if (p->get_root().has_child_recursive(node)) {
|
||||
p->select_item(node, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -908,7 +907,7 @@ void MainWindow::focused_panel_changed(PanelWidget *panel)
|
||||
|
||||
if (NodePanel *node_panel = dynamic_cast<NodePanel *>(panel)) {
|
||||
// Set param view contexts to these
|
||||
const QVector<Node *> &new_ctxs = node_panel->get_contexts();
|
||||
const QVector<oak::Node> &new_ctxs = node_panel->get_contexts();
|
||||
|
||||
if (new_ctxs != param_panel_->get_contexts()) {
|
||||
param_panel_->set_contexts(new_ctxs);
|
||||
|
||||
Reference in New Issue
Block a user