IDs are hashed from a stack of identifiers that forms a “path” to each widget:
ImGui::Begin("MyWindow"); // Push "MyWindow" to ID stack ImGui::Button("OK"); // ID = hash("MyWindow", "OK") ImGui::Button("Cancel"); // ID = hash("MyWindow", "Cancel")ImGui::End(); // Pop "MyWindow" from ID stackImGui::Begin("OtherWindow"); // Push "OtherWindow" to ID stack ImGui::Button("OK"); // ID = hash("OtherWindow", "OK") - Different!ImGui::End();
The same label in different windows produces different IDs because the window name is part of the ID path.
ImGui::Begin("Also Correct");ImGui::DragFloat2("My value##1", &objects[0]->pos.x); // Label: "My value", ID includes "##1"ImGui::DragFloat2("My value##2", &objects[1]->pos.x); // Label: "My value", ID includes "##2"ImGui::DragFloat2("My value##3", &objects[2]->pos.x); // Label: "My value", ID includes "##3"ImGui::End();
The ## and everything after it is used for the ID but not displayed.
// Checkbox with no visible label, just a checkboxImGui::Checkbox("##hidden_checkbox", &my_bool);// Button with no labelImGui::Button("##hidden_button");
The most powerful and flexible approach - add custom ID components to the stack:
ImGui::Begin("Window");for (int i = 0; i < 3; i++){ ImGui::PushID(i); // Push integer to ID stack ImGui::DragFloat2("Position", &objects[i]->pos.x); ImGui::ColorEdit3("Color", &objects[i]->color.x); ImGui::PopID(); // Pop from ID stack}ImGui::End();
ImGui::Button("Click"); // ID = hash(..., "Click")if (ImGui::TreeNode("Section")) // Pushes "Section" to ID stack{ ImGui::Button("Click"); // ID = hash(..., "Section", "Click") - Different! ImGui::TreePop(); // Pops "Section" from ID stack}ImGui::Button("Click"); // ID = hash(..., "Click") - Same as first
This is why you can have duplicate labels in different tree nodes:
if (ImGui::TreeNode("Player")){ ImGui::SliderInt("Health", &player.health, 0, 100); ImGui::TreePop();}if (ImGui::TreeNode("Enemy")){ ImGui::SliderInt("Health", &enemy.health, 0, 100); // Same label, different ID! ImGui::TreePop();}
// Window title shows current time, but window position/size persistschar title[64];sprintf(title, "Game Time: %02d:%02d:%02d###GameClock", hours, minutes, seconds);ImGui::Begin(title); // Title updates, but .ini file uses "GameClock"
void ShowProperty(const char* name, float* value){ ImGui::Text("%s:", name); ImGui::SameLine(); // Use name as unique ID component ImGui::PushID(name); ImGui::DragFloat("##value", value); // Label hidden, ID derived from PushID ImGui::PopID();}ShowProperty("Speed", &speed);ShowProperty("Strength", &strength);ShowProperty("Health", &health);
// These are the SAME window (same position/size in .ini)ImGui::Begin("Config");ImGui::Begin("Config###UniqueID"); // Everything after ### is the ID// These are DIFFERENT windows (different .ini entries) ImGui::Begin("Config###ID1");ImGui::Begin("Config###ID2");