From 12ebf08575cedb4fda62301d073c1014644bad2e Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Fri, 29 Nov 2024 16:55:13 +0000 Subject: [PATCH 1/6] Timeline arrow key navigation Add inital support for left and right arrow key navigation in the timeline. It was necessary to turn off the default navigation as it was interacting badly. Signed-off-by: Thomas Wilshaw --- app.h | 2 ++ main_glfw.cpp | 2 +- main_win32.cpp | 2 +- timeline.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/app.h b/app.h index 8522e73..0818dfd 100644 --- a/app.h +++ b/app.h @@ -92,6 +92,8 @@ struct AppState { float track_height = 30.0f; // current track height (pixels) otio::RationalTime playhead; bool scroll_to_playhead = false; // temporary flag, only true until next frame + bool scroll_left = false; // temporary flag, only true until next frame + bool scroll_right = false; // temporary flag, only true until next frame otio::TimeRange playhead_limit; // min/max limit for moving the playhead, auto-calculated float zebra_factor = 0.1; // opacity of the per-frame zebra stripes diff --git a/main_glfw.cpp b/main_glfw.cpp index e974208..a5e7830 100644 --- a/main_glfw.cpp +++ b/main_glfw.cpp @@ -121,7 +121,7 @@ int main(int argc, char** argv) ImGui::CreateContext(); ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows diff --git a/main_win32.cpp b/main_win32.cpp index 1cb1924..a31632b 100644 --- a/main_win32.cpp +++ b/main_win32.cpp @@ -87,7 +87,7 @@ int main(int argc, char** argv) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows diff --git a/timeline.cpp b/timeline.cpp index ccddf0e..f09233c 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -119,6 +119,23 @@ void DrawItem( ImGui::BeginGroup(); ImGui::InvisibleButton("##Item", size); + + // Don't skip invisible item if it is the item we have just selected + if (!ImGui::IsItemVisible() + && appState.selected_object == item + && (appState.scroll_left || appState.scroll_right)) { + + otio::Item* selected_item = dynamic_cast(appState.selected_object); + if (appState.scroll_right) { + ImGui::SetScrollX( ImGui::GetScrollX() + + selected_item->range_in_parent().duration().to_seconds() * scale); + appState.scroll_right = false; + } + if (appState.scroll_left) { + ImGui::SetScrollX(selected_item->range_in_parent().start_time().to_seconds() * scale); + appState.scroll_left = false; + } + } if (!ImGui::IsItemVisible()) { // exit early if this item is off-screen ImGui::EndGroup(); @@ -290,6 +307,24 @@ void DrawTransition( ImVec2 p0 = ImGui::GetItemRectMin(); ImVec2 p1 = ImGui::GetItemRectMax(); + + // Don't skip invisible item if it is the item we have just selected + if (!ImGui::IsItemVisible() + && appState.selected_object == transition + && (appState.scroll_left || appState.scroll_right)) { + + otio::Transition* selected_item = dynamic_cast(appState.selected_object); + if (appState.scroll_right) { + ImGui::SetScrollX( ImGui::GetScrollX() + + selected_item->range_in_parent()->duration().to_seconds() * scale); + appState.scroll_right = false; + } + if (appState.scroll_left) { + ImGui::SetScrollX( selected_item->range_in_parent()->start_time().to_seconds() * scale); + appState.scroll_left = false; + } + } + if (!ImGui::IsRectVisible(p0, p1)) { ImGui::EndGroup(); ImGui::PopID(); @@ -1490,6 +1525,45 @@ void DrawTimeline(otio::Timeline* timeline) { appState.scroll_to_playhead = false; } + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_RightArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Loop through selected items parent track to find the next item + auto parent = dynamic_cast(appState.selected_object)->parent(); + for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ + // If last item then do nothing + if (std::next(it) == parent->children().end()) { + break; + } + if (*it == appState.selected_object) { + std::advance(it, 1); + SelectObject(*it); + appState.scroll_right = true; + break; + } + } + } + } + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_LeftArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Loop through selected items parent track to find the previous item + auto parent = dynamic_cast(appState.selected_object)->parent(); + for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ + if (*it == appState.selected_object) { + // If first item do nothin + if (it == parent->children().begin()) { + break; + } + std::advance(it, -1); + SelectObject(*it); + appState.scroll_left = true; + break; + } + } + } + } + // This is helpful when debugging visibility performance optimization // ImGui::SetTooltip("Tracks rendered: %d\nItems rendered: %d", // __tracks_rendered, __items_rendered); From adb2a3feda9d50fd33e96c0249d6a8fc90068103 Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Mon, 2 Dec 2024 17:21:49 +0000 Subject: [PATCH 2/6] timeline: Add up arrow support Signed-off-by: Thomas Wilshaw --- timeline.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/timeline.cpp b/timeline.cpp index f09233c..9f68e96 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -1544,6 +1544,7 @@ void DrawTimeline(otio::Timeline* timeline) { } } } + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_LeftArrow)) { std::string selected_type = appState.selected_object->schema_name(); if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { @@ -1551,7 +1552,7 @@ void DrawTimeline(otio::Timeline* timeline) { auto parent = dynamic_cast(appState.selected_object)->parent(); for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ if (*it == appState.selected_object) { - // If first item do nothin + // If first item do nothing if (it == parent->children().begin()) { break; } @@ -1564,6 +1565,45 @@ void DrawTimeline(otio::Timeline* timeline) { } } + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Finding start time varies depending on object type + otio::RationalTime start_time; + if (selected_type == "Clip" || selected_type == "Gap") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().start_time(); + } else if (selected_type == "Transition") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().value().start_time(); + } + + auto parent = dynamic_cast(appState.selected_object)->parent(); + auto tracks = dynamic_cast(parent->parent()); + + // Loop through tracks until we find the current one + for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ + otio::Composable* track = *it; + if (track == parent) { + // If last item then do nothing + if (std::next(it) == tracks->children().end()) { + break; + } + // Select the next track up + std::advance(it, 1); + otio::Composable* next_track = *it; + + // If there is an iten that overlaps with the current selection start time + // select it + if (dynamic_cast(next_track)->child_at_time(start_time)){ + SelectObject(dynamic_cast(next_track)->child_at_time(start_time)); + break; + } + } + } + } + } + // This is helpful when debugging visibility performance optimization // ImGui::SetTooltip("Tracks rendered: %d\nItems rendered: %d", // __tracks_rendered, __items_rendered); From 0d5a0a560b0bcbcf1f787300c091c1c23987ecdd Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Mon, 2 Dec 2024 17:39:06 +0000 Subject: [PATCH 3/6] timeline: wrap keyboard commands in IsWindowFocused() Signed-off-by: Thomas Wilshaw --- timeline.cpp | 132 ++++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/timeline.cpp b/timeline.cpp index 9f68e96..0032f91 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -1525,83 +1525,87 @@ void DrawTimeline(otio::Timeline* timeline) { appState.scroll_to_playhead = false; } - if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_RightArrow)) { - std::string selected_type = appState.selected_object->schema_name(); - if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { - // Loop through selected items parent track to find the next item - auto parent = dynamic_cast(appState.selected_object)->parent(); - for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ - // If last item then do nothing - if (std::next(it) == parent->children().end()) { - break; - } - if (*it == appState.selected_object) { - std::advance(it, 1); - SelectObject(*it); - appState.scroll_right = true; - break; + if (ImGui::IsWindowFocused()){ + // Right arrow + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_RightArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Loop through selected items parent track to find the next item + auto parent = dynamic_cast(appState.selected_object)->parent(); + for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ + // If last item then do nothing + if (std::next(it) == parent->children().end()) { + break; + } + if (*it == appState.selected_object) { + std::advance(it, 1); + SelectObject(*it); + appState.scroll_right = true; + break; + } } } } - } - - if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_LeftArrow)) { - std::string selected_type = appState.selected_object->schema_name(); - if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { - // Loop through selected items parent track to find the previous item - auto parent = dynamic_cast(appState.selected_object)->parent(); - for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ - if (*it == appState.selected_object) { - // If first item do nothing - if (it == parent->children().begin()) { + // Left Arrow + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_LeftArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Loop through selected items parent track to find the previous item + auto parent = dynamic_cast(appState.selected_object)->parent(); + for(auto it = parent->children().begin(); it != parent->children().end(); it++ ){ + if (*it == appState.selected_object) { + // If first item do nothing + if (it == parent->children().begin()) { + break; + } + std::advance(it, -1); + SelectObject(*it); + appState.scroll_left = true; break; } - std::advance(it, -1); - SelectObject(*it); - appState.scroll_left = true; - break; } } } - } - - if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow)) { - std::string selected_type = appState.selected_object->schema_name(); - if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { - // Finding start time varies depending on object type - otio::RationalTime start_time; - if (selected_type == "Clip" || selected_type == "Gap") { - auto child = dynamic_cast(appState.selected_object); - start_time = child->range_in_parent().start_time(); - } else if (selected_type == "Transition") { - auto child = dynamic_cast(appState.selected_object); - start_time = child->range_in_parent().value().start_time(); - } - - auto parent = dynamic_cast(appState.selected_object)->parent(); - auto tracks = dynamic_cast(parent->parent()); + // Up Arrow + if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow)) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Finding start time varies depending on object type + otio::RationalTime start_time; + if (selected_type == "Clip" || selected_type == "Gap") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().start_time(); + } else if (selected_type == "Transition") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().value().start_time(); + } - // Loop through tracks until we find the current one - for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ - otio::Composable* track = *it; - if (track == parent) { - // If last item then do nothing - if (std::next(it) == tracks->children().end()) { - break; - } - // Select the next track up - std::advance(it, 1); - otio::Composable* next_track = *it; - - // If there is an iten that overlaps with the current selection start time - // select it - if (dynamic_cast(next_track)->child_at_time(start_time)){ - SelectObject(dynamic_cast(next_track)->child_at_time(start_time)); - break; + auto parent = dynamic_cast(appState.selected_object)->parent(); + auto tracks = dynamic_cast(parent->parent()); + + // Loop through tracks until we find the current one + for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ + otio::Composable* track = *it; + if (track == parent) { + // If last item then do nothing + if (std::next(it) == tracks->children().end()) { + break; + } + // Select the next track up + std::advance(it, 1); + otio::Composable* next_track = *it; + + // If there is an iten that overlaps with the current selection start time + // select it + if (dynamic_cast(next_track)->child_at_time(start_time)){ + SelectObject(dynamic_cast(next_track)->child_at_time(start_time)); + break; + } } } } } + } // This is helpful when debugging visibility performance optimization From f52b918f1f49bd9ec7542c26132899c246e0fa09 Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Mon, 2 Dec 2024 17:49:13 +0000 Subject: [PATCH 4/6] Video up and Audio down use same logic Signed-off-by: Thomas Wilshaw --- timeline.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/timeline.cpp b/timeline.cpp index 0032f91..1de7f77 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -1566,8 +1566,11 @@ void DrawTimeline(otio::Timeline* timeline) { } } } - // Up Arrow - if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow)) { + // Up arrow for Video tracks and down arrow for Audio tracks use the same logic + auto parent = dynamic_cast(appState.selected_object)->parent(); + auto selected_track = dynamic_cast(parent); + if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track->kind() == "Video") || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track->kind() == "Audio")) { std::string selected_type = appState.selected_object->schema_name(); if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { // Finding start time varies depending on object type From 7665ea0bf678212cf159ebd0735154c9be1de10f Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Mon, 2 Dec 2024 18:29:16 +0000 Subject: [PATCH 5/6] Improve logic and add down arrow support Signed-off-by: Thomas Wilshaw --- timeline.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/timeline.cpp b/timeline.cpp index 1de7f77..9bacb4e 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -1566,11 +1566,16 @@ void DrawTimeline(otio::Timeline* timeline) { } } } - // Up arrow for Video tracks and down arrow for Audio tracks use the same logic + // The Stacks of video and audio Tracks go in opposite directions + // therefore the logic for the for the Up Arrow on Video tracks is + // the same as the logic for Down Arrow on Audio tracks and vice versa + auto parent = dynamic_cast(appState.selected_object)->parent(); auto selected_track = dynamic_cast(parent); - if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track->kind() == "Video") || - (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track->kind() == "Audio")) { + std::string selected_track_type = selected_track->kind(); + + if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Video") || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Audio")) { std::string selected_type = appState.selected_object->schema_name(); if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { // Finding start time varies depending on object type @@ -1596,12 +1601,64 @@ void DrawTimeline(otio::Timeline* timeline) { } // Select the next track up std::advance(it, 1); - otio::Composable* next_track = *it; + otio::Composable* next_it = *it; + otio::Track* next_track = dynamic_cast(next_it); + + // Only iterate over tracks of the same kind + if(next_track->kind() != selected_track_type){ + break; + } + + // If there is an iten that overlaps with the current selection start time + // select it + if (next_track->child_at_time(start_time)){ + SelectObject(next_track->child_at_time(start_time)); + break; + } + } + } + } + } + + if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Video") || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Audio")) { + std::string selected_type = appState.selected_object->schema_name(); + if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Finding start time varies depending on object type + otio::RationalTime start_time; + if (selected_type == "Clip" || selected_type == "Gap") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().start_time(); + } else if (selected_type == "Transition") { + auto child = dynamic_cast(appState.selected_object); + start_time = child->range_in_parent().value().start_time(); + } + + auto parent = dynamic_cast(appState.selected_object)->parent(); + auto tracks = dynamic_cast(parent->parent()); + + // Loop through tracks until we find the current one + for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ + otio::Composable* track = *it; + if (track == parent) { + // If last item then do nothing + if (it == tracks->children().begin()) { + break; + } + // Select the next track up + std::advance(it, -1); + otio::Composable* next_it = *it; + otio::Track* next_track = dynamic_cast(next_it); + + // Only iterate over tracks of the same kind + if(next_track->kind() != selected_track_type){ + break; + } // If there is an iten that overlaps with the current selection start time // select it - if (dynamic_cast(next_track)->child_at_time(start_time)){ - SelectObject(dynamic_cast(next_track)->child_at_time(start_time)); + if (next_track->child_at_time(start_time)){ + SelectObject(next_track->child_at_time(start_time)); break; } } From 63a6fb1692c35d6e64eb2e878578832952d96309 Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Mon, 2 Dec 2024 18:46:15 +0000 Subject: [PATCH 6/6] Combine up and down arrow code Signed-off-by: Thomas Wilshaw --- timeline.cpp | 80 ++++++++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/timeline.cpp b/timeline.cpp index 9bacb4e..42595fe 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -1566,6 +1566,7 @@ void DrawTimeline(otio::Timeline* timeline) { } } } + // The Stacks of video and audio Tracks go in opposite directions // therefore the logic for the for the Up Arrow on Video tracks is // the same as the logic for Down Arrow on Audio tracks and vice versa @@ -1574,56 +1575,13 @@ void DrawTimeline(otio::Timeline* timeline) { auto selected_track = dynamic_cast(parent); std::string selected_track_type = selected_track->kind(); - if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Video") || - (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Audio")) { - std::string selected_type = appState.selected_object->schema_name(); - if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { - // Finding start time varies depending on object type - otio::RationalTime start_time; - if (selected_type == "Clip" || selected_type == "Gap") { - auto child = dynamic_cast(appState.selected_object); - start_time = child->range_in_parent().start_time(); - } else if (selected_type == "Transition") { - auto child = dynamic_cast(appState.selected_object); - start_time = child->range_in_parent().value().start_time(); - } - - auto parent = dynamic_cast(appState.selected_object)->parent(); - auto tracks = dynamic_cast(parent->parent()); + if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow)) || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow))) { - // Loop through tracks until we find the current one - for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ - otio::Composable* track = *it; - if (track == parent) { - // If last item then do nothing - if (std::next(it) == tracks->children().end()) { - break; - } - // Select the next track up - std::advance(it, 1); - otio::Composable* next_it = *it; - otio::Track* next_track = dynamic_cast(next_it); - - // Only iterate over tracks of the same kind - if(next_track->kind() != selected_track_type){ - break; - } - - // If there is an iten that overlaps with the current selection start time - // select it - if (next_track->child_at_time(start_time)){ - SelectObject(next_track->child_at_time(start_time)); - break; - } - } - } - } - } - - if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Video") || - (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Audio")) { + // Only run if the right type is selected std::string selected_type = appState.selected_object->schema_name(); if (selected_type == "Clip" || selected_type == "Gap" || selected_type == "Transition") { + // Finding start time varies depending on object type otio::RationalTime start_time; if (selected_type == "Clip" || selected_type == "Gap") { @@ -1634,6 +1592,7 @@ void DrawTimeline(otio::Timeline* timeline) { start_time = child->range_in_parent().value().start_time(); } + // Get the parent object and track stack auto parent = dynamic_cast(appState.selected_object)->parent(); auto tracks = dynamic_cast(parent->parent()); @@ -1641,12 +1600,29 @@ void DrawTimeline(otio::Timeline* timeline) { for(auto it = tracks->children().begin(); it != tracks->children().end(); it++ ){ otio::Composable* track = *it; if (track == parent) { - // If last item then do nothing - if (it == tracks->children().begin()) { + // Down Arrow and Video or Up Arrow and Audio + if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Video") || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Audio")) { + // If first item then do nothing + if (it == tracks->children().begin()) { + break; + } + // Select the next track up + std::advance(it, -1); + + // Up Arrow and Video or Down Arrow and Audio + } else if ((ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_UpArrow) && selected_track_type == "Video") || + (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_DownArrow) && selected_track_type == "Audio")) { + // If last item then do nothing + if (std::next(it) == tracks->children().end()) { + break; + } + // Select the next track up + std::advance(it, 1); + } else{ break; } - // Select the next track up - std::advance(it, -1); + otio::Composable* next_it = *it; otio::Track* next_track = dynamic_cast(next_it); @@ -1655,7 +1631,7 @@ void DrawTimeline(otio::Timeline* timeline) { break; } - // If there is an iten that overlaps with the current selection start time + // If there is an iten that overlaps with the current selection's start time // select it if (next_track->child_at_time(start_time)){ SelectObject(next_track->child_at_time(start_time));