| /******************************************************************************************* | |
| * | |
| *   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 <stdlib.h>     // 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; | |
| }
 |