ImGui
Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies.
Categories
Bindings
Extensions
- Useful Extensions · ocornut/imgui Wiki
- ImTui - 터미널에서 작동할 수 있는 Text-based User Interface
- Grid view / layout · Issue #857 · ocornut/imgui - 그리드 레이아웃
- Columns API work · Issue #125 · ocornut/imgui - 마우스로 조절 가능한 Splitter 샘플.
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
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;
}
한글 입력
ImGuiIO 객체의 유니코드 한글 폰트를 추가한다:
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());
굳이 ImGui 매니져를 사용하지 않더라도, 본인이 ImGui를 초기화하는 부분에서
호출해주면 된다. 나는 폰트는 제일 흔한 마이크로소프트 맑은 고딕을 사용했다.
io.Fonts->GetGlyphRangesKorean()
로 한국어의 필요한 특정 그리프 범위를 지정해준다.
(u8"한글 출력"):
#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
한글 부분에 u8를 붙여서 UTF-8 (유니코드) 글자임을 명시한다.
아마도 내부적으로 MultiBytetoWideChar()
를 쓰고 있지 않을까 싶다.
이렇게 간단하게 imgui에서 한글 출력을 할 수가 있다.
윈도우 하단에 위젯 출력
#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();
}
윈도우 타이틀바 커스터마이징
InputText 의 Callback 관련 플래그 목록
-
ImGuiInputTextFlags_CallbackCompletion
- 자동 완성 기능과 관련된 콜백입니다. 예를 들어, 사용자가 특정 키(주로 Tab 키)를 누르면 자동 완성 콜백이 호출됩니다. -
ImGuiInputTextFlags_CallbackHistory
- 입력 히스토리 탐색 시 호출되는 콜백입니다. 주로 위/아래 화살표 키를 눌렀을 때 히스토리 항목을 선택하도록 합니다. -
ImGuiInputTextFlags_CallbackAlways
- 텍스트 필드가 편집될 때마다 호출되는 콜백입니다. 어떤 변경이 발생하든지 콜백이 계속해서 실행되도록 설정할 때 유용합니다. -
ImGuiInputTextFlags_CallbackCharFilter
- 입력 문자 필터링을 위한 콜백입니다. 사용자가 문자를 입력할 때 해당 콜백이 호출되어 입력된 문자를 필터링하거나 수정할 수 있습니다. -
ImGuiInputTextFlags_CallbackResize
- 문자열의 크기가 변경될 때 호출되는 콜백입니다. 주로 동적 문자열 메모리 할당이 필요한 경우에 사용되며, std::string과 같은 가변 길이 문자열에 유용합니다. -
ImGuiInputTextFlags_CallbackEdit
- 텍스트가 편집될 때 호출되는 콜백으로, 편집이 실제로 발생했을 때만 호출됩니다.
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
윈도우 하단에 필요한 만큼 높이 남기고 Dynamic 하게 조정하기
imgui는 컨텐츠가 늘어나면 계속 스크롤되는 특징이 있다. 그래서 아래 그림처럼 Child 로 스크롤 영역 따로 만들고 높이를 윈도우의 하단 기준으로 Padding 시키는 방법
Imgui-footer_height_to_reserve.png
아래는 imgui_demo.cpp
의 약 7242 라인에 있는 코드 이다.
//-----------------------------------------------------------------------------
// [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();
// ...
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();
}
폰트 스케일 변경
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.
Animated Title
- Changing the window name · Issue #6872 · ocornut/imgui
- Change window title while keeping old window · Issue #251 · ocornut/imgui
ImGui::Begin("...")
으로 타이틀을 바꾸면 imgui.ini
에 할당되는 ID가 자꾸 바뀐다. (계속 포커스도 해당 윈도우로 옮겨진다)
이 때 ###
를 사용하면 된다. 타이틀명###윈도우ID
처럼 사용하면 된다.
imgui_demo.cpp
파일의 ShowExampleAppWindowTitles()
함수를 확인하면 된다.
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
MenuItem
에서 GuiOpenPopup
를 사용한 GuiBeginPopupModal
가 정상작동하지 않을 경우 해결 방법:
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
Favorite site
Node Graph Editors
Gallery
References
-
Creating_awesome_GUI_for_you_game_dev_tools_with_ImGui_and_SFML-Part_1.pdf ↩