ocornut / imgui

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

Modifying text contents of active InputText #3878

Open wolfgang371 opened 3 years ago

wolfgang371 commented 3 years ago

I have a use case where I want to be able to modify the currently active InputText. You already proposed a workaround in ocornut/imgui#3290, calling ClearActiveID() (see workaround 2 below), which I think is sort of a counterpart to SetKeyboardFocusHere(). I also came up with another (ugly) workaround, just blinking a temporary InputText for the sake of switching the focus (see workaround 1 below). I know either is currently necessary since you internally buffer the text in InputText, but nevertheless they both feel like workarounds to me. What do you think about adding an internal dirty flag to TextBuffer (comparable to the ones you have for the InputText callbacks anyway), which would be set by any TextBuffer modification and consumed on the next InputText rendering..? This might be more intuitive to use and also match better the stateless GUI philosophy.

If this is not an option to you - would you consider making the ClearActiveID() public (since it's on the same level as SetKeyboardFocusHere(), which is public)? :)

class ModifyInputText
    def initialize
        @text_buffer = ImGui::TextBuffer.new(64)
    end
    def paint
        ImGui.set_keyboard_focus_here # give permanent focus to following input_text
        if ImGui.input_text("a", @text_buffer, ImGui::ImGuiInputTextFlags::EnterReturnsTrue)
            modify # works because "enter" removes keyboard focus
        end
        if ImGui.is_item_active && ImGui.is_key_pressed(SF::Keyboard::Key::Insert.value)
            # workaround #1 - to make above input_text listen again to @text_buffer
            steal_focus

            # workaround #2
            # ImGui.clear_active_id # internal API, not exposed by crystal-imgui

            modify # works because we manually took away keyboard focus
        end
    end
    def modify
        puts(@text_buffer.to_s)
        @text_buffer.clear # if corresponding input_text has focus, this has no effect to it
        @text_buffer << "bla" # same
    end
    def steal_focus
        ImGui.set_keyboard_focus_here # this takes the focus from above input_text
        dummy = ImGui::TextBuffer.new(64)
        ImGui.input_text("b", dummy) # works but blinks; invisible_button doesn't have a keyboard focus, everything else is visible
        ImGui.set_item_allow_overlap
    end
end

(Crystal again, sorry)

ocornut commented 3 years ago

Would #2890 solve your problem? Been considering merging this but also add a InputText flags to temporary "trigger" this behavior as well.

if ImGui.is_item_active && ImGui.is_key_pressed(SF::Keyboard::Key::Insert.value)

Have you considered modifying the buffer inside the InputText callback, by using ImGuiInputTextCallbackData::InsertChars() etc? Although not perfect this is the currently officially supported way or modifying an active buffer. There are three callbacks: ImGuiInputTextFlags_CallbackAlways, ImGuiInputTextFlags_CallbackCompletion, ImGuiInputTextFlags_CallbackHistory, the two later ones are hardcoded to trigger on certain keys (TAB, Up/Down) while Always can be used for your own check? I think there are certainly ways to improve this system but I need to know if you tried using it.

would you consider making the ClearActiveID() public

I think this is a XY Problem. We don't need to make all internals public, but you may need to figure a path for your language bindings (generated or not) to allow you to access internals when needed.

wolfgang371 commented 3 years ago

Would #2890 solve your problem?

I understand the proposal is to extend InputText by a flag to allow re-initialization. This would work for me with a but: I need a helper variable since I need to take the decision at another place in the code, not during drawing of the widget (as in my sample code). From my point of view a more elegant solution would be what I proposed: imgui keeps track transparently if the TextBuffer was modified and reinitializes InputText automatically once in this case.

Have you considered modifying the buffer inside the InputText callback, by using ImGuiInputTextCallbackData::InsertChars() etc?

Indeed this is what I've currently implemented, but this feels quite awkward for my use case since I'm not interested in the entered characters in the first place.

I think this is a XY Problem

:) I was not familiar with that term, but it also was my understanding. I really want to have a TextBuffer change have automatic effect on it's InputText, independent of its active state (this is my X). Making ClearActiveID() public is one possible Y.