Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Different InputText() behavior with Selectable() and TreeNode() #7077

Closed
mondegreengames opened this issue Nov 30, 2023 · 4 comments
Closed

Comments

@mondegreengames
Copy link

mondegreengames commented Nov 30, 2023

Version/Branch of Dear ImGui:

Version: 1.90

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp
Compiler: gcc 11.4.0
Operating System: Mint Linux 21

My Issue/Question:

My goal: I want to click on an item and replace the contents of a string buffer, and have that update reflected in the next call to InputText(). (The string buffer's contents are changing, but the address of the buffer stays the same.)

What's happening: It works as expected when using Selectable(). In the first half of that video, I can modify the contents of the string buffer using the InputText(), then I click one of the Selectable()s (in the History tab), then the string buffer's contents gets replaced, and then InputText() (the URL textbox) has lost focus but displays the new string contents.

Something like this:

if (ImGui::Selectable(option.internal_buffer)) {
  stringBuffer = option;
}
// later
ImGui::TextInput("##url", stringBuffer.internal_buffer, stringBuffer.capacity);

But when using TreeNodeEx(), it doesn't work as expected. When I click a TreeNodeEx() (in the Collections tab), I can see it replacing the contents of the string buffer using the debugger, but then as soon as that InputText() is called it replaces the contents of the string buffer with the old value, and InputText() still loses focus.

if (ImGui::TreeNodeEx(option.internal_buffer, 0, "%s %s", http_verb_name, option.internal_buffer)) {
  if (ImGui::IsItemClicked()) {
    stringBuffer = option;
  }
  ImGui::TreePop();
}

Note that if I click a TreeNodeEx() twice it will correctly update the string that InputText() displays after the second click.

Screenshots/Video

2023-11-29.21-25-18.mp4

Edit: I just realized that the IDs of the different widgets is probably important here, so I updated the code example to show that I'm actually using TreeNodeEx() and passing in a unique pointer per node as the id, not just some text

@ocornut
Copy link
Owner

ocornut commented Nov 30, 2023

Hello,

You can only running the if (ImGui::IsItemClicked()) block when the TreeNode is open, and the ones you are clicking on the video are leaf nodes which don't open.

If instead you do

bool open = ImGui::TreeNodeEx("TreeNode", 0, "TreeNode");
if (ImGui::IsItemClicked())
    stringBuffer = option;
if (open)
{
    //...
    ImGui::TreePop();
}

It should work.

Please note that replacing the underlying contents of an InputText() only works here because the clicks cause the InputText() to be deactivated. (Replacing contents while activated is more complicated and currently subjects to many issues)

@mondegreengames
Copy link
Author

Unfortunately that didn't work. My code example wasn't very accurate, because I'm already doing something similar (calling IsItemClicked() regardless of whether TreeNodeEx() returns true or false).

I just created this as a reproducible test:

void showTreeTest()
{
    static char buffer[1024];

    ImGui::Begin("Tree test");

    if (ImGui::TreeNodeEx("root"))
    {
        if (ImGui::TreeNodeEx("folder 1"))
        {
            bool open = ImGui::TreeNodeEx("item 1", ImGuiTreeNodeFlags_Leaf);
            if (ImGui::IsItemClicked()) {
                strncpy(buffer, "Item 1 text", 1024);
            }
            if (open) {
                ImGui::TreePop();
            }

            ImGui::TreePop();
        }
        if (ImGui::TreeNodeEx("folder 2"))
        {
            bool open = ImGui::TreeNodeEx("item 2", ImGuiTreeNodeFlags_Leaf);
            if (ImGui::IsItemClicked()) {
                strncpy(buffer, "Item 2 text", 1024);
            }
            if (open) {
                ImGui::TreePop();
            }

            ImGui::TreePop();
        }

        ImGui::TreePop();
    }

    ImGui::InputText("##textbox", buffer, sizeof(buffer));

    ImGui::End();
}

@ocornut
Copy link
Owner

ocornut commented Nov 30, 2023

OK. It's an unfortunate side effect of the changes made for #4714. The same thing which is exactly desirable for #4714 is not a desirable in your case.

The hacky/undocumented workaround is:

ImGuiContext* ctx = ImGui::GetCurrentContext();
ctx->InputTextDeactivatedState.ID = 0;

Unfortunately I would bundle that with the one million other hanging issues related to modifying the InputText() contents when active (e.g. with a proposal at #2890 but it's been discussed and raised in many other issues).

@mondegreengames
Copy link
Author

Thanks, the workaround works. This issue can be closed, unless you want to keep it open to track the InputText() issues.

@ocornut ocornut closed this as completed Nov 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants