Using std::string in ImGui::InputText(...)
Asked Answered
E

2

6

The call to ImGui::InputText() takes a char array which I need to initialise from a std::string and then transfer the contents back to the std::string. In it's simplest form:

char buf[255]{};
std::string s{"foo"};
void fn() {    
    strncpy( buf, s.c_str(), sizeof(buf)-1 );
    ImGui::InputText( "Text", buf, sizeof(buf) );
    s=buf;
}

However, it appears wasteful to have two buffers (buf and the buffer allocated within std::string) both doing much the same thing. Can I avoid the buf buffer and the copying to and from it by using just the std::string and a simple wrapper "X". I don't care about efficiency, I just want the simplest code at the call site. This code does work but is it safe and is there a better way?

class X {
public:
    X(std::string& s) : s_{s} { s.resize(len_); }
    ~X() { s_.resize(strlen(s_.c_str())); }
    operator char*(){ return s_.data(); }
    static constexpr auto len() { return len_-1; }
private:
    std::string& s_;
    static constexpr auto len_=255;
};

std::string s{"foo"};

void fn() {
    ImGui::InputText( "Text", X(s), X::len() );
}
Edie answered 3/9, 2021 at 14:35 Comment(0)
G
12

If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.

misc/cpp/imgui_stdlib.h

namespace ImGui
{
    // ImGui::InputText() with std::string
    // Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
    IMGUI_API bool  InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
    IMGUI_API bool  InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
    IMGUI_API bool  InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}

Your first code

std::string s{"foo"};
void fn() {    
    ImGui::InputText( "Text", &s );
}

Reading manuals works wonders.

Gynecologist answered 3/9, 2021 at 14:54 Comment(6)
imgui_demo.cpp several times has the comment: "To wire InputText() with std::string or any other custom string type, see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file". Again, reading manuals works wonders.Gynecologist
Do you have a link to this wonderful manual? (not only example code)Procaine
You can get a lot from the ImGui demo that showcases most functionality with good code comments explaining things. github.com/ocornut/imgui/blob/master/imgui_demo.cppFeuchtwanger
I tried this, but the IDE show a error, because the InputText second argument don't support stringsHardunn
@Hardunn The second parameter is a pointer to a string.Gynecologist
Yes, I know, but it shows a error inside the Visual Studio too. I see the code from demo, and that show me a way that works without exceptionsHardunn
H
0
  1. Use the same line from imgui_stdlib.h:
IMGUI_API bool  InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
# A lot of exceptions.
  1. Change the arguments to real variables.
  2. Only use the normal constructor:
std::string s{"example"};
void startInput() {    
    ImGui::InputText( "Text", &s );
}
#"std::string *" is incompatible with parameter of type "char *"
  1. The content in the file imgui_demo.cpp:
struct Funcs
            {
                static int MyResizeCallback(ImGuiInputTextCallbackData* data)
                {
                    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
                    {
                        ImVector<char>* my_str = (ImVector<char>*)data->UserData;
                        IM_ASSERT(my_str->begin() == data->Buf);
                        my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
                        data->Buf = my_str->begin();
                    }
                    return 0;
                }

                // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
                // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
                static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
                {
                    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
                    return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);
                }
            };

            // For this demo we are using ImVector as a string container.
            // Note that because we need to store a terminating zero character, our size/capacity are 1 more
            // than usually reported by a typical string class.
            static ImVector<char> my_str;
            if (my_str.empty())
                my_str.push_back(0);
            Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
# It works without errors;

The last code works successfully, but you will need a way to convert the ImVector to a string. This will convert:

std::string ImVectorToString(ImVector<char> vec) {
    std::string str;
    for (int i = 0; i < vec.size(); i++) {
        str += vec[i];
    }
    return str;
}
Hardunn answered 6/3 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.