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

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

Open
catlowlevel opened this issue Jan 10, 2025 · 7 comments

Comments

@catlowlevel
Copy link

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

@ocornut
Copy link
Owner

ocornut commented Jan 10, 2025

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.

@ocornut ocornut changed the title how to unfocus InputText programmatically How to unfocus InputText programmatically Jan 10, 2025
@catlowlevel
Copy link
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?

@ocornut
Copy link
Owner

ocornut commented Jan 10, 2025

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; }

@catlowlevel
Copy link
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

@ocornut
Copy link
Owner

ocornut commented Jan 10, 2025

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?

@ocornut ocornut changed the title How to unfocus InputText programmatically How to unfocus InputText programmatically + IsItemDeactivatedAfterEdit() with ClearActiveID() Jan 10, 2025
@ocornut
Copy link
Owner

ocornut commented Jan 10, 2025

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

@catlowlevel
Copy link
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 completel
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

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