2018-12-29 16:44:28 +03:00
/*******************************************************************************************
*
2022-07-20 02:28:37 +03:00
* raylib [ text ] example - Rectangle bounds
2018-12-29 16:44:28 +03:00
*
2022-07-20 02:28:37 +03:00
* Example originally created with raylib 2.5 , last time updated with raylib 4.0
2018-12-29 16:44:28 +03:00
*
2019-05-14 16:34:23 +03:00
* Example contributed by Vlad Adrian ( @ demizdor ) and reviewed by Ramon Santamaria ( @ raysan5 )
*
2022-07-20 02:28:37 +03:00
* Example licensed under an unmodified zlib / libpng license , which is an OSI - certified ,
* BSD - like license that allows static linking with closed source software
*
2024-01-02 22:58:12 +03:00
* Copyright ( c ) 2018 - 2024 Vlad Adrian ( @ demizdor ) and Ramon Santamaria ( @ raysan5 )
2018-12-29 16:44:28 +03:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "raylib.h"
2021-08-25 01:22:16 +03:00
static void DrawTextBoxed ( Font font , const char * text , Rectangle rec , float fontSize , float spacing , bool wordWrap , Color tint ) ; // Draw text using font inside rectangle limits
static void DrawTextBoxedSelectable ( Font font , const char * text , Rectangle rec , float fontSize , float spacing , bool wordWrap , Color tint , int selectStart , int selectLength , Color selectTint , Color selectBackTint ) ; // Draw text using font inside rectangle limits with support for text selection
2021-08-11 19:31:32 +03:00
2022-06-21 20:53:18 +03:00
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
2019-05-20 17:36:42 +03:00
int main ( void )
2018-12-29 16:44:28 +03:00
{
// Initialization
//--------------------------------------------------------------------------------------
2019-05-20 17:36:42 +03:00
const int screenWidth = 800 ;
const int screenHeight = 450 ;
2018-12-29 16:44:28 +03:00
InitWindow ( screenWidth , screenHeight , " raylib [text] example - draw text inside a rectangle " ) ;
2019-05-20 17:36:42 +03:00
2019-12-01 15:28:14 +03:00
const char text [ ] = " Text cannot escape \t this 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 . " ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
bool resizing = false ;
bool wordWrap = true ;
2019-05-20 17:36:42 +03:00
2021-04-25 19:50:26 +03:00
Rectangle container = { 25.0f , 25.0f , screenWidth - 50.0f , screenHeight - 250.0f } ;
2018-12-29 16:44:28 +03:00
Rectangle resizer = { container . x + container . width - 17 , container . y + container . height - 17 , 14 , 14 } ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
// Minimum width and heigh for the container rectangle
2021-03-23 09:51:52 +03:00
const float minWidth = 60 ;
const float minHeight = 60 ;
2021-04-25 19:50:26 +03:00
const float maxWidth = screenWidth - 50.0f ;
const float maxHeight = screenHeight - 160.0f ;
2019-05-20 17:36:42 +03:00
2019-05-27 01:18:15 +03:00
Vector2 lastMouse = { 0.0f , 0.0f } ; // Stores last mouse coordinates
Color borderColor = MAROON ; // Container border color
Font font = GetFontDefault ( ) ; // Get default system font
2019-05-20 17:36:42 +03:00
2019-05-27 01:18:15 +03:00
SetTargetFPS ( 60 ) ; // Set our game to run at 60 frames-per-second
2018-12-29 16:44:28 +03:00
//--------------------------------------------------------------------------------------
// Main game loop
2019-05-27 01:18:15 +03:00
while ( ! WindowShouldClose ( ) ) // Detect window close button or ESC key
2018-12-29 16:44:28 +03:00
{
// Update
//----------------------------------------------------------------------------------
if ( IsKeyPressed ( KEY_SPACE ) ) wordWrap = ! wordWrap ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
Vector2 mouse = GetMousePosition ( ) ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
// 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 ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
// Container resizing logic
2019-05-20 17:36:42 +03:00
if ( resizing )
2018-12-29 16:44:28 +03:00
{
2021-05-08 19:26:24 +03:00
if ( IsMouseButtonReleased ( MOUSE_BUTTON_LEFT ) ) resizing = false ;
2019-05-20 17:36:42 +03:00
2021-03-23 09:51:52 +03:00
float width = container . width + ( mouse . x - lastMouse . x ) ;
2018-12-29 16:44:28 +03:00
container . width = ( width > minWidth ) ? ( ( width < maxWidth ) ? width : maxWidth ) : minWidth ;
2019-05-20 17:36:42 +03:00
2021-03-23 09:51:52 +03:00
float height = container . height + ( mouse . y - lastMouse . y ) ;
2018-12-29 16:44:28 +03:00
container . height = ( height > minHeight ) ? ( ( height < maxHeight ) ? height : maxHeight ) : minHeight ;
2019-05-20 17:36:42 +03:00
}
else
2018-12-29 16:44:28 +03:00
{
// Check if we're resizing
2021-05-08 19:26:24 +03:00
if ( IsMouseButtonDown ( MOUSE_BUTTON_LEFT ) & & CheckCollisionPointRec ( mouse , resizer ) ) resizing = true ;
2018-12-29 16:44:28 +03:00
}
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
// Move resizer rectangle properly
resizer . x = container . x + container . width - 17 ;
resizer . y = container . y + container . height - 17 ;
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
lastMouse = mouse ; // Update mouse
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing ( ) ;
ClearBackground ( RAYWHITE ) ;
2021-08-25 01:22:16 +03:00
DrawRectangleLinesEx ( container , 3 , borderColor ) ; // Draw container border
2019-05-20 17:36:42 +03:00
2018-12-29 16:44:28 +03:00
// Draw text in container (add some padding)
2021-08-25 01:22:16 +03:00
DrawTextBoxed ( font , text , ( Rectangle ) { container . x + 4 , container . y + 4 , container . width - 4 , container . height - 4 } , 20.0f , 2.0f , wordWrap , GRAY ) ;
2018-12-29 16:44:28 +03:00
2021-08-25 01:22:16 +03:00
DrawRectangleRec ( resizer , borderColor ) ; // Draw the resize box
2018-12-29 16:44:28 +03:00
2019-12-01 15:28:14 +03:00
// Draw bottom info
DrawRectangle ( 0 , screenHeight - 54 , screenWidth , 54 , GRAY ) ;
2021-04-25 19:50:26 +03:00
DrawRectangleRec ( ( Rectangle ) { 382.0f , screenHeight - 34.0f , 12.0f , 12.0f } , MAROON ) ;
2021-04-22 19:55:24 +03:00
2018-12-29 16:44:28 +03:00
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 ) ;
2021-04-22 19:55:24 +03:00
2019-12-01 15:28:14 +03:00
DrawText ( " Press [SPACE] to toggle word wrap " , 218 , screenHeight - 86 , 20 , GRAY ) ;
2018-12-29 16:44:28 +03:00
DrawText ( " Click hold & drag the to resize the container " , 155 , screenHeight - 38 , 20 , RAYWHITE ) ;
2021-04-22 19:55:24 +03:00
2018-12-29 16:44:28 +03:00
EndDrawing ( ) ;
//----------------------------------------------------------------------------------
}
// De-Initialization
2019-05-20 17:36:42 +03:00
//--------------------------------------------------------------------------------------
2018-12-29 16:44:28 +03:00
CloseWindow ( ) ; // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0 ;
2021-08-11 19:31:32 +03:00
}
//--------------------------------------------------------------------------------------
// Module functions definition
//--------------------------------------------------------------------------------------
// Draw text using font inside rectangle limits
2021-08-25 01:22:16 +03:00
static void DrawTextBoxed ( Font font , const char * text , Rectangle rec , float fontSize , float spacing , bool wordWrap , Color tint )
2021-08-11 19:31:32 +03:00
{
2021-08-25 01:22:16 +03:00
DrawTextBoxedSelectable ( font , text , rec , fontSize , spacing , wordWrap , tint , 0 , 0 , WHITE , WHITE ) ;
2021-08-11 19:31:32 +03:00
}
// Draw text using font inside rectangle limits with support for text selection
2021-08-25 01:22:16 +03:00
static void DrawTextBoxedSelectable ( Font font , const char * text , Rectangle rec , float fontSize , float spacing , bool wordWrap , Color tint , int selectStart , int selectLength , Color selectTint , Color selectBackTint )
2021-08-11 19:31:32 +03:00
{
int length = TextLength ( text ) ; // Total length in bytes of the text, scanned by codepoints in loop
float textOffsetY = 0 ; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f ; // Offset X to next character to draw
float scaleFactor = fontSize / ( float ) font . baseSize ; // Character rectangle scaling factor
// Word/character wrapping mechanism variables
enum { MEASURE_STATE = 0 , DRAW_STATE = 1 } ;
int state = wordWrap ? MEASURE_STATE : DRAW_STATE ;
int startLine = - 1 ; // Index where to begin drawing (where a line begins)
int endLine = - 1 ; // Index where to stop drawing (where a line ends)
int lastk = - 1 ; // Holds last value of the character position
for ( int i = 0 , k = 0 ; i < length ; i + + , k + + )
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0 ;
int codepoint = GetCodepoint ( & text [ i ] , & codepointByteCount ) ;
int index = GetGlyphIndex ( font , codepoint ) ;
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if ( codepoint = = 0x3f ) codepointByteCount = 1 ;
i + = ( codepointByteCount - 1 ) ;
float glyphWidth = 0 ;
if ( codepoint ! = ' \n ' )
{
2021-09-02 00:11:31 +03:00
glyphWidth = ( font . glyphs [ index ] . advanceX = = 0 ) ? font . recs [ index ] . width * scaleFactor : font . glyphs [ index ] . advanceX * scaleFactor ;
2021-08-11 19:31:32 +03:00
if ( i + 1 < length ) glyphWidth = glyphWidth + spacing ;
}
// NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
// We store this info in startLine and endLine, then we change states, draw the text between those two variables
// and change states again and again recursively until the end of the text (or until we get outside of the container).
// When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
// and begin drawing on the next line before we can get outside the container.
if ( state = = MEASURE_STATE )
{
// TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
// Ref: http://jkorpela.fi/chars/spaces.html
if ( ( codepoint = = ' ' ) | | ( codepoint = = ' \t ' ) | | ( codepoint = = ' \n ' ) ) endLine = i ;
if ( ( textOffsetX + glyphWidth ) > rec . width )
{
endLine = ( endLine < 1 ) ? i : endLine ;
if ( i = = endLine ) endLine - = codepointByteCount ;
if ( ( startLine + codepointByteCount ) = = endLine ) endLine = ( i - codepointByteCount ) ;
state = ! state ;
}
else if ( ( i + 1 ) = = length )
{
endLine = i ;
state = ! state ;
}
else if ( codepoint = = ' \n ' ) state = ! state ;
if ( state = = DRAW_STATE )
{
textOffsetX = 0 ;
i = startLine ;
glyphWidth = 0 ;
// Save character position when we switch states
int tmp = lastk ;
lastk = k - 1 ;
k = tmp ;
}
}
else
{
if ( codepoint = = ' \n ' )
{
if ( ! wordWrap )
{
textOffsetY + = ( font . baseSize + font . baseSize / 2 ) * scaleFactor ;
textOffsetX = 0 ;
}
}
else
{
if ( ! wordWrap & & ( ( textOffsetX + glyphWidth ) > rec . width ) )
{
textOffsetY + = ( font . baseSize + font . baseSize / 2 ) * scaleFactor ;
textOffsetX = 0 ;
}
// When text overflows rectangle height limit, just stop drawing
if ( ( textOffsetY + font . baseSize * scaleFactor ) > rec . height ) break ;
// Draw selection background
bool isGlyphSelected = false ;
if ( ( selectStart > = 0 ) & & ( k > = selectStart ) & & ( k < ( selectStart + selectLength ) ) )
{
DrawRectangleRec ( ( Rectangle ) { rec . x + textOffsetX - 1 , rec . y + textOffsetY , glyphWidth , ( float ) font . baseSize * scaleFactor } , selectBackTint ) ;
isGlyphSelected = true ;
}
// Draw current character glyph
if ( ( codepoint ! = ' ' ) & & ( codepoint ! = ' \t ' ) )
{
DrawTextCodepoint ( font , codepoint , ( Vector2 ) { rec . x + textOffsetX , rec . y + textOffsetY } , fontSize , isGlyphSelected ? selectTint : tint ) ;
}
}
if ( wordWrap & & ( i = = endLine ) )
{
textOffsetY + = ( font . baseSize + font . baseSize / 2 ) * scaleFactor ;
textOffsetX = 0 ;
startLine = endLine ;
endLine = - 1 ;
glyphWidth = 0 ;
selectStart + = lastk - k ;
k = lastk ;
state = ! state ;
}
}
2022-10-11 13:14:40 +03:00
if ( ( textOffsetX ! = 0 ) | | ( codepoint ! = ' ' ) ) textOffsetX + = glyphWidth ; // avoid leading spaces
2021-08-11 19:31:32 +03:00
}
2022-10-11 13:14:40 +03:00
}