ocornut / imgui

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

Deselect window from Keyboard #3200

Open u3shit opened 4 years ago

u3shit commented 4 years ago

Version/Branch of Dear ImGui:

Version: 1.77 WIP Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_opengl3.cpp + imgui_impl_glfw.cpp Compiler: clang Operating System: linux

My Issue/Question:

Sorry if this is a stupid question, but I can't seem to find a way to deselect a window with keyboard nav. When no window is selected, imgui will return WantCaptureKeyboard=0, but even in this case, ctrl-tab works and I can select a window. Doing this will capture the keyboard. The problem is that there doesn't seem to be a way to get back to the original all windows deselected state, because ctrl-tab only cycles between windows (and the main menu bar if there's one). With Escape I can close popup menus, but it does nothing when I have a window selected. (If there's only a main menu bar and no windows, pressing escape does actually deselect the menu bar and releases the keyboard, but as soon as there's an active window, it will just select it). Pressing tab, space, alt, arrow keys doesn't seem to help either, I can close the window by pressing alt then selecting the X button, if it's closable though. The only way I found to deselect all windows is to click with the mouse somewhere on the background. Am I overlooking something? If not, could you add some way to deselect all windows without having to use the mouse?

Standalone, minimal, complete and verifiable example: Start the demo, enable NavEnableKeyboard under configuration then watch WantCaptureKeyboard under Inputs, Navigation & focus

ocornut commented 4 years ago

Something would need to be added in NavUpdate() in the end of that large block:

// Process NavCancel input (to close a popup, get back to parent, clear focus)
if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))

Something that would eventually call FocusWindow(NULL), e.g. at the very end of that section, replace:

g.NavId = g.NavFocusScopeId = 0;

With

if (g.NavId)
    g.NavId = g.NavFocusScopeId = 0;
else if (g.NavWindow)
    FocusWindow(NULL);

The problem being to design that exact desirable behavior and various application may need different things. For instance, the "clearing g.NavId" behavior is ALREADY something that some application are considering to be undesirable and want removed. An app that doesn't run "over" a game has no reason to clear widget focus at all, let alone window focus.

You can side-step that decision and handle the code in your own application:

#include "imgui_internal.h"
ImGuiContext& g = *GImGui;
if (ImGui::IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
    if (g.NavWindow && g.ActiveId == 0 && g.OpenPopupStack.empty() && g.NavLayer == ImGuiNavLayer_Main)
        ImGui::FocusWindow(NULL);

Or we design a more generic solution, and I'm guessing that maybe a single flag would be enough: either enable clearing of NavWindow/NavId all-together on ESC, either now. I'll have to think about this a little more.

Note for later: A minor side-issue is that block is not playing along well with how we want to dispatch inputs, and I intend to move all those individual if {} blocks into the end scope of what they react to (so e.g. the "Exit child window" behavior should be polled in EndChild() etc) and it will be a bit of thoughtful testing to get that transition done right.

u3shit commented 4 years ago

I've pasted the last code snipped just after ImGui::NewFrame(), and it correctly deselects the window on Escape. However it also deselects it if I press escape inside a menu. I'm not keen on using Escape to deselect the window if it causes problems for other applications, I just like to have ANY way to deselect the window, even something like adding a new entry to the Ctrl-Tab list, but probably adding a flag is the best.