From 0e83d74698fdf02dcd1e677d1fca7d70dc2a6334 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 27 May 2018 22:32:46 +0200 Subject: [PATCH] Documentation: FAQ, ID Stack, Fonts (#1839, #1840), #1842) --- imgui.cpp | 70 ++++++++++++++++++++++++++++--------------- misc/fonts/README.txt | 19 ++++++++---- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bdfefb401..afeebb862 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -448,26 +448,46 @@ It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. - (C++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) + + // The example OpenGL back-end uses integers to identify textures. + // You can safely store an integer into a void* by casting it. e.g. (void*)(intptr_t)MY_GL_UINT to cast to void*. + GLuint my_opengl_texture; + glGenTextures(1, &my_opengl_texture); + // [...] load image, render to texture, etc. + ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(512,512)); + + // The example DirectX11 back-end uses ID3D11ShaderResourceView* to identify textures. + ID3D11ShaderResourceView* my_texture_view; + device->CreateShaderResourceView(my_texture, &my_shader_resource_view_desc, &my_texture_view); + ImGui::Image((void*)my_texture_view, ImVec2(512,512)); + To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated. It is your responsibility to get textures uploaded to your GPU. Q: How can I have multiple widgets with the same label or without a label? + Q: I have multiple widgets with the same label, and only the first one works. Why is that? A: A primer on labels and the ID Stack... - - Elements that are typically not clickable, such as Text() items don't need an ID. + Dear ImGui internally need to uniquely identify UI elements. + Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. + Interactive widgets (such as calls to Button buttons) need a unique ID. + Unique ID are used internally to track active widgets and occasionally associate state to widgets. + Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. - - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui - often needs to remember what is the "active" widget). To do so they need a unique ID. Unique ID - are typically derived from a string label, an integer index or a pointer. + - Unique ID are often derived from a string label: - Button("OK"); // Label = "OK", ID = top of id stack + hash of "OK" - Button("Cancel"); // Label = "Cancel", ID = top of id stack + hash of "Cancel" + Button("OK"); // Label = "OK", ID = hash of (..., "OK") + Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel") - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having two buttons labeled "OK" in different windows or different tree locations is fine. + We used "..." above to signify whatever was already pushed to the ID stack previously: + + Begin("MyWindow"); + Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") + End(); - If you have a same ID twice in the same location, you'll have a conflict: @@ -482,20 +502,22 @@ This helps solving the simple collision cases when you know e.g. at compilation time which items are going to be created: - Button("Play"); // Label = "Play", ID = top of id stack + hash of "Play" - Button("Play##foo1"); // Label = "Play", ID = top of id stack + hash of "Play##foo1" (different from above) - Button("Play##foo2"); // Label = "Play", ID = top of id stack + hash of "Play##foo2" (different from above) + Begin("MyWindow"); + Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") + Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above + Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above + End(); - If you want to completely hide the label, but still need an ID: - Checkbox("##On", &b); // Label = "", ID = top of id stack + hash of "##On" (no label!) + Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label! - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. For example you may want to include varying information in a window title bar, but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: - Button("Hello###ID"; // Label = "Hello", ID = top of id stack + hash of "ID" - Button("World###ID"; // Label = "World", ID = top of id stack + hash of "ID" (same as above) + Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID") + Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different sprintf(buf, "My game (%f FPS)###MyGame", fps); Begin(buf); // Variable label, ID = hash of "MyGame" @@ -507,45 +529,45 @@ You can push a pointer, a string or an integer value into the ID stack. Remember that ID are formed from the concatenation of _everything_ in the ID stack! + Begin("Window"); for (int i = 0; i < 100; i++) { - PushID(i); - Button("Click"); // Label = "Click", ID = top of id stack + hash of integer + hash of "Click" + PushID(i); // Push i to the id tack + Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj); - Button("Click"); // Label = "Click", ID = top of id stack + hash of pointer + hash of "Click" + Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj->Name); - Button("Click"); // Label = "Click", ID = top of id stack + hash of string + hash of "Click" + Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click") PopID(); } + End(); - More example showing that you can stack multiple prefixes into the ID stack: - Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "Click") PushID("node"); - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") PushID(my_ptr); - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of ptr + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") PopID(); PopID(); - Tree nodes implicitly creates a scope for you by calling PushID(). - Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "Click") if (TreeNode("node")) { - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") TreePop(); } diff --git a/misc/fonts/README.txt b/misc/fonts/README.txt index cd51dd83e..b42a07217 100644 --- a/misc/fonts/README.txt +++ b/misc/fonts/README.txt @@ -70,7 +70,7 @@ In this document: FONTS LOADING INSTRUCTIONS --------------------------------------- - Load default font with: + Load default font: ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefault(); @@ -78,15 +78,22 @@ In this document: Load .TTF/.OTF file with: ImGuiIO& io = ImGui::GetIO(); - io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); + ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); + ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels); - For advanced options create a ImFontConfig structure and pass it to the AddFont function (it will be copied internally) + // Select font at runtime + ImGui::Text("Hello"); // use the default font (which is the first loaded font) + ImGui::PushFont(font2); + ImGui::Text("Hello with another font"); + ImGui::PopFont(); + + For advanced options create a ImFontConfig structure and pass it to the AddFont function (it will be copied internally): ImFontConfig config; config.OversampleH = 3; config.OversampleV = 1; config.GlyphExtraSpacing.x = 1.0f; - io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); + ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); If you have very large number of glyphs or multiple fonts: @@ -99,7 +106,7 @@ In this document: Combine two fonts into one: // Load a first font - io.Fonts->AddFontDefault(); + ImFont* font = io.Fonts->AddFontDefault(); // Add character ranges and merge into the previous font // The ranges array is not copied by the AddFont* functions and is used lazily @@ -144,7 +151,7 @@ In this document: --------------------------------------- You can use the ImFontAtlas::GlyphRangesBuilder helper to create glyph ranges based on text input. - For exemple: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. + For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. ImVector ranges; ImFontAtlas::GlyphRangesBuilder builder;