From 0619571149f1fde5500dec4b64a94541ef0981f2 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 29 Dec 2018 14:44:28 +0100 Subject: [PATCH] ADDED: DrawTextRec() and example --- examples/text/text_draw_inside_rectangle.c | 120 ++++++++++++++++++ examples/text/text_draw_inside_rectangle.png | Bin 0 -> 17844 bytes src/raylib.h | 3 +- src/text.c | 125 +++++++++++++++++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 examples/text/text_draw_inside_rectangle.c create mode 100644 examples/text/text_draw_inside_rectangle.png diff --git a/examples/text/text_draw_inside_rectangle.c b/examples/text/text_draw_inside_rectangle.c new file mode 100644 index 000000000..e60fa5e56 --- /dev/null +++ b/examples/text/text_draw_inside_rectangle.c @@ -0,0 +1,120 @@ +/******************************************************************************************* +* +* raylib [text] example - Draw text inside a rectangle +* +* This example has been created using raylib 2.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2018 Vlad Adrian (@demizdor) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle"); + + char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's\ + a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget."; + + bool resizing = false; + bool wordWrap = true; + + Rectangle container = { 25, 25, screenWidth - 50, screenHeight - 250}; + Rectangle resizer = { container.x + container.width - 17, container.y + container.height - 17, 14, 14 }; + + // Minimum width and heigh for the container rectangle + const int minWidth = 60; + const int minHeight = 60; + const int maxWidth = screenWidth - 50; + const int maxHeight = screenHeight - 160; + + Vector2 lastMouse = { 0, 0 }; // Stores last mouse coordinates + + Color borderColor = MAROON; // Container border color + + Font font = GetFontDefault(); // Get default system font + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) wordWrap = !wordWrap; + + Vector2 mouse = GetMousePosition(); + + // Check if the mouse is inside the container and toggle border color + if (CheckCollisionPointRec(mouse, container)) borderColor = Fade(MAROON, 0.4f); + else if (!resizing) borderColor = MAROON; + + // Container resizing logic + if (resizing) + { + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) resizing = false; + + int width = container.width + (mouse.x - lastMouse.x); + container.width = (width > minWidth)? ((width < maxWidth)? width : maxWidth) : minWidth; + + int height = container.height + (mouse.y - lastMouse.y); + container.height = (height > minHeight)? ((height < maxHeight)? height : maxHeight) : minHeight; + } + else + { + // Check if we're resizing + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && CheckCollisionPointRec(mouse, resizer)) resizing = true; + } + + // Move resizer rectangle properly + resizer.x = container.x + container.width - 17; + resizer.y = container.y + container.height - 17; + + lastMouse = mouse; // Update mouse + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawRectangleLinesEx(container, 3, borderColor); // Draw container border + + // Draw text in container (add some padding) + DrawTextRec(font, text, + (Rectangle){ container.x + 4, container.y + 4, container.width - 4, container.height - 4 }, + 20.0f, 2.0f, wordWrap, GRAY); + + DrawRectangleRec(resizer, borderColor); // Draw the resize box + + // Draw info + DrawText("Word Wrap: ", 313, screenHeight-115, 20, BLACK); + if (wordWrap) DrawText("ON", 447, screenHeight - 115, 20, RED); + else DrawText("OFF", 447, screenHeight - 115, 20, BLACK); + DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 91, 20, GRAY); + + DrawRectangle(0, screenHeight - 54, screenWidth, 54, GRAY); + DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, RAYWHITE); + DrawRectangleRec((Rectangle){ 382, screenHeight - 34, 12, 12 }, MAROON); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/text/text_draw_inside_rectangle.png b/examples/text/text_draw_inside_rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..f46b10967d74f6f4e4b194ba9aab1b65c4822c49 GIT binary patch literal 17844 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 c0RjcvZ5LN^_iP6KHx`inE=Rjk8| [0xc2 0xbf](¿) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter); + i++; + } + else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! + { + // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter + 64); + i++; + } + else index = GetGlyphIndex(font, (unsigned char)text[i]); + + glyphWidth = (font.chars[index].advanceX == 0)? + (int)(font.chars[index].rec.width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + } + + // NOTE: When word wrap is active first we measure a `word`(measure until a ' ','\n','\t' is found) + // then set all the variables back to what they were before the measurement, change the state to + // draw that word then change the state again and repeat until the end of the string...when the word + // doesn't fit inside the rect we simple increase `textOffsetY` to draw it on the next line + if (state == MEASURE_WORD) + { + // Measuring state + if ((letter == ' ') || (letter == '\n') || (letter == '\t') || ((i + 1) == length)) + { + int t = textOffsetX + glyphWidth; + + if (textOffsetX+1>=rec.width) + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + lastTextOffsetX = t - lastTextOffsetX; + textOffsetX = 0; + } + else + { + textOffsetX = lastTextOffsetX; + lastTextOffsetX = t; + } + + glyphWidth = 0; + state = !state; // Change state + t = i; + i = firstWord?-1:wordStart; + wordStart = t; + } + } + else + { + // Drawing state + int t = textOffsetX + glyphWidth; + + if (letter == '\n') + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + lastTextOffsetX = t - lastTextOffsetX; + if (lastTextOffsetX < 0) lastTextOffsetX = 0; + textOffsetX = 0; + } + else if ((letter != ' ') && (letter != '\t')) + { + if ((t + 1) >= rec.width) + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + textOffsetX = 0; + } + + if ((textOffsetY + (int)((font.baseSize + font.baseSize/2)*scaleFactor)) > rec.height) break; + + DrawTexturePro(font.texture, font.chars[index].rec, + (Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor, + rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor, + font.chars[index].rec.width*scaleFactor, + font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); + } + + if (wordWrap) + { + if ((letter == ' ') || (letter == '\n') || (letter == '\t')) + { + // After drawing a word change the state + firstWord = false; + i = wordStart; + textOffsetX = lastTextOffsetX; + glyphWidth = 0; + state = !state; + } + } + } + + textOffsetX += glyphWidth; + } +} + // Measure string width for default font int MeasureText(const char *text, int fontSize) {