From 67e0876f8982884d03200e8d4c29e6d957b44ddd Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Sun, 28 Feb 2021 18:10:23 -0600 Subject: [PATCH] Improved Drag and Drop Support (#172) * prototyping enhanced dnd features * improve dnd demo using new utils * dnd stuff * finish up dnd improvements * remove unused code, fix timestamps --- implot.cpp | 249 ++++++++++++++++++++++------------- implot.h | 42 +++++- implot_demo.cpp | 329 +++++++++++++++++++++++++--------------------- implot_internal.h | 1 + 4 files changed, 369 insertions(+), 252 deletions(-) diff --git a/implot.cpp b/implot.cpp index 7189f9e..021d672 100644 --- a/implot.cpp +++ b/implot.cpp @@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. +- 2021/01/XX (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved to implot_internal.h due to its immaturity. - 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding @@ -2265,31 +2266,7 @@ void EndPlot() { DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); } - PopPlotClipRect(); - // render y-axis drag/drop hover - if ((plot.YAxis[1].Present || plot.YAxis[2].Present) && ImGui::IsDragDropPayloadBeingAccepted()) { - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (plot.YAxis[i].ExtHovered) { - float x_loc = gp.YAxisReference[i]; - ImVec2 p1(x_loc - 5, plot.PlotRect.Min.y - 5); - ImVec2 p2(x_loc + 5, plot.PlotRect.Max.y + 5); - DrawList.AddRect(p1, p2, ImGui::GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ImDrawCornerFlags_All, 2.0f); - } - } - } - - // render x-axis drag/drop hover - if (plot.XAxis.Present && ImGui::IsDragDropPayloadBeingAccepted()) { - if (plot.XAxis.ExtHovered) { - float y_loc = plot.XAxis.HoverRect.Min.y; - ImVec2 p1(plot.XAxis.HoverRect.Min.x - 5, y_loc - 5); - ImVec2 p2(plot.XAxis.HoverRect.Max.x + 5, y_loc + 5); - DrawList.AddRect(p1, p2, ImGui::GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ImDrawCornerFlags_All, 2.0f); - } - } - - PushPlotClipRect(); // render selection/query if (plot.Selecting) { const ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart)); @@ -2423,9 +2400,9 @@ void EndPlot() { legend_size, plot.LegendLocation, plot.LegendOutside ? gp.Style.PlotPadding : gp.Style.LegendPadding); - const ImRect legend_bb(legend_pos, legend_pos + legend_size); + plot.LegendRect = ImRect(legend_pos, legend_pos + legend_size); // test hover - plot.LegendHovered = plot.FrameHovered && legend_bb.Contains(IO.MousePos); + plot.LegendHovered = plot.FrameHovered && plot.LegendRect.Contains(IO.MousePos); if (plot.LegendOutside) ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); @@ -2433,11 +2410,14 @@ void EndPlot() { PushPlotClipRect(); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); - DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); - DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); - ShowLegendEntries(plot, legend_bb, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList); + DrawList.AddRectFilled(plot.LegendRect.Min, plot.LegendRect.Max, col_bg); + DrawList.AddRect(plot.LegendRect.Min, plot.LegendRect.Max, col_bd); + ShowLegendEntries(plot, plot.LegendRect, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList); ImGui::PopClipRect(); } + else { + plot.LegendRect = ImRect(); + } if (plot.LegendFlipSideNextFrame) { plot.LegendOutside = !plot.LegendOutside; plot.LegendFlipSideNextFrame = false; @@ -2924,6 +2904,150 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe return dragging; } +//----------------------------------------------------------------------------- + +#define IMPLOT_ID_PLT 10030910 +#define IMPLOT_ID_LEG 10030911 +#define IMPLOT_ID_XAX 10030912 +#define IMPLOT_ID_YAX 10030913 + +bool BeginDragDropTargetEx(int id, const ImRect& rect) { + ImGuiContext& G = *GImGui; + const ImGuiID ID = G.CurrentWindow->GetID(id); + if (ImGui::ItemAdd(rect, ID, &rect) && + ImGui::BeginDragDropTarget()) + return true; + return false; +} + +bool BeginDragDropTarget() { + return BeginDragDropTargetEx(IMPLOT_ID_PLT, GImPlot->CurrentPlot->PlotRect); +} + +bool BeginDragDropTargetX() { + return BeginDragDropTargetEx(IMPLOT_ID_XAX, GImPlot->CurrentPlot->XAxis.HoverRect); +} + +bool BeginDragDropTargetY(ImPlotYAxis axis) { + return BeginDragDropTargetEx(IMPLOT_ID_YAX + axis, GImPlot->CurrentPlot->YAxis[axis].HoverRect); +} + +bool BeginDragDropTargetLegend() { + return !ImHasFlag(GImPlot->CurrentPlot->Flags,ImPlotFlags_NoLegend) && + BeginDragDropTargetEx(IMPLOT_ID_LEG, GImPlot->CurrentPlot->LegendRect); +} + +void EndDragDropTarget() { + ImGui::EndDragDropTarget(); +} + +bool BeginDragDropSourceEx(ImGuiID source_id, bool is_hovered, ImGuiDragDropFlags flags, ImGuiKeyModFlags key_mods) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; + + if (g.IO.MouseDown[mouse_button] == false) { + if (g.ActiveId == source_id) + ImGui::ClearActiveID(); + return false; + } + + if (is_hovered && g.IO.MouseClicked[mouse_button] && g.IO.KeyMods == key_mods) { + ImGui::SetActiveID(source_id, window); + ImGui::FocusWindow(window); + } + + if (g.ActiveId != source_id) { + return false; + } + + g.ActiveIdAllowOverlap = is_hovered; + g.ActiveIdUsingNavDirMask = ~(ImU32)0; + g.ActiveIdUsingNavInputMask = ~(ImU32)0; + g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + + if (ImGui::IsMouseDragging(mouse_button)) { + + if (!g.DragDropActive) { + ImGui::ClearDragDrop(); + ImGuiPayload& payload = g.DragDropPayload; + payload.SourceId = source_id; + payload.SourceParentId = 0; + g.DragDropActive = true; + g.DragDropSourceFlags = 0; + g.DragDropMouseButton = mouse_button; + } + g.DragDropSourceFrameCount = g.FrameCount; + g.DragDropWithinSource = true; + + if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { + ImGui::BeginTooltip(); + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { + ImGuiWindow* tooltip_window = g.CurrentWindow; + tooltip_window->SkipItems = true; + tooltip_window->HiddenFramesCanSkipItems = 1; + } + } + return true; + } + return false; +} + +bool BeginDragDropSource(ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { + if (ImGui::GetIO().KeyMods == key_mods) { + GImPlot->CurrentPlot->XAxis.Dragging = false; + for (int i = 0; i < IMPLOT_Y_AXES; ++i) + GImPlot->CurrentPlot->YAxis[i].Dragging = false; + } + const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_PLT); + ImRect rect = GImPlot->CurrentPlot->PlotRect; + return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->PlotHovered, flags, key_mods); +} + +bool BeginDragDropSourceX(ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { + if (ImGui::GetIO().KeyMods == key_mods) + GImPlot->CurrentPlot->XAxis.Dragging = false; + const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_XAX); + ImRect rect = GImPlot->CurrentPlot->XAxis.HoverRect; + return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->XAxis.ExtHovered, flags, key_mods); +} + +bool BeginDragDropSourceY(ImPlotYAxis axis, ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { + if (ImGui::GetIO().KeyMods == key_mods) + GImPlot->CurrentPlot->YAxis[axis].Dragging = false; + const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_YAX + axis); + ImRect rect = GImPlot->CurrentPlot->YAxis[axis].HoverRect; + return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->YAxis[axis].ExtHovered, flags, key_mods); +} + +bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "BeginDragDropSourceItem() needs to be called between BeginPlot() and EndPlot()!"); + ImGuiID source_id = ImGui::GetID(label_id); + ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(source_id); + bool is_hovered = item && item->LegendHovered; + return BeginDragDropSourceEx(source_id, is_hovered, flags, ImGuiKeyModFlags_None); +} + +void EndDragDropSource() { + ImGui::EndDragDropSource(); +} + +void ItemIcon(const ImVec4& col) { + ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); +} + +void ItemIcon(ImU32 col) { + const float txt_size = ImGui::GetTextLineHeight(); + ImVec2 size(txt_size-4,txt_size); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImVec2 pos = window->DC.CursorPos; + ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); + ImGui::Dummy(size); +} + +//----------------------------------------------------------------------------- + void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetLegendLocation() needs to be called between BeginPlot() and EndPlot()!"); @@ -2947,74 +3071,9 @@ bool IsLegendEntryHovered(const char* label_id) { return item && item->LegendHovered; } -bool BeginLegendDragDropSource(const char* label_id, ImGuiDragDropFlags flags) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "BeginLegendDragDropSource() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID source_id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(source_id); - bool is_hovered = item && item->LegendHovered; - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; - if (g.IO.MouseDown[mouse_button] == false) { - if (g.ActiveId == source_id) - ImGui::ClearActiveID(); - return false; - } - - if (is_hovered && g.IO.MouseClicked[mouse_button]) { - ImGui::SetActiveID(source_id, window); - ImGui::FocusWindow(window); - } - - if (g.ActiveId != source_id) - return false; - - // Allow the underlying widget to display/return hovered during the mouse - // release frame, else we would get a flicker. - g.ActiveIdAllowOverlap = is_hovered; - - // Disable navigation and key inputs while dragging - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; - - if (ImGui::IsMouseDragging(mouse_button)) { - if (!g.DragDropActive) { - ImGui::ClearDragDrop(); - ImGuiPayload& payload = g.DragDropPayload; - payload.SourceId = source_id; - payload.SourceParentId = 0; - g.DragDropActive = true; - g.DragDropSourceFlags = 0; - g.DragDropMouseButton = mouse_button; - } - g.DragDropSourceFrameCount = g.FrameCount; - g.DragDropWithinSource = true; - - if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - // Target can request the Source to not display its tooltip (we use a - // dedicated flag to make this request explicit) We unfortunately can't - // just modify the source flags and skip the call to BeginTooltip, as - // caller may be emitting contents. - ImGui::BeginTooltip(); - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { - ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; - tooltip_window->HiddenFramesCanSkipItems = 1; - } - } - return true; - } - return false; -} - -void EndLegendDragDropSource() { - ImGui::EndDragDropSource(); -} bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { ImPlotContext& gp = *GImPlot; @@ -3885,7 +3944,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* ImVec4 col_btn = style.Colors[ImGuiCol_Button]; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); - + const float ht = ImGui::GetFrameHeight(); ImVec2 cell_size(ht*1.25f,ht); char buff[32]; diff --git a/implot.h b/implot.h index 44d0b96..69d8035 100644 --- a/implot.h +++ b/implot.h @@ -527,7 +527,7 @@ IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label // Legend Utils and Tools //----------------------------------------------------------------------------- -// The following functions MUST be called between Begin/EndPlot! +// The following functions MUST be called BETWEEN Begin/EndPlot! // Set the location of the current plot's legend. IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation = ImPlotOrientation_Vertical, bool outside = false); @@ -535,15 +535,43 @@ IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation ori IMPLOT_API void SetMousePosLocation(ImPlotLocation location); // Returns true if a plot item legend entry is hovered. IMPLOT_API bool IsLegendEntryHovered(const char* label_id); -// Begin a drag and drop source from a legend entry. The only supported flag is SourceNoPreviewTooltip -IMPLOT_API bool BeginLegendDragDropSource(const char* label_id, ImGuiDragDropFlags flags = 0); -// End legend drag and drop source. -IMPLOT_API void EndLegendDragDropSource(); + // Begin a popup for a legend entry. IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button = 1); // End a popup for a legend entry. IMPLOT_API void EndLegendPopup(); +//----------------------------------------------------------------------------- +// Drag and Drop Utils +//----------------------------------------------------------------------------- + +// The following functions MUST be called BETWEEN Begin/EndPlot! + +// Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget! +IMPLOT_API bool BeginDragDropTarget(); +// Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget! +IMPLOT_API bool BeginDragDropTargetX(); +// Turns the current plot's Y-Axis into a drag and drop target. Don't forget to call EndDragDropTarget! +IMPLOT_API bool BeginDragDropTargetY(ImPlotYAxis axis = ImPlotYAxis_1); +// Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget! +IMPLOT_API bool BeginDragDropTargetLegend(); +// Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget). +IMPLOT_API void EndDragDropTarget(); + +// NB: By default, plot and axes drag and drop sources require holding the Ctrl modifier to initiate the drag. +// You can change the modifier if desired. If ImGuiKeyModFlags_None is provided, the axes will be locked from panning. + +// Turns the current plot's plotting area into a drag and drop source. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSource(ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); +// Turns the current plot's X-axis into a drag and drop source. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSourceX(ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); +// Turns the current plot's Y-axis into a drag and drop source. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSourceY(ImPlotYAxis axis = ImPlotYAxis_1, ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); +// Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags = 0); +// Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). +IMPLOT_API void EndDragDropSource(); + //----------------------------------------------------------------------------- // Plot and Item Styling //----------------------------------------------------------------------------- @@ -645,6 +673,10 @@ IMPLOT_API const char* GetColormapName(ImPlotColormap colormap); // Miscellaneous //----------------------------------------------------------------------------- +// Render a icon similar to those that appear in legends (nifty for data lists). +IMPLOT_API void ItemIcon(const ImVec4& col); +IMPLOT_API void ItemIcon(ImU32 col); + // Get the plot draw list for rendering to the current plot area. IMPLOT_API ImDrawList* GetPlotDrawList(); // Push clip rect for rendering to current plot area. diff --git a/implot_demo.cpp b/implot_demo.cpp index c27dbf3..f645036 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -81,8 +81,8 @@ struct ScrollingBuffer { int MaxSize; int Offset; ImVector Data; - ScrollingBuffer() { - MaxSize = 2000; + ScrollingBuffer(int max_size = 2000) { + MaxSize = max_size; Offset = 0; Data.reserve(MaxSize); } @@ -663,8 +663,8 @@ void ShowDemoWindow(bool* p_open) { } if (ImGui::CollapsingHeader("Time Formatted Axes")) { - static double t_min = 1577836800; // 01/01/2020 @ 12:00:00am (UTC) - static double t_max = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) + static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) + static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" "UNIX timestamps in seconds and axis labels are formated as date/time."); @@ -969,175 +969,216 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Drag and Drop")) { - const int K_CHANNELS = 9; - srand((int)(10000000 * DEMO_TIME)); - static bool paused = false; - static bool init = true; - static ScrollingBuffer data[K_CHANNELS]; - static bool show[K_CHANNELS]; - static int yAxis[K_CHANNELS]; - if (init) { - for (int i = 0; i < K_CHANNELS; ++i) { - show[i] = false; - yAxis[i] = 0; + ImGui::BulletText("Drag/drop items from the left column."); + ImGui::BulletText("Drag/drop items between plots."); + ImGui::Indent(); + ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); + ImGui::BulletText("Plot 1 Sources: Legend Items"); + ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); + ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); + ImGui::Unindent(); + + // convenience struct to manage DND items; do this however you like + struct MyDndItem { + int Idx; + int Plt; + int Yax; + char Label[16]; + ImVector Data; + ImVec4 Color; + MyDndItem() { + static int i = 0; + Idx = i++; + Plt = 0; + Yax = ImPlotYAxis_1; + sprintf(Label, "%02d Hz", Idx+1); + Color = ImPlot::GetColormapColor(Idx); + Data.reserve(1001); + for (int k = 0; k < 1001; ++k) { + float t = k * 1.0f / 999; + Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); + } } - init = false; + void Reset() { Plt = 0; Yax = ImPlotYAxis_1; } + }; + + const int k_dnd = 20; + static MyDndItem dnd[k_dnd]; + static MyDndItem* dndx = NULL; // for plot 2 + static MyDndItem* dndy = NULL; // for plot 2 + + // child window to serve as initial source for our DND items + ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); + if (ImGui::Button("Reset Data", ImVec2(100, 0))) { + for (int k = 0; k < k_dnd; ++k) + dnd[k].Reset(); + dndx = dndy = NULL; } - ImGui::BulletText("Drag data items from the left column onto the plot or onto a specific y-axis."); - ImGui::BulletText("Redrag data items from the legend onto other y-axes."); - ImGui::BeginGroup(); - if (ImGui::Button("Clear", ImVec2(100, 0))) { - for (int i = 0; i < K_CHANNELS; ++i) { - show[i] = false; - data[i].Data.shrink(0); - data[i].Offset = 0; - } - } - if (ImGui::Button(paused ? "Resume" : "Pause", ImVec2(100,0))) - paused = !paused; - for (int i = 0; i < K_CHANNELS; ++i) { - char label[16]; - sprintf(label, show[i] ? "data_%d (Y%d)" : "data_%d", i, yAxis[i]+1); - ImGui::Selectable(label, false, 0, ImVec2(100, 0)); + for (int k = 0; k < k_dnd; ++k) { + if (dnd[k].Plt > 0) + continue; + ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); + ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("DND_PLOT", &i, sizeof(int)); - ImGui::TextUnformatted(label); + ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); + ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); + ImGui::TextUnformatted(dnd[k].Label); ImGui::EndDragDropSource(); } } - ImGui::EndGroup(); - ImGui::SameLine(); - srand((unsigned int)DEMO_TIME*10000000); - static float t = 0; - if (!paused) { - t += ImGui::GetIO().DeltaTime; - for (int i = 0; i < K_CHANNELS; ++i) { - if (show[i]) - data[i].AddPoint(t, (i+1)*0.1f + RandomRange(-0.01f,0.01f)); + ImGui::EndChild(); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Reset(); } + ImGui::EndDragDropTarget(); } - ImPlot::SetNextPlotLimitsX((double)t - 10, t, paused ? ImGuiCond_Once : ImGuiCond_Always); - if (ImPlot::BeginPlot("##DND", NULL, NULL, ImVec2(-1,0), ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3, ImPlotAxisFlags_NoTickLabels)) { - for (int i = 0; i < K_CHANNELS; ++i) { - if (show[i] && data[i].Data.size() > 0) { - char label[K_CHANNELS]; - sprintf(label, "data_%d", i); - ImPlot::SetPlotYAxis(yAxis[i]); - ImPlot::PlotLine(label, &data[i].Data[0].x, &data[i].Data[0].y, data[i].Data.size(), data[i].Offset, 2 * sizeof(float)); - // allow legend labels to be dragged and dropped - if (ImPlot::BeginLegendDragDropSource(label)) { - ImGui::SetDragDropPayload("DND_PLOT", &i, sizeof(int)); - ImGui::TextUnformatted(label); - ImPlot::EndLegendDragDropSource(); + + ImGui::SameLine(); + ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); + // plot 1 (time series) + ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines; + if (ImPlot::BeginPlot("##DND1", NULL, "[drop here]", ImVec2(-1,195), ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3, flags | ImPlotAxisFlags_Lock, flags, flags, flags, "[drop here]", "[drop here]")) { + for (int k = 0; k < k_dnd; ++k) { + if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { + ImPlot::SetPlotYAxis(dnd[k].Yax); + ImPlot::SetNextLineStyle(dnd[k].Color); + static char label[16]; + sprintf(label,"%s (Y%d)", dnd[k].Label, dnd[k].Yax+1); + ImPlot::PlotLine(label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 2 * sizeof(float)); + // allow legend item labels to be DND sources + if (ImPlot::BeginDragDropSourceItem(label)) { + ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); + ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); + ImGui::TextUnformatted(dnd[k].Label); + ImPlot::EndDragDropSource(); } } } - // make our plot a drag and drop target - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_PLOT")) { - int i = *(int*)payload->Data; - show[i] = true; - yAxis[i] = 0; - // set specific y-axis if hovered - for (int y = 0; y < 3; y++) { - if (ImPlot::IsPlotYAxisHovered(y)) - yAxis[i] = y; - } - } - ImGui::EndDragDropTarget(); - } + // allow the main plot area to be a DND target + if (ImPlot::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = 0; + } + ImPlot::EndDragDropTarget(); + } + // allow each y-axis to be a DND target + for (int y = 0; y < 3; ++y) { + if (ImPlot::BeginDragDropTargetY(y)) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; + } + ImPlot::EndDragDropTarget(); + } + } + // allow the legend to be a DND target + if (ImPlot::BeginDragDropTargetLegend()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = 0; + } + ImPlot::EndDragDropTarget(); + } ImPlot::EndPlot(); } + // plot 2 (Lissajous) + ImPlot::PushStyleColor(ImPlotCol_XAxis, dndx == NULL ? ImPlot::GetStyle().Colors[ImPlotCol_XAxis] : dndx->Color); + ImPlot::PushStyleColor(ImPlotCol_YAxis, dndy == NULL ? ImPlot::GetStyle().Colors[ImPlotCol_YAxis] : dndy->Color); + if (ImPlot::BeginPlot("##DND2", dndx == NULL ? "[drop here]" : dndx->Label, dndy == NULL ? "[drop here]" : dndy->Label, ImVec2(-1,195), 0, flags, flags )) { + if (dndx != NULL && dndy != NULL) { + ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); + ImPlot::SetNextLineStyle(mixed); + ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 2 * sizeof(float)); + } + // allow the x-axis to be a DND target + if (ImPlot::BeginDragDropTargetX()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndx = &dnd[i]; + } + ImPlot::EndDragDropTarget(); + } + // allow the x-axis to be a DND source + if (dndx != NULL && ImPlot::BeginDragDropSourceX()) { + ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); + ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); + ImGui::TextUnformatted(dndx->Label); + ImPlot::EndDragDropSource(); + } + // allow the y-axis to be a DND target + if (ImPlot::BeginDragDropTargetY()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndy = &dnd[i]; + } + ImPlot::EndDragDropTarget(); + } + // allow the y-axis to be a DND source + if (dndy != NULL && ImPlot::BeginDragDropSourceY()) { + ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); + ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); + ImGui::TextUnformatted(dndy->Label); + ImPlot::EndDragDropSource(); + } + // allow the plot area to be a DND target + if (ImPlot::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; + } + } + // allow the plot area to be a DND source + if (ImPlot::BeginDragDropSource()) { + ImGui::TextUnformatted("Yes, you can\ndrag this!"); + ImPlot::EndDragDropSource(); + } + ImPlot::EndPlot(); + } + ImPlot::PopStyleColor(2); + ImGui::EndChild(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Digital and Analog Signals")) { - static bool paused = false; - #define K_PLOT_DIGITAL_CH_COUNT 4 - #define K_PLOT_ANALOG_CH_COUNT 4 - static ScrollingBuffer dataDigital[K_PLOT_DIGITAL_CH_COUNT]; - static ScrollingBuffer dataAnalog[K_PLOT_ANALOG_CH_COUNT]; - static bool showDigital[K_PLOT_DIGITAL_CH_COUNT]; - static bool showAnalog[K_PLOT_ANALOG_CH_COUNT]; ImGui::BulletText("You can plot digital and analog signals on the same plot."); ImGui::BulletText("Digital signals do not respond to Y drag and zoom, so that"); ImGui::Indent(); ImGui::Text("you can drag analog signals over the rising/falling digital edge."); ImGui::Unindent(); - ImGui::BeginGroup(); - if (ImGui::Button("Clear", ImVec2(100, 0))) { - for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) - showDigital[i] = false; - for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) - showAnalog[i] = false; - } - if (ImGui::Button(paused ? "Resume" : "Pause", ImVec2(100,0))) - paused = !paused; - for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) { - char label[32]; - sprintf(label, "digital_%d", i); - ImGui::Checkbox(label, &showDigital[i]); - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("DND_DIGITAL_PLOT", &i, sizeof(int)); - ImGui::TextUnformatted(label); - ImGui::EndDragDropSource(); - } - } - for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) { - char label[32]; - sprintf(label, "analog_%d", i); - ImGui::Checkbox(label, &showAnalog[i]); - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("DND_ANALOG_PLOT", &i, sizeof(int)); - ImGui::TextUnformatted(label); - ImGui::EndDragDropSource(); - } - } - ImGui::EndGroup(); - ImGui::SameLine(); + + static bool paused = false; + static ScrollingBuffer dataDigital[2]; + static ScrollingBuffer dataAnalog[2]; + static bool showDigital[2] = {true, false}; + static bool showAnalog[2] = {true, false}; + + char label[32]; + ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); + ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); + ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); + ImGui::Checkbox("analog_1", &showAnalog[1]); + static float t = 0; if (!paused) { t += ImGui::GetIO().DeltaTime; //digital signal values - int i = 0; - if (showDigital[i]) - dataDigital[i].AddPoint(t, sinf(2*t) > 0.45); - i++; - if (showDigital[i]) - dataDigital[i].AddPoint(t, sinf(2*t) < 0.45); - i++; - if (showDigital[i]) - dataDigital[i].AddPoint(t, fmodf(t,5.0f)); - i++; - if (showDigital[i]) - dataDigital[i].AddPoint(t, sinf(2*t) < 0.17); + if (showDigital[0]) + dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); + if (showDigital[1]) + dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); //Analog signal values - i = 0; - if (showAnalog[i]) - dataAnalog[i].AddPoint(t, sinf(2*t)); - i++; - if (showAnalog[i]) - dataAnalog[i].AddPoint(t, cosf(2*t)); - i++; - if (showAnalog[i]) - dataAnalog[i].AddPoint(t, sinf(2*t) * cosf(2*t)); - i++; - if (showAnalog[i]) - dataAnalog[i].AddPoint(t, sinf(2*t) - cosf(2*t)); + if (showAnalog[0]) + dataAnalog[0].AddPoint(t, sinf(2*t)); + if (showAnalog[1]) + dataAnalog[1].AddPoint(t, cosf(2*t)); } ImPlot::SetNextPlotLimitsY(-1, 1); ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); if (ImPlot::BeginPlot("##Digital")) { - for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) { + for (int i = 0; i < 2; ++i) { if (showDigital[i] && dataDigital[i].Data.size() > 0) { - char label[32]; sprintf(label, "digital_%d", i); ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); } } - for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) { + for (int i = 0; i < 2; ++i) { if (showAnalog[i]) { - char label[32]; sprintf(label, "analog_%d", i); if (dataAnalog[i].Data.size() > 0) ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); @@ -1145,22 +1186,6 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::EndPlot(); } - if (ImGui::BeginDragDropTarget()) { - const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DIGITAL_PLOT"); - if (payload) { - int i = *(int*)payload->Data; - showDigital[i] = true; - } - else - { - payload = ImGui::AcceptDragDropPayload("DND_ANALOG_PLOT"); - if (payload) { - int i = *(int*)payload->Data; - showAnalog[i] = true; - } - } - ImGui::EndDragDropTarget(); - } } if (ImGui::CollapsingHeader("Tables")) { #ifdef IMGUI_HAS_TABLE diff --git a/implot_internal.h b/implot_internal.h index 3ef7ae9..136ad24 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -572,6 +572,7 @@ struct ImPlotPlot ImRect CanvasRect; ImRect PlotRect; ImRect AxesRect; + ImRect LegendRect; ImPlotPlot() { Flags = PreviousFlags = ImPlotFlags_None;