imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Feature request: support calculating longest string prefix within a wrap-box?

Open achabense opened this issue 11 months ago • 0 comments

Version/Branch of Dear ImGui:

Version 1.91

Back-ends:

Any

Compiler, OS:

Any

Full config/build information:

No response

Details:

It would be nice if there is a function to calculate the longest string prefix (that fits within wrap area). The API would be like: const char* CalcTextMaxPrefixWithin(ImVec2 max_size, const char* text, const char* text_end = 0)

(Or const char* CalcTextMaxPrefixWithin(int max_line, float wrap_width, const char* text, const char* text_end = 0);; maybe max-line makes more sense than max-height.)

vvv A barely-working impl for the first version (the time-complexity is unacceptable, and it's not dealing with utf-8). The example code is based on this.

namespace ImGui {
    const char* CalcTextMaxPrefixWithin(const ImVec2 max_size, const char* text, const char* text_end = 0) {
        const int max_len = text_end ? text_end - text : strlen(text);
        for (int len = max_len; len > 0; --len) {
            // ?? Un-comment `size.x <= max_size.x &&`, and run `example` (resize window width and see how 'Prefix' changes):
            // ?? it seems the result of `CalcTextSize(...).x` can be larger than wrap-width sometimes?
            const ImVec2 size = CalcTextSize(text, text + len, false, max_size.x);
            if (/*size.x <= max_size.x &&*/ size.y <= max_size.y) {
                return text + len;
            }
        }
        return text;
    }
} // namespace ImGui

Basically, a correct impl just adds additional comparisions during CalcTextSize (in one pass), but that's not easy to emulate outside of the library.

(Real use case: I defined a tooltip-like widget, and during construction wanted to store the string prefix based on predefined wrap size and line limit.)

Screenshots/Video:

Image

Minimal, Complete and Verifiable Example code:

void example() {
    if (ImGui::Begin("Example")) {
        static int prefix = 0;
        ImGui::Text("Prefix:%d", prefix);

        const char* text =
            "1111111111\n"
            "222222222222222222222222222222222222222222222222\n"
            "33\n"
            "4444444444444444444444444444444444";
        ImGui::InvisibleButton("Canvas", ImMax(ImVec2(1, 1), ImGui::GetContentRegionAvail()));
        const ImVec2 min = ImGui::GetItemRectMin();
        const ImVec2 max = ImGui::GetItemRectMax();
        const ImVec2 size = ImGui::GetItemRectSize();
        ImDrawList* drawlist = ImGui::GetWindowDrawList();
        const char* text_end = ImGui::CalcTextMaxPrefixWithin(size, text);
        drawlist->AddText(0, 0, min, IM_COL32_WHITE, text, text_end, size.x);
        drawlist->AddRect(min, max, IM_COL32_WHITE);
        prefix = text_end - text;
    }
    ImGui::End();

}

achabense avatar Mar 23 '25 10:03 achabense