ocornut / imgui

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

Can't get ImGui::Shortcut to work #7895

Closed zMidair closed 2 months ago

zMidair commented 2 months ago

Version/Branch of Dear ImGui:

Version 1.91.0

Back-ends:

imgui-cocos

Compiler, OS:

Windows 11 + MSVC 2022

Full config/build information:

No response

Details:

Hi, I tried using Shortcuts but they didn't work. I can't figure out how they work and everywhere I searched, the function has 3 params meanwhile I have 2 (no ID param). Can I get a little help?

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive);
std::optional keybind = KeybindsManager::get().getByName(name).value();
char inputKey[] = "Press a key...";

// I literally attributed it 1 FRAME AGO
if(editedKeybind == keybind->getName())
{
    ImGui::InputText(keybind->getName().c_str(), inputKey, strlen(inputKey));
}
else
{
    ImGui::InputText(keybind->getName().c_str(), const_cast<char*>(keybind->getKey()), strlen(keybind->getKey()), ImGuiInputTextFlags_ReadOnly);
}
if(ImGui::IsItemActivated() && editedKeybind != keybind->getName())
{
    // This is when the user clicks the input. We need to start listening for a key and change the keybind to that
    // We should also change the input text to "Press a key..."

    // If you need you can also use IsItemActive()

    editedKeybind = keybind->getName();
    ImGui::SetWindowFocus(NULL); // Unfocus from everything to listen for key
    KeybindsManager::get().setListener(&keybind.value());
}
if(ImGui::IsKeyPressed(ImGuiKey_Escape) && editedKeybind == keybind->getName())
{
    // This is when the user clicks and presses ESC, we remove the keybind in here
    keybind->setKey("None");
    editedKeybind = "";
}
if(KeybindsManager::get().getListener() == std::nullopt)
{
    editedKeybind = "";
}
ocornut commented 2 months ago

the function has 3 params meanwhile I have 2 (no ID param)

You can ignore the third param, this is for the imgui_internal.h version. The public version has 2 params.

There are a few demos of using Shortcut() and also SetNextItemShortcut() in imgui_demo.cpp

I don't understand what your code is about, it's doing fifty things, you only have 1 call to Shortcut() and you are not reading the return value. If you want code to let user set a new bind you are probably going to have to check every possible key, then check every mods too.

zMidair commented 2 months ago

the function has 3 params meanwhile I have 2 (no ID param)

You can ignore the third param, this is for the imgui_internal.h version. The public version has 2 params.

There are a few demos of using Shortcut() and also SetNextItemShortcut() in imgui_demo.cpp

I don't understand what your code is about, it's doing fifty things, you only have 1 call to Shortcut() and you are not reading the return value. If you want code to let user set a new bind you are probably going to have to check every possible key, then check every mods too.

i added Shortcut() in an if before but didnt work, imma try SetNextItemShortcut() and imma look in the demo

ocornut commented 2 months ago

Please read Issue Submitting Guidelines carefully. You are not providing enough information to receive help.

zMidair commented 2 months ago

Hello again! I did how I saw in the demo and it always returns false, I also tried with ImGui::IsKeyPressed() and it didn't work. Heres the code where I create the input then log the output in the console.

if(editedKeybind == keybind->getName())
{
    ImGui::InputText(keybind->getName().c_str(), inputKey, strlen(inputKey));
}
else
{
    ImGui::InputText(keybind->getName().c_str(), const_cast<char*>(keybind->getKey()), strlen(keybind->getKey()), ImGuiInputTextFlags_ReadOnly);
}
log::info("Keybind pressed: {}", ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive)); // This basically logs the value

log::info() logs the value and basically works like cout. I run it every frame and again, it returns false everytime.

Huge sorry for the inconvenience and have a nice day!

ocornut commented 2 months ago

Are you trying to steal the Esc key from the InputText() ? You are not being explicit with what you want to do.

if you want to claim Esc away from InputText() you indeed need to use a global route + route over active flag. OR use the default route and provide the item id using the extra owner id parameter.

zMidair commented 2 months ago

Are you trying to steal the Esc key from the InputText() ? You are not being explicit with what you want to do.

if you want to claim Esc away from InputText() you indeed need to use a global route + route over active flag. OR use the default route and provide the item id using the extra owner id parameter.

Im sorry for not providing enough context... I want to make a keybind modifier. When the user presses the input I want to unfocus for everything to listen for the keybind (which im doing it separately, not in imgui). When the user presses to listen for a keybind and then presses ESC, thats when I need this, to remove the keybind in that situation.

zMidair commented 2 months ago

Are you trying to steal the Esc key from the InputText() ? You are not being explicit with what you want to do.

if you want to claim Esc away from InputText() you indeed need to use a global route + route over active flag. OR use the default route and provide the item id using the extra owner id parameter.

I added the global route flag and it still didn't work. What do you mean by extra id parameter? Can I have an example?

log::info("Keybind pressed: {}", ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteGlobal)); // This basically logs the value
ocornut commented 2 months ago

I see. So you only want to use the InputText() for its blinking cursor essentially, as you could do the same thing with e.g. a button and popup.

It's not clear what you tried when you said "I also tried with ImGui::IsKeyPressed() and it didn't work" because obviously this function always works and doesn't do any ownership testing.

You could do:

InputText(....);
if (IsItemActive() && IsKeyPressed(ImGuiKey_Esc))
    // do your thing

I don't need you need a shortcut for this, there's little value.

One thing I am really concerned is that above you claimed you did:

log::info("Keybind pressed: {}", ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive)); 

THIS SHOULD HAVE ASSERTED as it is invalid to use ImGuiInputFlags_RouteOverActive without ImGuiInputFlags_RouteGlobal. So it means you may not have assert enabled when using dear imgui API, and that's quite terrible. Please ensure that calling IM_ASSERT(false) should abort your application. If it doesn't please solve this, I cannot provide you support if you don't have assert enabled.

This also absolutely works:

ImGui::InputText("buf", buf, IM_ARRAYSIZE(buf));
if (ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteGlobal))
    IMGUI_DEBUG_LOG("Pressed!\n");

But it catch the Esc key regardless of InputText() being active which is not what you want. So you actually need:

ImGui::InputText("buf", buf, IM_ARRAYSIZE(buf));
if (ImGui::IsItemActive() && ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteGlobal))
    IMGUI_DEBUG_LOG("Pressed!\n");

At this point it feels silly to use a Shortcut() instead of IsKeyPressed().

I am sorry but there are too many inconsistency and imprecision in your statements. I have tested the things above and they do work as well.

zMidair commented 2 months ago

Hello, It looks like assertion is indeed disabled unfortunately. Is there a way to enable it? How can I do that?

GamingMinds-DanielC commented 2 months ago

Is there a way to enable it? How can I do that?

Switch to a debug configuration. IM_ASSERT defaults to the standard assert function if not defined otherwise, that one is only active in debug builds.

ocornut commented 2 months ago

assert() in Visual Studio is always enabled. I find it crazy that it may be disabled anywhere.

GamingMinds-DanielC commented 2 months ago

assert() in Visual Studio is always enabled.

Only if NDEBUG is not defined. If it is, assert(...) expands to ((void)0). So no active assert in release builds.

ocornut commented 2 months ago

My mistake. For some reasons I always assumed that NDEBUG was not set in default MSVC solution, but it is set in release mode. It's just that I removed it from my solutions a decade ago. Argh.

zMidair commented 2 months ago

Hello, I’m using MSVC and I build with CMake. I can only build for Release or RelWithDebInfo, no debugging. Is there another way?

GamingMinds-DanielC commented 2 months ago

You can define IM_ASSERT appropriately in imconfig.h and report failed assertions however you like. You can look at the definition of the default assert as a reference.

ocornut commented 2 months ago

Check your cmake setup or related cmake tutorials to ensure you have a build configuration where NDEBUG is not defined.

zMidair commented 2 months ago

Update: I made my own assert macro which should works, I will leave the code here:

#define IM_ASSERT(_EXPR) (!(_EXPR) ? ((void)log::error("ImGui Assertion failed")) : ((void)0))
GamingMinds-DanielC commented 2 months ago

That just will tell you that an assertion failed, but no indication of which one. Solving problems will be very hard with that kind of error reporting. You should really look into proper build configurations or at least use a better assert definition based on the default one.