From 8dbc9f748c96cce20bd4e7152bd9d312f93d99d2 Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Wed, 13 Nov 2024 15:10:20 +0000 Subject: [PATCH 1/3] Handle loading none timeline schema and warn user Signed-off-by: Thomas Wilshaw --- app.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app.cpp b/app.cpp index e26e4ee..2bf0a42 100644 --- a/app.cpp +++ b/app.cpp @@ -275,9 +275,8 @@ void LoadFile(std::string path) { auto start = std::chrono::high_resolution_clock::now(); otio::ErrorStatus error_status; - auto timeline = dynamic_cast( - otio::Timeline::from_json_file(path, &error_status)); - if (!timeline || otio::is_error(error_status)) { + auto root = otio::Timeline::from_json_file(path, &error_status); + if (!root || otio::is_error(error_status)) { Message( "Error loading \"%s\": %s", path.c_str(), @@ -285,7 +284,19 @@ void LoadFile(std::string path) { return; } - LoadTimeline(timeline); + std::string file_name; + if (root->schema_name() == "Timeline") { + auto timeline = dynamic_cast(root); + LoadTimeline(timeline); + file_name = timeline->name(); + } else { + Message( + "Error loading \"%s\": Unsupported root schema: %s", + path.c_str(), + root->schema_name().c_str() + ); + return; + } appState.file_path = path; @@ -294,7 +305,7 @@ void LoadFile(std::string path) { double elapsed_seconds = elapsed.count(); Message( "Loaded \"%s\" in %.3f seconds", - timeline->name().c_str(), + file_name.c_str(), elapsed_seconds); } From 97e29b78406e0072950a5f5a46d45ae724c0693f Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Wed, 13 Nov 2024 15:51:19 +0000 Subject: [PATCH 2/3] Add single clip loading support Signed-off-by: Thomas Wilshaw --- app.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app.cpp b/app.cpp index 2bf0a42..141cca5 100644 --- a/app.cpp +++ b/app.cpp @@ -20,6 +20,8 @@ #include #include +#include + void DrawMenu(); void DrawToolbar(ImVec2 buttonSize); void DrawDroppedFilesPrompt(); @@ -289,7 +291,14 @@ void LoadFile(std::string path) { auto timeline = dynamic_cast(root); LoadTimeline(timeline); file_name = timeline->name(); - } else { + } else if (root->schema_name() == "Clip") { + auto clip = dynamic_cast(root); + file_name = clip->name(); + + auto timeline = new otio::Timeline(); + LoadTimeline(timeline); + SelectObject(clip); + } else{ Message( "Error loading \"%s\": Unsupported root schema: %s", path.c_str(), @@ -900,7 +909,7 @@ void SelectObject( appState.selected_text = "No selection"; } else { otio::ErrorStatus error_status; - appState.selected_text = object->to_json_string(&error_status); + //appState.selected_text = object->to_json_string(&error_status); if (otio::is_error(error_status)) { appState.selected_text = otio_error_string(error_status); } From 1d5c060b550a14b4c4c432915e752740b1f1857a Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Thu, 28 Nov 2024 14:37:23 +0000 Subject: [PATCH 3/3] Replace appState.timeline with appState.root We now store the root OTIO object that is opened (whatever type it is) and we handle it based on its schema type. Signed-off-by: Thomas Wilshaw --- app.cpp | 39 ++++++++++++++++++++++------------ app.h | 2 +- editing.cpp | 25 +++++++++++++++------- inspector.cpp | 58 ++++++++++++++++++++++++++++++++------------------- timeline.cpp | 2 +- 5 files changed, 82 insertions(+), 44 deletions(-) diff --git a/app.cpp b/app.cpp index 141cca5..2ef920b 100644 --- a/app.cpp +++ b/app.cpp @@ -240,7 +240,7 @@ std::string otio_error_string(otio::ErrorStatus const& error_status) { } void LoadTimeline(otio::Timeline* timeline) { - appState.timeline = timeline; + appState.root = timeline; DetectPlayheadLimits(); appState.playhead = appState.playhead_limit.start_time(); FitZoomWholeTimeline(); @@ -294,9 +294,9 @@ void LoadFile(std::string path) { } else if (root->schema_name() == "Clip") { auto clip = dynamic_cast(root); file_name = clip->name(); + appState.root = clip; auto timeline = new otio::Timeline(); - LoadTimeline(timeline); SelectObject(clip); } else{ Message( @@ -319,7 +319,7 @@ void LoadFile(std::string path) { } void SaveFile(std::string path) { - auto timeline = appState.timeline; + auto timeline = appState.root; if (!timeline) return; @@ -410,6 +410,12 @@ bool IconButton(const char* label, const ImVec2 size = ImVec2(0, 0)) { void AppUpdate() { } +void DrawRoot(){ + if (appState.root->schema_name() == "Timeline") { + DrawTimeline(dynamic_cast(appState.root)); + } +} + void MainGui() { AppUpdate(); @@ -552,12 +558,14 @@ void MainGui() { contentSize.y -= ImGui::GetTextLineHeightWithSpacing() + 7; ImGui::BeginChild("##TimelineContainer", contentSize); - DrawTimeline(appState.timeline); + DrawRoot(); ImGui::EndChild(); - if (DrawTransportControls(appState.timeline)) { - appState.scroll_to_playhead = true; + if (appState.root->schema_name() == "Timeline"){ + if (DrawTransportControls(dynamic_cast(appState.root))) { + appState.scroll_to_playhead = true; + } } } ImGui::End(); @@ -707,8 +715,8 @@ void DrawMenu() { if (ImGui::MenuItem("Revert")) { LoadFile(appState.file_path); } - if (ImGui::MenuItem("Close", NULL, false, appState.timeline)) { - appState.timeline = NULL; + if (ImGui::MenuItem("Close", NULL, false, appState.root)) { + appState.root = NULL; SelectObject(NULL); } #ifndef EMSCRIPTEN @@ -934,14 +942,19 @@ void SnapPlayhead() { } void DetectPlayheadLimits() { - const auto timeline = appState.timeline; - appState.playhead_limit = otio::TimeRange( - timeline->global_start_time().value_or(otio::RationalTime()), - timeline->duration()); + if (appState.root->schema_name() == "Timeline"){ + const auto timeline = dynamic_cast(appState.root); + appState.playhead_limit = otio::TimeRange( + timeline->global_start_time().value_or(otio::RationalTime()), + timeline->duration()); + } } void FitZoomWholeTimeline() { - appState.scale = appState.timeline_width / appState.timeline->duration().to_seconds(); + if (appState.root->schema_name() == "Timeline"){ + const auto timeline = dynamic_cast(appState.root); + appState.scale = appState.timeline_width / timeline->duration().to_seconds(); + } } // GUI utility to add dynamic height to GUI elements diff --git a/app.h b/app.h index 8522e73..a72841e 100644 --- a/app.h +++ b/app.h @@ -83,7 +83,7 @@ struct AppState { // This holds the main timeline object. // Pretty much everything drills into this one entry point. - otio::SerializableObject::Retainer timeline; + otio::SerializableObjectWithMetadata* root; // Timeline display settings float timeline_width = 100.0f; // automatically calculated (pixels) diff --git a/editing.cpp b/editing.cpp index 20335f7..12b6711 100644 --- a/editing.cpp +++ b/editing.cpp @@ -8,8 +8,8 @@ #include void DeleteSelectedObject() { - if (appState.selected_object == appState.timeline) { - appState.timeline = NULL; + if (appState.selected_object == appState.root) { + appState.root = NULL; SelectObject(NULL); return; } @@ -57,10 +57,16 @@ void DeleteSelectedObject() { void AddMarkerAtPlayhead(otio::Item* item, std::string name, std::string color) { auto playhead = appState.playhead; + + + if (!appState.root) + return; - const auto& timeline = appState.timeline; - if (!timeline) + if (appState.root->schema_name() != "Timeline"){ return; + } + + const auto& timeline = dynamic_cast(appState.root); // Default to the selected item, or the top-level timeline. if (item == NULL) { @@ -91,10 +97,15 @@ void AddMarkerAtPlayhead(otio::Item* item, std::string name, std::string color) } void AddTrack(std::string kind) { - const auto& timeline = appState.timeline; - if (!timeline) + if (!appState.root) return; + if (appState.root->schema_name() != "Timeline"){ + return; + } + + const auto& timeline = dynamic_cast(appState.root); + // Fall back to the top level stack. int insertion_index = -1; otio::Stack* stack = timeline->tracks(); @@ -150,7 +161,7 @@ void AddTrack(std::string kind) { } void FlattenTrackDown() { - const auto& timeline = appState.timeline; + const auto& timeline = appState.root; if (!timeline) { Message("Cannot flatten: No timeline."); return; diff --git a/inspector.cpp b/inspector.cpp index ec4f9ff..3b0512a 100644 --- a/inspector.cpp +++ b/inspector.cpp @@ -616,20 +616,27 @@ void DrawMarkersInspector() { typedef std::pair, otio::SerializableObject::Retainer> marker_parent_pair; std::vector pairs; - auto root = appState.timeline->tracks(); - auto global_start = appState.timeline->global_start_time().value_or(otio::RationalTime()); + auto root = new otio::Stack(); + auto global_start = otio::RationalTime(0.0); - for (const auto& marker : root->markers()) { - pairs.push_back(marker_parent_pair(marker, root)); - } + if (appState.root->schema_name() == "Timeline"){ + const auto& timeline = dynamic_cast(appState.root); - for (const auto& child : - appState.timeline->tracks()->find_children()) - { - if (const auto& item = dynamic_cast(&*child)) + root = timeline->tracks(); + global_start = timeline->global_start_time().value_or(otio::RationalTime()); + + for (const auto& marker : root->markers()) { + pairs.push_back(marker_parent_pair(marker, root)); + } + + for (const auto& child : + timeline->tracks()->find_children()) { - for (const auto& marker : item->markers()) { - pairs.push_back(marker_parent_pair(marker, item)); + if (const auto& item = dynamic_cast(&*child)) + { + for (const auto& marker : item->markers()) { + pairs.push_back(marker_parent_pair(marker, item)); + } } } } @@ -714,20 +721,27 @@ void DrawEffectsInspector() { typedef std::pair, otio::SerializableObject::Retainer> effect_parent_pair; std::vector pairs; - auto root = appState.timeline->tracks(); - auto global_start = appState.timeline->global_start_time().value_or(otio::RationalTime()); + auto root = new otio::Stack(); + auto global_start = otio::RationalTime(0.0); - for (const auto& effect : root->effects()) { - pairs.push_back(effect_parent_pair(effect, root)); - } + if (appState.root->schema_name() == "Timeline"){ + const auto& timeline = dynamic_cast(appState.root); - for (const auto& child : - appState.timeline->tracks()->find_children()) - { - if (const auto& item = dynamic_cast(&*child)) + root = timeline->tracks(); + global_start = timeline->global_start_time().value_or(otio::RationalTime()); + + for (const auto& effect : root->effects()) { + pairs.push_back(effect_parent_pair(effect, root)); + } + + for (const auto& child : + timeline->tracks()->find_children()) { - for (const auto& effect : item->effects()) { - pairs.push_back(effect_parent_pair(effect, item)); + if (const auto& item = dynamic_cast(&*child)) + { + for (const auto& effect : item->effects()) { + pairs.push_back(effect_parent_pair(effect, item)); + } } } } diff --git a/timeline.cpp b/timeline.cpp index ccddf0e..e0baf99 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -46,7 +46,7 @@ void TopLevelTimeRangeMap( std::map& range_map, otio::Item* context) { auto zero = otio::RationalTime(); - auto top = appState.timeline->tracks(); + auto top = dynamic_cast(appState.root)->tracks(); auto offset = context->transformed_time(zero, top); for (auto& pair : range_map) {