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:
@@ -49,9 +49,15 @@ void CurvePanel::deselect_all()
|
||||
static_cast<CurveWidget *>(get_time_based_widget())->deselect_all();
|
||||
}
|
||||
|
||||
void CurvePanel::set_nodes(const QVector<Node *> &nodes)
|
||||
void CurvePanel::set_nodes(const QVector<OakEngineNode *> &nodes)
|
||||
{
|
||||
static_cast<CurveWidget *>(get_time_based_widget())->set_nodes(nodes);
|
||||
// Convert to oak::Node for the curve widget (C ABI wrapper layer)
|
||||
QVector<oak::Node> oak_nodes;
|
||||
oak_nodes.reserve(nodes.size());
|
||||
foreach (OakEngineNode *n, nodes) {
|
||||
oak_nodes.append(oak::Node(n));
|
||||
}
|
||||
static_cast<CurveWidget *>(get_time_based_widget())->set_nodes(oak_nodes);
|
||||
}
|
||||
|
||||
void CurvePanel::increase_track_height()
|
||||
|
||||
@@ -43,20 +43,17 @@ public slots:
|
||||
void set_node(OakEngineNode *node)
|
||||
{
|
||||
// Convert single pointer to either an empty vector or a vector of one
|
||||
QVector<Node *> nodes;
|
||||
QVector<OakEngineNode *> nodes;
|
||||
|
||||
if (node) {
|
||||
nodes.append(reinterpret_cast<Node *>(node));
|
||||
nodes.append(node);
|
||||
}
|
||||
|
||||
set_nodes(nodes);
|
||||
}
|
||||
|
||||
public:
|
||||
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||
// boundary). All connections use new-style member-function syntax.
|
||||
void set_nodes(const QVector<Node *> &nodes);
|
||||
void set_nodes(const QVector<OakEngineNode *> &nodes);
|
||||
|
||||
public slots:
|
||||
virtual void increase_track_height() override;
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
return node_widget_;
|
||||
}
|
||||
|
||||
const QVector<Node *> &get_contexts() const
|
||||
const QVector<oak::Node> &get_contexts() const
|
||||
{
|
||||
return node_widget_->view()->get_contexts();
|
||||
}
|
||||
@@ -53,12 +53,12 @@ public:
|
||||
return node_widget_->view()->is_group_overlay();
|
||||
}
|
||||
|
||||
void set_contexts(const QVector<Node *> &nodes)
|
||||
void set_contexts(const QVector<oak::Node> &nodes)
|
||||
{
|
||||
node_widget_->set_contexts(nodes);
|
||||
}
|
||||
|
||||
void close_contexts_belonging_to_project(Project *project)
|
||||
void close_contexts_belonging_to_project(oak::Project project)
|
||||
{
|
||||
node_widget_->view()->close_contexts_belonging_to_project(project);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ void ParamPanel::deselect_all()
|
||||
static_cast<NodeParamView *>(get_time_based_widget())->deselect_all();
|
||||
}
|
||||
|
||||
void ParamPanel::set_contexts(const QVector<Node *> &contexts)
|
||||
void ParamPanel::set_contexts(const QVector<oak::Node> &contexts)
|
||||
{
|
||||
static_cast<NodeParamView *>(get_time_based_widget())->set_contexts(contexts);
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ public:
|
||||
return static_cast<NodeParamView *>(get_time_based_widget());
|
||||
}
|
||||
|
||||
const QVector<Node *> &get_contexts() const
|
||||
const QVector<oak::Node> &get_contexts() const
|
||||
{
|
||||
return get_param_view()->get_contexts();
|
||||
}
|
||||
|
||||
void close_contexts_belonging_to_project(Project *p)
|
||||
void close_contexts_belonging_to_project(oak::Project p)
|
||||
{
|
||||
get_param_view()->close_contexts_belonging_to_project(p);
|
||||
}
|
||||
@@ -63,10 +63,9 @@ public slots:
|
||||
virtual void deselect_all() override;
|
||||
|
||||
public:
|
||||
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||
// boundary). All connections use new-style member-function syntax.
|
||||
void set_contexts(const QVector<Node *> &contexts);
|
||||
// Not a slot: mirrors NodePanel::set_contexts(), which is also a plain
|
||||
// member function. All connections use new-style member-function syntax.
|
||||
void set_contexts(const QVector<oak::Node> &contexts);
|
||||
|
||||
signals:
|
||||
void focused_node_changed(OakEngineNode *n);
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "core.h"
|
||||
#include "oakengine/events.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "panel/footageviewer/footageviewer.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
#include "panel/panelmanager.h"
|
||||
@@ -89,12 +88,12 @@ ProjectPanel::ProjectPanel(const QString &unique_name)
|
||||
retranslate();
|
||||
}
|
||||
|
||||
Project *ProjectPanel::project() const
|
||||
oak::Project ProjectPanel::project() const
|
||||
{
|
||||
return explorer_->project();
|
||||
}
|
||||
|
||||
void ProjectPanel::set_project(Project *p)
|
||||
void ProjectPanel::set_project(oak::Project p)
|
||||
{
|
||||
if (project()) {
|
||||
if (project_name_sub_ > 0) {
|
||||
@@ -106,9 +105,8 @@ void ProjectPanel::set_project(Project *p)
|
||||
explorer_->set_project(p);
|
||||
|
||||
if (project()) {
|
||||
auto *ph = reinterpret_cast<OakEngineProject *>(project());
|
||||
project_name_sub_ = oakengine_event_subscribe(
|
||||
ph, OAKENGINE_EVENT_PROJECT_NAME_CHANGED,
|
||||
project().handle(), OAKENGINE_EVENT_PROJECT_NAME_CHANGED,
|
||||
[](const oakengine_event *, void *userdata) {
|
||||
auto *s = static_cast<ProjectPanel *>(userdata);
|
||||
s->update_subtitle();
|
||||
@@ -122,24 +120,24 @@ void ProjectPanel::set_project(Project *p)
|
||||
emit project_name_changed();
|
||||
}
|
||||
|
||||
Folder *ProjectPanel::get_root() const
|
||||
oak::Node ProjectPanel::get_root() const
|
||||
{
|
||||
return explorer_->get_root();
|
||||
}
|
||||
|
||||
void ProjectPanel::set_root(Folder *item)
|
||||
void ProjectPanel::set_root(oak::Node item)
|
||||
{
|
||||
explorer_->set_root(item);
|
||||
|
||||
retranslate();
|
||||
}
|
||||
|
||||
QVector<Node *> ProjectPanel::selected_items() const
|
||||
QVector<oak::Node> ProjectPanel::selected_items() const
|
||||
{
|
||||
return explorer_->selected_items();
|
||||
}
|
||||
|
||||
Folder *ProjectPanel::get_selected_folder() const
|
||||
oak::Node ProjectPanel::get_selected_folder() const
|
||||
{
|
||||
return explorer_->get_selected_folder();
|
||||
}
|
||||
@@ -176,7 +174,7 @@ void ProjectPanel::edit(OakEngineNode *item)
|
||||
|
||||
void ProjectPanel::retranslate()
|
||||
{
|
||||
if (project() && explorer_->get_root() != project()->root()) {
|
||||
if (project() && explorer_->get_root() != project().root()) {
|
||||
set_title(tr("Folder"));
|
||||
} else {
|
||||
set_title(tr("Project"));
|
||||
@@ -187,21 +185,20 @@ void ProjectPanel::retranslate()
|
||||
|
||||
void ProjectPanel::item_double_click_slot(OakEngineNode *item_handle)
|
||||
{
|
||||
Node *item = reinterpret_cast<Node *>(item_handle);
|
||||
oak::Node item(item_handle);
|
||||
if (item == nullptr) {
|
||||
// If the user double clicks on empty space, show the import dialog
|
||||
Core::instance()->dialog_import_show();
|
||||
} else if (dynamic_cast<Footage *>(item)) {
|
||||
} else if (item.is_footage()) {
|
||||
// Open this footage in a FootageViewer
|
||||
auto panel =
|
||||
PanelManager::instance()->most_recently_focused<FootageViewerPanel>();
|
||||
panel->connect_viewer_node(static_cast<Footage *>(item));
|
||||
panel->connect_viewer_node(item.handle());
|
||||
panel->raise();
|
||||
panel->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
} else if (dynamic_cast<Sequence *>(item)) {
|
||||
} else if (item.is_sequence()) {
|
||||
// Open this sequence in the Timeline
|
||||
Core::instance()->main_window()->open_sequence(
|
||||
static_cast<Sequence *>(item));
|
||||
Core::instance()->main_window()->open_sequence(item.handle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,23 +214,19 @@ void ProjectPanel::show_new_menu()
|
||||
void ProjectPanel::update_subtitle()
|
||||
{
|
||||
if (project()) {
|
||||
char name_buf[256];
|
||||
oakengine_project_name(
|
||||
reinterpret_cast<OakEngineProject *>(project()),
|
||||
name_buf, sizeof(name_buf));
|
||||
QString project_title = QString::fromUtf8(name_buf);
|
||||
QString project_title = project().name();
|
||||
|
||||
if (explorer_->get_root() != project()->root()) {
|
||||
if (explorer_->get_root() != project().root()) {
|
||||
QString folder_path;
|
||||
|
||||
Folder *item = explorer_->get_root();
|
||||
oak::Node item = explorer_->get_root();
|
||||
|
||||
do {
|
||||
folder_path.prepend(
|
||||
QStringLiteral("/%1").arg(item->get_label()));
|
||||
QStringLiteral("/%1").arg(item.get_label()));
|
||||
|
||||
item = item->folder();
|
||||
} while (item != project()->root());
|
||||
item = item.folder();
|
||||
} while (item != project().root());
|
||||
|
||||
project_title.append(folder_path);
|
||||
}
|
||||
@@ -251,12 +244,12 @@ void ProjectPanel::save_connected_project()
|
||||
|
||||
QVector<OakEngineNode *> ProjectPanel::get_selected_footage() const
|
||||
{
|
||||
QVector<Node *> items = selected_items();
|
||||
QVector<oak::Node> items = selected_items();
|
||||
QVector<OakEngineNode *> footage;
|
||||
|
||||
foreach (Node *i, items) {
|
||||
if (dynamic_cast<ViewerOutput *>(i)) {
|
||||
footage.append(reinterpret_cast<OakEngineNode *>(i));
|
||||
foreach (const oak::Node &i, items) {
|
||||
if (i.is_viewer_output()) {
|
||||
footage.append(i.handle());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#define OAK_PROJECT_PANEL_H
|
||||
|
||||
#include "footagemanagementpanel.h"
|
||||
#include "node/project.h"
|
||||
#include "panel/panel.h"
|
||||
#include "widget/projectexplorer/projectexplorer.h"
|
||||
|
||||
@@ -41,22 +40,22 @@ public:
|
||||
ProjectPanel(const QString &unique_name);
|
||||
~ProjectPanel() override;
|
||||
|
||||
Project *project() const;
|
||||
void set_project(Project *p);
|
||||
oak::Project project() const;
|
||||
void set_project(oak::Project p);
|
||||
|
||||
Folder *get_root() const;
|
||||
oak::Node get_root() const;
|
||||
|
||||
void set_root(Folder *item);
|
||||
void set_root(oak::Node item);
|
||||
|
||||
QVector<Node *> selected_items() const;
|
||||
QVector<oak::Node> selected_items() const;
|
||||
|
||||
Folder *get_selected_folder() const;
|
||||
oak::Node get_selected_folder() const;
|
||||
|
||||
virtual QVector<OakEngineNode *> get_selected_footage() const override;
|
||||
|
||||
ProjectViewModel *model() const;
|
||||
|
||||
bool select_item(Node *n, bool deselect_all_first = true)
|
||||
bool select_item(oak::Node n, bool deselect_all_first = true)
|
||||
{
|
||||
return explorer_->select_item(n, deselect_all_first);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ SequenceViewerPanel::SequenceViewerPanel()
|
||||
}
|
||||
|
||||
void SequenceViewerPanel::start_capture(const TimeRange &time,
|
||||
const Track::Reference &track)
|
||||
const TrackReference &track)
|
||||
{
|
||||
TimelinePanel *tp = static_cast<TimelinePanel *>(sender());
|
||||
static_cast<ViewerWidget *>(get_time_based_widget())
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef OAK_SEQUENCEVIEWERPANEL_H
|
||||
#define OAK_SEQUENCEVIEWERPANEL_H
|
||||
|
||||
#include "common/trackreferencehandle.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
|
||||
namespace olive
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
SequenceViewerPanel();
|
||||
|
||||
public slots:
|
||||
void start_capture(const TimeRange &time, const Track::Reference &track);
|
||||
void start_capture(const TimeRange &time, const TrackReference &track);
|
||||
|
||||
protected:
|
||||
virtual void retranslate() override;
|
||||
|
||||
@@ -34,15 +34,12 @@ public:
|
||||
NodeTablePanel();
|
||||
|
||||
public:
|
||||
// Not slots: signatures use the engine C++ type Node*, which must not be
|
||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||
// boundary). All connections use new-style member-function syntax.
|
||||
void select_nodes(const QVector<Node *> &nodes)
|
||||
void select_nodes(const QVector<OakEngineNode *> &nodes)
|
||||
{
|
||||
static_cast<NodeTableWidget *>(get_time_based_widget())->select_nodes(nodes);
|
||||
}
|
||||
|
||||
void deselect_nodes(const QVector<Node *> &nodes)
|
||||
void deselect_nodes(const QVector<OakEngineNode *> &nodes)
|
||||
{
|
||||
static_cast<NodeTableWidget *>(get_time_based_widget())
|
||||
->deselect_nodes(nodes);
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "timebased.h"
|
||||
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
@@ -116,7 +118,7 @@ void TimeBasedPanel::shuttle_right()
|
||||
emit shuttle_right_requested();
|
||||
}
|
||||
|
||||
void TimeBasedPanel::connect_viewer_node(ViewerOutput *node)
|
||||
void TimeBasedPanel::connect_viewer_node(OakEngineNode *node)
|
||||
{
|
||||
widget_->connect_viewer_node(node);
|
||||
}
|
||||
@@ -140,8 +142,8 @@ void TimeBasedPanel::set_time_based_widget(TimeBasedWidget *widget)
|
||||
|
||||
void TimeBasedPanel::retranslate()
|
||||
{
|
||||
if (get_time_based_widget()->get_connected_node()) {
|
||||
set_subtitle(get_time_based_widget()->get_connected_node()->get_label());
|
||||
if (get_connected_viewer()) {
|
||||
set_subtitle(oak::Node(get_connected_viewer()).get_label());
|
||||
} else {
|
||||
set_subtitle(tr("(none)"));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
|
||||
virtual ~TimeBasedPanel() override;
|
||||
|
||||
void connect_viewer_node(ViewerOutput *node);
|
||||
void connect_viewer_node(OakEngineNode *node);
|
||||
|
||||
void disconnect_viewer_node()
|
||||
{
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
// Get the timebase of this panels widget
|
||||
const Rational &timebase();
|
||||
|
||||
ViewerOutput *get_connected_viewer() const
|
||||
OakEngineNode *get_connected_viewer() const
|
||||
{
|
||||
return widget_->get_connected_node();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef OAK_TIMELINE_PANEL_H
|
||||
#define OAK_TIMELINE_PANEL_H
|
||||
|
||||
#include "common/trackreferencehandle.h"
|
||||
#include "panel/timebased/timebased.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
@@ -107,14 +108,29 @@ public:
|
||||
|
||||
void overwrite_footage_at_playhead(const QVector<OakEngineNode *> &footage);
|
||||
|
||||
const QVector<Block *> &get_selected_blocks() const
|
||||
QVector<OakEngineBlock *> get_selected_blocks() const
|
||||
{
|
||||
return timeline_widget()->get_selected_blocks();
|
||||
// TimelineWidget still exposes engine Block* (its own wave converts
|
||||
// it); re-wrap into the handle family here so callers never see the
|
||||
// engine type.
|
||||
const auto &selected = timeline_widget()->get_selected_blocks();
|
||||
QVector<OakEngineBlock *> handles;
|
||||
handles.reserve(selected.size());
|
||||
for (auto *b : selected) {
|
||||
handles.append(reinterpret_cast<OakEngineBlock *>(b));
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
Sequence *get_sequence() const
|
||||
OakEngineSequence *get_sequence() const
|
||||
{
|
||||
return dynamic_cast<Sequence *>(get_connected_viewer());
|
||||
// R8: type check via the C ABI facade predicate (replaces
|
||||
// dynamic_cast<Sequence*>, which needed the complete engine type);
|
||||
// the handle is shared, so the cast is a reinterpret.
|
||||
OakEngineNode *connected = get_connected_viewer();
|
||||
return (connected && oakengine_node_is_sequence(connected))
|
||||
? reinterpret_cast<OakEngineSequence *>(connected)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -124,7 +140,7 @@ signals:
|
||||
void block_selection_changed(const QVector<OakEngineBlock *> &selected_blocks);
|
||||
|
||||
void request_capture_start(const TimeRange &time,
|
||||
const Track::Reference &track);
|
||||
const TrackReference &track);
|
||||
|
||||
void reveal_viewer_in_project(OakEngineNode *r);
|
||||
void reveal_viewer_in_footage_viewer(OakEngineNode *r, const TimeRange &range);
|
||||
|
||||
@@ -95,7 +95,7 @@ void ViewerPanelBase::set_full_screen(QScreen *screen)
|
||||
|
||||
void ViewerPanelBase::set_gizmos(OakEngineNode *node)
|
||||
{
|
||||
get_viewer_widget()->set_gizmos(reinterpret_cast<Node *>(node));
|
||||
get_viewer_widget()->set_gizmos(node);
|
||||
}
|
||||
|
||||
void ViewerPanelBase::cache_entire_sequence()
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
get_viewer_widget()->add_playback_device(vw);
|
||||
}
|
||||
|
||||
void set_timeline_selected_blocks(const QVector<Block *> &b)
|
||||
void set_timeline_selected_blocks(const QVector<OakEngineBlock *> &b)
|
||||
{
|
||||
get_viewer_widget()->set_timeline_selected_blocks(b);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user