How to Implement Custom Data Types for Sliders and Drag Controls in ImGui
Dear ImGui's generic slider and drag widgets work with any scalar type by extending the ImGuiDataType_ enum and providing a corresponding ImGuiDataTypeInfo entry that describes size, format, and conversion traits.
Dear ImGui (ocornut/imgui) provides generic scalar widgets that manipulate raw memory through type descriptors, allowing you to implement custom data types for sliders and drag controls without modifying the core widget logic. Instead of limiting sliders to built-in primitives like float or int, the library exposes SliderScalar and DragScalar functions that accept any POD type through a declarative type system defined in imgui.h and imgui.cpp.
Understanding the Type System Architecture
The extensibility lives in three coordinated components that bridge the public API and internal implementations.
The ImGuiDataType_ Enum (imgui.h)
Located at line 1515 in imgui.h, the ImGuiDataType_ enumeration declares unique integer identifiers for each supported type. When you add a custom type, you append a new entry here (e.g., ImGuiDataType_MyVec2) before ImGuiDataType_COUNT. This identifier serves as the bridge between the generic widget API and the type metadata table.
The ImGuiDataTypeInfo Table (imgui.cpp)
Around line 778 in imgui.cpp, the GetImGuiDataTypeInfo() function (backed by the static GImGuiDataTypeInfo[] table) maps each enum value to an ImGuiDataTypeInfo struct. This metadata supplies the widget code with:
- Size: Byte size of the type (
sizeof(MyVec2)) - PrintFmt: Default format string for text rendering (e.g.,
"%0.2f") - IsIntegral and IsFloatingPoint: Classification flags that determine clamping behavior
Generic Widget Implementation (imgui_widgets.cpp)
The SliderScalar implementation in imgui_widgets.cpp (around line 3316) and the DragScalar functions treat the data pointer as opaque raw memory. They delegate bounds checking and interaction handling to SliderBehavior and DragBehavior, using the type metadata to perform clamping and string formatting without knowing the concrete C++ type.
Step-by-Step Implementation
Follow these steps to expose a new data type to the slider and drag controls.
Step 1: Extend the ImGuiDataType_ Enum
Add your custom type identifier to the enum in imgui.h:
enum ImGuiDataType_
{
// ... existing entries like ImGuiDataType_Float, ImGuiDataType_S32 ...
ImGuiDataType_MyVec2, // Custom 2D vector
ImGuiDataType_MyBlendMode, // Custom enum
ImGuiDataType_COUNT
};
Step 2: Define Type Metadata in ImGuiDataTypeInfo
Register the type characteristics in imgui.cpp within the GImGuiDataTypeInfo array:
static const ImGuiDataTypeInfo GImGuiDataTypeInfo[] =
{
// ... existing entries ...
{ sizeof(MyVec2), "%0.2f", false, false }, // MyVec2 - treated as floating point
{ sizeof(int), "%d", true, false } // MyBlendMode - stored as int
};
Step 3: Use SliderScalar and DragScalar with Custom Types
Call the generic widgets with your new type identifier:
MyVec2 v = {0.5f, 0.25f};
MyVec2 v_min = {0.0f, 0.0f};
MyVec2 v_max = {1.0f, 1.0f};
ImGui::SliderScalar("MyVec2", ImGuiDataType_MyVec2, &v, &v_min, &v_max, "%.2f");
Practical Code Examples
Below is a complete implementation showing a custom vector struct and enum working with the generic widget API.
// ------------------------------------------------------------
// 1. Define custom types
// ------------------------------------------------------------
struct MyVec2 { float x, y; };
enum class MyBlendMode : int { Opaque = 0, Transparent = 1, Additive = 2 };
// ------------------------------------------------------------
// 2. Extend ImGuiDataType_ (in imgui.h)
// ------------------------------------------------------------
enum ImGuiDataType_
{
// ... existing entries ...
ImGuiDataType_MyVec2, // Custom type 1
ImGuiDataType_MyBlendMode, // Custom type 2
ImGuiDataType_COUNT
};
// ------------------------------------------------------------
// 3. Add DataTypeInfo entries (in imgui.cpp)
// ------------------------------------------------------------
static const ImGuiDataTypeInfo GImGuiDataTypeInfo[] =
{
// ... existing entries ...
{ sizeof(MyVec2), "%0.2f", false, false }, // MyVec2
{ sizeof(int), "%d", true, false } // MyBlendMode (int-based)
};
// ------------------------------------------------------------
// 4. Use the generic widgets
// ------------------------------------------------------------
void ShowCustomWidgets()
{
static MyVec2 v = { 0.5f, 0.25f };
static MyVec2 v_min = { 0.0f, 0.0f };
static MyVec2 v_max = { 1.0f, 1.0f };
static MyBlendMode mode = MyBlendMode::Opaque;
static int mode_min = 0;
static int mode_max = 2;
// Slider for the whole vector (2 components)
ImGui::SliderScalarN("MyVec2", ImGuiDataType_MyVec2, &v, 2,
&v_min, &v_max, "%.2f");
// Drag control for a single component
ImGui::DragScalar("MyVec2 X", ImGuiDataType_Float, &v.x, 0.01f,
&v_min.x, &v_max.x, "%.3f");
// Slider for the custom enum using integer representation
ImGui::SliderScalar("Blend Mode", ImGuiDataType_MyBlendMode,
&mode, &mode_min, &mode_max, "%d");
// Display human-readable label
const char* mode_names[] = { "Opaque", "Transparent", "Additive" };
ImGui::SameLine();
ImGui::Text("(%s)", mode_names[(int)mode]);
}
Working with Multi-Component Types
For vector types containing multiple scalars, use SliderScalarN or DragScalarN as shown in the example above. These functions treat the data pointer as an array of the specified type, iterating through components elements. When operating on a MyVec2 with components=2, the widget applies the same min/max bounds to each float within the struct sequentially.
Since the widget operates on raw memory, you can also wrap existing types by creating a thin struct containing a single scalar value and exposing it via a custom ImGuiDataType_ entry. The widget reads and writes the underlying bytes transparently, allowing you to impose semantic meaning on standard primitives without changing the rendering logic.
Summary
- Extend
ImGuiDataType_inimgui.hto declare your custom type identifier beforeImGuiDataType_COUNT - Populate
ImGuiDataTypeInfoinimgui.cppwith size, format string, and type classification flags (IsIntegral,IsFloatingPoint) - Use
SliderScalar,DragScalar,SliderScalarN, orDragScalarNwith your new type enum and appropriate min/max pointers - The generic widgets in
imgui_widgets.cpphandle clamping viaSliderBehaviorandDragBehaviorusing the raw memory description - Multi-component types work transparently with the
...ScalarNvariants that iterate over contiguous memory blocks
Frequently Asked Questions
Can I implement custom data types without modifying Dear ImGui's source code?
Currently, extending ImGuiDataType_ and ImGuiDataTypeInfo requires modifying imgui.h and imgui.cpp because the type table is static and indexed by the enum. However, you can work around this limitation by treating your custom type as a supported underlying type (like float or int) and handling the conversion manually in your application code before calling the widget.
How does SliderScalar handle bounds checking for custom structs?
The SliderBehavior function performs bounds checking using raw memory comparisons based on the Size field from ImGuiDataTypeInfo. For multi-component types like MyVec2, the widget treats each component as an independent scalar when using SliderScalarN, applying the min/max values to each float within the struct. The widget clamps values by comparing the underlying bytes as if they were the specified scalar type.
What format strings work with custom types in DragScalar?
The PrintFmt field in your ImGuiDataTypeInfo entry determines the default formatting, but you can override this by passing a custom format string as the final parameter to DragScalar or SliderScalar. For integer-based enums, use "%d"; for floating-point vectors, use "%.2f" or similar precision specifiers. The format string follows standard printf conventions compatible with the underlying scalar representation.
Why does my custom type slider show incorrect values or corrupt memory?
Ensure the Size field in your ImGuiDataTypeInfo entry exactly matches sizeof(YourType) and that the IsFloatingPoint or IsIntegral flags correctly reflect the underlying representation. Mismatched sizes cause the widget to read incorrect memory offsets, while incorrect type flags affect the formatting logic and clamping behavior. Verify that your custom type is a POD (Plain Old Data) struct without virtual functions or complex constructors.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →