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

Change color within text / colored text #902

Open
JLFSL opened this issue Nov 11, 2016 · 52 comments
Open

Change color within text / colored text #902

JLFSL opened this issue Nov 11, 2016 · 52 comments

Comments

@JLFSL
Copy link

JLFSL commented Nov 11, 2016

Hi there, I was wondering if there was any way to change from color while still using the ::Text function, e.g:
ImGui::Text("Please fill in your #FF0000 to #0000FFlogin.");

Thanks.

@ocornut
Copy link
Owner

ocornut commented Nov 11, 2016

You'd need to write a custom function that does some sort of mark-down-ish parsing and render as it goes.
We don't have one, when we have one it'll most probably be a separate function.

@ocornut
Copy link
Owner

ocornut commented Nov 11, 2016

I have a simple one I made for my own use on a project but it's not really fit for use elsewhere.

You can grab here but it'd need fixing/reworking/finishing if you want to use it in your own project.
ui_richtext.h.txt
ui_richtext.cpp.txt

@pdoane
Copy link

pdoane commented Nov 12, 2016

The difficulty with it as a separate top-level function is that many ImGui functions take text as an argument (e.g. buttons, trees). I think that ImGui needs some kind of standard escape mechanism even if the particulars are left up to the application to interpret the content.

@ocornut
Copy link
Owner

ocornut commented Nov 12, 2016

That's a good point, we could have an escape mecanism for simple stuff (such as color change?), maybe with the possibility of enabling/disabling/setting a custom one.

What sort of feature would you want to use and in what sort of context?

@ocornut
Copy link
Owner

ocornut commented Nov 12, 2016

Also worth reminding that text size calculation can be a noticeable bottleneck for large UI and therefore this has to be handled properly to not affect perfs too much.

If the marking/escaping mechanism is optional it gives more flexibility there (because we aren't concerned by performances as much anymore).

Related because closely related: the shortcut system (#456) will also needs a way to render underscore, probably tagged Windows-style where "&File" will render an underscore under the F, potentially optional (for regular buttons, modern Windows tends to hide the underscore using ALT is held).
That probably means the system would need to accommodate for optional features.

@ratchetfreak
Copy link

About text size calc maybe worth doing some caching. Most of the strings being rendered will be fixed and be rendered again every frame.

@ocornut
Copy link
Owner

ocornut commented Nov 12, 2016

Would be worth it.

  • Font, font size, text wrapping (+pos) needs to be included in key.
  • A highly populated map with lots of insertion needs better code than current key-value map we use (which isn't insertion-friendly and doesn't clean up).
  • Hashing would lead to a worst worse case (maybe not a problem?)

So that's some work and a different feature that discussed here. Something to keep in mind.

For now implementing the marking/escaping would go first.

@ocornut
Copy link
Owner

ocornut commented Nov 12, 2016

Note that current text size computation is already fairly lightweight (inner loop pulling from a hot-only dense array of XAdvance values), so replacing it with Hash + Lookup isn't going to be trivial massive win.

@pdoane
Copy link

pdoane commented Nov 14, 2016

What sort of feature would you want to use and in what sort of context?

The main one I want has been highlighting a search substring in lists/trees. The escape mechanism feels like it fits in the best with ImGui design because so many entrypoints take strings as parameters.

@ocornut
Copy link
Owner

ocornut commented Nov 15, 2016

Possible features:

  • Change colors using values encoded in string e.g. [col=FF00000]test[/col]
  • Change colors using high-level semantic e.g. [b]test[/b] and we have a color for Text and a color for TestHighlight. The same way we currently have TextDisabled. I feel this would be simplest and a rather sane thing to do.
  • Set a background color (essentially filling behind characters).
  • Enable/disable underline (would that be an hardcoded concept in term of drawing?)
  • Changing font (to e.g. get actual bold/italic). Feels a bit hairy to get right

And what would be the syntax? Feels like using [] would collide too much with normal strings and lead to parsing problems, so \ would be a better start and we can have a push/pop system to enable/disable escaping.

@pdoane
Copy link

pdoane commented Nov 15, 2016

ANSI escape codes cover that feature set so that's one possibility.

If some other syntax is used, I'd prefer some non-printable character so that all existing strings continue to work without change.

@JLFSL
Copy link
Author

JLFSL commented Nov 15, 2016

Having something similar to BBCode would be amazing.

@pdoane
Copy link

pdoane commented Nov 16, 2016

Having something similar to BBCode would be amazing.

Are you suggesting you would want it applied pervasively or as a separate entrypoint? I'd be concerned if something like BBCode/Markdown/HTML were applied at the lowest level of string formatting as escaping source data is a lot more annoying/expensive with an immediate mode API.

@ratzlaff
Copy link

since I am familiar with ANSI escape codes, that one gets my vote
https://conemu.github.io/en/AnsiEscapeCodes.html#SGR_Select_Graphic_Rendition_parameters

@vivienneanthony
Copy link

I am curious. Is there any update about this topic?

@ocornut
Copy link
Owner

ocornut commented Jan 1, 2017

None. Someone may want to get ahead toward a proof of concept. I know it'll take me too much time (several full days) to do it totally properly and with perf/options I'm happy with, so I'm not looking into it at all now.

@ocornut
Copy link
Owner

ocornut commented Jan 19, 2017

Note that #986 is suggesting using a similar feature during text edition.

i just need word coloring in the text multi line control.

I hope i can have a string with information about word start, word end, and word color to pass to the multi-line control. like that maybe "start : end : color" => "10:22:#99ccff"

So before the multi-line control i imagine analyse the text and format a special string for it in the ImGuiTextEditCallback

Finally i imagine pass this buffer to a modified version of ImFont::RenderText.

Do you think i may be heavy , maybe you have another way ?

Thanks for your help

I don't think the suggested solution would be appropriate, but we can keep in mind and consider how coloring might work in text edition in the future.

@aiekick
Copy link
Contributor

aiekick commented Jan 19, 2017

Finally for the moment i use a special buffer who have the same length as the buffer of the text multi line control. and each char corresponding to a color settings in an array in ImGuiStyle.

struct ImGuiStyle { ImVec4 SyntaxColors[255]; }

So i have 254 possibility of coloration.

My function analyse the buffer by the callback when i edit some things and format the color of words in the special buffer passed to the multi-line control.

and in void ImFont::RenderText, i do that :

the buffer is a 'const char* syncol'

ImGuiStyle& style = ImGui::GetStyle();

ImU32 defaultCol = col;

    while (s < text_end)
    {
    	if (syncol != 0)
    	{
    		col = defaultCol;
    		char c = *(syncol + (s - text_begin));
    		if (c != '\0')
    			col = ImColor(style.SyntaxColors[c]);
    	}

It work like a charm. so now i need to write a good syntax coloring func for my needs :)

screenhunter_284

@David20321
Copy link

David20321 commented Feb 1, 2017

In Overgrowth I added the ##FFFFFFFF style coloring like this: https://gist.github.com/David20321/9e349d197b19e4919614652e4c0d175b

Doesn't work for editable text, just for display.

@bluebear94
Copy link

Actually, I was going to implement this myself with my own copy. ;)

Perhaps along with embedding icons inside text, but I doubt that would ever be added.

@ocornut
Copy link
Owner

ocornut commented Feb 2, 2017

@David20321

In Overgrowth

Your game looks amazing! Would be nice if you could post some shots showcasing your use of imgui in the screenshots threads sometime.

@bluebear94

Perhaps along with embedding icons inside text, but I doubt that would ever be added.

Merging an icon font such as FontAwesome into the main font is very easy and very very useful. Read the documentation in extra_font. I am using https://github.com/juliettef/IconFontCppHeaders as a helper to get #define for the various icons.

@bluebear94
Copy link

@ocornut That's possible, but then you can't have icons using multiple colours, can you? But then, I'm not sure why anyone would want full-colour icons outside of development tools.

@ocornut
Copy link
Owner

ocornut commented Feb 7, 2017

@bluebear94 You can't indeed. Give it a try someday, using FontAwesome merged within ImGui default font, it's really super convenient. Considering adding it to the demo app.

@chadlyb
Copy link

chadlyb commented Mar 21, 2017

I have a bit of a hack I'm using that works alright for my purposes (a read-only multiline buffer.) Not industrial-grade, but here goes:

void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
{
	const ImU32 originalCol = col;

	...
      		if (c == '\r')
            		continue;
	}

	if (c >= 0x100000 && c <= 0x10FFFD )
	{
		if ( c == 0x108000 )
		{
			col = originalCol;
		}
		else
		{
			col =   0xFF000000
				  + ((c >> 8) & 0x0F) * 0x00000011
				  + ((c >> 4) & 0x0F) * 0x00001100
				  + ((c     ) & 0x0F) * 0x00110000;
		}
		continue;
	}

	float char_width = 0.0f;
	if (const Glyph* glyph = FindGlyph((unsigned short)c))
	{
	...

Then, use Unicode 0x100rgb to represent your color (0x108000 resets to default color.)
Seems to work well enough for read-only buffers, including cursor navigation and selection, for my purposes.

@ldecarufel
Copy link

ldecarufel commented Apr 3, 2017

Hi there! I just wanted to share my simple solution to embed colors in ImGui text.

I basically parse the string while accumulating characters and looking for inline color blocks. When found I extract the color, then I simply use ImGui::Text() and ImGui::SameLine(0,0) to print the accumulated text, I push the new color using ImGui::PushStyleColor(), and I start to accumulate text again. I also use ImGui::PopStyleColor() before pushing a new color if one was pushed before.

The code supports 6-char colors (RGB), and 8-char colors (ARGB). Using an empty color block simply returns to the default color.

You can find the code here on PasteBin.

Here's how it's used, using "{" and "}" to mark colors:

const float colPos = 220.0f;
ImGui::TextWithColors( "{77ff77}(A)" ); ImGui::SameLine( colPos ); ImGui::Text( "Press buttons, toggle items" );
ImGui::TextWithColors( "{77ff77}(A){} + DPad Left/Right" ); ImGui::SameLine( colPos ); ImGui::Text( "Manipulate items" );
ImGui::TextWithColors( "{ff7777}(B)" ); ImGui::SameLine( colPos ); ImGui::Text( "Close popups, go to parent, clear focus" );
ImGui::TextWithColors( "{7777ff}(X)" ); ImGui::SameLine( colPos ); ImGui::Text( "Access menus" );

That's a pretty simple solution that works only for regular text, but that's all we really needed for now.

@styves
Copy link

styves commented Jul 20, 2017

I made a simple edit to RenderText to support Quake style color codes. It's rather simple, ^ is used as an escape followed by an index into the color code list.

void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
{
    const ImU32 alpha = (col >> 24);
    const ImU32 color_codes[9] =
    {
        col,                           // default 0
        ImColor(255,   0,   0, alpha), // red     1
        ImColor(  0, 255,   0, alpha), // green   2
        ImColor(255, 255,   0, alpha), // yellow  3
        ImColor(  0,   0, 255, alpha), // blue    4
        ImColor(  0, 255, 255, alpha), // cyan    5
        ImColor(255,   0, 255, alpha), // magenta 6
        ImColor(255, 255, 255, alpha), // white   7
        ImColor(  0,   0,   0, alpha), // black   8
    };
    ...
    while (s < text_end)
    {
        if (*s == '^' && *(s + 1) && *(s + 1) != '^')
        {
            col = color_codes[(*(s + 1) - '0') % 8];
            s += 2;
            continue;
        }

        if (word_wrap_enabled)
        {
        ...
                y += line_height;
                col = color_codes[0]; // reset color code on new line

                if (y > clip_rect.w)
                ...

There's unfortunately some cursor funkiness in my input fields, I'll update the comment when I get around to fixing it.

Edit: Here we go, changing InputTextCalcTextSizeW seems to have worked out. If anyone thinks there's a better way I'm all ears.

static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)
{
...
        if (c == '\r')
            continue;
        if (c == '^' && *(s) && *(s) != '^')
        {
            ++s;
            continue;
        }
        const float char_width = font->GetCharAdvance((unsigned short)c) * scale;
        ...

@connorjak
Copy link

https://gist.github.com/ddovod/be210315f285becc6b0e455b775286e1
Here's a gist with ImGui::TextAnsiColored function. Kinda dirty, but it works fine for me. The main part is in the ParseColor and ImFont_RenderAnsiText functions. Actually most of the code is copy-pasted from the original imgui code, just to not touch it. It supports only foreground colors (since there's no easy way to draw background color and bold/reversed/etc ansi formats). Based on this gist
https://gist.github.com/David20321/9e349d197b19e4919614652e4c0d175b

Looks like this
ansicolors

@ddovod I have made a port of this functionality on a fork of pyimgui: https://github.com/connorjak/pyimgui/tree/AnsiText

BruceCherniak pushed a commit to RenderKit/ospray-studio that referenced this issue Aug 9, 2021
I wish we could have inline color commands to make the type darker, but
that doesn't exist (yet) without custom solutions

ocornut/imgui#902
@ocornut ocornut mentioned this issue Apr 19, 2022
@TYSON-Alii
Copy link

Colorful text.

Type the character you have specified for the color you want after the $ sign.

Example:

static const auto& code = R"($r#include $y<iostream>
$busing namespace $wstd$l; $d// for easy

$bauto $wmain$l() -> $bint $l{
    $pif $l($o2 $l+ $o2 $l== $o4$l)
        $wcout $l<< $f"Hello, World." $l<< $m'\n'$l;
    $preturn $o0$l;
};
)";
static const ImVec4&
    white = { 1,1,1,1 },
    blue = { 0.000f, 0.703f, 0.917f,1 },
    red = { 0.976f, 0.117f, 0.265f ,1 },
    grey = { 0.230f, 0.226f, 0.289f,1 },
    lgrey = { 0.630f, 0.626f, 0.689f,1 },
    green = { 0.000f, 0.386f, 0.265f,1 },
    lime = { 0.55f, 0.90f, 0.06f,1 },
    yellow = { 0.91f, 1.00f, 0.21f,1 },
    purple = { 1,0,1,1 },
    orange = { 1.00f, 0.36f, 0.09f,1 };
ColorfulText(code, {
    {'w', white},
    {'b', blue},
    {'d', grey},
    {'l', lgrey},
    {'f', green},
    {'m', lime},
    {'y', yellow},
    {'p', purple},
    {'r', red},
    {'o', orange}
});

image
another ss
unknown

Souce Code

using str = std::string;
template <typename T>
using list = std::pmr::vector<T>;
namespace im = ImGui;
void ColorfulText(const str& text, const list<pair<char, ImVec4>>& colors = {}) {
    auto p = im::GetCursorScreenPos();
    const auto first_px = p.x, first_py = p.y;
    auto im_colors = ImGui::GetStyle().Colors;
    const auto default_color = im_colors[ImGuiCol_Text];
    str temp_str;
    struct text_t {
        ImVec4 color;
        str text;
    };
    list<text_t> texts;
    bool color_time = false;
    ImVec4 last_color = default_color;
    for (const auto& i : text) {
        if (color_time) {
            const auto& f = std::find_if(colors.begin(), colors.end(), [i](const auto& v) { return v.first == i; });
            if (f != colors.end())
                last_color = f->second;
            else
                temp_str += i;
            color_time = false;
            continue;
        };
        switch (i) {
        case '$':
            color_time = true;
            if (!temp_str.empty()) {
                texts.push_back({ last_color, temp_str });
                temp_str.clear();
            };
            break;
        default:
            temp_str += i;
        };
    };
    if (!temp_str.empty()) {
        texts.push_back({ last_color, temp_str });
        temp_str.clear();
    };
    float max_x = p.x;
    for (const auto& i : texts) {
        im_colors[ImGuiCol_Text] = i.color;
        list<str> lines;
        temp_str.clear();
        for (const auto& lc : i.text) {
            if (lc == '\n') {
                lines.push_back(temp_str += lc);
                temp_str.clear();
            }
            else
                temp_str += lc;
        };
        bool last_is_line = false;
        if (!temp_str.empty())
            lines.push_back(temp_str);
        else
            last_is_line = true;
        float last_px = 0.f;
        for (const auto& j : lines) {
            im::RenderText(p, j.c_str());
            p.y += 15.f;
            last_px = p.x;
            max_x = (max_x < last_px) ? last_px : max_x;
            p.x = first_px;
        };
        const auto& last = lines.back();
        if (last.back() != '\n')
            p.x = last_px;
        else
            p.x = first_px;
        if (!last_is_line)
            p.y -= 15.f;
        if (i.text.back() != '\n')
            p.x += im::CalcTextSize(last.c_str()).x;
    };
    im_colors[ImGuiCol_Text] = default_color;
    im::Dummy({ max_x - p.x, p.y - first_py });
};

@ForrestFeng
Copy link

ForrestFeng commented May 4, 2022

Change color within text / colored text

I have enhanced the ImGui::TextUnformated() method to make it support arbitrary text color, underline, strikethrough, highlight, mask

I aslo add one demo for this feature.

image

The code for the demo is shown below.

The new TextUnformatted signature was changed to support this feature. The wrapped, disabled, and customization are newly added args with default values. So it should be compatible with existing code.

IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL, bool wrapped = false, bool disabled = false, const ImTextCustomization *customization = NULL);

       IMGUI_DEMO_MARKER("Widgets/Text/Text Customization");
        if (ImGui::TreeNode("Text Customization Simple"))
        {
            ImColor red(255, 0, 0, 255);
            ImColor green(0, 255, 0, 255);
            ImColor blue(0, 0, 255, 255);
            ImColor yellow(255, 255, 0, 255); 
            ImColor brown(187, 126, 0, 255);
            ImColor cyan(0, 255, 255, 255);
            ImColor magenta(255, 0, 255, 125);

            const char* text = "The quick red fox jumps over the green box.";
            ImTextCustomization tc;

            ImGui::NewLine();
            ImGui::Text("Color the whole text");            
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Range(text).TextColor(green));

            ImGui::NewLine();
            ImGui::Text("Color the sustring of the text");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text + 14, text + 17).TextColor(red).Range(text + 39, text + 42).TextColor(green));

            ImGui::NewLine();
            ImGui::Text("Underline");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text).Unerline());

            ImGui::NewLine();
            ImGui::Text("Underline with color");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text).Unerline(blue));

            ImGui::NewLine();
            ImGui::Text("Strikethrough");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text).Strkethrough());

            ImGui::NewLine();
            ImGui::Text("Strikethrough with color");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text).Strkethrough(red));

            ImGui::NewLine();
            ImGui::Text("Hilight the text with brown");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text).Highlight(brown));

            ImGui::NewLine();
            ImGui::Text("Mask the text so it is not readable");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text+10, text+17).Mask());

            ImGui::NewLine();
            ImGui::Text("Mask the text with color");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text+10, text+17).Mask(cyan));

            ImGui::NewLine();
            ImGui::Text("Mask the text with semitransparent color");
            ImGui::TextUnformatted(text, NULL, true, false, &tc.Clear().Range(text+10, text+28).Mask(magenta));
            ImGui::NewLine();

            ImGui::TreePop();
        }

Those text styles are all with a text and can be combined as needed.

Here is another demo. It allows the user to adjust the ranges to apply a style and see the result.

image

The code is on Text Customization Branch

Any comments please let me know.

If it is possible I will create a pull request when ready. Thanks.

@ForrestFeng
Copy link

ForrestFeng commented May 4, 2022

Add one more video for this feature.

ImGui.Text.Customization.Video_2022-05-04_132237.mp4

Quintus added a commit to Quintus/ilmendur that referenced this issue Dec 28, 2022
This currently maps to coloring the text red. When ImGui's ticket
ocornut/imgui#902 is resolved, one can look
into supporting additional markup. For now, this should be sufficient.
@S-A-Martin
Copy link

Still seems to be a lot of interest in this. Has support for this been pulled into the official ImGui yet?

@ocornut
Copy link
Owner

ocornut commented Jan 24, 2023

I'm a little puzzled why many people are writing messages on an open pull-request or issues asking if the PR/issue has been solved :) no it hasn't.

But I have been recently working on new low-level text functions and I am confident we can make interesting progress on this in the coming quarters (borrowing some ideas from #3130 and #5288 but with a different implementation).

@codecat
Copy link
Contributor

codecat commented Jan 24, 2023

That's awesome! Personally, I hope that any official support for this can be customized; many people already seem to have this "hacked in" with their own syntax.

For example, in my case this would be \$f00Red Text\$zNormal text, but that's very different from what's suggested in this issue.

Additionally, I also implemented text shadow using the \$s syntax, so I wonder how extensible such a system would be.

@ocornut
Copy link
Owner

ocornut commented Jan 24, 2023

I don't have answer for this yet

A few things that are certain:

  • we'll probably to be able to feed decoration/markup inside text (e.g. Button("some label with markup")) as well as externally (e.g. InputText() with custom syntax coloring provided on a separate stream).
  • things need to be ultra fast. means parsing and skipping needs to be optimal and the syntax (IF there is a single syntax) needs to be designed around this.

Uncertain but obviously desirable:

  • Ways to creatively extend this. But maybe the extension will need to follow a common syntax etc. I don't know.

@ajh123
Copy link

ajh123 commented Apr 3, 2023

Example of ANSI text rendering:

Maybe use these as an idea for text colours.

They are a bit old, so they might be incompatible with recent ImGui versions.

@pheonixfirewingz
Copy link

pheonixfirewingz commented Aug 18, 2024

has there been any progress on this?
not to be rude but how has it taken almost 8 years to add escape code colour support to render coloured text for stuff like syntax highlighting for people who know the code base assume it would be an easy fix?
again not trying to be rude just confused.

@GamingMinds-DanielC
Copy link
Contributor

again not trying to be rude just confused.

This read might clear up some of the confusion:
#7892

If you consider the impact on performance, general usability (instead of just a single use case) and API simplicity, something as complex as syntax highlighting is not an easy fix at all. To do it correctly, some internal stuff has to be rewritten. If you look at the linked thread, you will see "Better Text Functions" (item 6.2), that would be a prerequisite and one of its goals is to allow for color markup.

But even if it would take just a week to implement correctly, it would still have to compete for time and attention with tons of other "small things". And all of those things have to compete with activities like eating and sleeping as well.

@ocornut
Copy link
Owner

ocornut commented Aug 19, 2024

It's not a feature that we can do half-right because it would be very hard to go backward and perform incremental updates after people start using it. I am hoping to make progress on it within the next 12 months.

@marcakafoddex
Copy link

marcakafoddex commented Sep 3, 2024

A while ago I ended up implementing a new TextAttr function:

// this takes e.g. #f00 (RGB, alpha 255) or e.g #fff8 (for RGBA), and the double precision versions
ImGui::TextAttr("{#fff}Row {#f00}%d {#fff}is {#0000ff}%s {#fff}and {#ff880088}orange transparent", 2, "blue");

which calls TextEx and passes a new ImGuiTextFlags_ParseColors flag I added:

void ImGui::TextAttr(const char* fmt, ...) {
	ImGuiWindow* window = GetCurrentWindow();
	if (window->SkipItems)
		return;

	va_list args;
	va_start(args, fmt);
	const char *text, *text_end;
	ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
	TextEx(text, text_end, ImGuiTextFlags_ParseColors);
	va_end(args);
}

A few changes to CalcTextSize related functions (that ignore the color markup), and some very basic parsing in RenderTextWrapped (all only in case the new flag is set), and i can have colored text super easily:

image

ImGui is awesome, it's super trivial to extend. Also made a skin system for it using nine-slices, epically simple. Kudos to @ocornut !

@aiekick
Copy link
Contributor

aiekick commented Sep 5, 2024

@marcakafoddex nice, and can you show your modification of TextEx ?

@marcakafoddex
Copy link

marcakafoddex commented Sep 5, 2024

@marcakafoddex nice, and can you show your modification of TextEx ?

Sure, check the commit in the fork i just made

a few notes:

  • it's kinda of a rough draft, not super well tested, so YMMV if you choose to use it :)
  • the hex color parsing is awful but it seems to work, relies heavily on the compiler optimizing it
  • if you plan on using it, be sure to test it more
  • the code's only tested with 1.89.5 which i used for my project

@db48x
Copy link

db48x commented Sep 14, 2024

I’ve been spending some free time working on a venerable game project. It started out (14 years ago) running in the terminal, using ncurses. Later it started using SDL, with a built‐in ncurses translation layer. Eventually the ncurses compatibility layer was hacked to draw graphical tiles in certain curses windows instead of text. Thus the game has graphics, but only within the narrow confines two specific curses windows, one for the character’s view of the world, and another for the world map. Aside from those two windows, the game is still limited to character cells with a foreground and a background color chosen from a palette of 16 colors. Now I’m troweling some ImGui on over the top, for fun.

Here's a little screenshot of me examining an item (an atomic lamp) from my inventory:
Screenshot from 2024-07-22 16-10-15

Only the central menu of actions that I can take is rendered using ImGui, in this screenshot.

As you can see, a major design aesthetic of the game is colorful text. Text which wraps pretty nicely to adapt to different screen sizes. Much of this text is stored in JSON files, where color tags are used. These look a little like HTML: “Volume: <color_yellow>1.00</color> L Weight: <color_yellow>3.17</color> lbs”, and they can name any combination of the 16 colors as foreground and background. The game also has a lot of ASCII art that is stored in strings using these color codes. A lot of text with color tags is generated at run‐time as well. Obviously to display this text in ImGui I need something like what everyone is talking about in this feature request.

I’ve only started experimenting with my own variation on this theme, but I want to put in my 2¢ anyway. I’ve seen a lot of suggestions for ways to embed styles in text. Markdown, ANSI color codes, etc. Obviously I prefer that whatever text system ImGui gets is something that I can extend to support the huge amount of text that I already have. I can’t just convert it all to markdown!

But more than that, I don't want to be forced to generate strings with embedded codes at run‐time if I don’t have to. I’d prefer an API that allowed me to get the same results without allocating anything.

To that end, I started with the code that @ocornut wrote in #2313 (comment). This code defines a struct representing a colored span of text. It runs down a list of these spans, rendering them inline and wrapping to new lines as needed. It works pretty well for what I need, because I can parse strings into spans very easily. I can even store the list of spans to reuse in future frames. Here’s a simple example:

Paragraph stuff = cata_imgui::parse_colored_text( "Some long text that will wrap around nicely. <color_red>Some red text in the middle.</color> Some long text that will wrap around nicely.", c_white );
cata_imgui::TextStyled( stuff );

But it still requires allocation, so I’ve also written a slightly modified version that doesn’t require it. Here’s what that looks like:

cata_imgui::TextParagraph( "Some long text that will wrap around nicely.", c_white );
cata_imgui::TextParagraph( " Some red text in the middle.", c_red );
cata_imgui::TextParagraph( " Some long text that will wrap around nicely.", c_white );

These render and wrap identically.

I’ve posted my code to a gist; please feel free to use it however you want. It includes a demo.

Of course it’s not perfect. The first word of any span wraps differently than the other words, due to a particular choice deep in the text rendering. When less than a whole word fits, it renders as many characters as it can before going to the next line. In this case that’s the wrong choice. As you can see above, spaces between successive spans require some thought; for now I make the user put them in where they are needed but it might be nice if there were a Space() function that advanced by the width of a single space. I also haven't written any Formatted versions of these, but I'll need them to insert numbers where appropriate. There are probably other defects that I’ve completely forgotten or haven’t noticed yet. Speaking of which, I just realized that I put the color argument in the opposite position compared to ImGui::TextColored. I’ll definitely have to fix that before I make a PR.

So those are my requests to add to the list (if they aren’t already there): whatever else you do, let us parse things ourselves and don't forget about the allocation‐free API that we all love.

Thanks!

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