ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
58.37k stars 10k forks source link

Why does ImGui tables insert extra ID into the id path of the child widgets? #7598

Open roniabusayeed opened 2 months ago

roniabusayeed commented 2 months ago

Version/Branch of Dear ImGui:

Version 1.90.6, Branch: /docking/

Back-ends:

imgui_impl_SDL2.cpp + imgui_impl_OpenGL.cpp

Compiler, OS:

Linux + GCC

Full config/build information:

No response

Details:

// ID conflict. Expected behaviour.
static float value1;
static float value2;

ImGui::DragFloat("##drag-float", &value1);  // .../##drag-float
ImGui::DragFloat("##drag-float", &value2);  // .../##drag-float

If either of the DragFloats are changed, the other DragFloat will get updated, which is expected since they both share the same id during runtime (in comment)

However, I expected the following code to have the same behaviour. But it doesn't.

// No ID conflict. Unexpected behaviour.
static float table_value1;
static float table_value2;

if (ImGui::BeginTable("table", 1)) {
    ImGui::TableNextRow();
    ImGui::TableSetColumnIndex(0);
    ImGui::DragFloat("##drag-float", &table_value1); // .../table/##drag-float
    ImGui::EndTable();
}
if (ImGui::BeginTable("table", 1)) {
    ImGui::TableNextRow();
    ImGui::TableSetColumnIndex(0);
    ImGui::DragFloat("##drag-float", &table_value2);  // .../table/0x8CD5DC7A [override]/##drag-float
    ImGui::EndTable();
}

In the second case ImGui adds 0x8CD5DC7A [override] between the table id and the DragFloat id.

The reason I don't want this behaviour is because I want to create my own wrapper widgets that will make use of the ImGui Tables API and I also want to support the "label" behaviour as the ImGui Widgets (i.e., if there's "##" in the label then display label is the substring before that and id is the entire thing. If there's "###" in the label then display label is the substring before that and id is the substring after that).

So, when I create my custom widgets like this:

void WrapperWidget(const std::string& label, float& value) {
    std::pair<std::string, std::string> label_id_pair = extractLabelAndId(label);

    ImGui::PushID(label_id_pair.second.c_str());

    ImGui::Text(label_id_pair.first.c_str());
    ImGui::SameLine();

    if (ImGui::BeginTable("table", 1)) {
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::DragFloat("##drag-float", &value);  // .../table/0x8CD5DC7A [override]/##drag-float
        ImGui::EndTable();
    }

    ImGui::PopID();
}

static float value1;
static float value2;
WrapperWidget("WrapDrag1###ID", value1);
WrapperWidget("WrapDrag2###ID", value2);  // Supposed to have the same id as the previous one.

I want them to conflict with one another when they share same ID-just like native ImGui widgets. How do I achieve that behaviour?

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

ocornut commented 2 months ago

When you call BeginTable() multiple times with the same, you create multiple instances of a same table (sharing settings, columns width) which needs a unique ID so e.g. headers, columns resize elements can be unique and function. You can call PushOverrideID() with the previous instance ID if you like subsequent widgets to conflicts.

    ImGuiID table_id = ImGui::GetID("table");
    if (ImGui::BeginTable("table", 1)) {
        ImGui::PushOverrideID(table_id);
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::DragFloat("##drag-float", &value);  // .../table/0x8CD5DC7A [override]/##drag-float
        ImGui::PopID();
        ImGui::EndTable();
    }

Your request and explanation still sounds bizarre to me.