From bffffce2f7b986a289dbcd2aaa052a294f29a2d7 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Mon, 18 Jan 2021 21:49:23 -0600 Subject: [PATCH] make context menus open on single right click, move ImPlotInputMap out of public API (see implot_internal.h) (#170) --- README.md | 1 - implot.cpp | 42 +++++++++++++++++++++++++++------------ implot.h | 50 ++++++++++++++--------------------------------- implot_internal.h | 27 ++++++++++++++++++++++++- 4 files changed, 70 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 5cb0b7f1..30c7efa2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ ImPlot is an immediate mode, GPU accelerated plotting library for [Dear ImGui](h - up to three independent y-axes - controls for zooming, panning, box selection, and auto-fitting data - controls for creating persistent query ranges (see demo) -- remappable input controls - several plot styling options: 10 marker types, adjustable marker sizes, line weights, outline colors, fill colors, etc. - 10 built-in and user definable colormaps - optional plot titles, axis labels, and grid labels diff --git a/implot.cpp b/implot.cpp index 47c7f43f..2ae66e13 100644 --- a/implot.cpp +++ b/implot.cpp @@ -31,6 +31,8 @@ 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/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 - 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. - 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) @@ -1708,16 +1710,20 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con if (!plot.YAxis[i].IsLockedMax() && y_can_change) plot.YAxis[i].SetMax(ImMax(p1.y, p2.y)); } + if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod))) + plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; } plot.Selecting = false; } // bad selection if (plot.Selecting && (ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) || plot.IsLocked()) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); + plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; } // cancel selection if (plot.Selecting && (IO.MouseClicked[gp.InputMap.BoxSelectCancelButton] || IO.MouseDown[gp.InputMap.BoxSelectCancelButton])) { plot.Selecting = false; + plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; } // begin selection or query if (plot.FrameHovered && plot.PlotHovered && IO.MouseClicked[gp.InputMap.BoxSelectButton] && ImHasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) { @@ -1738,8 +1744,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con // end query if (plot.Querying && (IO.MouseReleased[gp.InputMap.QueryButton] || IO.MouseReleased[gp.InputMap.BoxSelectButton])) { plot.Querying = false; - if (plot.QueryRect.GetWidth() > 2 && plot.QueryRect.GetHeight() > 2) + if (plot.QueryRect.GetWidth() > 2 && plot.QueryRect.GetHeight() > 2) { plot.Queried = true; + plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; + } else plot.Queried = false; } @@ -2466,14 +2474,16 @@ void EndPlot() { // CONTEXT MENUS ----------------------------------------------------------- - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.PlotHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + // main ctx menu + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.PlotHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) ImGui::OpenPopup("##PlotContext"); if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); ImGui::EndPopup(); } - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.XAxis.ExtHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + // x-axis ctx menu + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.XAxis.ExtHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) ImGui::OpenPopup("##XContext"); if (ImGui::BeginPopup("##XContext")) { ImGui::Text("X-Axis"); ImGui::Separator(); @@ -2481,9 +2491,10 @@ void EndPlot() { ImGui::EndPopup(); } + // y-axes ctx menus for (int i = 0; i < IMPLOT_Y_AXES; ++i) { ImGui::PushID(i); - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.YAxis[i].ExtHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.YAxis[i].ExtHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) ImGui::OpenPopup("##YContext"); if (ImGui::BeginPopup("##YContext")) { if (i == 0) { @@ -2507,6 +2518,11 @@ void EndPlot() { // CLEANUP ---------------------------------------------------------------- + // resset context locked flag + if (plot.ContextLocked && IO.MouseReleased[gp.InputMap.BoxSelectButton]) + plot.ContextLocked = false; + + // reset the plot items for the next frame for (int i = 0; i < gp.CurrentPlot->Items.GetSize(); ++i) { gp.CurrentPlot->Items.GetByIndex(i)->SeenThisFrame = false; @@ -3719,27 +3735,27 @@ void ShowStyleEditor(ImPlotStyle* ref) { } void ShowUserGuide() { - ImGui::BulletText("Left click and drag within the plot area to pan X and Y axes."); + ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes."); ImGui::Indent(); - ImGui::BulletText("Left click and drag on an axis to pan an individual axis."); + ImGui::BulletText("Left-click drag on axis labels to pan an individual axis."); ImGui::Unindent(); ImGui::BulletText("Scroll in the plot area to zoom both X any Y axes."); ImGui::Indent(); - ImGui::BulletText("Scroll on an axis to zoom an individual axis."); + ImGui::BulletText("Scroll on axis labels to zoom an individual axis."); ImGui::Unindent(); - ImGui::BulletText("Right click and drag to box select data."); + ImGui::BulletText("Right-click drag to box select data."); ImGui::Indent(); ImGui::BulletText("Hold Alt to expand box selection horizontally."); ImGui::BulletText("Hold Shift to expand box selection vertically."); - ImGui::BulletText("Left click while box selecting to cancel the selection."); + ImGui::BulletText("Left-click while box selecting to cancel the selection."); ImGui::Unindent(); - ImGui::BulletText("Double left click to fit all visible data."); + ImGui::BulletText("Double left-click to fit all visible data."); ImGui::Indent(); - ImGui::BulletText("Double left click on an axis to fit the individual axis."); + ImGui::BulletText("Double left-click axis labels to fit the individual axis."); ImGui::Unindent(); - ImGui::BulletText("Double right click to open the full plot context menu."); + ImGui::BulletText("Right-click open the full plot context menu."); ImGui::Indent(); - ImGui::BulletText("Double right click on an axis to open the axis context menu."); + ImGui::BulletText("Right-click axis labels to open an individual axis context menu."); ImGui::Unindent(); ImGui::BulletText("Click legend label icons to show/hide plot items."); } diff --git a/implot.h b/implot.h index a3f66447..44d0b966 100644 --- a/implot.h +++ b/implot.h @@ -67,24 +67,24 @@ enum ImPlotFlags_ { ImPlotFlags_None = 0, // default ImPlotFlags_NoTitle = 1 << 0, // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot") ImPlotFlags_NoLegend = 1 << 1, // the legend will not be displayed - ImPlotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with double-right click - ImPlotFlags_NoBoxSelect = 1 << 3, // the user will not be able to box-select with right-mouse + ImPlotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click + ImPlotFlags_NoBoxSelect = 1 << 3, // the user will not be able to box-select with right-click drag ImPlotFlags_NoMousePos = 1 << 4, // the mouse position, in plot coordinates, will not be displayed inside of the plot ImPlotFlags_NoHighlight = 1 << 5, // plot items will not be highlighted when their legend entry is hovered ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_Equal = 1 << 7, // primary x and y axes will be constrained to have the same units/pixel (does not apply to auxiliary y axes) + ImPlotFlags_Equal = 1 << 7, // primary x and y axes will be constrained to have the same units/pixel (does not apply to auxiliary y-axes) ImPlotFlags_YAxis2 = 1 << 8, // enable a 2nd y-axis on the right side ImPlotFlags_YAxis3 = 1 << 9, // enable a 3rd y-axis on the right side - ImPlotFlags_Query = 1 << 10, // the user will be able to draw query rects with middle-mouse + ImPlotFlags_Query = 1 << 10, // the user will be able to draw query rects with middle-mouse or CTRL + right-click drag ImPlotFlags_Crosshairs = 1 << 11, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_AntiAliased = 1 << 12, // plot lines will be software anti-aliased (not recommended for density plots, prefer MSAA) + ImPlotFlags_AntiAliased = 1 << 12, // plot lines will be software anti-aliased (not recommended for high density plots, prefer MSAA) ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMousePos }; // Options for plot axes (X and Y). enum ImPlotAxisFlags_ { ImPlotAxisFlags_None = 0, // default - ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels also hidden if string is NULL) + ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels also hidden if the supplied string name is NULL) ImPlotAxisFlags_NoGridLines = 1 << 1, // the axis grid lines will not be displayed ImPlotAxisFlags_NoTickMarks = 1 << 2, // the axis tick marks will not be displayed ImPlotAxisFlags_NoTickLabels = 1 << 3, // the axis tick labels will not be displayed @@ -291,23 +291,6 @@ struct ImPlotStyle { IMPLOT_API ImPlotStyle(); }; -// Input mapping structure, default values listed in the comments. -struct ImPlotInputMap { - ImGuiMouseButton PanButton; // LMB enables panning when held - ImGuiKeyModFlags PanMod; // none optional modifier that must be held for panning - ImGuiMouseButton FitButton; // LMB fits visible data when double clicked - ImGuiMouseButton ContextMenuButton; // RMB opens plot context menu (if enabled) when double clicked - ImGuiMouseButton BoxSelectButton; // RMB begins box selection when pressed and confirms selection when released - ImGuiKeyModFlags BoxSelectMod; // none optional modifier that must be held for box selection - ImGuiMouseButton BoxSelectCancelButton; // LMB cancels active box selection when pressed - ImGuiMouseButton QueryButton; // MMB begins query selection when pressed and end query selection when released - ImGuiKeyModFlags QueryMod; // none optional modifier that must be held for query selection - ImGuiKeyModFlags QueryToggleMod; // Ctrl when held, active box selections turn into queries - ImGuiKeyModFlags HorizontalMod; // Alt expands active box selection/query horizontally to plot edge when held - ImGuiKeyModFlags VerticalMod; // Shift expands active box selection/query vertically to plot edge when held - IMPLOT_API ImPlotInputMap(); -}; - //----------------------------------------------------------------------------- // ImPlot End-User API //----------------------------------------------------------------------------- @@ -461,7 +444,7 @@ IMPLOT_API void PlotDummy(const char* label_id); // Plot Utils //----------------------------------------------------------------------------- -// The following functions MUST be called before BeginPlot! +// The following functions MUST be called BEFORE BeginPlot! // Set the axes range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axes limits will be locked. IMPLOT_API void SetNextPlotLimits(double xmin, double xmax, double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once); @@ -482,7 +465,7 @@ IMPLOT_API void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const IMPLOT_API void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[] = NULL, bool show_default = false, ImPlotYAxis y_axis = 0); IMPLOT_API void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[] = NULL, bool show_default = false, ImPlotYAxis y_axis = 0); -// The following functions MUST be called between Begin/EndPlot! +// The following functions MUST be called BETWEEN Begin/EndPlot! // Select which Y axis will be used for subsequent plot elements. The default is ImPlotYAxis_1, or the first (left) Y axis. Enable 2nd and 3rd axes with ImPlotFlags_YAxisX. IMPLOT_API void SetPlotYAxis(ImPlotYAxis y_axis); @@ -519,7 +502,7 @@ IMPLOT_API ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis = IMPLOT_AUTO); // Plot Tools //----------------------------------------------------------------------------- -// The following functions MUST be called between Begin/EndPlot! +// The following functions MUST be called BETWEEN Begin/EndPlot! // Shows an annotation callout at a chosen point. IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const char* fmt, ...) IM_FMTARGS(4); @@ -622,12 +605,12 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // Colormaps //----------------------------------------------------------------------------- -// Item styling is based on Colormaps when the relevant ImPlotCol is set to +// Item styling is based on Colormaps when the relevant ImPlotCol_ is set to // IMPLOT_AUTO_COL (default). Several built in colormaps are available and can be // toggled in the demo. You can push/pop or set your own colormaps as well. -// The Colormap data will be ignored and a custom color will be used if you have either: -// 1) Modified an item style color in your ImPlotStyle to anything but IMPLOT_AUTO_COL. +// The Colormap data will be ignored and a custom color will be used if you have done one of the following: +// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. @@ -638,9 +621,9 @@ IMPLOT_API void PushColormap(const ImVec4* colormap, int size); // Undo temporary colormap modification. IMPLOT_API void PopColormap(int count = 1); -// Permanently sets a custom colormap. The colors will be copied to internal memory. Prefer PushColormap instead of calling this each frame. +// Permanently sets a custom colormap. The colors will be copied to internal memory. Typically used on startup. Prefer PushColormap instead of calling this each frame. IMPLOT_API void SetColormap(const ImVec4* colormap, int size); -// Permanently switch to one of the built-in colormaps. If samples is greater than 1, the map will be linearly resampled. Don't call this each frame. +// Permanently switch to one of the built-in colormaps. If samples is greater than 1, the map will be linearly resampled. Typically used on startup. Don't call this each frame. IMPLOT_API void SetColormap(ImPlotColormap colormap, int samples = 0); // Returns the size of the current colormap. @@ -652,7 +635,7 @@ IMPLOT_API ImVec4 LerpColormap(float t); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImVec4 NextColormapColor(); -// Renders a vertical color scale using the current color map. Call this outside of Begin/EndPlot. +// Renders a vertical color scale using the current color map. Call this before or after Begin/EndPlot. IMPLOT_API void ShowColormapScale(double scale_min, double scale_max, float height); // Returns a null terminated string name for a built-in colormap. @@ -662,9 +645,6 @@ IMPLOT_API const char* GetColormapName(ImPlotColormap colormap); // Miscellaneous //----------------------------------------------------------------------------- -// Allows changing how keyboard/mouse interaction works. -IMPLOT_API ImPlotInputMap& GetInputMap(); - // 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_internal.h b/implot_internal.h index 7df277cd..3ef7ae95 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -207,6 +207,23 @@ enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] ImPlotTimeFmt_Hr // 7pm [ 19:00 ] }; +// Input mapping structure, default values listed in the comments. +struct ImPlotInputMap { + ImGuiMouseButton PanButton; // LMB enables panning when held + ImGuiKeyModFlags PanMod; // none optional modifier that must be held for panning + ImGuiMouseButton FitButton; // LMB fits visible data when double clicked + ImGuiMouseButton ContextMenuButton; // RMB opens plot context menu (if enabled) when clicked + ImGuiMouseButton BoxSelectButton; // RMB begins box selection when pressed and confirms selection when released + ImGuiKeyModFlags BoxSelectMod; // none optional modifier that must be held for box selection + ImGuiMouseButton BoxSelectCancelButton; // LMB cancels active box selection when pressed + ImGuiMouseButton QueryButton; // MMB begins query selection when pressed and end query selection when released + ImGuiKeyModFlags QueryMod; // none optional modifier that must be held for query selection + ImGuiKeyModFlags QueryToggleMod; // Ctrl when held, active box selections turn into queries + ImGuiKeyModFlags HorizontalMod; // Alt expands active box selection/query horizontally to plot edge when held + ImGuiKeyModFlags VerticalMod; // Shift expands active box selection/query vertically to plot edge when held + IMPLOT_API ImPlotInputMap(); +}; + //----------------------------------------------------------------------------- // [SECTION] ImPlot Structs //----------------------------------------------------------------------------- @@ -537,6 +554,7 @@ struct ImPlotPlot ImVec2 QueryStart; ImRect QueryRect; bool Selecting; + bool ContextLocked; bool Querying; bool Queried; bool DraggingQuery; @@ -561,7 +579,7 @@ struct ImPlotPlot for (int i = 0; i < IMPLOT_Y_AXES; ++i) YAxis[i].Orientation = ImPlotOrientation_Vertical; SelectStart = QueryStart = ImVec2(0,0); - Selecting = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSideNextFrame = false; + Selecting = ContextLocked = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSideNextFrame = false; ColormapIdx = CurrentYAxis = 0; LegendLocation = ImPlotLocation_North | ImPlotLocation_West; LegendOrientation = ImPlotOrientation_Vertical; @@ -714,6 +732,13 @@ IMPLOT_API void Initialize(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginPlot IMPLOT_API void Reset(ImPlotContext* ctx); +//----------------------------------------------------------------------------- +// [SECTION] Input Utils +//----------------------------------------------------------------------------- + +// Allows changing how keyboard/mouse interaction works. +IMPLOT_API ImPlotInputMap& GetInputMap(); + //----------------------------------------------------------------------------- // [SECTION] Plot Utils //-----------------------------------------------------------------------------