text_inline_styling.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*******************************************************************************************
  2. *
  3. * raylib [text] example - inline styling
  4. *
  5. * Example complexity rating: [★★★☆] 3/4
  6. *
  7. * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
  8. *
  9. * Example contributed by Wagner Barongello (@SultansOfCode) and reviewed by Ramon Santamaria (@raysan5)
  10. *
  11. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  12. * BSD-like license that allows static linking with closed source software
  13. *
  14. * Copyright (c) 2025 Wagner Barongello (@SultansOfCode) and Ramon Santamaria (@raysan5)
  15. *
  16. ********************************************************************************************/
  17. #include "raylib.h"
  18. #include <stdlib.h> // Required for: strtoul(), NULL
  19. //----------------------------------------------------------------------------------
  20. // Module Functions Declaration
  21. //----------------------------------------------------------------------------------
  22. static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color);
  23. static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing);
  24. //------------------------------------------------------------------------------------
  25. // Program main entry point
  26. //------------------------------------------------------------------------------------
  27. int main(void)
  28. {
  29. // Initialization
  30. //--------------------------------------------------------------------------------------
  31. const int screenWidth = 800;
  32. const int screenHeight = 450;
  33. InitWindow(screenWidth, screenHeight, "raylib [text] example - inline styling");
  34. Vector2 textSize = { 0 }; // Measure text box for provided font and text
  35. Color colRandom = RED; // Random color used on text
  36. int frameCounter = 0; // Used to generate a new random color every certain frames
  37. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  38. //--------------------------------------------------------------------------------------
  39. // Main game loop
  40. while (!WindowShouldClose()) // Detect window close button or ESC key
  41. {
  42. // Update
  43. //----------------------------------------------------------------------------------
  44. frameCounter++;
  45. if ((frameCounter%20) == 0)
  46. {
  47. colRandom.r = (unsigned char)GetRandomValue(0, 255);
  48. colRandom.g = (unsigned char)GetRandomValue(0, 255);
  49. colRandom.b = (unsigned char)GetRandomValue(0, 255);
  50. colRandom.a = 255;
  51. }
  52. //----------------------------------------------------------------------------------
  53. // Draw
  54. //----------------------------------------------------------------------------------
  55. BeginDrawing();
  56. ClearBackground(RAYWHITE);
  57. // Text inline styling strategy used: [ ] delimiters for format
  58. // - Define foreground color: [cRRGGBBAA]
  59. // - Define background color: [bRRGGBBAA]
  60. // - Reset formating: [r]
  61. // Colors defined with [cRRGGBBAA] or [bRRGGBBAA] are multiplied by the base color alpha
  62. // This allows global transparency control while keeping per-section styling (ex. text fade effects)
  63. // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
  64. DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!",
  65. (Vector2){ 100, 80 }, 20.0f, 2.0f, BLACK);
  66. DrawTextStyled(GetFontDefault(), "This changes the [bFF00FFFF]background color[r] of provided text!!!",
  67. (Vector2){ 100, 120 }, 20.0f, 2.0f, BLACK);
  68. DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff][bff0000ff]foreground and background colors[r]!!!",
  69. (Vector2){ 100, 160 }, 20.0f, 2.0f, BLACK);
  70. DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff]alpha[r] relative [cffffffff][b000000ff]from source[r] [cff000088]color[r]!!!",
  71. (Vector2){ 100, 200 }, 20.0f, 2.0f, (Color){ 0, 0, 0, 100 });
  72. // Get pointer to formated text
  73. const char *text = TextFormat("Let's be [c%02x%02x%02xFF]CREATIVE[r] !!!", colRandom.r, colRandom.g, colRandom.b);
  74. DrawTextStyled(GetFontDefault(), text, (Vector2){ 100, 240 }, 40.0f, 2.0f, BLACK);
  75. textSize = MeasureTextStyled(GetFontDefault(), text, 40.0f, 2.0f);
  76. DrawRectangleLines(100, 240, (int)textSize.x, (int)textSize.y, GREEN);
  77. EndDrawing();
  78. //----------------------------------------------------------------------------------
  79. }
  80. // De-Initialization
  81. //--------------------------------------------------------------------------------------
  82. CloseWindow(); // Close window and OpenGL context
  83. //--------------------------------------------------------------------------------------
  84. return 0;
  85. }
  86. //----------------------------------------------------------------------------------
  87. // Module Functions Definition
  88. //----------------------------------------------------------------------------------
  89. // Draw text using inline styling
  90. // PARAM: color is the default text color, background color is BLANK by default
  91. // NOTE: Using input color as the base alpha multiplied to inline styles
  92. static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color)
  93. {
  94. // Text inline styling strategy used: [ ] delimiters for format
  95. // - Define foreground color: [cRRGGBBAA]
  96. // - Define background color: [bRRGGBBAA]
  97. // - Reset formating: [r]
  98. // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
  99. if (font.texture.id == 0) font = GetFontDefault();
  100. int textLen = TextLength(text);
  101. Color colFront = color;
  102. Color colBack = BLANK;
  103. int backRecPadding = 4; // Background rectangle padding
  104. float textOffsetY = 0.0f;
  105. float textOffsetX = 0.0f;
  106. float textLineSpacing = 0.0f;
  107. float scaleFactor = fontSize/font.baseSize;
  108. for (int i = 0; i < textLen;)
  109. {
  110. int codepointByteCount = 0;
  111. int codepoint = GetCodepointNext(&text[i], &codepointByteCount);
  112. if (codepoint == '\n')
  113. {
  114. textOffsetY += (fontSize + textLineSpacing);
  115. textOffsetX = 0.0f;
  116. }
  117. else
  118. {
  119. if (codepoint == '[') // Process pipe styling
  120. {
  121. if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling
  122. {
  123. colFront = color;
  124. colBack = BLANK;
  125. i += 3; // Skip "[r]"
  126. continue; // Do not draw characters
  127. }
  128. else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b')))
  129. {
  130. i += 2; // Skip "[c" or "[b" to start parsing color
  131. // Parse following color
  132. char colHexText[9] = { 0 };
  133. const char *textPtr = &text[i]; // Color should start here, let's see...
  134. int colHexCount = 0;
  135. while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']'))
  136. {
  137. if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) ||
  138. ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) ||
  139. ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f')))
  140. {
  141. colHexText[colHexCount] = textPtr[colHexCount];
  142. colHexCount++;
  143. }
  144. else break; // Only affects while loop
  145. }
  146. // Convert hex color text into actual Color
  147. unsigned int colHexValue = strtoul(colHexText, NULL, 16);
  148. if (text[i - 1] == 'c')
  149. {
  150. colFront = GetColor(colHexValue);
  151. colFront.a *= (float)color.a/255.0f;
  152. }
  153. else if (text[i - 1] == 'b')
  154. {
  155. colBack = GetColor(colHexValue);
  156. colBack.a *= (float)color.a/255.0f;
  157. }
  158. i += (colHexCount + 1); // Skip color value retrieved and ']'
  159. continue; // Do not draw characters
  160. }
  161. }
  162. int index = GetGlyphIndex(font, codepoint);
  163. float increaseX = 0.0f;
  164. if (font.glyphs[index].advanceX == 0) increaseX = ((float)font.recs[index].width*scaleFactor + spacing);
  165. else increaseX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing);
  166. // Draw background rectangle color (if required)
  167. if (colBack.a > 0) DrawRectangleRec((Rectangle) { position.x + textOffsetX, position.y + textOffsetY - backRecPadding, increaseX, fontSize + 2*backRecPadding }, colBack);
  168. if ((codepoint != ' ') && (codepoint != '\t'))
  169. {
  170. DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, colFront);
  171. }
  172. textOffsetX += increaseX;
  173. }
  174. i += codepointByteCount;
  175. }
  176. }
  177. // Measure inline styled text
  178. // NOTE: Measuring styled text requires skipping styling data
  179. // WARNING: Not considering line breaks
  180. static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing)
  181. {
  182. Vector2 textSize = { 0 };
  183. if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check
  184. int textLen = TextLength(text); // Get size in bytes of text
  185. //float textLineSpacing = fontSize*1.5f; // Not used...
  186. float textWidth = 0.0f;
  187. float textHeight = fontSize;
  188. float scaleFactor = fontSize/(float)font.baseSize;
  189. int codepoint = 0; // Current character
  190. int index = 0; // Index position in sprite font
  191. int validCodepointCounter = 0;
  192. for (int i = 0; i < textLen;)
  193. {
  194. int codepointByteCount = 0;
  195. codepoint = GetCodepointNext(&text[i], &codepointByteCount);
  196. if (codepoint == '[') // Ignore pipe inline styling
  197. {
  198. if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling
  199. {
  200. i += 3; // Skip "[r]"
  201. continue; // Do not measure characters
  202. }
  203. else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b')))
  204. {
  205. i += 2; // Skip "[c" or "[b" to start parsing color
  206. const char *textPtr = &text[i]; // Color should start here, let's see...
  207. int colHexCount = 0;
  208. while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']'))
  209. {
  210. if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) ||
  211. ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) ||
  212. ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f')))
  213. {
  214. colHexCount++;
  215. }
  216. else break; // Only affects while loop
  217. }
  218. i += (colHexCount + 1); // Skip color value retrieved and ']'
  219. continue; // Do not measure characters
  220. }
  221. }
  222. else if (codepoint != '\n')
  223. {
  224. index = GetGlyphIndex(font, codepoint);
  225. if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX;
  226. else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
  227. validCodepointCounter++;
  228. i += codepointByteCount;
  229. }
  230. }
  231. textSize.x = textWidth*scaleFactor + (validCodepointCounter - 1)*spacing;
  232. textSize.y = textHeight;
  233. return textSize;
  234. }