Jesse A. Green / notebook

Dear ImGui — User Guide

Generated May 2026 from ocornut/imgui at bda4982

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Dear ImGui is a bloat-free graphical user interface library for C++. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer-agnostic, and self-contained (no external dependencies). Dear ImGui is designed to enable fast iterations and to empower programmers to create content creation tools, visualization/debug tools, and any UI for games or real-time applications.

Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any application where operating-system features are non-standard.


1. Quick Start

What Is Dear ImGui?

Dear ImGui (short for "Dear Immediate Mode Graphical User Interface") is a C++ library that makes it trivially easy to add debug UIs, tools, and overlays to any application that already has a rendering pipeline. Unlike retained-mode GUI frameworks (Qt, wxWidgets, GTK), Dear ImGui redraws the entire UI every frame from your code — there is no hidden widget state to synchronize. You describe the UI, Dear ImGui draws it, and returns interaction results immediately.

Key Features

  • Immediate mode — no widget objects, no callbacks, no synchronization. UI code lives alongside your logic.
  • Renderer agnostic — outputs vertex buffers (ImDrawList) that you feed to any GPU API: OpenGL, DirectX 9/10/11/12, Vulkan, Metal, WebGPU, SDL_GPU, or your own.
  • Self-contained — no external dependencies. STB-style single-header libraries for font rasterization and text editing are bundled.
  • Keyboard & gamepad navigation — full navigation support without a mouse, including tabbing, directional movement, and activation.
  • Tables — feature-rich table widget with sorting, reordering, hiding columns, horizontal scrolling, and freeze rows/columns.
  • Multi-select — beta API for multi-selection in lists, tables, and tree nodes with range-select and Ctrl+click.
  • Drag and drop — built-in drag-and-drop system with typed payloads between any widgets.
  • Extensive widget library — text inputs, sliders, drags, color pickers, trees, tabs, menus, popups, modals, plots, images, and more.

Prerequisites

Requirement Minimum Recommended
C++ standard C++11 C++14 or later
Operating system Any (Windows, macOS, Linux, iOS, Android, Emscripten) —
Graphics API Any (or software rasterizer) OpenGL 3+, DirectX 11, Vulkan
Build system Any (add .cpp files to your project) —

Minimal Code Example

#include "imgui.h"
#include "imgui_impl_allegro5.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>

int main()
{
    // Platform/renderer setup (Allegro5 example)
    al_init();
    al_init_primitives_addon();
    ALLEGRO_DISPLAY* display = al_create_display(1280, 720);

    // Initialize Dear ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();

    // Setup platform/renderer backends
    ImGui_ImplAllegro5_Init(display);

    // Style
    ImGui::StyleColorsDark();

    bool running = true;
    while (running)
    {
        // Poll events...
        ImGui_ImplAllegro5_NewFrame();
        ImGui::NewFrame();

        // Your UI code — this is the entire API pattern:
        ImGui::Begin("My Tool");
        ImGui::Text("Hello, Dear ImGui!");
        static float value = 0.0f;
        ImGui::SliderFloat("Value", &value, 0.0f, 1.0f);
        if (ImGui::Button("Press Me"))
        {
            // React to button press
        }
        ImGui::End();

        // Render
        ImGui::Render();
        al_clear_to_color(al_map_rgb(0, 0, 0));
        ImGui_ImplAllegro5_RenderDrawData(ImGui::GetDrawData());
        al_flip_display();
    }

    // Cleanup
    ImGui_ImplAllegro5_Shutdown();
    ImGui::DestroyContext();
    al_destroy_display(display);
    return 0;
}

What to Try Next

  1. ShowDemoWindow() — call ImGui::ShowDemoWindow() anywhere in your frame loop to see every widget in action with source code.
  2. Examples — the examples/ folder contains 26 ready-to-build projects for every major platform/renderer combination.
  3. Wiki — the Dear ImGui Wiki covers integration, third-party bindings, and community resources.

Tip

ImGui::ShowDemoWindow() is the single best learning resource for Dear ImGui. Every widget, layout technique, and API pattern is demonstrated with inline code. You can Ctrl+click on any demo code in the Demo window to jump to the corresponding source in imgui_demo.cpp. Treat it as a living, interactive reference manual.


2. Installation

Dear ImGui is a source-level library — there is no package manager install, no pre-built binaries, and no cmake install target. You add the source files directly to your project.

System Requirements

Component Requirement
Language C++11 or later
OS Windows, macOS, Linux, iOS, Android, Emscripten, consoles
GPU API Any — Dear ImGui outputs vertex buffers; you provide the renderer
Dependencies None — all dependencies are bundled
Build system Any — Visual Studio, Make, CMake, Meson, Premake, Xcode, etc.

Step 1: Add Core Files

Copy these files into your project (or add them as includes from the imgui directory):

File Purpose
imgui.cpp Core library (context, windows, widgets, navigation)
imgui_draw.cpp Drawing, font atlas, ImDrawList
imgui_tables.cpp Table widget
imgui_widgets.cpp Standard widgets (buttons, sliders, inputs, trees, etc.)
imgui.h Main header — include this in your code
imgui_internal.h Internal header (included by imgui.cpp; rarely needed by users)
imconfig.h Compile-time configuration (user-editable)
imstb_rectpack.h STB rect packing for font atlas
imstb_textedit.h STB text editing for InputText
imstb_truetype.h STB TrueType rasterizer (unless using FreeType)

Step 2: Pick a Platform + Renderer Backend

Backends live in the backends/ folder. You need one platform backend (handles window creation, input, clipboard) and one renderer backend (handles drawing via GPU API).

Backend Combinations

Platform ↓ / Renderer → OpenGL2 OpenGL3 DirectX9 DirectX10 DirectX11 DirectX12 Vulkan Metal WebGPU SDL_GPU SDL_Renderer Allegro5
GLFW Yes Yes — — — — Yes Yes Yes — — —
SDL2 Yes Yes Yes Yes Yes Yes Yes Yes — — Yes (2) —
SDL3 — Yes — — — — Yes Yes Yes Yes Yes (3) —
Win32 — — Yes Yes Yes Yes Yes — — — — —
GLUT — Yes — — — — — — — — — —
OSX (Cocoa) — Yes — — — — — Metal — — — —
Android — Yes — — — — — — — — — —
Allegro5 — — — — — — — — — — — Yes

Backend files follow the naming convention imgui_impl_<name>.h and imgui_impl_<name>.cpp. For example, to use GLFW + OpenGL3:

// Add these files to your build:
// backends/imgui_impl_glfw.cpp
// backends/imgui_impl_glfw.h
// backends/imgui_impl_opengl3.cpp
// backends/imgui_impl_opengl3.h

#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

Step 3: Review Example Applications

The examples/ folder contains ready-to-build projects for every supported backend combination:

Example Platform Renderer
example_allegro5/ Allegro5 Allegro5
example_android_opengl3/ Android OpenGL ES 3
example_apple_metal/ OSX (Cocoa) Metal
example_apple_opengl2/ OSX (Cocoa) OpenGL 2
example_emscripten_wgpu/ Emscripten + GLFW WebGPU
example_glfw_metal/ GLFW Metal
example_glfw_opengl2/ GLFW OpenGL 2
example_glfw_opengl3/ GLFW OpenGL 3
example_glfw_vulkan/ GLFW Vulkan
example_glfw_wgpu/ GLFW WebGPU
example_glut_opengl2/ GLUT OpenGL 2
example_null/ Null Null (no rendering)
example_sdl2_directx11/ SDL2 DirectX 11
example_sdl2_metal/ SDL2 Metal
example_sdl2_opengl2/ SDL2 OpenGL 2
example_sdl2_opengl3/ SDL2 OpenGL 3
example_sdl2_sdlrenderer2/ SDL2 SDL_Renderer (SDL2)
example_sdl2_vulkan/ SDL2 Vulkan
example_sdl3_opengl3/ SDL3 OpenGL 3
example_sdl3_sdlgpu3/ SDL3 SDL_GPU
example_sdl3_sdlrenderer3/ SDL3 SDL_Renderer (SDL3)
example_sdl3_vulkan/ SDL3 Vulkan
example_win32_directx9/ Win32 DirectX 9
example_win32_directx10/ Win32 DirectX 10
example_win32_directx11/ Win32 DirectX 11
example_win32_directx12/ Win32 DirectX 12
example_win32_opengl3/ Win32 OpenGL 3
example_win32_vulkan/ Win32 Vulkan

Each example directory contains a main.cpp and platform-specific project files (.vcxproj, Makefile, build_win32.bat, etc.).

Step 4: Build

Add the .cpp files to your existing build system. No special build process is required — no code generation, no pre-processing, no configure step.

Visual Studio: Add the .cpp files to your project. Add the imgui/ and imgui/backends/ directories to your include paths.

CMake:

add_executable(my_app
    main.cpp
    imgui/imgui.cpp
    imgui/imgui_draw.cpp
    imgui/imgui_tables.cpp
    imgui/imgui_widgets.cpp
    imgui/backends/imgui_impl_glfw.cpp
    imgui/backends/imgui_impl_opengl3.cpp
)
target_include_directories(my_app PRIVATE imgui imgui/backends)

Makefile:

IMGUI_SRCS = imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp
BACKEND_SRCS = imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp

Step 5: Verify

// In your initialization code:
IMGUI_CHECKVERSION();  // Verify header/library version match
ImGui::CreateContext();
ImGui::ShowDemoWindow(); // Should display the demo window

Important

imconfig.h is a compile-time configuration file. If you modify it, you must recompile ALL translation units that include imgui.h. Inconsistent imconfig.h across translation units causes subtle memory corruption and crashes. If Dear ImGui is compiled as a static library or DLL, the same imconfig.h must be used for both the library build and your application.

Optional: FreeType Font Rasterizer

For higher-quality font rendering, enable FreeType:

  1. #define IMGUI_ENABLE_FREETYPE in imconfig.h
  2. Add misc/freetype/imgui_freetype.cpp to your build
  3. Link against FreeType library

For SVG-in-font support (color emoji, icon fonts), additionally define IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG and link the corresponding library.

Optional: std::string Support

// Add to your build:
// misc/cpp/imgui_stdlib.cpp
// misc/cpp/imgui_stdlib.h

#include "misc/cpp/imgui_stdlib.h"

std::string my_text = "Hello";
ImGui::InputText("Input", &my_text);  // Works with std::string directly

Optional: Debugger Helpers

File Debugger Purpose
misc/debuggers/imgui.natvis Visual Studio Pretty-print ImVector, ImGuiID, ImGuiCol, etc.
misc/debuggers/imgui.gdb GDB Pretty-print ImVector
misc/debuggers/imgui.natstepfilter Visual Studio Skip internal functions when stepping
misc/debuggers/imgui.py LLDB Pretty-print for LLDB

Optional: Single-File Build

For projects that prefer a single compilation unit:

// imgui_single_file.h is provided in misc/single_file/
#include "misc/single_file/imgui_single_file.h"

This #includes all four core .cpp files into a single translation unit.


3. Configuration

Compile-Time Configuration (imconfig.h)

These #defines must be set in imconfig.h before compiling. They cannot be changed at runtime.

Define Default Purpose
IM_ASSERT(EXPR) assert(EXPR) Override assertion macro. Define to your own crash/log handler.
IMGUI_API (empty) Decorate public symbols for DLL export/import (__declspec(dllexport), etc.)
IMGUI_DISABLE_OBSOLETE_FUNCTIONS Not set Remove deprecated functions to catch outdated usage at compile time
IMGUI_DISABLE_OBSOLETE_KEYIO Not set Remove legacy key mapping (pre-1.87 backends)
IMGUI_DISABLE Not set Stub out all functions (for conditional compilation)
IMGUI_DISABLE_DEMO_WINDOWS Not set Remove ShowDemoWindow() and imgui_demo.cpp code
IMGUI_DISABLE_DEBUG_TOOLS Not set Remove debug/metrics tools
IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS Not set Disable Win32 clipboard handling
IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS Not set Disable Win32 IME handling
IMGUI_DISABLE_WIN32_FUNCTIONS Not set Disable all Win32-specific functions
IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS Not set Disable default ImGui::PlatformOpenInShellFn
IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS Not set Disable default ImFileOpen/ImFileClose/ImFileRead/ImFileWrite
IMGUI_DISABLE_DEFAULT_ALLOCATORS Not set Disable default malloc/free-based allocators
IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS Not set Disable bundled ImFormatString/ImFormatStringV
IMGUI_ENABLE_TEST_ENGINE Not set Enable hooks for Dear ImGui Test Engine
IMGUI_USE_BGRA_PACKED_COLOR Not set Use BGRA color packing (for some Direct3D backends)
IMGUI_USE_WCHAR32 Not set Use 32-bit ImWchar for full Unicode support beyond BMP
IMGUI_ENABLE_FREETYPE Not set Use FreeType for font rasterization instead of stb_truetype
IMGUI_ENABLE_FREETYPE_PLUTOSVG Not set Enable PlutoSVG for SVG-in-font support (requires FreeType)
IMGUI_ENABLE_FREETYPE_LUNASVG Not set Enable LunaSVG for SVG-in-font support (requires FreeType)
IMGUI_DEFINE_MATH_OPERATORS Not set Enable +, -, *, / operators on ImVec2/ImVec4
ImDrawIdx unsigned short Set to unsigned int for 32-bit draw indices (meshes >64K vertices)
IM_VEC2_CLASS_EXTRA Not set Inject methods/operators into ImVec2 (e.g., conversion to your math type)
IM_VEC4_CLASS_EXTRA Not set Inject methods/operators into ImVec4
IMGUI_STB_TRUETYPE_FILENAME "imstb_truetype.h" Override bundled stb_truetype
IMGUI_STB_RECT_PACK_FILENAME "imstb_rectpack.h" Override bundled stb_rectpack
IMGUI_STB_SPRINTF_FILENAME Not set Use stb_sprintf instead of system snprintf

Math type interop example:

// In imconfig.h — let ImVec2 convert to/from your engine's Vector2:
#define IM_VEC2_CLASS_EXTRA \
    constexpr ImVec2(const MyEngine::Vector2& v) : x(v.x), y(v.y) {} \
    operator MyEngine::Vector2() const { return MyEngine::Vector2(x, y); }

Runtime Configuration (ImGuiIO)

After CreateContext(), configure behavior through ImGui::GetIO():

ImGuiIO& io = ImGui::GetIO();

// Navigation
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Keyboard navigation
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;    // Gamepad navigation

// Behavior
io.ConfigInputTextCursorBlink = true;          // Blinking cursor in text inputs
io.ConfigWindowsResizeFromEdges = true;        // Resize from window edges (not just corner)
io.ConfigWindowsMoveFromTitleBarOnly = false;   // If true, only title bar drags windows
io.ConfigScrollbarScrollByPage = true;         // Page-scroll when clicking scrollbar track

// Persistence
io.IniFilename = "imgui.ini";  // Set to NULL to disable auto-save/load

// Display
io.FontGlobalScale = 1.0f;    // Global font scale multiplier

Key ImGuiConfigFlags:

Flag Purpose
ImGuiConfigFlags_NavEnableKeyboard Enable keyboard navigation
ImGuiConfigFlags_NavEnableGamepad Enable gamepad navigation
ImGuiConfigFlags_NoMouseCursorChange Prevent Dear ImGui from changing the OS cursor shape
ImGuiConfigFlags_NoMouse Disable mouse input
ImGuiConfigFlags_NavNoCaptureKeyboard Do not set io.WantCaptureKeyboard during navigation

Built-In Styles

ImGui::StyleColorsDark();     // Dark theme (default) — best for most tools
ImGui::StyleColorsLight();    // Light theme
ImGui::StyleColorsClassic();  // Classic ImGui theme (gray with blue highlights)

Tip

Call ImGui::ShowStyleEditor() anywhere in your frame loop to open an interactive style editor. You can tweak every color, size, and rounding value in real time and export the result as code. This is the fastest way to create a custom theme.

For a complete reference of all configuration options, style variables, and color IDs, see the Admin Guide.


4. Core Usage

Dear ImGui follows a consistent pattern: every frame, you call functions that describe your UI, and Dear ImGui handles layout, drawing, and interaction. This section covers every widget category organized by what you want to accomplish.

4.1 Creating Windows

Every piece of Dear ImGui content lives inside a window created with Begin()/End():

ImGui::Begin("My Window");
// ... widgets go here ...
ImGui::End();

Begin() returns false when the window is collapsed, but you must always call End() regardless. For optimization, skip widget code when collapsed:

if (ImGui::Begin("My Window"))
{
    ImGui::Text("Visible content");
}
ImGui::End();  // Always required

Window Flags

Pass flags as the third parameter to Begin():

ImGui::Begin("My Window", &show_window, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoResize);
Flag Effect
ImGuiWindowFlags_NoTitleBar No title bar
ImGuiWindowFlags_NoResize User cannot resize
ImGuiWindowFlags_NoMove User cannot move
ImGuiWindowFlags_NoScrollbar No scrollbar (content clips)
ImGuiWindowFlags_NoScrollWithMouse Disable mouse wheel scrolling
ImGuiWindowFlags_NoCollapse No collapse button
ImGuiWindowFlags_AlwaysAutoResize Resize to fit content every frame
ImGuiWindowFlags_NoBackground No background fill
ImGuiWindowFlags_NoSavedSettings Never save/load position/size in .ini
ImGuiWindowFlags_MenuBar Has a menu bar (use with BeginMenuBar())
ImGuiWindowFlags_HorizontalScrollbar Enable horizontal scrollbar
ImGuiWindowFlags_NoFocusOnAppearing Don't take focus when appearing
ImGuiWindowFlags_NoBringToFrontOnFocus Don't raise to front when focused
ImGuiWindowFlags_AlwaysVerticalScrollbar Always show vertical scrollbar
ImGuiWindowFlags_AlwaysHorizontalScrollbar Always show horizontal scrollbar
ImGuiWindowFlags_NoNavInputs No navigation within this window
ImGuiWindowFlags_NoNavFocus No focusing via navigation
ImGuiWindowFlags_NoNav Shorthand for NoNavInputs + NoNavFocus
ImGuiWindowFlags_NoDecoration Shorthand for NoTitleBar + NoResize + NoScrollbar + NoCollapse
ImGuiWindowFlags_NoInputs Shorthand for NoMouseInputs + NoNavInputs + NoNavFocus

Window Manipulation

Set properties for the next Begin() call:

ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowCollapsed(false, ImGuiCond_Once);
ImGui::SetNextWindowFocus();
ImGui::SetNextWindowBgAlpha(0.8f);

Condition flags (ImGuiCond_):

Flag When applied
ImGuiCond_Always Every frame
ImGuiCond_Once First time only (per session)
ImGuiCond_FirstUseEver First time ever (across .ini sessions)
ImGuiCond_Appearing Each time window appears after being hidden

Child Windows

Create scrollable sub-regions within a window:

// With explicit size
ImGui::BeginChild("ScrollRegion", ImVec2(0, 300), ImGuiChildFlags_Borders);
for (int i = 0; i < 100; i++)
    ImGui::Text("Item %d", i);
ImGui::EndChild();

// Auto-sizing child
ImGui::BeginChild("AutoChild", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY);
ImGui::Text("This child sizes to its content vertically");
ImGui::EndChild();

Window Queries

bool focused = ImGui::IsWindowFocused();
bool hovered = ImGui::IsWindowHovered();
ImVec2 pos   = ImGui::GetWindowPos();
ImVec2 size  = ImGui::GetWindowSize();

4.2 Displaying Text and Basic Widgets

Text Display

ImGui::Text("Simple text");
ImGui::Text("Formatted: %d items, %.2f seconds", count, elapsed);
ImGui::TextColored(ImVec4(1,0,0,1), "Red warning text");
ImGui::TextDisabled("Grayed out hint");
ImGui::TextWrapped("This long text will wrap to the available width "
                   "of the current window or column.");
ImGui::BulletText("Bulleted item");
ImGui::SeparatorText("Section Header");

Buttons

if (ImGui::Button("Click Me"))
{
    // Action happens on the frame the button is clicked
}

if (ImGui::Button("Sized Button", ImVec2(200, 40)))
{
    // Button with explicit size
}

ImGui::SmallButton("Compact");          // Small, no frame padding
ImGui::ArrowButton("##left", ImGuiDir_Left);  // Directional arrow

// Invisible button — a clickable region with no visual
if (ImGui::InvisibleButton("zone", ImVec2(100, 100)))
{
    // Clicked in the invisible region
}

// Hyperlinks
ImGui::TextLink("Open Documentation");     // Returns true when clicked
ImGui::TextLinkOpenURL("Visit GitHub", "https://github.com/ocornut/imgui");

Checkboxes and Radio Buttons

static bool enabled = true;
ImGui::Checkbox("Enable Feature", &enabled);

static int mode = 0;
ImGui::RadioButton("Mode A", &mode, 0); ImGui::SameLine();
ImGui::RadioButton("Mode B", &mode, 1); ImGui::SameLine();
ImGui::RadioButton("Mode C", &mode, 2);

Progress Bar

ImGui::ProgressBar(0.65f, ImVec2(0, 0), "65%");

4.3 Input Widgets

Text Input

static char buf[256] = "";
if (ImGui::InputText("Name", buf, sizeof(buf)))
{
    // Text changed this frame
}

static char multiline[1024] = "";
ImGui::InputTextMultiline("Description", multiline, sizeof(multiline),
                          ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 8));

static char search[128] = "";
ImGui::InputTextWithHint("##search", "Search...", search, sizeof(search));

Key ImGuiInputTextFlags:

Flag Effect
ImGuiInputTextFlags_CharsDecimal Allow 0123456789.+-*/
ImGuiInputTextFlags_CharsHexadecimal Allow 0123456789ABCDEFabcdef
ImGuiInputTextFlags_CharsScientific Allow 0123456789.+-*/eE
ImGuiInputTextFlags_CharsUppercase Force uppercase
ImGuiInputTextFlags_CharsNoBlank Filter spaces and tabs
ImGuiInputTextFlags_EnterReturnsTrue Return true on Enter instead of on every edit
ImGuiInputTextFlags_CallbackCompletion Tab key triggers callback
ImGuiInputTextFlags_CallbackHistory Up/Down triggers callback
ImGuiInputTextFlags_CallbackResize Callback when buffer needs to grow (for std::string)
ImGuiInputTextFlags_Password Mask display with *
ImGuiInputTextFlags_ReadOnly Display only, no editing
ImGuiInputTextFlags_AutoSelectAll Select all on focus
ImGuiInputTextFlags_NoHorizontalScroll Disable horizontal scrolling
ImGuiInputTextFlags_EscapeClearsAll Escape clears the field

Numeric Input

static float f = 0.5f;
static int i = 42;

// Drag widgets — click and drag to adjust
ImGui::DragFloat("Speed", &f, 0.01f, 0.0f, 1.0f, "%.3f");
ImGui::DragInt("Count", &i, 1, 0, 100);

// Slider widgets — slide within a fixed range
ImGui::SliderFloat("Volume", &f, 0.0f, 1.0f, "%.2f");
ImGui::SliderInt("Quality", &i, 0, 10);
ImGui::SliderAngle("Rotation", &f);  // Displays in degrees, stores radians

// Direct numeric input
ImGui::InputFloat("Value", &f, 0.1f, 1.0f, "%.3f");
ImGui::InputInt("Count", &i, 1, 10);
ImGui::InputDouble("Precision", &d, 0.001, 0.01, "%.6f");

// Multi-component
static float vec3[3] = {0, 0, 0};
ImGui::DragFloat3("Position", vec3, 0.1f);
ImGui::SliderFloat3("Color", vec3, 0.0f, 1.0f);
ImGui::InputFloat3("Coords", vec3);

Note

Ctrl+Click on any SliderFloat, SliderInt, DragFloat, or DragInt widget to convert it into a direct text input box. This lets users type exact values when precision matters. The behavior is automatic and requires no extra code.

Color Editing

static float col3[3] = {1.0f, 0.5f, 0.0f};
static float col4[4] = {1.0f, 0.5f, 0.0f, 1.0f};

ImGui::ColorEdit3("Color", col3);           // RGB
ImGui::ColorEdit4("Color+Alpha", col4);     // RGBA
ImGui::ColorPicker3("Pick Color", col3);    // Full picker widget
ImGui::ColorPicker4("Pick Color+A", col4);  // Full picker with alpha

// Color button (shows a color swatch, opens picker on click)
ImGui::ColorButton("Preview", ImVec4(col4[0], col4[1], col4[2], col4[3]));

std::string Support

#include "misc/cpp/imgui_stdlib.h"

std::string name = "Default";
ImGui::InputText("Name", &name);  // Automatically resizes the string

4.4 Layout and Positioning

Cursor and Content Region

// The two most important layout functions:
ImVec2 pos = ImGui::GetCursorScreenPos();     // Where the next widget will be drawn (screen coords)
ImVec2 avail = ImGui::GetContentRegionAvail(); // Remaining space in current region

Horizontal Layout

ImGui::Button("A"); ImGui::SameLine();
ImGui::Button("B"); ImGui::SameLine();
ImGui::Button("C");
// Result: [A] [B] [C] on one line

ImGui::SameLine(200);    // Position next item at x=200 within the window
ImGui::SameLine(0, 20);  // Default position, but with 20px spacing

Spacing and Separators

ImGui::NewLine();        // Empty line
ImGui::Spacing();        // Small vertical gap
ImGui::Dummy(ImVec2(0, 20));  // Explicit spacer of given size
ImGui::Separator();      // Horizontal line
ImGui::SeparatorText("Section");  // Line with centered text

Indentation

ImGui::Text("Root");
ImGui::Indent();
ImGui::Text("Indented");
ImGui::Indent();
ImGui::Text("More indented");
ImGui::Unindent(2 * ImGui::GetTreeNodeToLabelSpacing());  // Reset

Groups

Groups let you treat multiple widgets as a single item for layout purposes:

ImGui::BeginGroup();
ImGui::Text("Label:");
ImGui::Text("Value:");
ImGui::EndGroup();

ImGui::SameLine();

ImGui::BeginGroup();
ImGui::Text("Name");
ImGui::Text("42");
ImGui::EndGroup();

Item Width

// Set width for the next widget
ImGui::SetNextItemWidth(200);
ImGui::InputFloat("##val", &val);

// Push width for multiple widgets
ImGui::PushItemWidth(200);
ImGui::InputFloat("A", &a);
ImGui::InputFloat("B", &b);
ImGui::PopItemWidth();

// Negative width: relative to right edge
ImGui::PushItemWidth(-100);  // Leave 100px on the right for labels

Text Wrapping

ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 300); // Wrap at 300px from cursor
ImGui::TextWrapped("This text wraps within the specified width...");
ImGui::PopTextWrapPos();

// Wrap to window edge:
ImGui::PushTextWrapPos(0.0f);
ImGui::TextWrapped("Wraps to the window edge.");
ImGui::PopTextWrapPos();

4.5 Trees and Hierarchies

if (ImGui::TreeNode("Root Node"))
{
    ImGui::Text("Leaf content");
    if (ImGui::TreeNode("Child Node"))
    {
        ImGui::Text("Nested content");
        ImGui::TreePop();
    }
    ImGui::TreePop();  // Required for every TreeNode that returns true
}

TreeNodeEx with Flags

ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow
                         | ImGuiTreeNodeFlags_OpenOnDoubleClick
                         | ImGuiTreeNodeFlags_SpanAvailWidth;

// Leaf node (no arrow, not expandable)
ImGuiTreeNodeFlags leaf_flags = flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;

if (ImGui::TreeNodeEx("Parent", flags))
{
    ImGui::TreeNodeEx("Leaf Item", leaf_flags);
    ImGui::TreePop();
}

CollapsingHeader

Unlike TreeNode, CollapsingHeader does not require TreePop():

if (ImGui::CollapsingHeader("Settings"))
{
    ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f);
    ImGui::Checkbox("Mute", &mute);
}

// With close button:
static bool show_header = true;
if (ImGui::CollapsingHeader("Closeable", &show_header))
{
    ImGui::Text("Content");
}

Controlling Open State

ImGui::SetNextItemOpen(true, ImGuiCond_Once);  // Open by default
if (ImGui::TreeNode("Open by default"))
{
    // ...
    ImGui::TreePop();
}

4.6 Tables

The table widget is one of Dear ImGui's most powerful features, supporting sorting, reordering, column hiding, freezing, and more.

// Basic table
if (ImGui::BeginTable("my_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
{
    // Setup columns (optional but recommended)
    ImGui::TableSetupColumn("Name");
    ImGui::TableSetupColumn("Type");
    ImGui::TableSetupColumn("Value");
    ImGui::TableHeadersRow();

    // Rows
    for (int row = 0; row < 5; row++)
    {
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("Item %d", row);
        ImGui::TableSetColumnIndex(1);
        ImGui::Text("Type A");
        ImGui::TableSetColumnIndex(2);
        ImGui::Text("%.2f", row * 1.5f);
    }
    ImGui::EndTable();
}

Alternative row traversal using TableNextColumn():

if (ImGui::BeginTable("table2", 3))
{
    for (int row = 0; row < 5; row++)
    {
        ImGui::TableNextColumn(); ImGui::Text("Cell %d,0", row);
        ImGui::TableNextColumn(); ImGui::Text("Cell %d,1", row);
        ImGui::TableNextColumn(); ImGui::Text("Cell %d,2", row);
    }
    ImGui::EndTable();
}

Table Flags

Flag Effect
ImGuiTableFlags_Resizable Columns can be resized
ImGuiTableFlags_Reorderable Columns can be reordered by dragging headers
ImGuiTableFlags_Hideable Columns can be hidden via context menu
ImGuiTableFlags_Sortable Enable sorting (click headers)
ImGuiTableFlags_SortMulti Allow sorting on multiple columns (Shift+click)
ImGuiTableFlags_RowBg Alternate row background colors
ImGuiTableFlags_Borders All borders (alias for BordersInner + BordersOuter)
ImGuiTableFlags_BordersV Vertical borders
ImGuiTableFlags_BordersH Horizontal borders
ImGuiTableFlags_NoBordersInBody No borders in body (headers only)
ImGuiTableFlags_ScrollX Enable horizontal scrolling
ImGuiTableFlags_ScrollY Enable vertical scrolling (requires outer size)
ImGuiTableFlags_SizingFixedFit Columns default to fixed width
ImGuiTableFlags_SizingStretchSame Columns stretch with equal weight
ImGuiTableFlags_SizingStretchProp Columns stretch proportionally

Sorting

if (ImGui::BeginTable("sortable", 3, ImGuiTableFlags_Sortable))
{
    ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort);
    ImGui::TableSetupColumn("Name");
    ImGui::TableSetupColumn("Score");
    ImGui::TableHeadersRow();

    if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
    {
        if (sort_specs->SpecsDirty)
        {
            // Re-sort your data based on sort_specs->Specs[0].ColumnIndex
            // and sort_specs->Specs[0].SortDirection
            sort_specs->SpecsDirty = false;
        }
    }

    // ... render sorted rows ...
    ImGui::EndTable();
}

Freeze Rows/Columns

if (ImGui::BeginTable("frozen", 3, ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                      ImVec2(0, 300)))
{
    ImGui::TableSetupScrollFreeze(1, 1);  // Freeze 1 column, 1 row
    ImGui::TableSetupColumn("Frozen Col");
    ImGui::TableSetupColumn("Col 2");
    ImGui::TableSetupColumn("Col 3");
    ImGui::TableHeadersRow();
    // ...
    ImGui::EndTable();
}

Warning

The legacy Columns() API is deprecated. Use the BeginTable()/EndTable() API instead. The table widget offers vastly superior features including sorting, column reordering, column hiding, scrolling, and freeze rows/columns. The old Columns() API is retained only for backward compatibility and will be removed in a future version.

4.7 Tab Bars

if (ImGui::BeginTabBar("Tabs"))
{
    if (ImGui::BeginTabItem("General"))
    {
        ImGui::Text("General settings");
        ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Graphics"))
    {
        ImGui::Text("Graphics settings");
        ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Audio"))
    {
        ImGui::Text("Audio settings");
        ImGui::EndTabItem();
    }
    ImGui::EndTabBar();
}

Closeable tabs:

static bool show_tab = true;
if (ImGui::BeginTabItem("Closeable", &show_tab))
{
    ImGui::Text("This tab can be closed");
    ImGui::EndTabItem();
}

Tab bar utilities:

ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing);  // Add-tab button
ImGui::SetTabItemClosed("Tab Name");  // Programmatically close

4.8 Menus

Window Menu Bar

ImGui::Begin("Editor", nullptr, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar())
{
    if (ImGui::BeginMenu("File"))
    {
        if (ImGui::MenuItem("New", "Ctrl+N"))  { /* action */ }
        if (ImGui::MenuItem("Open", "Ctrl+O")) { /* action */ }
        ImGui::Separator();
        if (ImGui::MenuItem("Quit", "Alt+F4")) { /* action */ }
        ImGui::EndMenu();
    }
    if (ImGui::BeginMenu("Edit"))
    {
        if (ImGui::MenuItem("Undo", "Ctrl+Z", false, has_undo)) { /* action */ }
        if (ImGui::MenuItem("Redo", "Ctrl+Y", false, has_redo)) { /* action */ }
        ImGui::EndMenu();
    }
    ImGui::EndMenuBar();
}
ImGui::End();
if (ImGui::BeginMainMenuBar())
{
    if (ImGui::BeginMenu("File")) { /* ... */ ImGui::EndMenu(); }
    if (ImGui::BeginMenu("Edit")) { /* ... */ ImGui::EndMenu(); }
    ImGui::EndMainMenuBar();
}
if (ImGui::BeginMenu("View"))
{
    ImGui::MenuItem("Show Grid", nullptr, &show_grid);     // Toggle
    ImGui::MenuItem("Show FPS", nullptr, &show_fps);       // Toggle
    if (ImGui::BeginMenu("Layout"))                        // Submenu
    {
        ImGui::MenuItem("Horizontal");
        ImGui::MenuItem("Vertical");
        ImGui::EndMenu();
    }
    ImGui::EndMenu();
}

4.9 Popups and Modals

Basic Popup

if (ImGui::Button("Open Popup"))
    ImGui::OpenPopup("my_popup");

if (ImGui::BeginPopup("my_popup"))
{
    ImGui::Text("Popup content");
    if (ImGui::Button("Close"))
        ImGui::CloseCurrentPopup();
    ImGui::EndPopup();
}
if (ImGui::Button("Delete"))
    ImGui::OpenPopup("Confirm Delete");

if (ImGui::BeginPopupModal("Confirm Delete", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
    ImGui::Text("Are you sure you want to delete this item?");
    ImGui::Separator();
    if (ImGui::Button("OK", ImVec2(120, 0)))
    {
        // Perform delete
        ImGui::CloseCurrentPopup();
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel", ImVec2(120, 0)))
    {
        ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
}

Context Menus

// Context menu on the last item
ImGui::Text("Right-click me");
if (ImGui::BeginPopupContextItem("item_context"))
{
    if (ImGui::MenuItem("Copy"))  { /* ... */ }
    if (ImGui::MenuItem("Paste")) { /* ... */ }
    ImGui::EndPopup();
}

// Context menu on window background
if (ImGui::BeginPopupContextWindow())
{
    if (ImGui::MenuItem("Add Item"))  { /* ... */ }
    ImGui::EndPopup();
}

// Context menu on void (no window hovered)
if (ImGui::BeginPopupContextVoid())
{
    if (ImGui::MenuItem("Settings")) { /* ... */ }
    ImGui::EndPopup();
}

Warning

In Dear ImGui 1.92.6, the default popup trigger changed to right-click (ImGuiPopupFlags_MouseButtonRight). If you relied on left-click to open popups via OpenPopupOnItemClick(), you must now explicitly pass ImGuiPopupFlags_MouseButtonLeft. The BeginPopupContextItem/Window/Void helpers already default to right-click and are unaffected.

4.10 Tooltips

// Simple tooltip on hover
ImGui::Button("Hover me");
if (ImGui::IsItemHovered())
    ImGui::SetTooltip("This is a tooltip");

// Rich tooltip with multiple widgets
ImGui::Button("Rich tooltip");
if (ImGui::BeginItemTooltip())
{
    ImGui::Text("Detailed information:");
    ImGui::BulletText("Point 1");
    ImGui::BulletText("Point 2");
    ImGui::ProgressBar(0.7f);
    ImGui::EndTooltip();
}

// Shorthand for text-only item tooltip
ImGui::Button("Short");
ImGui::SetItemTooltip("Tooltip text for the preceding item");

4.11 Images

Dear ImGui can render application textures via Image() and ImageButton(). You must create the texture using your graphics API and pass its handle.

// ImTextureRef wraps a texture identifier + size information (new in 1.92)
ImTextureRef tex = ...; // Backend-specific: e.g., (ImTextureID)(intptr_t)opengl_texture_id

ImGui::Image(tex, ImVec2(256, 256));                        // Display at 256x256
ImGui::Image(tex, ImVec2(128, 128), ImVec2(0,0), ImVec2(1,1)); // With UV coords

// Image with background color (for transparent images)
ImGui::ImageWithBg(tex, ImVec2(256, 256), ImVec2(0,0), ImVec2(1,1),
                   ImVec4(0,0,0,1));  // Black background

// Image button
if (ImGui::ImageButton("btn_id", tex, ImVec2(32, 32)))
{
    // Clicked
}

The exact type of ImTextureID depends on your renderer backend:

Backend ImTextureID value
OpenGL (ImTextureID)(intptr_t)gl_texture
DirectX9 (ImTextureID)d3d9_texture_ptr
DirectX11 (ImTextureID)srv_ptr
DirectX12 (ImTextureID)gpu_descriptor_handle.ptr
Vulkan (ImTextureID)vk_descriptor_set
Metal (ImTextureID)mtl_texture

4.12 Selectables and Multi-Select

Selectable

static int selected = -1;
const char* items[] = { "Apple", "Banana", "Cherry", "Date" };
for (int i = 0; i < 4; i++)
{
    if (ImGui::Selectable(items[i], selected == i))
        selected = i;
}

Multi-Select (Beta)

static ImGuiSelectionBasicStorage selection;
ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, item_count);
selection.ApplyRequests(ms_io);

for (int i = 0; i < item_count; i++)
{
    ImGui::SetNextItemSelectionUserData(i);
    bool is_selected = selection.Contains((ImGuiID)i);
    ImGui::Selectable(items[i], is_selected);
}

ms_io = ImGui::EndMultiSelect();
selection.ApplyRequests(ms_io);

4.13 List Boxes and Combo Boxes

Combo Box

static int current_item = 0;
const char* items[] = { "Option A", "Option B", "Option C" };

// Simple version
ImGui::Combo("Combo", &current_item, items, IM_ARRAYSIZE(items));

// Begin/End version (custom content)
if (ImGui::BeginCombo("Custom Combo", items[current_item]))
{
    for (int i = 0; i < IM_ARRAYSIZE(items); i++)
    {
        bool is_selected = (current_item == i);
        if (ImGui::Selectable(items[i], is_selected))
            current_item = i;
        if (is_selected)
            ImGui::SetItemDefaultFocus();
    }
    ImGui::EndCombo();
}

List Box

static int current = 0;
if (ImGui::BeginListBox("Items", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
{
    for (int i = 0; i < item_count; i++)
    {
        bool is_selected = (current == i);
        if (ImGui::Selectable(items[i], is_selected))
            current = i;
        if (is_selected)
            ImGui::SetItemDefaultFocus();
    }
    ImGui::EndListBox();
}

4.14 Data Plotting

static float values[100];
// ... fill values ...

ImGui::PlotLines("Signal", values, IM_ARRAYSIZE(values), 0, "overlay text",
                 -1.0f, 1.0f, ImVec2(0, 80));

ImGui::PlotHistogram("Distribution", values, IM_ARRAYSIZE(values), 0, nullptr,
                     0.0f, 1.0f, ImVec2(0, 80));

For advanced plotting (axes, legends, multiple series, heatmaps), use the companion library ImPlot.

4.15 Drag and Drop

// Source: the widget you drag FROM
if (ImGui::Button("Drag me"))
{
}
if (ImGui::BeginDragDropSource())
{
    int payload_data = 42;
    ImGui::SetDragDropPayload("MY_TYPE", &payload_data, sizeof(int));
    ImGui::Text("Dragging value: %d", payload_data);  // Preview tooltip
    ImGui::EndDragDropSource();
}

// Target: the region you drop INTO
ImGui::Button("Drop here");
if (ImGui::BeginDragDropTarget())
{
    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_TYPE"))
    {
        int received = *(const int*)payload->Data;
        // Handle the drop
    }
    ImGui::EndDragDropTarget();
}

The payload type string ("MY_TYPE") acts as a type filter — only matching payloads are accepted.

4.16 Keyboard and Gamepad Navigation

Enabling Navigation

ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Arrow keys, Tab, Enter, Escape
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;    // D-pad, face buttons, triggers

Keyboard Shortcuts

// Global shortcut (works when Dear ImGui has focus)
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S))
{
    // Save
}

// Shortcut bound to a menu item
if (ImGui::MenuItem("Save", "Ctrl+S"))
{
    // Save action
}

// Shortcut assigned to a specific widget
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Z);
if (ImGui::Button("Undo"))
{
    // Undo action — also triggered by Ctrl+Z
}

Modifier keys: ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiMod_Alt, ImGuiMod_Super.

4.17 Fonts

Default Fonts

ImGuiIO& io = ImGui::GetIO();

// Vector-based default font (scalable, new approach)
io.Fonts->AddFontDefaultVector();

// Bitmap-based default font (ProggyClean 13px)
io.Fonts->AddFontDefaultBitmap();

Loading Custom Fonts

// From file
ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 16.0f);

// From memory (e.g., embedded font data)
ImFont* font2 = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f);

// With specific glyph ranges
ImFont* font_jp = io.Fonts->AddFontFromFileTTF("fonts/NotoSansJP.ttf", 18.0f,
    nullptr, io.Fonts->GetGlyphRangesJapanese());

Dynamic Font Sizing (New in 1.92)

ImFont* font = io.Fonts->AddFontFromFileTTF("Roboto.ttf", 0);  // Base size 0 = vector font

// In your frame loop:
ImGui::PushFont(font, 24.0f);  // Render at 24px
ImGui::Text("Large text");
ImGui::PopFont();

ImGui::PushFont(font, 12.0f);  // Render at 12px
ImGui::Text("Small text");
ImGui::PopFont();

Font Merging (Icon Fonts)

// Load primary font
io.Fonts->AddFontFromFileTTF("Roboto.ttf", 16.0f);

// Merge icon font into primary font
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = 16.0f;
static const ImWchar icon_ranges[] = { 0xf000, 0xf3ff, 0 };
io.Fonts->AddFontFromFileTTF("FontAwesome.ttf", 16.0f, &config, icon_ranges);

Available Glyph Ranges

Method Coverage
GetGlyphRangesDefault() Basic Latin + supplement (U+0020–U+00FF)
GetGlyphRangesGreek() Greek and Coptic
GetGlyphRangesCyrillic() Cyrillic
GetGlyphRangesKorean() Korean (Hangul)
GetGlyphRangesJapanese() Hiragana, Katakana, CJK Unified
GetGlyphRangesChineseFull() Full Chinese character set
GetGlyphRangesChineseSimplifiedCommon() ~2500 most common simplified Chinese
GetGlyphRangesThai() Thai
GetGlyphRangesVietnamese() Vietnamese

4.18 Drawing API (ImDrawList)

The low-level drawing API lets you render arbitrary shapes. Every window has a draw list, and there are global background and foreground draw lists.

ImDrawList* draw_list = ImGui::GetWindowDrawList();

ImVec2 p = ImGui::GetCursorScreenPos();  // Use as origin
ImU32 col = IM_COL32(255, 255, 0, 255);  // Yellow, fully opaque

// Lines
draw_list->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + 100, p.y + 50), col, 2.0f);

// Rectangles
draw_list->AddRect(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col);
draw_list->AddRectFilled(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col);
draw_list->AddRectFilled(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col, 10.0f);  // Rounded

// Circles
draw_list->AddCircle(ImVec2(p.x + 40, p.y + 40), 30.0f, col, 0, 2.0f);
draw_list->AddCircleFilled(ImVec2(p.x + 40, p.y + 40), 30.0f, col);

// Triangles
draw_list->AddTriangle(ImVec2(p.x, p.y + 60), ImVec2(p.x + 60, p.y + 60),
                       ImVec2(p.x + 30, p.y), col, 2.0f);
draw_list->AddTriangleFilled(ImVec2(p.x, p.y + 60), ImVec2(p.x + 60, p.y + 60),
                             ImVec2(p.x + 30, p.y), col);

// Text
draw_list->AddText(ImVec2(p.x, p.y), col, "Hello");

// Bezier curves
draw_list->AddBezierCubic(ImVec2(p.x, p.y), ImVec2(p.x+50, p.y-50),
                          ImVec2(p.x+100, p.y+50), ImVec2(p.x+150, p.y), col, 2.0f);
draw_list->AddBezierQuadratic(ImVec2(p.x, p.y), ImVec2(p.x+75, p.y-50),
                              ImVec2(p.x+150, p.y), col, 2.0f);

// N-gons (regular polygons)
draw_list->AddNgon(ImVec2(p.x+40, p.y+40), 30.0f, col, 6, 2.0f);       // Hexagon outline
draw_list->AddNgonFilled(ImVec2(p.x+40, p.y+40), 30.0f, col, 6);       // Filled hexagon

// Ellipses
draw_list->AddEllipse(ImVec2(p.x+50, p.y+30), ImVec2(50, 30), col, 0.0f, 0, 2.0f);
draw_list->AddEllipseFilled(ImVec2(p.x+50, p.y+30), ImVec2(50, 30), col);

// Images
draw_list->AddImage(tex_id, ImVec2(p.x, p.y), ImVec2(p.x + 100, p.y + 100));

// Polyline
ImVec2 points[] = { {p.x,p.y}, {p.x+30,p.y-20}, {p.x+60,p.y+10}, {p.x+90,p.y-5} };
draw_list->AddPolyline(points, 4, col, ImDrawFlags_None, 2.0f);

// Convex filled polygon
draw_list->AddConvexPolyFilled(points, 4, col);

Draw List Layers

ImDrawList* bg = ImGui::GetBackgroundDrawList();   // Behind all windows
ImDrawList* fg = ImGui::GetForegroundDrawList();   // In front of all windows
ImDrawList* wl = ImGui::GetWindowDrawList();       // Current window's draw list

4.19 ID System

Dear ImGui uses IDs to track widget state (open/closed, active, edited, scroll position). Understanding IDs is essential to avoid bugs.

How IDs Work

Every widget needs a unique ID within its parent window/tree scope. By default, the ID is derived from the label string:

ImGui::Button("OK");    // ID = hash of "OK" in current scope
ImGui::Button("OK");    // BUG: duplicate ID — second button won't work

Solving ID Conflicts

Method 1: ## separator — text after ## is part of the ID but not displayed:

ImGui::Button("OK##save");    // Displays "OK", ID includes "save"
ImGui::Button("OK##cancel");  // Displays "OK", different ID

Method 2: ### separator — only text after ### forms the ID:

// Useful when label changes but you want stable widget state
char buf[64];
sprintf(buf, "Frame %d###AnimatingTitle", frame_count);
ImGui::Begin(buf);  // Window ID stays the same even as title changes
ImGui::End();

Method 3: PushID()/PopID() — create a unique ID scope:

for (int i = 0; i < items.size(); i++)
{
    ImGui::PushID(i);          // Push integer as ID scope
    ImGui::Button("Delete");   // "Delete" is now unique within this scope
    ImGui::SameLine();
    ImGui::Button("Edit");
    ImGui::PopID();
}

PushID() accepts int, const char*, const void*, or a string range.

4.20 Disabling Widgets

bool can_edit = user_has_permission();
ImGui::BeginDisabled(!can_edit);
ImGui::InputText("Name", buf, sizeof(buf));
ImGui::Button("Save");
ImGui::EndDisabled();
// When disabled: widgets are grayed out and do not respond to input

4.21 Clipping

ImGui::PushClipRect(ImVec2(100, 100), ImVec2(300, 300), true);
// Only content within the rectangle is visible
ImGui::Text("This text may be clipped");
ImGui::PopClipRect();

4.22 Logging and Capture

Dear ImGui can capture UI output as text — useful for copying widget content to the clipboard or a file.

ImGui::LogToClipboard();   // Start capturing to clipboard
ImGui::LogToFile(-1, "output.txt");  // Capture to file
ImGui::LogToTTY();         // Capture to stdout

// All subsequent widget output is captured as text...

ImGui::LogFinish();        // Stop capturing

// Convenience: show buttons for all log targets
ImGui::LogButtons();

4.23 Settings Persistence

Dear ImGui automatically saves and loads window positions, sizes, collapsed states, and table column settings to an .ini file.

// Default behavior: saves to "imgui.ini" on exit
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = "my_app_layout.ini";  // Custom filename
io.IniFilename = nullptr;              // Disable auto-save/load

// Manual control
ImGui::LoadIniSettingsFromDisk("layout.ini");
ImGui::SaveIniSettingsToDisk("layout.ini");

// In-memory (useful for custom storage)
const char* ini_data = ImGui::SaveIniSettingsToMemory();
ImGui::LoadIniSettingsFromMemory(ini_data);

// Detect when settings need saving (useful for custom persistence)
if (io.WantSaveIniSettings)
{
    const char* data = ImGui::SaveIniSettingsToMemory();
    // Save 'data' to your custom storage
    io.WantSaveIniSettings = false;
}

4.24 Item Queries

After any widget call, you can query its state:

ImGui::InputFloat("Value", &val);

// Hover and focus
bool hovered   = ImGui::IsItemHovered();          // Mouse is over it
bool focused   = ImGui::IsItemFocused();           // Has keyboard focus
bool active    = ImGui::IsItemActive();            // Being interacted with (held, editing)

// Interaction events
bool clicked   = ImGui::IsItemClicked();           // Was clicked this frame
bool edited    = ImGui::IsItemEdited();            // Value was changed this frame
bool activated = ImGui::IsItemActivated();         // Became active this frame
bool deacted   = ImGui::IsItemDeactivated();       // Stopped being active this frame
bool deact_edit = ImGui::IsItemDeactivatedAfterEdit(); // Stopped being active AND value changed

// Useful for commit-on-deactivate patterns (undo/redo):
ImGui::DragFloat("Position", &pos);
if (ImGui::IsItemActivated())
    undo_stack.push(pos);           // Save old value when editing starts
if (ImGui::IsItemDeactivatedAfterEdit())
    redo_stack.clear();             // Commit when editing ends with a change

Input Dispatch

// Check if Dear ImGui wants to capture input (critical for game/app integration):
ImGuiIO& io = ImGui::GetIO();

if (io.WantCaptureMouse)
{
    // Dear ImGui is using the mouse — do NOT pass mouse events to your game
}
if (io.WantCaptureKeyboard)
{
    // Dear ImGui is using the keyboard — do NOT pass key events to your game
}

5. Common Tasks

5.1 Build a Property Editor

Goal: Display and edit properties of a selected object in a two-column layout.

Steps:

void DrawPropertyEditor(GameObject* obj)
{
    if (!obj) return;

    ImGui::Text("Editing: %s", obj->name.c_str());
    ImGui::Separator();

    if (ImGui::BeginTable("properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV))
    {
        ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150.0f);
        ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);

        // Transform section
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0); ImGui::Text("Position");
        ImGui::TableSetColumnIndex(1);
        ImGui::SetNextItemWidth(-FLT_MIN);
        ImGui::DragFloat3("##pos", &obj->position.x, 0.1f);

        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0); ImGui::Text("Rotation");
        ImGui::TableSetColumnIndex(1);
        ImGui::SetNextItemWidth(-FLT_MIN);
        ImGui::DragFloat3("##rot", &obj->rotation.x, 1.0f);

        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0); ImGui::Text("Scale");
        ImGui::TableSetColumnIndex(1);
        ImGui::SetNextItemWidth(-FLT_MIN);
        ImGui::DragFloat3("##scale", &obj->scale.x, 0.01f, 0.01f, 100.0f);

        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0); ImGui::Text("Visible");
        ImGui::TableSetColumnIndex(1);
        ImGui::Checkbox("##visible", &obj->visible);

        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0); ImGui::Text("Color");
        ImGui::TableSetColumnIndex(1);
        ImGui::SetNextItemWidth(-FLT_MIN);
        ImGui::ColorEdit4("##color", &obj->color.x);

        ImGui::EndTable();
    }
}

Verify: Run the property editor, change values, confirm they apply to the object in real time.

If it fails: Check that PushID/PopID scopes are correct if multiple objects share property names. The ## prefix on labels avoids duplicate IDs within the table.

5.2 Create a Dockable Tool Window with Menu Bar

Goal: A standalone tool window with its own menu bar.

static bool show_tool = true;

void DrawToolWindow()
{
    if (!show_tool) return;

    ImGui::Begin("Asset Browser", &show_tool, ImGuiWindowFlags_MenuBar);

    if (ImGui::BeginMenuBar())
    {
        if (ImGui::BeginMenu("View"))
        {
            static bool show_thumbnails = true;
            ImGui::MenuItem("Thumbnails", nullptr, &show_thumbnails);
            static bool show_details = false;
            ImGui::MenuItem("Details", nullptr, &show_details);
            ImGui::EndMenu();
        }
        if (ImGui::BeginMenu("Sort"))
        {
            if (ImGui::MenuItem("By Name"))  { /* sort */ }
            if (ImGui::MenuItem("By Date"))  { /* sort */ }
            if (ImGui::MenuItem("By Size"))  { /* sort */ }
            ImGui::EndMenu();
        }
        ImGui::EndMenuBar();
    }

    // Content
    static char filter[128] = "";
    ImGui::InputTextWithHint("##filter", "Filter assets...", filter, sizeof(filter));

    ImGui::BeginChild("AssetList", ImVec2(0, 0), ImGuiChildFlags_Borders);
    for (int i = 0; i < asset_count; i++)
    {
        if (filter[0] && !strstr(assets[i].name, filter))
            continue;
        ImGui::Selectable(assets[i].name);
    }
    ImGui::EndChild();

    ImGui::End();
}

5.3 Add a File Browser Dialog

Goal: Let the user pick a file path using Dear ImGui widgets.

struct FileBrowser
{
    char current_path[512] = ".";
    char selected_file[256] = "";
    bool is_open = false;

    void Draw()
    {
        if (!is_open) return;
        ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
        if (ImGui::BeginPopupModal("Select File", &is_open))
        {
            ImGui::InputText("Path", current_path, sizeof(current_path));
            ImGui::Separator();

            ImGui::BeginChild("FileList", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()));
            // List directory contents here using your OS API
            // For each entry:
            //   if (ImGui::Selectable(entry_name, is_selected))
            //       strncpy(selected_file, entry_name, sizeof(selected_file));
            ImGui::EndChild();

            if (ImGui::Button("Open", ImVec2(120, 0)))
            {
                // Process selected_file
                ImGui::CloseCurrentPopup();
            }
            ImGui::SameLine();
            if (ImGui::Button("Cancel", ImVec2(120, 0)))
            {
                selected_file[0] = '\0';
                ImGui::CloseCurrentPopup();
            }
            ImGui::EndPopup();
        }
    }

    void Open() { is_open = true; ImGui::OpenPopup("Select File"); }
};

5.4 Implement Undo/Redo with IsItemDeactivatedAfterEdit

Goal: Track value changes for undo/redo with proper commit semantics.

struct UndoEntry { float old_value; float new_value; };
std::vector<UndoEntry> undo_stack;
int undo_index = -1;

static float value = 0.0f;
static float value_before_edit = 0.0f;

ImGui::DragFloat("Value", &value, 0.01f);

if (ImGui::IsItemActivated())
{
    value_before_edit = value;  // Snapshot before editing begins
}
if (ImGui::IsItemDeactivatedAfterEdit())
{
    // Editing ended and value changed — commit to undo stack
    undo_stack.resize(undo_index + 1);  // Truncate any redo entries
    undo_stack.push_back({ value_before_edit, value });
    undo_index = (int)undo_stack.size() - 1;
}

// Undo
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z) && undo_index >= 0)
{
    value = undo_stack[undo_index].old_value;
    undo_index--;
}
// Redo
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y) && undo_index < (int)undo_stack.size() - 1)
{
    undo_index++;
    value = undo_stack[undo_index].new_value;
}

5.5 Create a Log/Console Window with Filtering

Goal: Scrollable, filterable log display with auto-scroll.

struct AppLog
{
    ImGuiTextBuffer buf;
    ImGuiTextFilter filter;
    ImVector<int>   line_offsets;
    bool            auto_scroll = true;

    void Clear() { buf.clear(); line_offsets.clear(); line_offsets.push_back(0); }

    void AddLog(const char* fmt, ...)
    {
        int old_size = buf.size();
        va_list args;
        va_start(args, fmt);
        buf.appendfv(fmt, args);
        va_end(args);
        for (int i = old_size; i < buf.size(); i++)
            if (buf[i] == '\n')
                line_offsets.push_back(i + 1);
    }

    void Draw(const char* title, bool* p_open = nullptr)
    {
        ImGui::Begin(title, p_open);

        // Options
        ImGui::Checkbox("Auto-scroll", &auto_scroll);
        ImGui::SameLine();
        if (ImGui::Button("Clear")) Clear();
        ImGui::SameLine();
        filter.Draw("Filter", -100.0f);
        ImGui::Separator();

        // Log content
        ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None,
                          ImGuiWindowFlags_HorizontalScrollbar);

        const char* buf_begin = buf.begin();
        const char* buf_end = buf.end();

        if (filter.IsActive())
        {
            for (int i = 0; i < line_offsets.Size - 1; i++)
            {
                const char* line_start = buf_begin + line_offsets[i];
                const char* line_end = (i + 1 < line_offsets.Size)
                    ? buf_begin + line_offsets[i + 1] - 1 : buf_end;
                if (filter.PassFilter(line_start, line_end))
                    ImGui::TextUnformatted(line_start, line_end);
            }
        }
        else
        {
            // Use clipper for large logs
            ImGuiListClipper clipper;
            clipper.Begin(line_offsets.Size - 1);
            while (clipper.Step())
            {
                for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
                {
                    const char* line_start = buf_begin + line_offsets[i];
                    const char* line_end = (i + 1 < line_offsets.Size)
                        ? buf_begin + line_offsets[i + 1] - 1 : buf_end;
                    ImGui::TextUnformatted(line_start, line_end);
                }
            }
            clipper.End();
        }

        if (auto_scroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
            ImGui::SetScrollHereY(1.0f);

        ImGui::EndChild();
        ImGui::End();
    }
};

5.6 Build a Color Theme Editor

Goal: Interactive theme editor that exports code.

void DrawThemeEditor()
{
    ImGuiStyle& style = ImGui::GetStyle();

    // Preset buttons
    if (ImGui::Button("Dark"))    ImGui::StyleColorsDark();
    ImGui::SameLine();
    if (ImGui::Button("Light"))   ImGui::StyleColorsLight();
    ImGui::SameLine();
    if (ImGui::Button("Classic")) ImGui::StyleColorsClassic();

    ImGui::Separator();

    // Sizes
    ImGui::SliderFloat("Window Rounding", &style.WindowRounding, 0.0f, 12.0f);
    ImGui::SliderFloat("Frame Rounding", &style.FrameRounding, 0.0f, 12.0f);
    ImGui::SliderFloat2("Window Padding", (float*)&style.WindowPadding, 0.0f, 20.0f);
    ImGui::SliderFloat2("Frame Padding", (float*)&style.FramePadding, 0.0f, 20.0f);

    ImGui::Separator();

    // Colors
    if (ImGui::BeginChild("Colors", ImVec2(0, 0), ImGuiChildFlags_Borders))
    {
        for (int i = 0; i < ImGuiCol_COUNT; i++)
        {
            const char* name = ImGui::GetStyleColorName(i);
            ImGui::PushID(i);
            ImGui::ColorEdit4("##color", (float*)&style.Colors[i],
                            ImGuiColorEditFlags_AlphaBar);
            ImGui::SameLine();
            ImGui::TextUnformatted(name);
            ImGui::PopID();
        }
    }
    ImGui::EndChild();

    // Export button
    if (ImGui::Button("Export to Clipboard"))
    {
        ImGui::LogToClipboard();
        ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;\n");
        for (int i = 0; i < ImGuiCol_COUNT; i++)
        {
            const ImVec4& c = style.Colors[i];
            ImGui::LogText("colors[ImGuiCol_%s] = ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n",
                          ImGui::GetStyleColorName(i), c.x, c.y, c.z, c.w);
        }
        ImGui::LogFinish();
    }
}

5.7 Display an Image/Texture from Your Application

Goal: Load and display a texture (OpenGL example).

// Step 1: Create texture using your graphics API
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, pixels);

// Step 2: Display in Dear ImGui
ImGui::Begin("Image Viewer");
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2((float)width, (float)height));

// With UV coordinates (display a sub-region):
ImGui::Image((ImTextureID)(intptr_t)texture,
             ImVec2(128, 128),       // display size
             ImVec2(0.0f, 0.0f),     // UV top-left
             ImVec2(0.5f, 0.5f));    // UV bottom-right
ImGui::End();

5.8 Create a Sortable Data Table

Goal: Display tabular data with clickable column headers for sorting.

struct Employee { int id; const char* name; float salary; };
static Employee employees[] = {
    {1, "Alice", 75000}, {2, "Bob", 82000}, {3, "Carol", 91000},
    {4, "Dave", 68000},  {5, "Eve", 95000}
};

void DrawEmployeeTable()
{
    ImGuiTableFlags flags = ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable
                          | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders
                          | ImGuiTableFlags_ScrollY;

    if (ImGui::BeginTable("employees", 3, flags, ImVec2(0, 300)))
    {
        ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort);
        ImGui::TableSetupColumn("Name");
        ImGui::TableSetupColumn("Salary", ImGuiTableColumnFlags_PreferSortDescending);
        ImGui::TableSetupScrollFreeze(0, 1);
        ImGui::TableHeadersRow();

        if (ImGuiTableSortSpecs* specs = ImGui::TableGetSortSpecs())
        {
            if (specs->SpecsDirty)
            {
                auto& s = specs->Specs[0];
                std::sort(std::begin(employees), std::end(employees),
                    [&](const Employee& a, const Employee& b) {
                        int cmp = 0;
                        switch (s.ColumnIndex) {
                            case 0: cmp = a.id - b.id; break;
                            case 1: cmp = strcmp(a.name, b.name); break;
                            case 2: cmp = (a.salary > b.salary) ? 1 : -1; break;
                        }
                        return (s.SortDirection == ImGuiSortDirection_Ascending) ? cmp < 0 : cmp > 0;
                    });
                specs->SpecsDirty = false;
            }
        }

        for (auto& e : employees)
        {
            ImGui::TableNextRow();
            ImGui::TableSetColumnIndex(0); ImGui::Text("%d", e.id);
            ImGui::TableSetColumnIndex(1); ImGui::Text("%s", e.name);
            ImGui::TableSetColumnIndex(2); ImGui::Text("$%.0f", e.salary);
        }
        ImGui::EndTable();
    }
}

5.9 Implement Keyboard Shortcuts

Goal: Add application-wide and widget-specific keyboard shortcuts.

// Global shortcuts (evaluated every frame)
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_N)) NewDocument();
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_O)) OpenDocument();
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S)) SaveDocument();

// Widget-bound shortcut (activates the button via keyboard)
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Z);
if (ImGui::Button("Undo")) Undo();

ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Y);
if (ImGui::Button("Redo")) Redo();

// Menu items with shortcut display
if (ImGui::BeginMainMenuBar())
{
    if (ImGui::BeginMenu("File"))
    {
        if (ImGui::MenuItem("New", "Ctrl+N"))  NewDocument();
        if (ImGui::MenuItem("Open", "Ctrl+O")) OpenDocument();
        if (ImGui::MenuItem("Save", "Ctrl+S")) SaveDocument();
        ImGui::EndMenu();
    }
    ImGui::EndMainMenuBar();
}

5.10 Create a Search/Filter Widget

Goal: Filterable list using ImGuiTextFilter.

static ImGuiTextFilter filter;
filter.Draw("Search", -100.0f);

ImGui::Separator();

const char* items[] = { "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig" };
for (int i = 0; i < IM_ARRAYSIZE(items); i++)
{
    if (filter.PassFilter(items[i]))
        ImGui::BulletText("%s", items[i]);
}

ImGuiTextFilter supports comma-separated include terms and - prefix for exclusion (e.g., apple,banana,-rotten).

5.11 Build a Tree View with Drag-and-Drop

Goal: Hierarchical tree where items can be rearranged via drag-and-drop.

struct TreeNode { const char* name; int id; std::vector<TreeNode> children; };

void DrawTreeNode(TreeNode& node)
{
    ImGui::PushID(node.id);

    ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow
                             | ImGuiTreeNodeFlags_SpanAvailWidth;
    if (node.children.empty())
        flags |= ImGuiTreeNodeFlags_Leaf;

    bool open = ImGui::TreeNodeEx(node.name, flags);

    // Drag source
    if (ImGui::BeginDragDropSource())
    {
        ImGui::SetDragDropPayload("TREE_NODE", &node.id, sizeof(int));
        ImGui::Text("Move: %s", node.name);
        ImGui::EndDragDropSource();
    }

    // Drop target
    if (ImGui::BeginDragDropTarget())
    {
        if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("TREE_NODE"))
        {
            int dropped_id = *(const int*)payload->Data;
            // Reparent the node with dropped_id under this node
        }
        ImGui::EndDragDropTarget();
    }

    if (open)
    {
        for (auto& child : node.children)
            DrawTreeNode(child);
        ImGui::TreePop();
    }
    ImGui::PopID();
}

5.12 Create a Right-Click Context Menu

Goal: Show a context menu when right-clicking items in a list.

for (int i = 0; i < item_count; i++)
{
    ImGui::PushID(i);
    ImGui::Selectable(items[i], selected == i);

    if (ImGui::BeginPopupContextItem())
    {
        ImGui::Text("Actions for: %s", items[i]);
        ImGui::Separator();
        if (ImGui::MenuItem("Rename")) { /* start rename */ }
        if (ImGui::MenuItem("Duplicate")) { /* duplicate item */ }
        ImGui::Separator();
        if (ImGui::MenuItem("Delete")) { /* delete item */ }
        ImGui::EndPopup();
    }
    ImGui::PopID();
}

5.13 Modal Confirmation Dialog

Goal: Block all other interaction until the user confirms or cancels.

// Trigger
static bool pending_delete = false;
if (ImGui::Button("Delete All"))
{
    pending_delete = true;
    ImGui::OpenPopup("Confirm Deletion");
}

// Modal
if (ImGui::BeginPopupModal("Confirm Deletion", nullptr,
                           ImGuiWindowFlags_AlwaysAutoResize))
{
    ImGui::Text("This will permanently delete all items.");
    ImGui::Text("This action cannot be undone.");
    ImGui::Separator();

    if (ImGui::Button("Delete", ImVec2(120, 0)))
    {
        // Perform deletion
        pending_delete = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel", ImVec2(120, 0)))
    {
        pending_delete = false;
        ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
}

5.14 Build a Node Editor Canvas (Using ImDrawList)

Goal: Draggable nodes connected by curves on a scrollable canvas.

struct Node { int id; ImVec2 pos; const char* name; };

void DrawNodeEditor(std::vector<Node>& nodes)
{
    ImGui::Begin("Node Editor");
    ImDrawList* draw_list = ImGui::GetWindowDrawList();
    ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
    ImVec2 canvas_size = ImGui::GetContentRegionAvail();

    // Canvas background
    draw_list->AddRectFilled(canvas_pos,
        ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
        IM_COL32(40, 40, 40, 255));

    // Grid
    float grid_step = 32.0f;
    for (float x = 0; x < canvas_size.x; x += grid_step)
        draw_list->AddLine(ImVec2(canvas_pos.x + x, canvas_pos.y),
                           ImVec2(canvas_pos.x + x, canvas_pos.y + canvas_size.y),
                           IM_COL32(200, 200, 200, 40));
    for (float y = 0; y < canvas_size.y; y += grid_step)
        draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + y),
                           ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + y),
                           IM_COL32(200, 200, 200, 40));

    // Draw nodes
    for (auto& node : nodes)
    {
        ImGui::PushID(node.id);
        ImVec2 node_pos = ImVec2(canvas_pos.x + node.pos.x, canvas_pos.y + node.pos.y);
        ImVec2 node_size = ImVec2(120, 60);

        // Node box
        draw_list->AddRectFilled(node_pos,
            ImVec2(node_pos.x + node_size.x, node_pos.y + node_size.y),
            IM_COL32(60, 60, 60, 255), 4.0f);
        draw_list->AddRect(node_pos,
            ImVec2(node_pos.x + node_size.x, node_pos.y + node_size.y),
            IM_COL32(100, 100, 100, 255), 4.0f);
        draw_list->AddText(ImVec2(node_pos.x + 8, node_pos.y + 8),
                          IM_COL32(255, 255, 255, 255), node.name);

        // Invisible button for dragging
        ImGui::SetCursorScreenPos(node_pos);
        ImGui::InvisibleButton("node", node_size);
        if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
        {
            node.pos.x += ImGui::GetIO().MouseDelta.x;
            node.pos.y += ImGui::GetIO().MouseDelta.y;
        }
        ImGui::PopID();
    }

    // Draw connections (Bezier curves between nodes)
    // draw_list->AddBezierCubic(start, cp1, cp2, end, color, thickness);

    ImGui::End();
}

5.15 Create a Multi-Select List

Goal: List with Ctrl+click, Shift+click, and range selection.

static ImGuiSelectionBasicStorage selection;
const char* items[] = { "Item A", "Item B", "Item C", "Item D", "Item E",
                        "Item F", "Item G", "Item H", "Item I", "Item J" };
int item_count = IM_ARRAYSIZE(items);

ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape
                            | ImGuiMultiSelectFlags_BoxSelect1d;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, item_count);
selection.ApplyRequests(ms_io);

for (int i = 0; i < item_count; i++)
{
    ImGui::SetNextItemSelectionUserData(i);
    bool is_selected = selection.Contains((ImGuiID)i);
    ImGui::Selectable(items[i], is_selected);
}

ms_io = ImGui::EndMultiSelect();
selection.ApplyRequests(ms_io);

ImGui::Text("Selected: %d items", selection.Size);

5.16 Load Custom Fonts with Specific Glyph Ranges

Goal: Display Chinese and icon glyphs alongside Latin text.

ImGuiIO& io = ImGui::GetIO();

// Primary font (Latin)
ImFont* font_main = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 16.0f);

// Chinese font merged into primary
ImFontConfig cfg;
cfg.MergeMode = true;
io.Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.ttf", 16.0f,
    &cfg, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());

// Icon font merged into primary
cfg.GlyphMinAdvanceX = 16.0f;
static const ImWchar icon_ranges[] = { 0xf000, 0xf3ff, 0 };
io.Fonts->AddFontFromFileTTF("fonts/FontAwesome5.ttf", 16.0f, &cfg, icon_ranges);

// Build atlas
io.Fonts->Build();
// Upload atlas texture to GPU (backend-specific)

5.17 Create a Toolbar with Image Buttons

Goal: Horizontal toolbar with icon buttons.

void DrawToolbar(ImTextureID icons_texture)
{
    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 4));
    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));

    ImVec2 btn_size(24, 24);
    float icon_w = 1.0f / 8.0f;  // 8 icons in texture atlas

    // Each button uses a different UV region of the icon atlas
    if (ImGui::ImageButton("##new",  icons_texture, btn_size, ImVec2(0,0), ImVec2(icon_w,1)))
        NewFile();
    ImGui::SameLine();
    if (ImGui::ImageButton("##open", icons_texture, btn_size, ImVec2(icon_w,0), ImVec2(icon_w*2,1)))
        OpenFile();
    ImGui::SameLine();
    if (ImGui::ImageButton("##save", icons_texture, btn_size, ImVec2(icon_w*2,0), ImVec2(icon_w*3,1)))
        SaveFile();

    ImGui::SameLine();
    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
    ImGui::SameLine();

    if (ImGui::ImageButton("##undo", icons_texture, btn_size, ImVec2(icon_w*3,0), ImVec2(icon_w*4,1)))
        Undo();
    ImGui::SameLine();
    if (ImGui::ImageButton("##redo", icons_texture, btn_size, ImVec2(icon_w*4,0), ImVec2(icon_w*5,1)))
        Redo();

    ImGui::PopStyleVar(2);
}

5.18 Build a Status Bar

Goal: Fixed-height bar at the bottom of a window showing status information.

void DrawStatusBar()
{
    // Reserve space at the bottom
    float status_height = ImGui::GetFrameHeightWithSpacing();
    ImVec2 window_size = ImGui::GetWindowSize();

    ImGui::SetCursorPosY(window_size.y - status_height - ImGui::GetStyle().WindowPadding.y);
    ImGui::Separator();

    ImGui::Text("Ready");
    ImGui::SameLine(200);
    ImGui::Text("Line: %d, Col: %d", cursor_line, cursor_col);
    ImGui::SameLine(400);
    ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
}

5.19 Implement Value Editing with DragFloat

Goal: Precise value editing with variable-speed dragging.

static float position[3] = {0, 0, 0};
static float rotation = 0.0f;
static float scale = 1.0f;

// Speed parameter controls sensitivity
ImGui::DragFloat3("Position", position, 0.1f);         // 0.1 units per pixel
ImGui::DragFloat("Rotation", &rotation, 1.0f, 0, 360, "%.1f deg");  // 1 degree per pixel
ImGui::DragFloat("Scale", &scale, 0.01f, 0.01f, 100.0f, "%.2fx");   // Clamped range

// Format string controls display
ImGui::DragFloat("Speed", &speed, 0.1f, 0.0f, FLT_MAX, "%.1f m/s");
ImGui::DragFloat("Opacity", &opacity, 0.005f, 0.0f, 1.0f, "%.0f%%",
                 ImGuiSliderFlags_AlwaysClamp);

// Integer drag
static int count = 10;
ImGui::DragInt("Count", &count, 1, 0, 1000);

// Range drag (two values)
static float range[2] = {20.0f, 80.0f};
ImGui::DragFloatRange2("Range", &range[0], &range[1], 0.5f, 0.0f, 100.0f);

5.20 Create Animated Widgets Using GetTime()

Goal: Widgets that animate smoothly using Dear ImGui's built-in time.

// Pulsing button
float pulse = 0.5f + 0.5f * sinf((float)ImGui::GetTime() * 3.0f);
ImGui::PushStyleColor(ImGuiCol_Button,
    ImVec4(pulse * 0.8f, 0.2f, 0.2f, 1.0f));
ImGui::Button("Alert!", ImVec2(100, 40));
ImGui::PopStyleColor();

// Animated progress bar
float progress = fmodf((float)ImGui::GetTime() * 0.3f, 1.0f);
ImGui::ProgressBar(progress, ImVec2(0, 0), "Loading...");

// Spinning indicator using ImDrawList
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 center = ImGui::GetCursorScreenPos();
center.x += 20; center.y += 20;
float radius = 15.0f;
float t = (float)ImGui::GetTime();
int segments = 12;
for (int i = 0; i < segments; i++)
{
    float a = (float)i / segments * IM_PI * 2.0f + t * 4.0f;
    float alpha = (float)i / segments;
    draw_list->AddCircleFilled(
        ImVec2(center.x + cosf(a) * radius, center.y + sinf(a) * radius),
        3.0f, IM_COL32(255, 255, 255, (int)(alpha * 255)));
}
ImGui::Dummy(ImVec2(40, 40));  // Reserve space

// Smooth scrolling
static float target_scroll = 0.0f;
static float current_scroll = 0.0f;
current_scroll += (target_scroll - current_scroll) * ImGui::GetIO().DeltaTime * 10.0f;

5.21 Custom Widget Background Colors

Goal: Highlight table rows or widget backgrounds conditionally.

if (ImGui::BeginTable("colored_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders))
{
    for (int row = 0; row < 10; row++)
    {
        ImGui::TableNextRow();

        // Highlight rows conditionally
        if (data[row].has_error)
            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(255, 0, 0, 50));
        else if (data[row].is_selected)
            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(0, 120, 255, 50));

        ImGui::TableSetColumnIndex(0); ImGui::Text("%d", row);
        ImGui::TableSetColumnIndex(1); ImGui::Text("%s", data[row].name);
        ImGui::TableSetColumnIndex(2); ImGui::Text("%.2f", data[row].value);
    }
    ImGui::EndTable();
}

5.22 Using ImGuiListClipper for Large Lists

Goal: Efficiently render lists with thousands of items.

ImGui::BeginChild("LargeList", ImVec2(0, 400));
ImGuiListClipper clipper;
clipper.Begin(100000);  // 100,000 items
while (clipper.Step())
{
    for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
    {
        ImGui::Text("Item %d: data here", i);
    }
}
clipper.End();
ImGui::EndChild();

The clipper calculates which items are visible and only calls your code for those items, making even million-item lists performant.


6. Troubleshooting

Error: Assertion failed: thickness > 0.0f

Cause: A draw call (e.g., AddLine, AddRect, AddCircle) was given a zero or negative thickness value.

Solution:

  1. Search your code for draw calls passing 0.0f as the thickness parameter.
  2. Ensure thickness variables are initialized to a positive value.
  3. If thickness comes from user input, clamp it: ImMax(thickness, 1.0f).

Prevention: Always validate draw parameters before passing them. Use a minimum thickness of 1.0f for visible lines.


Error: Assertion failed: g.CurrentWindow != NULL

Cause: You called a widget function (e.g., ImGui::Text(), ImGui::Button()) outside of a Begin()/End() block, or after calling End() without a matching Begin().

Solution:

  1. Ensure every widget call is between a Begin()/End() pair.
  2. Check for early returns that skip the End() call.
  3. Never call widget functions before NewFrame() or after Render().

Prevention: Structure your code so End() is always called — even when Begin() returns false:

if (ImGui::Begin("Window")) { /* widgets */ }
ImGui::End();  // Always called

Error: Assertion failed: (flags & ImGuiWindowFlags_NoInputs) == 0

Cause: Attempted to focus or interact with a window that has ImGuiWindowFlags_NoInputs set.

Solution:

  1. Remove ImGuiWindowFlags_NoInputs if the window needs interaction.
  2. If using NoInputs intentionally (overlay windows), ensure no code tries to focus or interact with it.

Prevention: Only use NoInputs for purely visual overlay windows.


Error: Duplicate widget IDs causing interaction failures

Cause: Two or more widgets in the same scope share the same label, producing the same ID. Only the first widget receives interactions.

Solution:

  1. Add unique suffixes with ##: Button("Delete##item1"), Button("Delete##item2").
  2. Use PushID(i) / PopID() in loops.
  3. Use the ID Stack Debugger: ImGui::ShowIDStackToolWindow() to inspect active IDs.

Prevention: Use ## or PushID whenever creating widgets in a loop or with repeated labels. This is the most common beginner mistake.


Error: WantCaptureMouse not preventing game input

Cause: The application is not checking io.WantCaptureMouse / io.WantCaptureKeyboard before dispatching input to the game layer.

Solution:

  1. After ImGui::NewFrame(), check the flags before processing input:
if (!io.WantCaptureMouse)
{
    // Process mouse for game
}
if (!io.WantCaptureKeyboard)
{
    // Process keyboard for game
}
  1. Ensure you are checking the flags after NewFrame(), not before.

Prevention: Always gate your application's input handling behind these flags.


Error: Font atlas texture fails to build

Cause: The combined size of all fonts exceeds the maximum texture dimensions, or a font file path is incorrect.

Solution:

  1. Verify font file paths exist and are readable.
  2. Reduce the number of glyph ranges or fonts.
  3. Increase atlas size: io.Fonts->TexDesiredWidth = 4096;
  4. Check return value of AddFontFromFileTTF() — it returns nullptr on failure.

Prevention: Build the font atlas early in initialization and check the return values. Use GetGlyphRangesChineseSimplifiedCommon() instead of GetGlyphRangesChineseFull() when full CJK is not needed.


Error: Linker errors — undefined reference to ImGui::*

Cause: The imgui .cpp files are not compiled and linked into your application.

Solution:

  1. Ensure all four core files are in your build: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp.
  2. Ensure backend .cpp files are also compiled: imgui_impl_<platform>.cpp and imgui_impl_<renderer>.cpp.
  3. If using a static library, ensure it was built with the same imconfig.h.

Prevention: Add all required .cpp files to your build system at setup time.


Error: Assertion failed: g.FrameCount > 0 or NewFrame not called

Cause: You called Render() or widget functions without first calling NewFrame(), or called NewFrame() twice without an intervening Render().

Solution:

  1. Ensure every frame follows the pattern: NewFrame() → widgets → Render().
  2. Check that backend _NewFrame() calls happen before ImGui::NewFrame().

Prevention: Use the standard frame structure from the example applications.


Error: Window positions reset every launch

Cause: The .ini file is not being saved or loaded. Common causes: IniFilename set to nullptr, working directory changes between runs, file permissions.

Solution:

  1. Check io.IniFilename is set (default is "imgui.ini").
  2. Verify the working directory is consistent.
  3. Use an absolute path: io.IniFilename = "C:/myapp/imgui.ini";
  4. If using manual persistence, call SaveIniSettingsToDisk() before destroying the context.

Prevention: Set IniFilename to an absolute path in a known-writable directory.


Error: DLL boundary crashes when using Dear ImGui across DLL boundaries

Cause: Each DLL gets its own copy of Dear ImGui's global state (GImGui). Widgets called from a DLL use a different context than the main application.

Solution:

  1. Use ImGui::SetCurrentContext() at the start of DLL functions to set the context created by the host application.
  2. Pass the context pointer from the host to the DLL via an initialization function.
  3. Alternatively, configure IMGUI_API to export/import symbols so all code shares one copy.

Prevention: Design your architecture so all Dear ImGui calls share a single context. Pass the ImGuiContext* across DLL boundaries.


Error: Text renders as rectangles or question marks

Cause: The requested glyphs are not in the font atlas. The default font only covers Basic Latin (U+0020–U+00FF).

Solution:

  1. Load a font with the required glyph ranges: AddFontFromFileTTF("font.ttf", size, nullptr, io.Fonts->GetGlyphRangesJapanese()).
  2. For custom ranges, build an ImWchar array: { 0x4e00, 0x9fff, 0 }.
  3. Merge icon fonts using ImFontConfig::MergeMode = true.

Prevention: Plan your glyph requirements at initialization time. Test with the target language immediately.


Error: Colors look wrong (swapped red and blue channels)

Cause: Your renderer expects BGRA color packing but Dear ImGui outputs RGBA by default (or vice versa).

Solution:

  1. Define IMGUI_USE_BGRA_PACKED_COLOR in imconfig.h if your renderer expects BGRA.
  2. Alternatively, swap channels in your pixel shader.

Prevention: Check your renderer's expected color format and configure imconfig.h accordingly.


Error: Assertion failed: draw_list->CmdBuffer.Size > 0 or empty draw data

Cause: Render() was called without any Begin()/End() pairs, or the Dear ImGui context is not properly initialized.

Solution:

  1. Ensure at least one window exists (even an empty one) before rendering.
  2. Check that CreateContext() was called before any other Dear ImGui function.
  3. Verify the backend initialization order: platform init → renderer init → main loop.

Prevention: Follow the initialization order from the example applications.


Error: ImGui::TableNextRow() called without calling ImGui::TableNextColumn()

Cause: Incorrect table traversal pattern — you must call TableNextColumn() or TableSetColumnIndex() after TableNextRow() before emitting content.

Solution:

// Correct pattern:
ImGui::TableNextRow();
ImGui::TableNextColumn();  // Move to column 0
ImGui::Text("Content");

Prevention: Use either the TableNextRow() + TableSetColumnIndex() pattern or the simpler TableNextColumn() flow consistently.


Error: Scrollbar appears but content is not scrollable

Cause: The BeginChild() region has no height limit, so the child auto-sizes to fit content and never needs scrolling.

Solution:

  1. Specify an explicit height: BeginChild("list", ImVec2(0, 300)).
  2. Or use ImGui::BeginChild("list", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())) to fill remaining space minus a footer.

Prevention: Always specify a height for scrollable child regions.


Error: Assertion failed: user_callback != NULL in ImDrawCmd

Cause: A draw command has a user callback set but the function pointer is null or invalid. This can happen with corrupted draw lists.

Solution:

  1. If using custom draw callbacks, ensure the callback pointer is valid for the lifetime of the frame.
  2. Check for memory corruption in your draw list manipulation code.

Prevention: Only use AddCallback() when necessary, and ensure callbacks survive until Render() completes.


Error: Mouse cursor is offset or in the wrong position

Cause: Display scaling (HiDPI), multiple monitors with different DPIs, or incorrect io.DisplayFramebufferScale.

Solution:

  1. Ensure io.DisplaySize matches your window's client area in screen coordinates.
  2. Set io.DisplayFramebufferScale to the ratio of framebuffer pixels to screen coordinates (typically 2.0 on Retina displays).
  3. Use the backend's built-in handling (most backends handle this automatically).

Prevention: Use the platform backend's DPI handling. Check ImGui_ImplXxx_NewFrame() in your backend for DPI-related code.


Error: Widgets stop responding after a popup is closed unexpectedly

Cause: A popup was opened but never properly closed (e.g., the code path that calls EndPopup() was skipped).

Solution:

  1. Ensure EndPopup() is always called when BeginPopup() / BeginPopupModal() returns true.
  2. Check for early returns between BeginPopup() and EndPopup().

Prevention: Structure popup code as:

if (ImGui::BeginPopup("name"))
{
    // All code here
    ImGui::EndPopup();
}

Never place returns or conditionals between BeginPopup and EndPopup.


Error: Table column widths reset unexpectedly

Cause: The table ID changed between frames (dynamic label, different scope), or .ini settings are not being persisted.

Solution:

  1. Use a stable string ID for BeginTable().
  2. If the table is in a loop, use PushID() to create a stable scope.
  3. Check that io.IniFilename is set and writable.

Prevention: Use fixed table identifiers and ensure .ini persistence is working.


Error: ImGui::Shortcut() does not trigger

Cause: Another window or widget has focus and is consuming the key. Or ImGuiConfigFlags_NavEnableKeyboard is not set.

Solution:

  1. Enable keyboard navigation: io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard.
  2. Use ImGuiInputFlags_RouteGlobal if you want the shortcut to fire regardless of focus: ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_RouteGlobal).

Prevention: Plan your shortcut routing strategy. Use route flags to control priority.


Error: Assertion failed: "Mismatched Begin/End calls" or stack mismatch

Cause: A Begin* call (e.g., BeginChild, BeginGroup, BeginTable, BeginMenu, PushStyleColor) does not have a matching End* / Pop* call, or vice versa.

Solution:

  1. Use the Metrics/Debug window (ShowMetricsWindow()) — it displays the active stack and highlights mismatches.
  2. Search your code for all Begin* / Push* calls and verify matching End* / Pop* calls.
  3. Pay attention to conditional logic — if BeginChild() is inside an if, EndChild() must be in the same if block.

Prevention: Keep Begin*/End* pairs at the same indentation level and in the same scope. Never conditionally skip an End* call.


Error: IMGUI_CHECKVERSION() assertion fails

Cause: The Dear ImGui version compiled into your application does not match the version compiled into a library or DLL you are linking against. This typically means mismatched headers and .cpp files.

Solution:

  1. Ensure all translation units use the same version of Dear ImGui headers.
  2. If using a precompiled library, rebuild it with the same imgui.h as your application.

Prevention: Always compile Dear ImGui from source as part of your project rather than using prebuilt binaries.


Error: AddFontFromFileTTF returns nullptr

Cause: The font file path is invalid, the file cannot be read, or memory allocation failed.

Solution:

  1. Verify the file exists at the specified path.
  2. Use an absolute path or verify the working directory.
  3. On mobile platforms, ensure the font is bundled in the app's assets.
  4. Check that ImFileOpen works for your platform (override via IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS if needed).

Prevention: Check the return value of AddFontFromFileTTF(). Log the attempted path if it fails.


Error: Draw list generates too many vertices (overflow with 16-bit indices)

Cause: A single draw call generated more than 65,535 vertices, which exceeds the range of ImDrawIdx (default unsigned short).

Solution:

  1. Define ImDrawIdx as unsigned int in imconfig.h for 32-bit indices.
  2. Or reduce the complexity of your drawing (fewer segments in circles, fewer items per draw list).

Prevention: For complex applications with lots of drawing, use 32-bit indices from the start: #define ImDrawIdx unsigned int in imconfig.h.


7. FAQ

General

Q: Which version should I use — master or docking branch? The master branch is the stable, mainline version. The docking branch adds multi-window docking and multi-viewport support but is experimental. For new projects, start with master. If you specifically need window docking (tabbing windows together like an IDE), use the docking branch and be prepared for occasional API changes.

Q: How do I integrate Dear ImGui into my existing application? Add the source files to your project, pick a platform+renderer backend from backends/, and call the initialization functions once. In your main loop, add the three-phase pattern: NewFrame() → widget calls → Render() + backend draw. Dear ImGui does not take over your main loop or event handling — you keep full control. See the examples/ folder for complete working integrations.

Q: Is Dear ImGui thread-safe? No. All Dear ImGui calls must happen on the same thread. The library uses global state (GImGui) that is not protected by locks. If you need to update the UI from another thread, collect the data on that thread and read it on the main thread during UI rendering.

Q: Can I use Dear ImGui with language X? Dear ImGui is a C++ library, but there are third-party bindings for many languages: Python (pyimgui, imgui-bundle), C# (ImGui.NET), Rust (imgui-rs), Go, Java, Lua, and others. The C binding (cimgui) enables integration with most languages that support C FFI. See the Wiki for a list of bindings.

Q: How do I handle high-DPI / Retina displays? Load fonts at the appropriate size for the display (e.g., 2x the normal size for a 2x Retina display), and set io.FontGlobalScale if you want to uniformly scale all text. Most backends handle io.DisplayFramebufferScale automatically. With the new dynamic font sizing in 1.92, you can load a single vector font and render it at any size, which simplifies DPI handling.

Q: Does Dear ImGui support Unicode? Yes. Dear ImGui uses UTF-8 for all text. The default font covers Basic Latin. For other scripts (CJK, Cyrillic, Thai, etc.), load a font with the appropriate glyph ranges. Define IMGUI_USE_WCHAR32 for characters outside the Basic Multilingual Plane (above U+FFFF).

Q: What is the performance overhead of Dear ImGui? Minimal. Dear ImGui generates vertex buffers and draw commands, which are very fast to produce. The GPU cost is typically negligible compared to your application's own rendering. The main cost is the Render() call, which flattens the draw lists. For large UIs with thousands of items, use ImGuiListClipper to cull off-screen items.

Windows

Q: How do I make a window that fills the entire screen?

ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::Begin("Fullscreen", nullptr,
    ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove
    | ImGuiWindowFlags_NoBringToFrontOnFocus);
// Content...
ImGui::End();

Q: How do I create a window that cannot be closed? Do not pass a p_open pointer (second parameter) to Begin(). Without it, no close button is shown.

Q: How do I prevent a window from being moved or resized? Use ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize.

Q: How can I have a window with no title bar? Use ImGuiWindowFlags_NoTitleBar. The window still needs a unique string name for identification — this name is used as the ID, not displayed.

Widgets

Q: Why is my widget not reacting when I click on it? Most likely a duplicate ID conflict. Two widgets with the same label in the same scope will share an ID, and only the first one receives interactions. Use ##unique_suffix to differentiate or PushID() in loops. Use ShowIDStackToolWindow() to debug.

Q: How can I have widgets with empty labels? Use ## with a unique ID: ImGui::Button("##hidden_button"). The label is empty but the ID is derived from "hidden_button". You can also use "" as a label, but then you must ensure uniqueness via PushID().

Q: How can I have multiple widgets with the same visible label? Use the ## separator: ImGui::Button("OK##dialog1") and ImGui::Button("OK##dialog2") both display "OK" but have different IDs.

Q: How do I make a button that fills the available width?

ImGui::SetNextItemWidth(-FLT_MIN);
// or for buttons specifically:
if (ImGui::Button("Full Width", ImVec2(-FLT_MIN, 0))) { }

Q: How do I change a widget's color temporarily?

ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.1f, 0.1f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.1f, 0.1f, 1.0f));
ImGui::Button("Red Button");
ImGui::PopStyleColor(3);

Q: How do I get the text height for layout calculations? Use ImGui::GetTextLineHeight() for a single line or ImGui::GetTextLineHeightWithSpacing() for a line including the spacing between lines.

Input

Q: How do I dispatch inputs between Dear ImGui and my application? Check io.WantCaptureMouse and io.WantCaptureKeyboard after NewFrame(). When they are true, Dear ImGui needs the input — do not pass it to your application. When false, Dear ImGui does not need it.

Q: How do I handle text input (character input) correctly? Use io.AddInputCharacter() or io.AddInputCharactersUTF8() for text input. Do not convert key presses to characters yourself — that misses dead keys, IME, and system-specific text input. Most backends handle this automatically.

Q: How does navigation (keyboard/gamepad) work? Enable it with io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard. Then Arrow keys move between widgets, Enter activates, Escape cancels/closes. Tab cycles through input fields. Ctrl+Tab switches between windows. The system works automatically with all standard widgets.

Q: Can I detect when a slider value has been committed (not just changed)? Yes. Use IsItemDeactivatedAfterEdit() — it returns true on the frame the user releases the slider (or confirms input), and only if the value changed during the interaction.

Fonts

Q: How do I load a custom font?

ImGuiIO& io = ImGui::GetIO();
ImFont* font = io.Fonts->AddFontFromFileTTF("my_font.ttf", 18.0f);
// In your frame loop, to use it:
ImGui::PushFont(font, 18.0f);
ImGui::Text("Custom font text");
ImGui::PopFont();

Q: How do I display an icon font alongside text? Load the icon font with MergeMode = true so it merges into the previous font. Then use the icon's Unicode codepoint inline: ImGui::Text("Save " ICON_FA_SAVE) (where ICON_FA_SAVE is a define for the Unicode codepoint string like "\xef\x83\x87").

Q: Why is my font blurry? Bitmap fonts look best at their native size. If you need crisp text at various sizes, use the new vector font support (AddFontDefaultVector() or PushFont(font, size)) or enable FreeType with IMGUI_ENABLE_FREETYPE. Also ensure io.FontGlobalScale is 1.0 and that you are loading fonts at the correct size for your display DPI.

Q: How do I use the new dynamic font sizing (1.92)? Load a font without specifying a pixel size (or with 0), then use PushFont(font, desired_size) and PopFont() at render time. This allows a single font to be drawn at different sizes without reloading.

Performance

Q: How do I render a large scrollable list efficiently? Use ImGuiListClipper to skip off-screen items:

ImGuiListClipper clipper;
clipper.Begin(10000);
while (clipper.Step())
    for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
        ImGui::Text("Item %d", i);

Without the clipper, Dear ImGui would process all 10,000 items even if only 20 are visible.

Q: Is there a limit to how many widgets I can have per frame? No hard limit. Practically, thousands of visible widgets per frame is fine. For tens of thousands of items, use ImGuiListClipper or virtual scrolling. The CPU cost is proportional to the number of visible widgets, not the total data size.

Q: How do I reduce Dear ImGui's draw call count? Dear ImGui automatically batches draw calls by texture. To minimize draw calls: avoid switching fonts/textures frequently, merge icon fonts into your text font, and use a single texture atlas. The Metrics window (ShowMetricsWindow()) shows draw call counts per window.

Integration

Q: How do I display a texture/image from my application? Create the texture using your graphics API, then pass its identifier as ImTextureID to ImGui::Image(). The exact cast depends on your backend (see section 4.11). The texture must remain valid until Render() completes.

Q: How do I add Dear ImGui to a project that uses CMake? Add the source files directly — there is no CMakeLists.txt provided by Dear ImGui (by design). Create your own add_library(imgui STATIC ...) or add the files to your executable's source list. Add the imgui/ and imgui/backends/ directories to your include path.

Q: How do I render Dear ImGui on top of my 3D scene? Dear ImGui's render output (ImGui::GetDrawData()) is just a list of draw commands with vertex buffers. Render it after your 3D scene in your main render pass. Most backends handle this automatically — you just call ImGui_ImplXxx_RenderDrawData(ImGui::GetDrawData()) at the end of your frame.

Q: Can I use Dear ImGui without a window/display (headless)? Yes. Use the Null backend (example_null/). This is useful for testing, generating screenshots from a CI pipeline, or running the test engine.

Building

Q: Do I need CMake to build Dear ImGui? No. Dear ImGui has no build system of its own. Add the .cpp files to whatever build system you use. It works with Visual Studio, Make, CMake, Meson, Premake, Xcode, Ninja, or any build tool that can compile C++.

Q: I get linker errors about undefined ImGui symbols. What am I missing? You need to compile and link all four core files: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp. Also compile the backend files you are using. If you added the headers but not the source files, you will get linker errors.

Q: How do I compile Dear ImGui as a DLL/shared library? Define IMGUI_API as __declspec(dllexport) when building the DLL and __declspec(dllimport) when consuming it. Use the same imconfig.h for both. Pass ImGuiContext* across the DLL boundary using ImGui::SetCurrentContext().

Q: What compiler warnings should I expect? Dear ImGui compiles cleanly with most compilers at high warning levels. If you use -Wall -Wextra (GCC/Clang) or /W4 (MSVC), you may see a few warnings in the STB headers — these are safe to suppress with pragmas or by excluding them from high-warning-level builds.