|
|
@@ -109,43 +109,33 @@
|
|
|
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
|
|
|
|
|
|
- Run and study the examples and demo to get acquainted with the library.
|
|
|
- - Add the Dear ImGui source files to your projects, using your preferred build system.
|
|
|
+ - Add the Dear ImGui source files to your projects or using your preferred build system.
|
|
|
It is recommended you build the .cpp files as part of your project and not as a library.
|
|
|
- You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
|
|
|
- You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
|
|
|
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
|
|
|
+ - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
|
|
|
+ Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
|
|
|
+ phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
|
|
|
+ - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
|
|
|
|
|
|
- - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
|
|
|
- (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic
|
|
|
- integration you don't need to worry about it all.
|
|
|
- - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
|
|
|
- - Every frame:
|
|
|
- - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
|
|
|
- - Call ImGui::NewFrame() to begin the frame
|
|
|
- - You can use any ImGui function you want between NewFrame() and Render()
|
|
|
- - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
|
|
|
- (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwise some features will break)
|
|
|
- - All rendering information are stored into command-lists until ImGui::Render() is called.
|
|
|
- - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
|
|
|
- - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
|
|
|
- of your own application.
|
|
|
- - Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
|
|
|
- - A minimal application skeleton may be:
|
|
|
+ THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
|
|
|
|
|
|
// Application init
|
|
|
+ // Create a context
|
|
|
ImGui::CreateContext();
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
- io.DisplaySize.x = 1920.0f;
|
|
|
- io.DisplaySize.y = 1280.0f;
|
|
|
- // TODO: Fill others settings of the io structure later.
|
|
|
+ // TODO: Fill optional settings of the io structure later.
|
|
|
+ // TODO: Load fonts if you don't want to use the default font.
|
|
|
|
|
|
- // Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
|
|
|
- unsigned char* pixels;
|
|
|
+ // Build and load the texture atlas into a texture
|
|
|
+ unsigned char* pixels = NULL;
|
|
|
int width, height;
|
|
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
|
|
- // TODO: At this points you've got the texture data and you need to upload that your your graphic system:
|
|
|
- MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
|
|
|
- // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
|
|
|
+ // At this point you've got the texture data and you need to upload that your your graphic system:
|
|
|
+ MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
|
|
|
+ // Store your texture pointer/identifier (in whatever formatyour engine uses) in 'io.Fonts->TexID'.
|
|
|
+ // This will be passed back to your via the renderer. Read FAQ for details about ImTextureID.
|
|
|
io.Fonts->TexID = (void*)texture;
|
|
|
|
|
|
// Application main loop
|
|
|
@@ -154,18 +144,24 @@
|
|
|
// Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
io.DeltaTime = 1.0f/60.0f;
|
|
|
- io.MousePos = mouse_pos;
|
|
|
- io.MouseDown[0] = mouse_button_0;
|
|
|
- io.MouseDown[1] = mouse_button_1;
|
|
|
+ io.DisplaySize.x = 1920.0f;
|
|
|
+ io.DisplaySize.y = 1280.0f;
|
|
|
+ io.MousePos = my_mouse_pos;
|
|
|
+ io.MouseDown[0] = my_mouse_buttons[0];
|
|
|
+ io.MouseDown[1] = my_mouse_buttons[1];
|
|
|
|
|
|
// Call NewFrame(), after this point you can use ImGui::* functions anytime
|
|
|
+ // (So you want to try calling Newframe() as early as you can in your mainloop to be able to use imgui everywhere)
|
|
|
ImGui::NewFrame();
|
|
|
|
|
|
// Most of your application code here
|
|
|
+ ImGui::Text("Hello, world!");
|
|
|
MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
|
|
|
MyGameRender(); // may use any ImGui functions as well!
|
|
|
|
|
|
- // Render & swap video buffers
|
|
|
+ // Render imgui, swap buffers
|
|
|
+ // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
|
|
|
+ ImGui::EndFrame();
|
|
|
ImGui::Render();
|
|
|
MyImGuiRenderFunction(ImGui::GetDrawData());
|
|
|
SwapBuffers();
|
|
|
@@ -174,13 +170,13 @@
|
|
|
// Shutdown
|
|
|
ImGui::DestroyContext();
|
|
|
|
|
|
+ THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
|
|
|
|
|
|
- - A minimal render function skeleton may be:
|
|
|
-
|
|
|
- void void MyRenderFunction(ImDrawData* draw_data)
|
|
|
+ void void MyImGuiRenderFunction(ImDrawData* draw_data)
|
|
|
{
|
|
|
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
|
|
|
- // TODO: Setup viewport, orthographic projection matrix
|
|
|
+ // TODO: Setup viewport using 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.
|
|
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
|
|
{
|
|
|
@@ -199,10 +195,16 @@
|
|
|
// The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
|
|
|
MyEngineBindTexture(pcmd->TextureId);
|
|
|
|
|
|
- // We are using scissoring to clip some objects. All low-level graphics API 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 everywhere else works!
|
|
|
- MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
|
|
|
+ // 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 everywhere 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, 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 if your engine doesn't support 16-bits indices.
|
|
|
@@ -2252,6 +2254,8 @@ void ImGui::KeepAliveID(ImGuiID id)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
if (g.ActiveId == id)
|
|
|
g.ActiveIdIsAlive = true;
|
|
|
+ if (g.ActiveIdPreviousFrame == id)
|
|
|
+ g.ActiveIdPreviousFrameIsAlive = true;
|
|
|
}
|
|
|
|
|
|
static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
|
|
|
@@ -3720,7 +3724,8 @@ void ImGui::NewFrame()
|
|
|
g.ActiveIdTimer += g.IO.DeltaTime;
|
|
|
g.LastActiveIdTimer += g.IO.DeltaTime;
|
|
|
g.ActiveIdPreviousFrame = g.ActiveId;
|
|
|
- g.ActiveIdIsAlive = false;
|
|
|
+ g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
|
|
|
+ g.ActiveIdIsAlive = g.ActiveIdPreviousFrameIsAlive = false;
|
|
|
g.ActiveIdIsJustActivated = false;
|
|
|
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
|
|
|
g.ScalarAsInputTextId = 0;
|
|
|
@@ -3941,7 +3946,7 @@ void ImGui::Shutdown(ImGuiContext* context)
|
|
|
g.NavWindow = NULL;
|
|
|
g.HoveredWindow = NULL;
|
|
|
g.HoveredRootWindow = NULL;
|
|
|
- g.ActiveIdWindow = NULL;
|
|
|
+ g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
|
|
|
g.MovingWindow = NULL;
|
|
|
g.ColorModifiers.clear();
|
|
|
g.StyleModifiers.clear();
|
|
|
@@ -4979,6 +4984,13 @@ bool ImGui::IsItemActive()
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+bool ImGui::IsItemDeactivated()
|
|
|
+{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
+ return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
|
|
|
+}
|
|
|
+
|
|
|
bool ImGui::IsItemFocused()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
@@ -9740,7 +9752,6 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
|
|
|
TextUnformatted(label, FindRenderedTextEnd(label));
|
|
|
EndGroup();
|
|
|
PopID();
|
|
|
-
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
@@ -10099,9 +10110,7 @@ bool ImGui::RadioButton(const char* label, int* v, int v_button)
|
|
|
{
|
|
|
const bool pressed = RadioButton(label, *v == v_button);
|
|
|
if (pressed)
|
|
|
- {
|
|
|
*v = v_button;
|
|
|
- }
|
|
|
return pressed;
|
|
|
}
|
|
|
|
|
|
@@ -10382,7 +10391,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
|
|
|
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
|
|
|
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
|
|
|
|
|
|
- if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
|
|
|
+ if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope,
|
|
|
BeginGroup();
|
|
|
const ImGuiID id = window->GetID(label);
|
|
|
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
|
|
@@ -11091,7 +11100,6 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in
|
|
|
|
|
|
TextUnformatted(label, FindRenderedTextEnd(label));
|
|
|
EndGroup();
|
|
|
-
|
|
|
return value_changed;
|
|
|
}
|
|
|
|
|
|
@@ -11469,6 +11477,7 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+// FIXME: Rename to BeginListBox()
|
|
|
// Helper to calculate the size of a listbox and display a label on the right.
|
|
|
// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
|
|
|
bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
|
|
|
@@ -11496,6 +11505,7 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+// FIXME: Rename to BeginListBox()
|
|
|
bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
|
|
|
{
|
|
|
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
|
|
@@ -11512,6 +11522,7 @@ bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_item
|
|
|
return ListBoxHeader(label, size);
|
|
|
}
|
|
|
|
|
|
+// FIXME: Rename to EndListBox()
|
|
|
void ImGui::ListBoxFooter()
|
|
|
{
|
|
|
ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
|
|
|
@@ -11659,7 +11670,7 @@ bool ImGui::BeginMenuBar()
|
|
|
return false;
|
|
|
|
|
|
IM_ASSERT(!window->DC.MenuBarAppending);
|
|
|
- BeginGroup(); // Save position
|
|
|
+ BeginGroup(); // Backup position on layer 0
|
|
|
PushID("##menubar");
|
|
|
|
|
|
// We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
|
|
|
@@ -11711,7 +11722,7 @@ void ImGui::EndMenuBar()
|
|
|
PopID();
|
|
|
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
|
|
|
window->DC.GroupStack.back().AdvanceCursor = false;
|
|
|
- EndGroup();
|
|
|
+ EndGroup(); // Restore position on layer 0
|
|
|
window->DC.LayoutType = ImGuiLayoutType_Vertical;
|
|
|
window->DC.NavLayerCurrent--;
|
|
|
window->DC.NavLayerCurrentMask >>= 1;
|
|
|
@@ -12793,6 +12804,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
|
|
|
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
|
|
|
void ImGui::BeginGroup()
|
|
|
{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
|
|
|
window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
|
|
|
@@ -12804,21 +12816,21 @@ void ImGui::BeginGroup()
|
|
|
group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
|
|
|
group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
|
|
|
group_data.BackupLogLinePosY = window->DC.LogLinePosY;
|
|
|
- group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
|
|
|
+ group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
|
|
|
+ group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
|
|
|
group_data.AdvanceCursor = true;
|
|
|
|
|
|
window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
|
|
|
window->DC.IndentX = window->DC.GroupOffsetX;
|
|
|
window->DC.CursorMaxPos = window->DC.CursorPos;
|
|
|
window->DC.CurrentLineHeight = 0.0f;
|
|
|
- window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
|
|
|
+ window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
|
|
|
}
|
|
|
|
|
|
void ImGui::EndGroup()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
|
-
|
|
|
IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
|
|
|
|
|
|
ImGuiGroupData& group_data = window->DC.GroupStack.back();
|
|
|
@@ -12828,11 +12840,11 @@ void ImGui::EndGroup()
|
|
|
|
|
|
window->DC.CursorPos = group_data.BackupCursorPos;
|
|
|
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
|
|
|
- window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
|
|
|
- window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
|
|
|
window->DC.IndentX = group_data.BackupIndentX;
|
|
|
window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
|
|
|
- window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
|
|
|
+ window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
|
|
|
+ window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
|
|
|
+ window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
|
|
|
|
|
|
if (group_data.AdvanceCursor)
|
|
|
{
|
|
|
@@ -12841,11 +12853,13 @@ void ImGui::EndGroup()
|
|
|
ItemAdd(group_bb, 0);
|
|
|
}
|
|
|
|
|
|
- // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group.
|
|
|
- // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
|
|
|
- const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
|
|
|
- if (active_id_within_group)
|
|
|
+ // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
|
|
|
+ // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but put a little more burden on individual widgets.
|
|
|
+ // (and if you grep for LastItemId you'll notice it is only used in that context.
|
|
|
+ if (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
|
|
|
window->DC.LastItemId = g.ActiveId;
|
|
|
+ else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
|
|
|
+ window->DC.LastItemId = g.ActiveIdPreviousFrame;
|
|
|
window->DC.LastItemRect = group_bb;
|
|
|
|
|
|
window->DC.GroupStack.pop_back();
|