ImGui
lastmod 2026-11-02created 2016-10-2642 edits
Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies.
Categories
Bindings
Extensions
- Useful Extensions · ocornut/imgui Wiki
- ImTui - A Text-based User Interface that runs in the terminal
- Grid view / layout · Issue #857 · ocornut/imgui - Grid layout
- Columns API work · Issue #125 · ocornut/imgui - Sample of a Splitter adjustable with the mouse.
Custom Render Example
raylib example:
void RenderRayGui()
{
ImGui::EndFrame();
ImGui::Render();
ImDrawData * draw_data = ImGui::GetDrawData();
assert(draw_data != nullptr);
if (draw_data->CmdListsCount == 0) {
return;
}
ImGuiIO & io = ImGui::GetIO();
// Avoid rendering when minimized,
// scale coordinates for retina displays
// (screen coordinates != framebuffer coordinates)
auto const FRAME_BUFFER_WIDTH = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
auto const FRAME_BUFFER_HEIGHT = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (FRAME_BUFFER_WIDTH <= 0 || FRAME_BUFFER_HEIGHT <= 0) {
return;
}
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 const CLIP_OFF = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 const CLIP_SCALE = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
// TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
// TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
// TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++) {
ImDrawList const * cmd_list = draw_data->CmdLists[n];
ImDrawVert const * vtx_buffer = cmd_list->VtxBuffer.Data;
ImDrawIdx const * idx_buffer = cmd_list->IdxBuffer.Data;
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
ImDrawCmd const * pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
// The texture for the draw call is specified by pcmd->TextureId.
// The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
//MyEngineBindTexture((MyTexture*)pcmd->TextureId);
// We are using scissoring to clip some objects. All low-level graphics API should supports it.
// - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
// (some elements visible outside their bounds) but you can fix that once everything else works!
// - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
// In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
// However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
// always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
// - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
ImVec2 pos = draw_data->DisplayPos;
//MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
// Render 'pcmd->ElemCount/3' indexed triangles.
// By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
//MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
auto texture_id = (std::intptr_t)pcmd->TextureId;
rlEnableTexture((unsigned int)texture_id);
rlBegin(RL_TRIANGLES);
assert(pcmd->ElemCount >= 3 && pcmd->ElemCount % 3 == 0);
auto const TRIANGLE_COUNT = pcmd->ElemCount / 3;
for (int i = 0; i < TRIANGLE_COUNT; ++i) {
auto const POINT0 = vtx_buffer[idx_buffer[(i*3)+0]];
auto const POINT1 = vtx_buffer[idx_buffer[(i*3)+1]];
auto const POINT2 = vtx_buffer[idx_buffer[(i*3)+2]];
auto const COLOR0 = GetColor(POINT0.col);
auto const COLOR1 = GetColor(POINT1.col);
auto const COLOR2 = GetColor(POINT2.col);
rlColor4ub(COLOR0.r, COLOR0.g, COLOR0.b, COLOR0.a);
rlTexCoord2f(POINT0.uv.x, POINT0.uv.y);
rlVertex2f(POINT0.pos.x, POINT0.pos.y);
rlColor4ub(COLOR1.r, COLOR1.g, COLOR1.b, COLOR1.a);
rlTexCoord2f(POINT1.uv.x, POINT1.uv.y);
rlVertex2f(POINT1.pos.x, POINT1.pos.y);
rlColor4ub(COLOR2.r, COLOR2.g, COLOR2.b, COLOR2.a);
rlTexCoord2f(POINT2.uv.x, POINT2.uv.y);
rlVertex2f(POINT2.pos.x, POINT2.pos.y);
}
rlEnd();
rlDisableTexture();
}
idx_buffer += pcmd->ElemCount;
}
}
}
imgui-sfml
- Github - imgui-sfml
- [Recommended] Creating awesome GUI for you game dev tools with ImGui and SFML. Part 1 1
ImGui binding for use with SFML.
Simple example
#include <imgui/imgui.h>
#include <imgui/imgui-sfml.h>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/Window/Event.hpp>
int runDefaultImGuiSfml()
{
sf::RenderWindow window(sf::VideoMode(200, 200), LIBWORLD_MAIN_TITLE);
window.setVerticalSyncEnabled(true);
ImGui::SFML::Init(window);
sf::Clock deltaClock;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
ImGui::SFML::ProcessEvent(event);
if (event.type == sf::Event::Closed) {
window.close();
}
}
ImGui::SFML::Update(deltaClock.restart());
ImGui::Begin(LIBWORLD_MAIN_TITLE); // begin window
ImGui::End(); // end window
window.clear();
ImGui::Render();
window.display();
}
ImGui::SFML::Shutdown();
return 0;
}
Korean (Hangul) input
- DirectX11 3D - How to output Korean Unicode UTF-8 in the ImGui user interface and fix broken Korean text - Study
- Pygame:HangulInputText - Notes on Korean text input.
Add a Unicode Korean font to the ImGuiIO object:
ImGuiManager::ImGuiManager()
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\malgun.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesKorean());
}
ImGuiManager::~ImGuiManager()
{
ImGui::DestroyContext();
}
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\malgun.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesKorean());
Even if you do not use an ImGui manager, just call it in the part where you initialize ImGui
yourself. For the font, I used Microsoft Malgun Gothic, the most common one.
io.Fonts->GetGlyphRangesKorean() specifies the particular glyph ranges needed for Korean.
(u8"한글 출력", i.e. "Korean output"):
#pragma region IMGUI INTERFACE
if (ImGui::Begin("Chatting Box"))
{
ImGui::TextColored(color,isConnect.c_str());
//연결이 안되어 있으면 연결 재 시도
if (!m_Net.m_bConnect)
{
if (ImGui::Button(u8"연결 재시도"))
{
if (m_Net.Connect(g_hWnd, SOCK_STREAM, 10000, IP_DD))
{
m_bConnect = true;
}
}
}
else
{
ImGui::BeginChild(u8"채팅창", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing() - 15));
ImGui::Text(chatItems);
ImGui::EndChild();
ImGui::Dummy(ImVec2(0.0f, 5));
ImGui::InputText("", buffer, sizeof(buffer));
ImGui::SameLine();
if (ImGui::Button("Send"))
{
char clear[MAX_PATH] = { 0, };
KPacket kPacket(PACKET_CHAT_MSG);
kPacket << 123 << "Test" << (short)12 << buffer;
//리턴 값이 0보다 작으면 전송되지 않았음
if (m_Net.SendMsg(m_Net.m_Sock, kPacket.m_uPacket) < 0)
{
ZeroMemory(&chatItems, sizeof(char) * 2048);
strcat(chatItems, "Error\n");
m_Net.m_bConnect = false;
}
strcpy(buffer, clear);
}
}
}
ImGui::End();
#pragma endregion
Prefix the Korean strings with u8 to mark them explicitly as UTF-8 (Unicode) characters.
I suspect it uses MultiBytetoWideChar() internally.
That is how simply you can output Korean text in imgui.
Displaying a widget at the bottom of a window
#include "imgui.h"
void ExampleWindowWithBottomTextInput()
{
// 입력 버퍼를 정의합니다.
static char inputBuffer[128] = "";
// 새로운 창을 생성합니다.
ImGui::Begin("Example Window");
// 창의 크기를 가져옵니다.
ImVec2 windowSize = ImGui::GetWindowSize();
// 현재 커서의 위치를 가져옵니다.
ImVec2 cursorPos = ImGui::GetCursorPos();
// 텍스트 입력 필드의 높이를 설정합니다.
float inputFieldHeight = ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().FramePadding.y * 2;
// 창의 하단에 텍스트 입력 필드를 배치하기 위한 위치를 계산합니다.
float yOffset = windowSize.y - inputFieldHeight - ImGui::GetStyle().WindowPadding.y * 2;
// 텍스트 입력 필드의 위치를 창의 하단으로 설정합니다.
ImGui::SetCursorPosY(yOffset);
// 텍스트 입력 필드를 추가합니다.
ImGui::InputText("##input", inputBuffer, IM_ARRAYSIZE(inputBuffer));
// 다시 커서를 원래 위치로 이동시킵니다.
ImGui::SetCursorPos(cursorPos);
// 추가적인 내용이 있을 수 있으므로 창을 끝냅니다.
ImGui::End();
}
Customizing the window title bar
List of Callback-related flags for InputText
-
ImGuiInputTextFlags_CallbackCompletion- A callback related to autocompletion. For example, the autocompletion callback is called when the user presses a specific key (usually Tab). -
ImGuiInputTextFlags_CallbackHistory- A callback called when navigating input history. Typically used to select history entries when the up/down arrow keys are pressed. -
ImGuiInputTextFlags_CallbackAlways- A callback called every time the text field is edited. Useful when you want the callback to keep running whatever change occurs. -
ImGuiInputTextFlags_CallbackCharFilter- A callback for filtering input characters. It is called when the user types a character, letting you filter or modify the entered character. -
ImGuiInputTextFlags_CallbackResize- A callback called when the size of the string changes. Mainly used when dynamic string memory allocation is needed, and useful for variable-length strings such as std::string. -
ImGuiInputTextFlags_CallbackEdit- A callback called when the text is edited, only when an edit actually occurs.
HTML Rendering
- Rendering html? · Issue #4257 · ocornut/imgui
- (Question)CEF Off-Screen-Rendering + ImGUI · Issue #1140 · ocornut/imgui
- How to render a web browser in imgui · Issue #1075 · ocornut/imgui
- CEF (Chromium Embedded Framework)
- litehtml
Dynamically reserving the needed height at the bottom of a window
imgui keeps scrolling as content grows. So, as in the figure below, a method of creating a separate scrolling region with a Child and padding its height relative to the bottom of the window
Imgui-footer_height_to_reserve.png
Below is the code at around line 7242 of imgui_demo.cpp.
//-----------------------------------------------------------------------------
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
//-----------------------------------------------------------------------------
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
struct ExampleAppConsole
{
// ...
void Draw(const char* title, bool* p_open)
{
// ...
// Reserve enough left-over height for 1 separator + 1 input text
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NavFlattened))
{
// ...
}
ImGui::EndChild();
ImGui::Separator();
// ...
Implementing double-click on Selectable
if (ImGui::TreeNode("Basic"))
{
static bool selection[5] = { false, true, false, false };
ImGui::Selectable("1. I am selectable", &selection[0]);
ImGui::Selectable("2. I am selectable", &selection[1]);
ImGui::Selectable("3. I am selectable", &selection[2]);
if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick))
if (ImGui::IsMouseDoubleClicked(0))
selection[3] = !selection[3];
ImGui::TreePop();
}
Calculating the Size of the triangle button on the right of a Combo
Check the "BeginCombo" function in imgui_widgets.cpp:
Find the code that calculates the Size by looking around ImGuiComboFlags_NoArrowButton.
bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
{
// ...
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id, &bb))
return false;
// ...
// Render shape
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);
RenderNavHighlight(bb, id);
if (!(flags & ImGuiComboFlags_NoPreview))
window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
if (!(flags & ImGuiComboFlags_NoArrowButton))
{
ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImU32 text_col = GetColorU32(ImGuiCol_Text);
window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);
if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)
RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);
}
// ...
}
GUI DPI Scale
The proper way to make a Custom Widget
Developer's recommendation:
-
InvisibleButton()
A passerby's recommendation:
- Give an ID to your widget using
PushID()/PopID(). This will solve your widget affecting other widgets. - Use
ItemAdd()/ItemSize(). - If your widget is interactable use
SetActiveID()/ClearActiveID().
Changing the font scale
As a dubious workaround, you can get your current ImFont*, change the font->Scale value and then call PushFont() on this font. After you call PopFont you want to restore the size.
You can also use SetWindowFontScale() which is a stupidly old API (that will be removed by an eventual PushFontSize/PopFontSize()`.
I won't add the explicit API until font sizes are handled more correctly at the atlas rasterizing level.
How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
- Detecting if any text input is active · Issue #5177 · ocornut/imgui
- imgui/docs/FAQ.md at master · ocornut/imgui
- When
io.WantCaptureMouseis set, you need to discard/hide the mouse inputs from your underlying application. - When
io.WantCaptureKeyboardis set, you need to discard/hide the keyboard inputs from your underlying application. - When
io.WantTextInputis set, you can notify your OS/engine to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
Animated Title
- Changing the window name · Issue #6872 · ocornut/imgui
- Change window title while keeping old window · Issue #251 · ocornut/imgui
If you change the title with ImGui::Begin("..."), the ID assigned in imgui.ini keeps changing. (Focus also keeps moving to that window.)
In this case, use ###. Use it like TitleName###WindowID.
See the ShowExampleAppWindowTitles() function in the imgui_demo.cpp file.
BeginViewportSideBar
You can use it if you want to make something like a "Status Bar" or "Tool Bar".
// [1] BeginViewportSideBar signature receives ImGuiViewport* viewport
//
// You can change this:
// ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)ImGui::GetMainViewport();
// To this:
// ImGuiViewport* viewport = ImGui::GetMainViewport();
//
// But you can pass just NULL because main viewport is the default.
//
// [2] ImGui::BeginViewportSideBar uses ImGui::Begin so the call to ImGui::End should be out of the {if scope}
//
// Updated version:
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
float height = ImGui::GetFrameHeight();
if (ImGui::BeginViewportSideBar("##SecondaryMenuBar", NULL, ImGuiDir_Up, height, window_flags)) {
if (ImGui::BeginMenuBar()) {
ImGui::Text("Happy secondary menu bar");
ImGui::EndMenuBar();
}
}
ImGui::End();
if (ImGui::BeginViewportSideBar("##MainStatusBar", NULL, ImGuiDir_Down, height, window_flags)) {
if (ImGui::BeginMenuBar()) {
ImGui::Text("Happy status bar");
ImGui::EndMenuBar();
}
}
ImGui::End();
DockBuilder Example
- A commented example for Dear ImGui's DockBuilder API.
- Docking branch available for testing · Issue #2109 · ocornut/imgui
ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_Dockspace); // Add empty node
ImGui::DockBuilderSetNodeSize(dockspace_id, dockspace_size);
ImGuiID dock_main_id = dockspace_id; // This variable will track the document node, however we are not using it here as we aren't docking anything into it.
ImGuiID dock_id_prop = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.20f, NULL, &dock_main_id);
ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id);
ImGui::DockBuilderDockWindow("Log", dock_id_bottom);
ImGui::DockBuilderDockWindow("Properties", dock_id_prop);
ImGui::DockBuilderDockWindow("Mesh", dock_id_prop);
ImGui::DockBuilderDockWindow("Extra", dock_id_prop);
ImGui::DockBuilderFinish(dockspace_id);
You must use ImGuiDockNodeFlags_Dockspace so that the empty part of the Parent remains after an empty Split.
APIs
BeginChild
Begin a scrolling region.
sizing of child region allows for three modes:
-
0.0- use remaining window size -
>0.0- fixed size -
<0.0- use remaining window size minus abs(size)
Troubleshooting
MenuItem & BeginPopupModal
Fix for when GuiBeginPopupModal using GuiOpenPopup in a MenuItem does not work properly:
bool openMyPopup = false;
if (ImGui::BeginPopupContextItem("BeginPopupContextItem")) {
if (ImGui::MenuItem("Rename")) {
openMyPopup = true;
}
ImGui::EndPopup(); // <--- remember this
}
if (openMyPopup) {
ImGui::OpenPopup("OpenPopup");
}
if (ImGui::BeginPopupModal("OpenPopup")) {
// ...
ImGui::EndPopup();
}
Local Download
- ImGui 1.49 release
-
Imgui-1.49.tar.gz
- ImGui + SFML b764eb7
-
Imgui-sfml-b764eb7.zip
See also
- Graphical user interface
- LumixEngine
- Nuklear
- pyimgui - Cython-based Python bindings for dear imgui
- PyOpenGL
- egui (rust)
- Prospero - A new coding harness with a lightweight design and support for remote execution
Favorite site
- Github - ImGui
- Github - ImGui Club - Nice things to use along dear imgui
- [Recommended] ImGui Interactive Manual - Rendered with WebGL; shows the CPP code under the cursor.
Node Graph Editors
Gallery
- Issues · ocornut/imgui - List of the "Gallery" Label.
- Gallery: Post your screenshots / code here (PART 7) · Issue #1902 · ocornut/imgui
References
-
Creating_awesome_GUI_for_you_game_dev_tools_with_ImGui_and_SFML-Part_1.pdf ↩