/******************************************************************************************* * * raylib [text] example - inline styling * * Example complexity rating: [★★★☆] 3/4 * * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev * * Example contributed by Wagner Barongello (@SultansOfCode) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * * Copyright (c) 2025 Wagner Barongello (@SultansOfCode) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" #include // Required for: strtoul(), NULL //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color); static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing); //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ int main(void) { // Initialization //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; InitWindow(screenWidth, screenHeight, "raylib [text] example - inline styling"); Vector2 textSize = { 0 }; // Measure text box for provided font and text Color colRandom = RED; // Random color used on text int frameCounter = 0; // Used to generate a new random color every certain frames SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- frameCounter++; if ((frameCounter%20) == 0) { colRandom.r = (unsigned char)GetRandomValue(0, 255); colRandom.g = (unsigned char)GetRandomValue(0, 255); colRandom.b = (unsigned char)GetRandomValue(0, 255); colRandom.a = 255; } //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); ClearBackground(RAYWHITE); // Text inline styling strategy used: [ ] delimiters for format // - Define foreground color: [cRRGGBBAA] // - Define background color: [bRRGGBBAA] // - Reset formating: [r] // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!", (Vector2){ 100, 80 }, 20.0f, 2.0f, BLACK); DrawTextStyled(GetFontDefault(), "This changes the [bFF00FFFF]background color[r] of provided text!!!", (Vector2){ 100, 120 }, 20.0f, 2.0f, BLACK); DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff][bff0000ff]foreground and background colors[r]!!!", (Vector2){ 100, 160 }, 20.0f, 2.0f, BLACK); // Get pointer to formated text const char *text = TextFormat("Let's be [c%02x%02x%02xFF]CREATIVE[r] !!!", colRandom.r, colRandom.g, colRandom.b); DrawTextStyled(GetFontDefault(), text, (Vector2){ 100, 220 }, 40.0f, 2.0f, BLACK); textSize = MeasureTextStyled(GetFontDefault(), text, 40.0f, 2.0f); DrawRectangleLines(100, 220, textSize.x, textSize.y, GREEN); EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- // Draw text using inline styling // PARAM: color is the default text color, background color is BLANK by default static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color) { // Text inline styling strategy used: [ ] delimiters for format // - Define foreground color: [cRRGGBBAA] // - Define background color: [bRRGGBBAA] // - Reset formating: [r] // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text if (font.texture.id == 0) font = GetFontDefault(); int textLen = TextLength(text); Color colFront = color; Color colBack = BLANK; int backRecPadding = 4; // Background rectangle padding float textOffsetY = 0.0f; float textOffsetX = 0.0f; float textLineSpacing = 0.0f; float scaleFactor = fontSize/font.baseSize; for (int i = 0; i < textLen;) { int codepointByteCount = 0; int codepoint = GetCodepointNext(&text[i], &codepointByteCount); if (codepoint == '\n') { textOffsetY += (fontSize + textLineSpacing); textOffsetX = 0.0f; } else { if (codepoint == '[') // Process pipe styling { if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling { colFront = color; colBack = BLANK; i += 3; // Skip "[r]" continue; // Do not draw characters } else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b'))) { i += 2; // Skip "[c" or "[b" to start parsing color // Parse following color char colHexText[9] = { 0 }; char *textPtr = &text[i]; // Color should start here, let's see... int colHexCount = 0; while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']')) { if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) || ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) || ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f'))) { colHexText[colHexCount] = textPtr[colHexCount]; colHexCount++; } else break; // Only affects while loop } // Convert hex color text into actual Color unsigned int colHexValue = strtoul(colHexText, NULL, 16); if (text[i - 1] == 'c') colFront = GetColor(colHexValue); else if (text[i - 1] == 'b') colBack = GetColor(colHexValue); i += (colHexCount + 1); // Skip color value retrieved and ']' continue; // Do not draw characters } } int index = GetGlyphIndex(font, codepoint); float increaseX = 0.0f; if (font.glyphs[index].advanceX == 0) increaseX = ((float)font.recs[index].width*scaleFactor + spacing); else increaseX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing); // Draw background rectangle color (if required) if (colBack.a > 0) DrawRectangle(position.x + textOffsetX, position.y + textOffsetY - backRecPadding, increaseX, fontSize + 2*backRecPadding, colBack); if ((codepoint != ' ') && (codepoint != '\t')) { DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, colFront); } textOffsetX += increaseX; } i += codepointByteCount; } } // Measure inline styled text // NOTE: Measuring styled text requires skipping styling data // WARNING: Not considering line breaks static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing) { Vector2 textSize = { 0 }; if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check int textLen = TextLength(text); // Get size in bytes of text float textLineSpacing = fontSize*1.5f; float textWidth = 0.0f; float textHeight = fontSize; float scaleFactor = fontSize/(float)font.baseSize; int codepoint = 0; // Current character int index = 0; // Index position in sprite font int validCodepointCounter = 0; for (int i = 0; i < textLen;) { int codepointByteCount = 0; codepoint = GetCodepointNext(&text[i], &codepointByteCount); if (codepoint == '[') // Ignore pipe inline styling { if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling { i += 3; // Skip "[r]" continue; // Do not measure characters } else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b'))) { i += 2; // Skip "[c" or "[b" to start parsing color char *textPtr = &text[i]; // Color should start here, let's see... int colHexCount = 0; while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']')) { if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) || ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) || ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f'))) { colHexCount++; } else break; // Only affects while loop } i += (colHexCount + 1); // Skip color value retrieved and ']' continue; // Do not measure characters } } else if (codepoint != '\n') { index = GetGlyphIndex(font, codepoint); if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX; else textWidth += (font.recs[index].width + font.glyphs[index].offsetX); validCodepointCounter++; i += codepointByteCount; } } textSize.x = textWidth*scaleFactor + (validCodepointCounter - 1)*spacing; textSize.y = textHeight; return textSize; }