diff --git a/app.threedee/app.threedee.cpp b/app.threedee/app.threedee.cpp index 98596b8..995a000 100644 --- a/app.threedee/app.threedee.cpp +++ b/app.threedee/app.threedee.cpp @@ -5,9 +5,9 @@ #include "examples/imgui_impl_glfw.h" #include "examples/imgui_impl_opengl3.h" +#include "imgui_addons/imgui_Timeline.h" #include "imgui_addons/imgui_checkbutton.h" #include "imgui_addons/imgui_knob.h" -#include "imgui_addons/imgui_Timeline.h" #include "stb_image.h" #include #include @@ -128,6 +128,12 @@ void AppThreeDee::Tick() _stepper.Tick(); } +struct timelineEvent +{ + float values[2]; +}; + +static std::vector values{{20.f, 40.f}, {15.f, 22.5f}}; void AppThreeDee::Render() { ImGui_ImplOpenGL3_NewFrame(); @@ -167,20 +173,34 @@ void AppThreeDee::Render() } ImGui::PopStyleVar(); - static double time_in = 0.0; - static double time_out = 0.0; - if (ImGui::BeginTimeline("MyTimeline",50.f,4,6)) // label, max_value, num_visible_rows, opt_exact_num_rows (for item culling) + if (ImGui::BeginTimelines("MyTimeline", 50.f, 4, 6)) // label, max_value, num_visible_rows, opt_exact_num_rows (for item culling) { - static float events[12]={10.f,20.f,0.5f,30.f,40.f,50.f,20.f,40.f,15.f,22.5f,35.f,45.f}; - if (ImGui::TimelineEvent("Event1",&events[0])) {/*events[0] and/or events[1] modified*/} - ImGui::TimelineEvent("Event2",&events[2]); - ImGui::TimelineEvent("Event3",&events[4],true); // Event3 can only be shifted - ImGui::TimelineEvent("Event4",&events[6]); - ImGui::TimelineEvent("Event5",&events[8]); - ImGui::TimelineEvent("Event6",&events[10]); + static float events[12] = {10.f, 20.f, 0.5f, 30.f, 40.f, 50.f, 20.f, 40.f, 15.f, 22.5f, 35.f, 45.f}; + if (ImGui::TimelineEvent("Event1", &events[0])) + { /*events[0] and/or events[1] modified*/ + } + ImGui::TimelineEvent("Event2", &events[2]); + ImGui::TimelineEvent("Event3", &events[4], true); // Event3 can only be shifted + ImGui::TimelineStart("Event4"); + for (size_t i = 0; i < values.size(); i += 2) + { + ImGui::TimelineEvent(values[i].values); + } + float new_values[2]; + if (ImGui::TimelineEnd(new_values)) + { + timelineEvent e{ + std::fmin(new_values[0], new_values[1]), + std::fmax(new_values[0], new_values[1]) + }; + + values.push_back(e); + } + // ImGui::TimelineEvent("Event5", &events[8]); + ImGui::TimelineEvent("Event6", &events[10]); } - const float elapsedTime = (float)(((unsigned)(ImGui::GetTime()*1000))%50000)/1000.f; // So that it's always in [0,50] - ImGui::EndTimeline(5,elapsedTime); // num_vertical_grid_lines, current_time (optional), timeline_running_color (optional) + const float elapsedTime = static_cast((static_cast(ImGui::GetTime() * 1000)) % 50000) / 1000.f; // So that it's always in [0,50] + ImGui::EndTimelines(5, elapsedTime); // num_vertical_grid_lines, current_time (optional), timeline_running_color (optional) ImGui::Render(); diff --git a/app.threedee/imgui.ini b/app.threedee/imgui.ini index 9889dfe..f9e4cfe 100644 --- a/app.threedee/imgui.ini +++ b/app.threedee/imgui.ini @@ -1,7 +1,7 @@ [Window][Debug##Default] -ViewportPos=1250,637 +ViewportPos=911,482 ViewportId=0x9F5F46A1 -Size=731,681 +Size=1592,651 Collapsed=0 [Window][Hello, world!] @@ -43,8 +43,8 @@ Size=1024,768 Collapsed=0 [Window][Sequencer] -Pos=435,98 -Size=856,1305 +Pos=8,98 +Size=1167,1305 Collapsed=0 DockId=0x0000000D,1 @@ -71,7 +71,7 @@ Collapsed=0 DockId=0x00000009,0 [Window][Pattern editor] -Pos=1293,98 +Pos=1177,98 Size=602,1305 Collapsed=0 DockId=0x0000000E,0 @@ -179,7 +179,7 @@ DockId=0x00000012,2 [Docking][Data] DockSpace ID=0x353B2190 Pos=207,162 Size=1008,733 CentralNode=1 SelectedTab=0xEBE6C6E6 -DockSpace ID=0xB073454D Pos=350,294 Size=1008,728 Split=X SelectedTab=0x421602A9 +DockSpace ID=0xB073454D Pos=286,230 Size=1008,728 Split=X SelectedTab=0x421602A9 DockNode ID=0x0000001F Parent=0xB073454D SizeRef=446,728 SelectedTab=0x1E4E8AD5 DockNode ID=0x00000020 Parent=0xB073454D SizeRef=1987,728 Split=Y DockNode ID=0x00000005 Parent=0x00000020 SizeRef=1008,549 Split=Y diff --git a/app.threedee/imgui_addons/imgui_Timeline.h b/app.threedee/imgui_addons/imgui_Timeline.h index a2238ab..9c25906 100644 --- a/app.threedee/imgui_addons/imgui_Timeline.h +++ b/app.threedee/imgui_addons/imgui_Timeline.h @@ -1,6 +1,7 @@ - +#include #include #include +#include ImVec2 operator+(ImVec2 const &a, ImVec2 const &b) { @@ -20,9 +21,12 @@ namespace ImGui { * Add zooming with CTRL+MouseWheel, and a horizontal scrollbar * Add different types of TimelineEvent (e.g. multiple ranges in a single line, dot-like markers, etc.) */ -IMGUI_API bool BeginTimeline(const char *str_id, float max_value = 0.f, int num_visible_rows = 5, int opt_exact_num_rows = 0); // last arg, when !=0, enables item culling +IMGUI_API bool BeginTimelines(const char *str_id, float max_value = 0.f, int num_visible_rows = 5, int opt_exact_num_rows = 0); // last arg, when !=0, enables item culling IMGUI_API bool TimelineEvent(const char *str_id, float *values, bool keep_range_constant = false); -IMGUI_API void EndTimeline(int num_vertical_grid_lines = 5.f, float current_time = 0.f, ImU32 timeline_running_color = IM_COL32(0, 128, 0, 200)); +IMGUI_API void TimelineStart(const char *str_id, bool keep_range_constant = false); +IMGUI_API bool TimelineEvent(float *values); +IMGUI_API bool TimelineEnd(float *new_values = nullptr); +IMGUI_API void EndTimelines(int num_vertical_grid_lines = 5.f, float current_time = 0.f, ImU32 timeline_running_color = IM_COL32(0, 128, 0, 200)); } // namespace ImGui namespace ImGui { @@ -32,8 +36,16 @@ static int s_timeline_num_rows = 0; static int s_timeline_display_start = 0; static int s_timeline_display_end = 0; static int s_timeline_display_index = 0; +static const char *s_str_id = nullptr; +static bool s_keep_range_constant = false; +static ImVec2 s_cursor_pos; +static const float TIMELINE_RADIUS = 6; +static int s_event_counter = 0; +static float *s_lastval = nullptr; +static bool s_is_event_hovered = false; +static float s_start_new_value = 0.0f; -bool BeginTimeline(const char *str_id, float max_value, int num_visible_rows, int opt_exact_num_rows) +bool BeginTimelines(const char *str_id, float max_value, int num_visible_rows, int opt_exact_num_rows) { // reset global variables s_max_timeline_value = 0.f; @@ -57,10 +69,87 @@ bool BeginTimeline(const char *str_id, float max_value, int num_visible_rows, in } return rv; } -static const float TIMELINE_RADIUS = 6; + bool TimelineEvent(const char *str_id, float *values, bool keep_range_constant) +{ + TimelineStart(str_id, keep_range_constant); + auto result = TimelineEvent(values); + TimelineEnd(); + + return result; +} + +void TimelineStart(const char *str_id, bool keep_range_constant) { ++s_timeline_display_index; + + if (s_timeline_num_rows > 0 && + (s_timeline_display_index < s_timeline_display_start || s_timeline_display_index >= s_timeline_display_end)) return; // item culling + + s_str_id = str_id; + s_keep_range_constant = keep_range_constant; + ImGuiWindow *win = GetCurrentWindow(); + const float columnOffset = ImGui::GetColumnOffset(1); + s_cursor_pos = ImVec2(GetWindowContentRegionMin().x + win->Pos.x + columnOffset - TIMELINE_RADIUS, win->DC.CursorPos.y); + + ImGui::Text("%s", str_id); + ImGui::NextColumn(); + + s_event_counter = 0; + s_lastval = nullptr; + s_is_event_hovered = false; +} + +bool TimelineEnd(float *new_values) +{ + if (s_timeline_num_rows > 0 && + (s_timeline_display_index < s_timeline_display_start || s_timeline_display_index >= s_timeline_display_end)) return false; // item culling + + bool result = false; + ImGuiWindow *win = GetCurrentWindow(); + const ImU32 active_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_ButtonHovered]); + const float columnWidth = ImGui::GetColumnWidth(1) - GImGui->Style.ScrollbarSize; + + auto nextPos = s_cursor_pos + ImVec2(0, GetTextLineHeightWithSpacing()); + + float end_new_value = (GetIO().MousePos.x - s_cursor_pos.x) / columnWidth * s_max_timeline_value; + + SetCursorScreenPos(s_cursor_pos); + if (InvisibleButton(s_str_id, ImVec2(GetWindowContentRegionWidth(), GetTextLineHeight())) && new_values != nullptr) + { + std::cout << s_start_new_value << " => " << end_new_value << std::endl; + new_values[0] = s_start_new_value; + new_values[1] = end_new_value; + result = true; + } + if (!s_is_event_hovered && IsItemHovered()) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + } + if (IsItemActive() && !IsMouseDragging(0)) + { + s_start_new_value = (GetIO().MousePos.x - s_cursor_pos.x) / columnWidth * s_max_timeline_value; + } + if (IsItemHovered() && IsItemActive() && IsMouseDragging(0)) + { + ImVec2 start = s_cursor_pos; + start.x += columnWidth * s_start_new_value / s_max_timeline_value + (2 * TIMELINE_RADIUS); + start.y += (ImGui::GetTextLineHeight() - TIMELINE_RADIUS) / 2.0f + TIMELINE_RADIUS * 0.5f; + ImVec2 end = start + ImVec2(columnWidth * (end_new_value - s_start_new_value) / s_max_timeline_value - (2 * TIMELINE_RADIUS), + TIMELINE_RADIUS); + + win->DrawList->AddRectFilled(start, end, active_color); + } + + SetCursorScreenPos(nextPos); + + ImGui::NextColumn(); + + return result; +} + +bool TimelineEvent(float *values) +{ if (s_timeline_num_rows > 0 && (s_timeline_display_index < s_timeline_display_start || s_timeline_display_index >= s_timeline_display_end)) return false; // item culling @@ -70,79 +159,74 @@ bool TimelineEvent(const char *str_id, float *values, bool keep_range_constant) const ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_ColumnActive]); bool changed = false; bool hovered = false; + bool allhovered = false; bool active = false; - ImGui::Text("%s", str_id); - ImGui::NextColumn(); - - const float columnOffset = ImGui::GetColumnOffset(1); const float columnWidth = ImGui::GetColumnWidth(1) - GImGui->Style.ScrollbarSize; - ImVec2 cursor_pos(GetWindowContentRegionMin().x + win->Pos.x + columnOffset - TIMELINE_RADIUS, win->DC.CursorPos.y); + + PushID(s_event_counter++); bool mustMoveBothEnds = false; const bool isMouseDraggingZero = IsMouseDragging(0); + ImVec2 start = s_cursor_pos; + start.x += columnWidth * values[0] / s_max_timeline_value + (2 * TIMELINE_RADIUS); + start.y += (ImGui::GetTextLineHeight() - TIMELINE_RADIUS) / 2.0f; + ImVec2 end = start + ImVec2(columnWidth * (values[1] - values[0]) / s_max_timeline_value - (2 * TIMELINE_RADIUS), TIMELINE_RADIUS * 2.0f); + + PushID(-1); + SetCursorScreenPos(start); + InvisibleButton(s_str_id, end - start); + if ((IsItemActive() && isMouseDraggingZero) || mustMoveBothEnds) + { + const float deltaX = GetIO().MouseDelta.x / columnWidth * s_max_timeline_value; + values[0] += deltaX; + values[1] += deltaX; + changed = hovered = allhovered = true; + } + else if (IsItemHovered()) + { + hovered = allhovered = true; + } + PopID(); + + win->DrawList->AddRectFilled(start, end, IsItemActive() || IsItemHovered() ? active_color : inactive_color); + for (int i = 0; i < 2; ++i) { - ImVec2 pos = cursor_pos; + ImVec2 pos = s_cursor_pos; pos.x += columnWidth * values[i] / s_max_timeline_value + TIMELINE_RADIUS; - pos.y += TIMELINE_RADIUS; + pos.y += (ImGui::GetTextLineHeight() - TIMELINE_RADIUS) / 2.0f + TIMELINE_RADIUS; SetCursorScreenPos(pos - ImVec2(TIMELINE_RADIUS, TIMELINE_RADIUS)); PushID(i); - InvisibleButton(str_id, ImVec2(2 * TIMELINE_RADIUS, 2 * TIMELINE_RADIUS)); + InvisibleButton(s_str_id, ImVec2(2 * TIMELINE_RADIUS, 2 * TIMELINE_RADIUS)); active = IsItemActive(); if (active || IsItemHovered()) { ImGui::SetTooltip("%f", values[i]); - if (!keep_range_constant) + if (!s_keep_range_constant) { // @meshula:The item hovered line needs to be compensated for vertical scrolling. Thx! ImVec2 a(pos.x, GetWindowContentRegionMin().y + win->Pos.y + win->Scroll.y); ImVec2 b(pos.x, GetWindowContentRegionMax().y + win->Pos.y + win->Scroll.y); - // possible aternative: - //ImVec2 a(pos.x, win->Pos.y); - //ImVec2 b(pos.x, win->Pos.y+win->Size.y); win->DrawList->AddLine(a, b, line_color); } hovered = true; } if (active && isMouseDraggingZero) { - if (!keep_range_constant) + if (!s_keep_range_constant) values[i] += GetIO().MouseDelta.x / columnWidth * s_max_timeline_value; else mustMoveBothEnds = true; changed = hovered = true; } PopID(); - win->DrawList->AddCircleFilled( - pos, TIMELINE_RADIUS, IsItemActive() || IsItemHovered() ? active_color : inactive_color); + win->DrawList->AddRectFilled(pos - ImVec2(TIMELINE_RADIUS, TIMELINE_RADIUS), + pos + ImVec2(TIMELINE_RADIUS, TIMELINE_RADIUS), + IsItemActive() || IsItemHovered() || allhovered ? active_color : inactive_color); } - ImVec2 start = cursor_pos; - start.x += columnWidth * values[0] / s_max_timeline_value + 2 * TIMELINE_RADIUS; - start.y += TIMELINE_RADIUS * 0.5f; - ImVec2 end = start + ImVec2(columnWidth * (values[1] - values[0]) / s_max_timeline_value - 2 * TIMELINE_RADIUS, - TIMELINE_RADIUS); - - PushID(-1); - SetCursorScreenPos(start); - InvisibleButton(str_id, end - start); - if ((IsItemActive() && isMouseDraggingZero) || mustMoveBothEnds) - { - const float deltaX = GetIO().MouseDelta.x / columnWidth * s_max_timeline_value; - values[0] += deltaX; - values[1] += deltaX; - changed = hovered = true; - } - else if (IsItemHovered()) - hovered = true; - PopID(); - - SetCursorScreenPos(cursor_pos + ImVec2(0, GetTextLineHeightWithSpacing())); - - win->DrawList->AddRectFilled(start, end, IsItemActive() || IsItemHovered() ? active_color : inactive_color); - if (values[0] > values[1]) { float tmp = values[0]; @@ -159,13 +243,19 @@ bool TimelineEvent(const char *str_id, float *values, bool keep_range_constant) values[1] -= values[0]; values[0] = 0; } + s_lastval = values + 1; + values += 2; + PopID(); + if (hovered) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + s_is_event_hovered = true; + } -// if (hovered) ImGui::SetMouseCursor(ImGuiMouseCursor_Move); - - ImGui::NextColumn(); return changed; } -void EndTimeline(int num_vertical_grid_lines, float current_time, ImU32 timeline_running_color) + +void EndTimelines(int num_vertical_grid_lines, float current_time, ImU32 timeline_running_color) { const float row_height = ImGui::GetTextLineHeightWithSpacing(); if (s_timeline_num_rows > 0) ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ((s_timeline_num_rows - s_timeline_display_end) * row_height));