From 12ebf08575cedb4fda62301d073c1014644bad2e Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Fri, 29 Nov 2024 16:55:13 +0000 Subject: [PATCH] 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);