ocornut / imgui

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

ImGui::Selectable with nested components #2184

Open kbirk opened 5 years ago

kbirk commented 5 years ago

Is there a way to get ImGui::Selectable behavior but have the component be comprised of several other nested components?

For example:

image

Where each ImGui::Selectable is contains an ImGui::Image and two ImGui::Text calls but acts as a single "selectable" item.

ocornut commented 5 years ago

You may call Selectable() first which a non-visible label ("##title") and specify a custom size, and then draw your stuff over it. You'll need to know the size ahead and call the imgui_internal.h text drawing functions that handle clipping.

christophe-f8 commented 5 years ago

Sorry to revive an old post, but I run into the same issue and I more or less managed to make it work so I thought some people might want to know the following.

As stated above, using a non-visible label in Selectable, then drawing images/texts work, but the selectable label MUST be unique, otherwise only the first item can be selected. Here is a short example:

int i = 0;
for (std::vector<std::string>::iterator it = m_items.begin(); it != m_items.end(); ++it) {
    std::string itemid = "##" + std::to_string(i);
    if (ImGui::Selectable(itemid.c_str(), i == m_selectedItem)) {
        m_selectedItem = i;
    }
    ImGui::SameLine();
    ImGui::Text("Item: ");
    ImGui::SameLine();
    ImGui::Text((*it).c_str());
    i++;
}

This only displays texts (or image/buttons/...) on the same line. To draw on separate lines you indeed need to move the cursor and set the clipping manually.

ocornut commented 5 years ago

As stated above, using a non-visible label in Selectable, then drawing images/texts work, but the selectable label MUST be unique, otherwise only the first item can be selected. Here is a short example:

You can also use PushID(), this is all explained in the FAQ "Why are multiple widgets reacting when I interact with a single one? How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack..."

bootzin commented 3 years ago

Sorry to revive this issue again, but I'm having a problem where if I specify the width of the selectable it stops being drawn above the items that I want, but if I don't specify it overflows into the next item.. Is there a better way to make images selectable?

dicroce commented 2 years ago

Here is an example I got working. Creates a custom Selectable with 2 stacked text fields and a clickable button to the side that opens a modal dialog.

` auto pos = ImGui::GetCursorPos();

// selectable list
for (int n = 0; n < 10; n++)
{
    ImGui::PushID(n);

    char buf[32];
    sprintf(buf, "##Object %d", n);

    ImGui::SetCursorPos(ImVec2(pos.x, pos.y));
    if (ImGui::Selectable(buf, n == selected, 0, ImVec2(width, 50))) {
        selected = n;
    }
    ImGui::SetItemAllowOverlap();

    ImGui::SetCursorPos(ImVec2(pos.x, pos.y));
    ImGui::Text("foo");

    ImGui::SetCursorPos(ImVec2(pos.x + 30, pos.y+5));
    if(ImGui::Button("do thing", ImVec2(70, 30)))
    {
        ImGui::OpenPopup("Setup?");
        selected = n;
        printf("SETUP CLICKED %d\n", n);
    }

    if (ImGui::BeginPopupModal("Setup?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
    {
        ImGuiContext& g = *GImGui;
        ImGuiWindow* window = g.CurrentWindow;
        ImVec2 pos_before = window->DC.CursorPos;

        ImGui::Text("Setup Popup");
        if (ImGui::Button("OK", ImVec2(120, 0))) { printf("OK PRESSED!\n"); ImGui::CloseCurrentPopup(); }
        ImGui::EndPopup();
    }

    ImGui::SetCursorPos(ImVec2(pos.x, pos.y+20));
    ImGui::Text("bar");

    pos.y += 55;

    ImGui::PopID();
}

`

Displee commented 9 months ago

I know this is an old one, but the above looks a bit too complicated, here's my solution (I use imgui Java bindings):

    fun selectableButton(label: String, selected: Boolean, width: Float, height: Float, onSelect: (() -> Unit)? = null, content: (() -> Unit)? = null) {
        val cursorPosX = ImGui.getCursorPosX()
        if (ImGui.selectable(label, selected, ImGuiSelectableFlags.None, width, height)) {
            onSelect?.invoke()
        }
        ImGui.sameLine()
        val afterCursorPosX = ImGui.getCursorPosX()
        ImGui.setCursorPosX(cursorPosX)
        content?.invoke()
        ImGui.sameLine()
        ImGui.setCursorPosX(afterCursorPosX)
    }