1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-13 22:48:50 -05:00

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
This commit is contained in:
Evan Pezent 2021-02-28 18:10:23 -06:00 committed by GitHub
parent 7234801868
commit 67e0876f89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 369 additions and 252 deletions

View File

@ -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. 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. 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 - 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. to implot_internal.h due to its immaturity.
- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding - 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.AddRectFilled(rect.Min, rect.Max, an.ColorBg);
DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); 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 // render selection/query
if (plot.Selecting) { if (plot.Selecting) {
const ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart)); const ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart));
@ -2423,9 +2400,9 @@ void EndPlot() {
legend_size, legend_size,
plot.LegendLocation, plot.LegendLocation,
plot.LegendOutside ? gp.Style.PlotPadding : gp.Style.LegendPadding); 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 // test hover
plot.LegendHovered = plot.FrameHovered && legend_bb.Contains(IO.MousePos); plot.LegendHovered = plot.FrameHovered && plot.LegendRect.Contains(IO.MousePos);
if (plot.LegendOutside) if (plot.LegendOutside)
ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);
@ -2433,11 +2410,14 @@ void EndPlot() {
PushPlotClipRect(); PushPlotClipRect();
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); DrawList.AddRectFilled(plot.LegendRect.Min, plot.LegendRect.Max, col_bg);
DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); DrawList.AddRect(plot.LegendRect.Min, plot.LegendRect.Max, col_bd);
ShowLegendEntries(plot, legend_bb, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList); ShowLegendEntries(plot, plot.LegendRect, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList);
ImGui::PopClipRect(); ImGui::PopClipRect();
} }
else {
plot.LegendRect = ImRect();
}
if (plot.LegendFlipSideNextFrame) { if (plot.LegendFlipSideNextFrame) {
plot.LegendOutside = !plot.LegendOutside; plot.LegendOutside = !plot.LegendOutside;
plot.LegendFlipSideNextFrame = false; plot.LegendFlipSideNextFrame = false;
@ -2924,6 +2904,150 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe
return dragging; 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) { void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetLegendLocation() needs to be called between BeginPlot() and EndPlot()!"); 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; 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) { bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;

View File

@ -527,7 +527,7 @@ IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label
// Legend Utils and Tools // 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. // Set the location of the current plot's legend.
IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation = ImPlotOrientation_Vertical, bool outside = false); 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); IMPLOT_API void SetMousePosLocation(ImPlotLocation location);
// Returns true if a plot item legend entry is hovered. // Returns true if a plot item legend entry is hovered.
IMPLOT_API bool IsLegendEntryHovered(const char* label_id); 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. // Begin a popup for a legend entry.
IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button = 1); IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button = 1);
// End a popup for a legend entry. // End a popup for a legend entry.
IMPLOT_API void EndLegendPopup(); 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 // Plot and Item Styling
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -645,6 +673,10 @@ IMPLOT_API const char* GetColormapName(ImPlotColormap colormap);
// Miscellaneous // 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. // Get the plot draw list for rendering to the current plot area.
IMPLOT_API ImDrawList* GetPlotDrawList(); IMPLOT_API ImDrawList* GetPlotDrawList();
// Push clip rect for rendering to current plot area. // Push clip rect for rendering to current plot area.

View File

@ -81,8 +81,8 @@ struct ScrollingBuffer {
int MaxSize; int MaxSize;
int Offset; int Offset;
ImVector<ImVec2> Data; ImVector<ImVec2> Data;
ScrollingBuffer() { ScrollingBuffer(int max_size = 2000) {
MaxSize = 2000; MaxSize = max_size;
Offset = 0; Offset = 0;
Data.reserve(MaxSize); Data.reserve(MaxSize);
} }
@ -663,8 +663,8 @@ void ShowDemoWindow(bool* p_open) {
} }
if (ImGui::CollapsingHeader("Time Formatted Axes")) { if (ImGui::CollapsingHeader("Time Formatted Axes")) {
static double t_min = 1577836800; // 01/01/2020 @ 12:00:00am (UTC) static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC)
static double t_max = 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" 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."); "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")) { if (ImGui::CollapsingHeader("Drag and Drop")) {
const int K_CHANNELS = 9; ImGui::BulletText("Drag/drop items from the left column.");
srand((int)(10000000 * DEMO_TIME)); ImGui::BulletText("Drag/drop items between plots.");
static bool paused = false; ImGui::Indent();
static bool init = true; ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend");
static ScrollingBuffer data[K_CHANNELS]; ImGui::BulletText("Plot 1 Sources: Legend Items");
static bool show[K_CHANNELS]; ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis");
static int yAxis[K_CHANNELS]; ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)");
if (init) { ImGui::Unindent();
for (int i = 0; i < K_CHANNELS; ++i) {
show[i] = false; // convenience struct to manage DND items; do this however you like
yAxis[i] = 0; struct MyDndItem {
} int Idx;
init = false; int Plt;
} int Yax;
ImGui::BulletText("Drag data items from the left column onto the plot or onto a specific y-axis."); char Label[16];
ImGui::BulletText("Redrag data items from the legend onto other y-axes."); ImVector<ImVec2> Data;
ImGui::BeginGroup(); ImVec4 Color;
if (ImGui::Button("Clear", ImVec2(100, 0))) { MyDndItem() {
for (int i = 0; i < K_CHANNELS; ++i) { static int i = 0;
show[i] = false; Idx = i++;
data[i].Data.shrink(0); Plt = 0;
data[i].Offset = 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))));
} }
} }
if (ImGui::Button(paused ? "Resume" : "Pause", ImVec2(100,0))) void Reset() { Plt = 0; Yax = ImPlotYAxis_1; }
paused = !paused; };
for (int i = 0; i < K_CHANNELS; ++i) {
char label[16]; const int k_dnd = 20;
sprintf(label, show[i] ? "data_%d (Y%d)" : "data_%d", i, yAxis[i]+1); static MyDndItem dnd[k_dnd];
ImGui::Selectable(label, false, 0, ImVec2(100, 0)); 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;
}
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)) { if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
ImGui::SetDragDropPayload("DND_PLOT", &i, sizeof(int)); ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int));
ImGui::TextUnformatted(label); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine();
ImGui::TextUnformatted(dnd[k].Label);
ImGui::EndDragDropSource(); ImGui::EndDragDropSource();
} }
} }
ImGui::EndGroup(); ImGui::EndChild();
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));
}
}
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();
}
}
}
// make our plot a drag and drop target
if (ImGui::BeginDragDropTarget()) { if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_PLOT")) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) {
int i = *(int*)payload->Data; int i = *(int*)payload->Data; dnd[i].Reset();
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(); ImGui::EndDragDropTarget();
} }
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();
}
}
}
// 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(); 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")) { 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("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::BulletText("Digital signals do not respond to Y drag and zoom, so that");
ImGui::Indent(); ImGui::Indent();
ImGui::Text("you can drag analog signals over the rising/falling digital edge."); ImGui::Text("you can drag analog signals over the rising/falling digital edge.");
ImGui::Unindent(); ImGui::Unindent();
ImGui::BeginGroup();
if (ImGui::Button("Clear", ImVec2(100, 0))) { static bool paused = false;
for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) static ScrollingBuffer dataDigital[2];
showDigital[i] = false; static ScrollingBuffer dataAnalog[2];
for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) static bool showDigital[2] = {true, false};
showAnalog[i] = false; static bool showAnalog[2] = {true, 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]; char label[32];
sprintf(label, "digital_%d", i); ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine();
ImGui::Checkbox(label, &showDigital[i]); ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine();
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine();
ImGui::SetDragDropPayload("DND_DIGITAL_PLOT", &i, sizeof(int)); ImGui::Checkbox("analog_1", &showAnalog[1]);
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 float t = 0; static float t = 0;
if (!paused) { if (!paused) {
t += ImGui::GetIO().DeltaTime; t += ImGui::GetIO().DeltaTime;
//digital signal values //digital signal values
int i = 0; if (showDigital[0])
if (showDigital[i]) dataDigital[0].AddPoint(t, sinf(2*t) > 0.45);
dataDigital[i].AddPoint(t, sinf(2*t) > 0.45); if (showDigital[1])
i++; dataDigital[1].AddPoint(t, sinf(2*t) < 0.45);
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);
//Analog signal values //Analog signal values
i = 0; if (showAnalog[0])
if (showAnalog[i]) dataAnalog[0].AddPoint(t, sinf(2*t));
dataAnalog[i].AddPoint(t, sinf(2*t)); if (showAnalog[1])
i++; dataAnalog[1].AddPoint(t, cosf(2*t));
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));
} }
ImPlot::SetNextPlotLimitsY(-1, 1); ImPlot::SetNextPlotLimitsY(-1, 1);
ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always);
if (ImPlot::BeginPlot("##Digital")) { 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) { if (showDigital[i] && dataDigital[i].Data.size() > 0) {
char label[32];
sprintf(label, "digital_%d", i); 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)); 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]) { if (showAnalog[i]) {
char label[32];
sprintf(label, "analog_%d", i); sprintf(label, "analog_%d", i);
if (dataAnalog[i].Data.size() > 0) 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)); 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(); 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")) { if (ImGui::CollapsingHeader("Tables")) {
#ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_TABLE

View File

@ -572,6 +572,7 @@ struct ImPlotPlot
ImRect CanvasRect; ImRect CanvasRect;
ImRect PlotRect; ImRect PlotRect;
ImRect AxesRect; ImRect AxesRect;
ImRect LegendRect;
ImPlotPlot() { ImPlotPlot() {
Flags = PreviousFlags = ImPlotFlags_None; Flags = PreviousFlags = ImPlotFlags_None;