ocornut / imgui

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

How to handle key events properly. #7490

Open audetto opened 7 months ago

audetto commented 7 months ago

Version/Branch of Dear ImGui:

1.90.5

Back-ends:

imgui_impl_sdl2.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

gcc 13, Ubuntu 23.10

Full config/build information:

No response

Details:

For the first time, I am trying to handle key events in ImGui.

I would like to handle some key combinations when a certain widget (or its children) is selected. At the same time I use io.WantCaptureKeyboard not to pass events to my application when it is True.

This works only halfway

    if (ImGui::Begin("window"))
    {
      if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
      {
        if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_Space))
        {
          // do something
        }
      }

I say 50/50 because 1) it gets triggered correctly only when the relevant widgets are focused 2) but io.WantCaptureKeyboard does not become true and so I still pass the event to my app.

I think I am missing something bing here. Who is supposed to set io.WantCaptureKeyboard?

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

cfillion commented 7 months ago

Who is supposed to set io.WantCaptureKeyboard?

Active text input fields, or via ImGui::SetNextFrameWantCaptureKeyboard(true). See Demo > Inputs & Focus > WantCapture override.

audetto commented 7 months ago

Yes, that does it and the right place to put it seems to be

          if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
          {
            ImGui::SetNextFrameWantCaptureKeyboard(true);  // << GOOD LOCATION
            if (ImGui::IsKeyChordPressed(ImGuiKey_Space | ImGuiMod_Ctrl))
            {
               // DO NOT PUT IT HERE!
               //
               // handle keypress

I looked at InputText and it seems to use a different system (g.ActiveId), but I think it is beyond the scope of the issue to understand the difference.

Instead I wanted to ask: imagine a child of the above widget is already handling key events (via the same method or like InputText): how to I filter this case to only capture residual key events? In a more traditional widget system, when an event is accepted it stops being propagated. Is it possible here?

audetto commented 7 months ago

Don't know if I should open a new issue or continue here.

This is about selecting widgets using TAB. I see it is already enable for InputText and this can be controlled via PushTabStop.

But for TabItems, is it possible to cycle through the selected tab using TAB?

ocornut commented 6 months ago

Instead I wanted to ask: imagine a child of the above widget is already handling key events (via the same method or like InputText): how to I filter this case to only capture residual key events? In a more traditional widget system, when an event is accepted it stops being propagated. Is it possible here?

The current mechanism which I am pushing for but isn't fully documented is a mix of setting and testing for key ownership and/or registering shortcut routes (the later register key ownership when the mods matches).

If you call Shortcut(ImGuiKey_Space | ImGuiMod_Ctrl) in both parent and child location, you'll find that only the deep most one gets the shortcut. It's a rather complex multi-faceted problem that doesn't have a single answer, but if you want to detail your use case I may be able to suggest a direction.

Unfortunately this mechanism doesn't necessarily set io.WantCaptureKeyboard for the "background" application to use. Technically your key handler could also check if a key is owned (if (ImGui::GetKeyOwner(ImGuiKey_XXXX) == ImGuiKeyOwner_None) and use that as a secondary filter to not pass keys to background application. However this would only work for code that is setting key ownership correctly. Some randomly placed IsKeyPressed(ImGuiKey_XXX) unless SetKeyOwner() is also called, which standard widgets do, and which e.g. Shortcut() will do indirectly do.

This is about selecting widgets using TAB. I see it is already enable for InputText and this can be controlled via PushTabStop. But for TabItems, is it possible to cycle through the selected tab using TAB?

I don't understand what you are saying here. It looks like a different topic, is it? If it a different topic please open a new issue and provide more details.

audetto commented 6 months ago

Thank you for the explanation. I was not aware of Shortcut, nor KeyOwner.

I see them in imgui_internal.h. I will look to see if I understand them, but I fear that until they land in the Demo I will have a hard time.

For the time being, I have rearranged a bit the hierarchy to remove the ambiguity, but I will keep an eye on further developments.

ocornut commented 6 months ago

Sorry I meant to add there is a public branch with a bunch of demos for those functions as well. I can’t merge the demos before i move those public to api realm, but i think most are pretty stable now. My main bother is that outside of just using Shortcut(), the system can be a bit confusing

ocornut commented 6 months ago

The demos are here: https://github.com/ocornut/imgui/compare/master...features/demo_input_owner_and_routing