From 47ad5b394f7fba54fbec10fe3999c85f04f917b3 Mon Sep 17 00:00:00 2001 From: Tim Lehr Date: Thu, 14 May 2026 16:48:46 -0700 Subject: [PATCH 1/2] Upgrade ImGui to v1.92.8, ImPlot to v1.0, and C++17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Important: This commit cannot build standalone. It requires the TextEditor fork switch in the next commit due to API incompatibilities between ImGui v1.92.8 and the currently used ImGuiColorTextEdit fork by Josh Minor. The old fork uses ImGui::GetKeyIndex() which was removed in v1.91.0. These two commits are separated for review purposes (dependency upgrade vs. fork switch), but must be merged together. Updated ImGui and ImPlot libraries to latest versions to support the newer ImGuiColorTextEdit fork. This required an upgrade to C++17 for the Imgui target too. Note: Added ConfigDebugHighlightIdConflicts=false to disable ID conflict warnings introduced in ImGui v1.91.2. These are pre-existing issues with the UI from before the upgrade, not caused by the update itself. We should address the root causes in a separate MR. Changes: - ImGui: v2023 → v1.92.8-docking (b61e563, May 2026) - ImPlot: Feb 2023 → v1.0 (524f9fc) - C++ standard: 14 → 17 (required for new library versions) API changes required for ImGui v1.92.8: - SetItemAllowOverlap() → SetNextItemAllowOverlap() (moved before widget) - ImGuiSelectableFlags_AllowItemOverlap → ImGuiSelectableFlags_AllowOverlap API changes required for ImPlot v1.0: - Removed ImPlotFlags_NoChild (no longer exists) - SetNextLineStyle() → ImPlotSpec struct - DragPoint() color parameter updated Disclosure: I used Claude Sonnet 4.5 to help with the porting process. Signed-off-by: Tim Lehr --- inspector.cpp | 23 +++++++++++------------ libs/CMakeLists.txt | 2 +- libs/imgui | 2 +- libs/implot | 2 +- main_emscripten.cpp | 1 + main_glfw.cpp | 1 + main_macos.mm | 1 + main_win32.cpp | 1 + timeline.cpp | 15 +++++++-------- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/inspector.cpp b/inspector.cpp index 5b4eadc..3d13d97 100644 --- a/inspector.cpp +++ b/inspector.cpp @@ -571,7 +571,7 @@ void DrawLinearTimeWarp(otio::LinearTimeWarp* timewarp, otio::Item* item) { const ImColor knot_color = appTheme.colors[AppThemeCol_ItemSelected]; ImPlotFlags plot_flags = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoInputs - | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoChild + | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoFrame | ImPlotFlags_Equal | ImPlotFlags_None; ImPlotDragToolFlags drag_flags = ImPlotDragToolFlags_NoInputs | ImPlotDragToolFlags_None; @@ -585,35 +585,34 @@ void DrawLinearTimeWarp(otio::LinearTimeWarp* timewarp, otio::Item* item) { fmax(start->y, end->y), ImGuiCond_Always); - ImPlot::SetNextLineStyle(line_color, line_width); + ImPlotSpec line_spec; + line_spec.LineColor = line_color; + line_spec.LineWeight = line_width; + line_spec.Stride = sizeof(ImPlotPoint); ImPlot::PlotLine( "##Line", &start->x, &start->y, 2, - 0, - 0, - sizeof(ImPlotPoint)); + line_spec); // start handle - ImPlot::SetNextLineStyle(knot_color); if (ImPlot::DragPoint( 0, &start->x, &start->y, - ImVec4(0, 0.9f, 0, 1), + knot_color, knot_radius, drag_flags)) { ; } // end handle - ImPlot::SetNextLineStyle(knot_color); if (ImPlot::DragPoint( 3, &end->x, &end->y, - ImVec4(0, 0.9f, 0, 1), + knot_color, knot_radius, drag_flags)) { ; @@ -918,7 +917,7 @@ void DrawMarkersInspector() { } } - auto selectable_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap; + auto selectable_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap; if (ImGui::BeginTable("Markers", 5, @@ -1032,7 +1031,7 @@ void DrawEffectsInspector() { } } - auto selectable_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap; + auto selectable_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap; if (ImGui::BeginTable("Effects", 4, @@ -1181,7 +1180,7 @@ void DrawTreeInspector() { // instead of only the 1st column with the tree node. ImGui::TableNextColumn(); bool just_clicked = ImGui::IsItemClicked(); - bool just_selected = ImGui::Selectable(composable->schema_name().c_str(), is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap); + bool just_selected = ImGui::Selectable(composable->schema_name().c_str(), is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap); if (just_clicked || just_selected) { SelectObject(composable); appState.active_tab->playhead = global_time; diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 3801c7f..9956ef6 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -35,7 +35,7 @@ endif() add_library(IMGUI STATIC) -set_property(TARGET IMGUI PROPERTY CXX_STANDARD 14) +set_property(TARGET IMGUI PROPERTY CXX_STANDARD 17) set(IMGUI_DIR imgui) set(IMPLOT_DIR implot) diff --git a/libs/imgui b/libs/imgui index 9937660..b61e563 160000 --- a/libs/imgui +++ b/libs/imgui @@ -1 +1 @@ -Subproject commit 9937660b1cdf79a98b74647608f0e57a62a36e62 +Subproject commit b61e56346a92cfcaf1f43a545ca37b0b32239654 diff --git a/libs/implot b/libs/implot index 33c5a96..524f9fc 160000 --- a/libs/implot +++ b/libs/implot @@ -1 +1 @@ -Subproject commit 33c5a965f55f80057f197257d1d1cdb06523e963 +Subproject commit 524f9fcd48d76c13fdf94c5ffbba8787a1ff7e39 diff --git a/main_emscripten.cpp b/main_emscripten.cpp index 7cd3a2a..f17aae6 100644 --- a/main_emscripten.cpp +++ b/main_emscripten.cpp @@ -75,6 +75,7 @@ int main(int argc, char** argv) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigDebugHighlightIdConflicts = false; // Disable ID conflict warnings introduced in ImGui v1.91.2 (pre-existing issues from before update) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking diff --git a/main_glfw.cpp b/main_glfw.cpp index a2e3d81..834375a 100644 --- a/main_glfw.cpp +++ b/main_glfw.cpp @@ -121,6 +121,7 @@ int main(int argc, char** argv) ImGui::CreateContext(); ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigDebugHighlightIdConflicts = false; // Disable ID conflict warnings introduced in ImGui v1.91.2 (pre-existing issues from before update) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking diff --git a/main_macos.mm b/main_macos.mm index 481dd77..4e94781 100644 --- a/main_macos.mm +++ b/main_macos.mm @@ -35,6 +35,7 @@ int main(int argc, char** argv) ImGui::CreateContext(); ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigDebugHighlightIdConflicts = false; // Disable ID conflict warnings introduced in ImGui v1.91.2 (pre-existing issues from before update) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking diff --git a/main_win32.cpp b/main_win32.cpp index d8932c0..ce42d57 100644 --- a/main_win32.cpp +++ b/main_win32.cpp @@ -104,6 +104,7 @@ int main(int argc, char** argv) ImGui::CreateContext(); ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigDebugHighlightIdConflicts = false; // Disable ID conflict warnings introduced in ImGui v1.91.2 (pre-existing issues from before update) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking diff --git a/timeline.cpp b/timeline.cpp index 7bda8b6..d240fd9 100644 --- a/timeline.cpp +++ b/timeline.cpp @@ -125,6 +125,7 @@ void DrawItem( ImGui::PushID(item); ImGui::BeginGroup(); + ImGui::SetNextItemAllowOverlap(); ImGui::InvisibleButton("##Item", size); // Don't skip invisible item if it is the item we have just selected @@ -158,8 +159,6 @@ void DrawItem( return; } - ImGui::SetItemAllowOverlap(); - // Dragging... // if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) // { @@ -339,7 +338,7 @@ void DrawTransition( ImGui::SetCursorPos(old_pos); return; } - // ImGui::SetItemAllowOverlap(); + // ImGui::SetNextItemAllowOverlap(); if (ImGui::IsItemHovered()) { fill_color = hover_fill_color; @@ -454,7 +453,7 @@ void DrawEffects( ImGui::SetCursorPos(old_pos); return; } - // ImGui::SetItemAllowOverlap(); + // ImGui::SetNextItemAllowOverlap(); if (ImGui::IsItemHovered()) { fill_color = hover_fill_color; @@ -570,7 +569,7 @@ void DrawMarkers( continue; ; } - // ImGui::SetItemAllowOverlap(); + // ImGui::SetNextItemAllowOverlap(); if (ImGui::IsItemHovered()) { fill_color = hover_fill_color; @@ -813,10 +812,10 @@ void DrawTimecodeRuler( ImGui::PushID(ptr_id); ImGui::BeginGroup(); + ImGui::SetNextItemAllowOverlap(); ImGui::Dummy(size); const ImVec2 p0 = ImGui::GetItemRectMin(); const ImVec2 p1 = ImGui::GetItemRectMax(); - ImGui::SetItemAllowOverlap(); if (!ImGui::IsRectVisible(p0, p1)) { ImGui::EndGroup(); ImGui::PopID(); @@ -945,13 +944,13 @@ bool DrawTimecodeTrack( ImGui::PushID("##DrawTimecodeTrack"); ImGui::BeginGroup(); + ImGui::SetNextItemAllowOverlap(); if (interactive) { ImGui::InvisibleButton("##empty", size); } else { ImGui::Dummy(size); } const ImVec2 p0 = ImGui::GetItemRectMin(); - ImGui::SetItemAllowOverlap(); if (interactive && ImGui::IsItemActive()) // && // ImGui::IsMouseDragging(ImGuiMouseButton_Left)) @@ -1020,12 +1019,12 @@ float DrawPlayhead( ImGui::PushID("##Playhead"); ImGui::BeginGroup(); + ImGui::SetNextItemAllowOverlap(); ImGui::InvisibleButton("##Playhead2", size); // Compute where we are rendering in screen space for draw list functions. ImVec2 p0 = ImGui::GetItemRectMin(); ImVec2 p1 = ImGui::GetItemRectMax(); - ImGui::SetItemAllowOverlap(); // compute the playhead x position in the local (aka window) coordinate system // so that later we can use SetScrollFromPosX() to scroll the timeline. From cd1b672c40326c5befdc72e8c45ee678490a88e6 Mon Sep 17 00:00:00 2001 From: Tim Lehr Date: Thu, 14 May 2026 16:52:31 -0700 Subject: [PATCH 2/2] Switch ImGuiColorTextEdit to goossens fork MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses #155. Switched from Joshs fork to goossens' fork of ImGuiColorTextEdit for better performance with larger OTIO files, which were not highlighted at a satisfactory speed before. One downside: this implementation drops the special highlighting of the "OTIO_SCHEMA" string found in the original code. The old fork used regex patterns to highlight the string. The new fork requires a custom tokenizer wrapper to achieve this, which I figured is too much overhead for minimal benefit. Added TODO comment documenting the decision and implementation approach for future reference. This commit completes the dependency upgrade started in the previous commit. The two commits are separated for review purposes (dependency upgrade vs. fork switch), but the first commit requires this one to build successfully. Submodule changes: - URL: github.com/jminor/ImGuiColorTextEdit → github.com/goossens/ImGuiColorTextEdit - Commit: 7dec5d8 (Nov 2022) → a74fb09 (May 2026, master branch) API migrations in inspector.cpp: - OTIOLanguageDef() → OTIOLanguage() (LanguageDefinition → Language class) - SetReadOnly() → SetReadOnlyEnabled() - SetLanguageDefinition() → SetLanguage() - SetErrorMarkers() → ClearMarkers() + AddMarker() - Render(title, false, size) → Render(title, size) - IsTextChanged() → CanUndo() - Added palette initialization to fix static initialization order bug - Removed global otioLangDef variable Disclosure: I used Claude Sonnet 4.5 to help with the porting process. Signed-off-by: Tim Lehr --- .gitmodules | 2 +- inspector.cpp | 94 +++++++++++++++++++---------------------- libs/ImGuiColorTextEdit | 2 +- 3 files changed, 45 insertions(+), 53 deletions(-) diff --git a/.gitmodules b/.gitmodules index a425b63..c3401a1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,7 +15,7 @@ url = https://github.com/ocornut/imgui.git [submodule "libs/ImGuiColorTextEdit"] path = libs/ImGuiColorTextEdit - url = https://github.com/jminor/ImGuiColorTextEdit.git + url = https://github.com/goossens/ImGuiColorTextEdit.git [submodule "libs/minizip-ng"] path = libs/minizip-ng url = https://github.com/zlib-ng/minizip-ng.git diff --git a/inspector.cpp b/inspector.cpp index 3d13d97..848b525 100644 --- a/inspector.cpp +++ b/inspector.cpp @@ -23,67 +23,56 @@ #include #include +#include + static const char* marker_color_names[] = { "PINK", "RED", "ORANGE", "YELLOW", "GREEN", "CYAN", "BLUE", "PURPLE", "MAGENTA", "BLACK", "WHITE" }; -const TextEditor::LanguageDefinition& OTIOLanguageDef() +const TextEditor::Language* OTIOLanguage() { - static bool inited = false; - static TextEditor::LanguageDefinition langDef; - if (!inited) - { - static const char* const keywords[] = { - "true", "false", "null" - }; - - for (auto& k : keywords) - langDef.mKeywords.insert(k); - - static const char* const identifiers[] = { - "\"OTIO_SCHEMA\"" - }; - for (auto& k : identifiers) - { - TextEditor::Identifier id; - id.mDeclaration = "OpenTimelineIO Schema"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } - - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\"OTIO_SCHEMA\\\"", TextEditor::PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("\\\"(\\\\.|[^\\\"])*\\\"", TextEditor::PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?", TextEditor::PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", TextEditor::PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\,\\:]", TextEditor::PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; + static bool initialized = false; + static TextEditor::Language language; - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; - - langDef.mName = "JSON"; - - inited = true; + if (!initialized) + { + const TextEditor::Language* jsonLang = TextEditor::Language::Json(); + language = *jsonLang; + language.name = "OTIO/JSON"; + + // TODO: Special highlighting for "OTIO_SCHEMA" strings + // Old jminor fork: used mTokenRegexStrings with pattern \\\"OTIO_SCHEMA\\\" + // New goossens fork: removed regex API, requires custom tokenizer wrapper + // The problem: "OTIO_SCHEMA" appears quoted in JSON. keywords/identifiers only match bare + // tokens. Recoloring string content requires intercepting with customTokenizer + // before the string state machine consumes it. Too complex for minimal benefit. + + initialized = true; } - return langDef; + + return &language; } TextEditor jsonEditor; -TextEditor::LanguageDefinition otioLangDef = OTIOLanguageDef(); bool json_rendered = false; bool json_edited = false; std::string json_error_message; int json_error_line = -1; void UpdateJSONInspector() { - jsonEditor.SetReadOnly(false); - jsonEditor.SetLanguageDefinition(otioLangDef); + // Ensure palette is set (fixes static initialization order issue) + static bool paletteInitialized = false; + if (!paletteInitialized) { + jsonEditor.SetPalette(TextEditor::GetDarkPalette()); + paletteInitialized = true; + } + + jsonEditor.SetReadOnlyEnabled(false); + jsonEditor.SetLanguage(OTIOLanguage()); jsonEditor.SetText(appState.selected_text); - jsonEditor.SetErrorMarkers({}); + jsonEditor.ClearMarkers(); json_rendered = false; json_edited = false; json_error_message = ""; @@ -122,10 +111,13 @@ void SetJSONErrorMessage(std::string message) { wrapped_message += c; } + jsonEditor.ClearMarkers(); if (json_error_line >= 0) { - jsonEditor.SetErrorMarkers({ { json_error_line, wrapped_message } }); - } else { - jsonEditor.SetErrorMarkers({}); + jsonEditor.AddMarker(json_error_line, + IM_COL32(255, 0, 0, 255), + IM_COL32(255, 100, 100, 50), + wrapped_message.c_str(), + ""); } json_error_message = wrapped_message; @@ -222,17 +214,17 @@ void DrawJSONApplyEditButtons() { } void DrawJSONInspector() { - // Check if the text was edited this frame. - // Note that IsTextChanged() is true only until Render is called. - // We have to also check if Render was called since the text - // was last set via SetText() inside UpdateJSONInspector(). - if (json_rendered && jsonEditor.IsTextChanged()) { + // Check if the text was edited. + // We use CanUndo() to detect if there are any edits since SetText() was called, + // as SetText() clears the undo stack. We also check json_rendered to ensure + // Render() was called at least once since UpdateJSONInspector(). + if (json_rendered && jsonEditor.CanUndo()) { json_edited = true; } auto available_size = ImGui::GetContentRegionAvail(); available_size.y -= ImGui::GetFrameHeightWithSpacing(); - jsonEditor.Render("JSON",false, available_size); + jsonEditor.Render("JSON", available_size); json_rendered = true; if (json_edited) { diff --git a/libs/ImGuiColorTextEdit b/libs/ImGuiColorTextEdit index 7dec5d8..a74fb09 160000 --- a/libs/ImGuiColorTextEdit +++ b/libs/ImGuiColorTextEdit @@ -1 +1 @@ -Subproject commit 7dec5d81ca529ea674ee7d7cd9dc32a108650248 +Subproject commit a74fb090d2ea9276ae6c35c2f6ab39491c7d404f