-
Notifications
You must be signed in to change notification settings - Fork 518
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
[New feature] Add ability to change text color/background color via (optional) inline code; should be backward compatible #560
base: master
Are you sure you want to change the base?
Conversation
…a backward compatible manner
Really great feature! I also would like to see it merged. For now, I will have to integrate this into my own, already modified version of Nuklear. Having this merged into master would make life easier! |
Hi @YgorVasilenko thanks for the kind words! Please let me know of any issues you may have while trying to use this feature. I'm looking for feedback on potential issues in the implementation if you could:
There are likely other concerns with the PR but these are of my own for now. |
Hi, first issue I faced: MSVC compiler turns '\e' into same ASCII as 'e' (101). This resulted in 'e' being stripped out from any colored text completely. I commented out branches of code, that do something, if symbol is '\e', and that helped. So far no memory issues. |
As |
Sorry my bad for not realizing that |
Seems working well, i like this and solved my problem of having multiple texts in different colors in a horizontal row, |
|
Updated first post to reflect the changes. A macro |
Old commentCurrently edit box can be styled this way as well, leading to cursor being in the wrong place.Should it be changed so that edit boxes are always wrapped in between
Or should edit box supports this way of styling? The latter is more complicated.. Updated commits to not do inline color with edit buffer. |
Thanks a lot for wrapping text fix, we'll use that! |
I use c++ to get a gradient color output: // C
const char* custom_strcat(size_t numStrings, ...) {
// Initialize va_list and iterate through the strings to calculate the total length
va_list args;
va_start(args, numStrings);
size_t totalLen = 0;
for (size_t i = 0; i < numStrings; i++) {
const char* currentString = va_arg(args, const char*);
totalLen += strlen(currentString);
}
va_end(args);
// Allocate memory for the result string
char* result = (char*)malloc(totalLen + 1); // +1 for the null terminator
if (result == NULL) {
return NULL; // Memory allocation failed
}
// Copy the contents of each string to the result
char* currentPos = result;
va_start(args, numStrings);
for (size_t i = 0; i < numStrings; i++) {
const char* currentString = va_arg(args, const char*);
size_t currentLen = strlen(currentString);
memcpy(currentPos, currentString, currentLen);
currentPos += currentLen;
}
va_end(args);
// Null-terminate the result string
*currentPos = '\0';
return result;
}
// You have to decleare an extra variable for gradientText()
// If u nest it into custom_strcat() u will still get memory leaked by gradientText()
const char* grText = gradientText("Nyaruku", "#7C7CFF", "#FF016F");
const char* labelText = custom_strcat(2, "My Discord: @", grText);
nk_label(ctx, labelText, 17);
// Prevent Memory Leak
free((void*)grText);
free((void*)labelText); // C++
struct Color {
int r, g, b;
Color(int red, int green, int blue) : r(red), g(green), b(blue) {}
// Linear interpolation between two colors
static Color interpolate(const Color& start, const Color& end, double t) {
int r = static_cast<int>((1 - t) * start.r + t * end.r);
int g = static_cast<int>((1 - t) * start.g + t * end.g);
int b = static_cast<int>((1 - t) * start.b + t * end.b);
return Color(r, g, b);
}
};
Color getColorForStep(const Color& start, const Color& end, int steps, int step) {
if (step < 0) step = 0;
if (step > steps) step = steps;
double t = static_cast<double>(step) / steps;
return Color::interpolate(start, end, t);
}
Color HexColorToRGB(const char* hexColor) {
Color color{255,255,255};
if (hexColor[0] == '#' && strlen(hexColor) == 7) {
std::stringstream ss;
ss << std::hex << hexColor + 1; // Skip the '#' character
int colorValue;
ss >> colorValue;
color.r = (colorValue >> 16) & 0xFF;
color.g = (colorValue >> 8) & 0xFF;
color.b = colorValue & 0xFF;
ss.clear();
}
else {
// Invalid hex color format, return the original color
}
return color;
}
std::string RGBColorToHex(Color color) {
std::stringstream ss;
ss << "#" << std::setfill('0') << std::setw(2) << std::hex << color.r
<< std::setw(2) << std::hex << color.g << std::setw(2) << std::hex << color.b;
std::string temp = ss.str();
ss.clear();
return temp;
}
const char* gradientText(const char* text, const char* color_start, const char* color_end) {
std::stringstream result;
Color color1 = HexColorToRGB(color_start);
Color color2 = HexColorToRGB(color_end);
int totalSteps = strlen(text);
for (int i = 0; i < totalSteps; i++) {
result << "[color=" << RGBColorToHex(getColorForStep(color1, color2, totalSteps, i)) << "]" << text[i] << "[/color]";
}
std::string resultStr = result.str();
char* cString = new char[resultStr.size() + 1];
strcpy(cString, resultStr.c_str());
return cString;
} |
@yukyduky I've added the casts to the commits to help C++ compat. For the wrapping text, did you update the container size for the wrapped text when you resize the window? Does it not work properly even when you do that? Edit: seems to work with the updated sdl_opengl3 demo (changed width of scalable area to be a factor of the containing window instead of being fixed): Recording.2023-09-12.035216.mp4Edit 2: Looks like I didn't read through the docs properly, so dynamic layout is a thing haha ( ty @yukyduky ). |
@xzn Nice! Thanks for the quick update :) One of the benefits is and should be to be able to resize the windows on the fly while the app is running, imo. It's possible to do so in the current version so can't be too hard to get it working again. |
@xzn Ahh this is my fault, I've pretty much only used the nk_layout_dynamic_row and didn't think about that I was using your window with the static layout to test your code. In that case I have no further issues, sorry for the confusion^^ |
…n the wrong location
Why is this not merged? |
Any plans to merge this feature? |
Originally requested here: vurtun/nuklear#575
I happen to need this feature in my app so this is an implementation of it.
How it works
A new property
draw_config
is stored innk_context
and passed tonk_command_buffer
when windows are drawn.nk_draw_text
then use the settings in the command buffer to draw text as required.There are two ways to encode inline color information:
The difference is that in the first option,
NK_ESC
can be used to escape and display the opening tag without changing the color of subsequent text, e.g:In the second option
NK_ESC
must be used before each opening and closing tag for them to take effect.You can also give names to colors like so:
Notes on possible improvement in code
Most of the changes in this file should not be needed, most of it can be cut down if we say, include
stb_ds.h
and use the hash map implementation for user-defined color name to color map.Alternatively we can choose to not support user-define color names (although it's already implemented in this PR).
Testing done so far
This PR has only been moderately tested. There should not be any buffer overrun or off by one error. If you've found any bugs please let me know so I can fix them.
Only the built-in
stb_truetype
font renderer has been tested. No idea right now how this feature will fare against different font rendering backend.TODO?
struct nk_map_name_color
in a file in thedemo
directory.Limitation in code
Tags can be nested up to only 16 levels before they have no effect, e.g.:
"[color=#ff0000]red [color=#0000ff]blue ...[/color][/color]"
In general there's no need to nest color tag to begin with unless you are generating text programmatically in some different way.
Also the nested color tags are kept track of with variables on the stack so I did not include a macro define that can be used to change the limit.Nesting levels can now be changed by defining
NK_INLINE_TAG_STACK_SIZE
(it's still on allocated on the stack though so don't set it too large).Example usages:
More will be added later.
Screenshots
Will be added later.