Skip to content

How to unfocus InputText programmatically + IsItemDeactivatedAfterEdit() with ClearActiveID()  #8303

Closed

Description

@catlowlevel

Version/Branch of Dear ImGui:

Version 1.91.3, Branch: docking

Back-ends:

imgui_impl_android.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

ndk, Android

Full config/build information:

No response

Details:

tldr: how to unfocus InputText programmatically and still trigger the IsItemDeactivatedAfterEdit?

i have a special use-case on how i handle InputText, im using imgui on android devices and my way of handling keyboard is to popup an input dialog, send the value to c++ and then explitly set the InputText buffer inside ImGuiInputTextCallback like so (do tell me if there's a better way):

static auto InputTextCallbackAlways(ImGuiInputTextCallbackData *data) -> int
{
    if (data->EventFlag == ImGuiInputTextFlags_CallbackAlways)
    {
        if (g_SetInputText)
        {
            {
                bool changed = false;
                for (auto it = g_InputTextBuf.begin(); it != g_InputTextBuf.end();)
                {
                    unsigned int c = *it;
                    if (!InputTextFilterCharacter(GImGui, &c, data->Flags, nullptr, nullptr, false))
                    {
                        it = g_InputTextBuf.erase(it);
                        changed = true;
                    }
                    else
                    {
                        ++it;
                    }
                }
                if (changed)
                {
                    s_App->handleInputTextChange(g_InputTextBuf);
                }

                // char *end = fmt::format_to(data->Buf, "{}", g_InputTextBuf);
                // *end = '\0';
                strcpy(data->Buf, g_InputTextBuf.c_str());
                data->BufTextLen = (int)g_InputTextBuf.size();
                data->BufDirty = true;
                // data->CursorPos = data->SelectionStart = data->SelectionEnd = (int)strlen(data->Buf);
                g_InputTextBuf.clear();
            }
            g_SetInputText = false;
        }
    }
    // else
    if (userData->ChainCallback)
    {
        data->UserData = userData->ChainCallbackUserData;
        return userData->ChainCallback(data);
    }
    return 0;
}

now i managed to emulate the behaviour as if user press Enter on a real keyboard as seen below, however i fail to emulate the unfocus behaviour, for example ImGui::IsItemDeactivatedAfterEdit would not return true

    static char s_Buf[256];
    ImGui::InputText("Text", s_Buf, IM_ARRAYSIZE(s_Buf));
    if (ImGui::IsItemDeactivatedAfterEdit())
    {
        LOG_INFO("Text: {}", s_Buf); // This code would not execute
    }

the isEnter is true if user press the Enter on their soft-keyboard, otherwise it would be false if the popup dialog is closed, i want to unfocus the InputText if the popup dialog is closed, which should trigger IsItemDeactivatedAfterEdit, but i fail to do so, any help would be appreciated

void nativeTextDialogAction(JNIEnv *env, jclass clazz, jstring text, jboolean isEnter) // NOLINT
{
    if (isEnter)
    {
        ImGui::GetIO().AddKeyEvent(ImGuiKey_Enter, true);
        ImGui::GetIO().AddKeyEvent(ImGuiKey_Enter, false);
    }
    else
    {
        ImGui::ClearActiveID(); 

        // ImGui::SetWindowFocus(nullptr); // unfocus window to clear keyboard focus
        // ImGui::SetWindowFocus();        // refocus window
    }
}

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

Activity

  1. ocornut commented on Jan 10, 2025

    @ocornut
    Owner

    i have a special use-case on how i handle InputText, im using imgui on android devices and my way of handling keyboard is to popup an input dialog, send the value to c++ and then explitly set the InputText buffer inside ImGuiInputTextCallback like so (do tell me if there's a better way):

    What are you doing this and not simply calling io.AddInputCharacter() with the contents of g_InputTextBuf ?

    I believe it should then behave 100% normally.

    Note that nowadays to replace the text you can do:

    if (ImGuiInputTextState* input_state = ImGui::GetInputTextState(ImGui::GetID("Text"))
       input_state-ReloadUserBufAndMoveToEnd(); // or other variant

    And it will reload from user provided buffer.

  2. changed the title [-]how to unfocus InputText programmatically[/-] [+]How to unfocus InputText programmatically[/+] on Jan 10, 2025
  3. catlowlevel commented on Jan 10, 2025

    @catlowlevel
    Author

    What are you doing this and not simply calling io.AddInputCharacter() with the contents of g_InputTextBuf ?

    i am also manuallly setting the selection (which has been omitted from the example)

               // somewhere inside the callback
                data->CursorPos = data->SelectionStart = util::imgui::CharIndexToByteIndex(data->Buf, g_SelectionStart);
                data->SelectionEnd = util::imgui::CharIndexToByteIndex(data->Buf, g_SelectionEnd);
    
    
    // public static native void nativeTextDialogChange(String text);
    void nativeTextDialogChange(JNIEnv *env, jclass clazz, jstring text)
    {
        const char *cstr = env->GetStringUTFChars(text, nullptr);
        g_InputTextBuf = text;
        g_SetInputText = true;
        env->ReleaseStringUTFChars(text, cstr);
    }
    
    // public static native void nativeTextDialogSelectionChanged(int selStart, int selEnd);
    void nativeTextDialogSelectionChanged(JNIEnv *env, jclass clazz, jint selStart, jint selEnd)
    {
        g_SelectionStart = selStart;
        g_SelectionEnd = selEnd;
    }

    i believe that's not possible with io.AddInputCharacter() ?

    Note that nowadays to replace the text you can do:

    if (ImGuiInputTextState* input_state = ImGui::GetInputTextState(ImGui::GetID("Text"))
       input_state-ReloadUserBufAndMoveToEnd(); // or other variant

    i've seen this function but never understood how and where to use it
    is it possible to set cursor/selection there?

  4. ocornut commented on Jan 10, 2025

    @ocornut
    Owner

    i've seen this function but never understood how and where to use it
    is it possible to set cursor/selection there?

    Look at the other functions and how they are implemented...

    void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
    void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
    void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
  5. catlowlevel commented on Jan 10, 2025

    @catlowlevel
    Author

    i've seen this function but never understood how and where to use it
    is it possible to set cursor/selection there?

    Look at the other functions and how they are implemented...

    void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
    void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
    void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }

    yes, i've just look at it.

            if (ImGuiInputTextState *state = ImGui::GetInputTextState(ImGui::GetActiveID()))
            {
                // how to set the buffer??
                state->ReloadUserBufAndKeepSelection();
            }
    

    but how can i set the buffer when i don't have access to it?

    anyway, i don't think this is relevant to what im trying to achieve

    i want to unfocus the InputText if the popup dialog is closed, which should trigger IsItemDeactivatedAfterEdit

    ClearActiveID does not do this unfortunately

  6. ocornut commented on Jan 10, 2025

    @ocornut
    Owner

    i want to unfocus the InputText if the popup dialog is closed, which should trigger IsItemDeactivatedAfterEdit
    ClearActiveID does not do this unfortunately

    I can confirm this doesn't seem to work, and I will investigate it.

    However I also separately think that it is unclear why you can't have your virtual keyboard send only io events and it would work. Can you clarify what you are trying to achieve with this keyboard?

  7. changed the title [-]How to unfocus InputText programmatically[/-] [+]How to unfocus InputText programmatically + IsItemDeactivatedAfterEdit() with ClearActiveID() [/+] on Jan 10, 2025
  8. ocornut commented on Jan 10, 2025

    @ocornut
    Owner

    I believe the IsItemDeactivatedAfterEdit() issue is likely the same as #6766 and #5904 (and might be the same as #8004 too).

  9. catlowlevel commented on Jan 10, 2025

    @catlowlevel
    Author

    However I also separately think that it is unclear why you can't have your virtual keyboard send only io events and it would work. Can you clarify what you are trying to achieve with this keyboard?

    I did have an implementation that directly sent the keys events to imgui, without the input dialog, but unfortunately this implementation has many limitations that needed to be implemented manually like clipboard & auto suggestions
    With dialog, you have the keyboard with full features

    This is how it looks like using the keyboard with and without the dialog box
    https://github.com/user-attachments/assets/f8346bdf-a7b3-4e01-95d9-c976451bd2db

  10. ocornut commented on Jan 10, 2025

    @ocornut
    Owner

    but unfortunately this implementation has many limitations that needed to be implemented manually like clipboard & auto suggestions

    You may need to discuss/solve that by getting into precise details (and it would need a separate issue). Right now I don't know if your problems actually required this approach or not.

  11. catlowlevel commented on Jan 10, 2025

    @catlowlevel
    Author

    but unfortunately this implementation has many limitations that needed to be implemented manually like clipboard & auto suggestions

    You may need to discuss/solve that by getting into precise details (and it would need a separate issue). Right now I don't know if your problems actually required this approach or not.

    I don't need or want to solve it, which is why i use another implementation (with the dialog box)
    Everything's perfect, the only problem i had is this issue here

  12. ocornut commented on Jan 13, 2025

    @ocornut
    Owner

    This should now be fixed by a604d4f
    Basic regression test: ocornut/imgui_test_engine@b85d832
    Let me know if you run into any other issue or have related questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions