DrawTextBoxedEx.

This commit is contained in:
jussi
2024-06-15 16:31:40 +03:00
parent e9539e9373
commit d3e1842203
7 changed files with 161 additions and 192 deletions

14
API.md
View File

@@ -6858,19 +6858,19 @@ Draw multiple character (codepoint)
--- ---
> mouseCharId = RL.DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint ) > RL.DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint )
Draw text using font inside rectangle limits. Return character id from mouse position (default -1). Function from raylib [text] example - Rectangle bounds. Draw text using font inside rectangle limits.
- Success return int
--- ---
> mouseCharId = RL.DrawTextBoxedTinted( Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tints{}, Color backTints{} ) > mouseCharId, textOffset = RL.DrawTextBoxedEx( Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, Vector2 textOffset )
Draw text using font inside rectangle limits with support for tint and background tint for each character. Return character id from mouse position (default -1) Draw text using font inside rectangle limits. Return character id from mouse position (default 0).
textOffset can be used to set start position inside rectangle. Usefull to pass from previous
DrawTextBoxedEx for continuous text.
- Success return int - Success return int, Vector2
--- ---

View File

@@ -4044,8 +4044,7 @@ function RL.DrawTextCodepoint( font, codepoint, position, fontSize, tint ) end
---@return any RL.DrawTextCodepoints ---@return any RL.DrawTextCodepoints
function RL.DrawTextCodepoints( font, codepoints, position, fontSize, spacing, tint ) end function RL.DrawTextCodepoints( font, codepoints, position, fontSize, spacing, tint ) end
---Draw text using font inside rectangle limits. Return character id from mouse position (default -1). Function from raylib [text] example - Rectangle bounds. ---Draw text using font inside rectangle limits.
---- Success return int
---@param font any ---@param font any
---@param text string ---@param text string
---@param rec table ---@param rec table
@@ -4053,21 +4052,24 @@ function RL.DrawTextCodepoints( font, codepoints, position, fontSize, spacing,
---@param spacing number ---@param spacing number
---@param wordWrap boolean ---@param wordWrap boolean
---@param tint table ---@param tint table
---@return any mouseCharId ---@return any RL.DrawTextBoxed
function RL.DrawTextBoxed( font, text, rec, fontSize, spacing, wordWrap, tint ) end function RL.DrawTextBoxed( font, text, rec, fontSize, spacing, wordWrap, tint ) end
---Draw text using font inside rectangle limits with support for tint and background tint for each character. Return character id from mouse position (default -1) ---Draw text using font inside rectangle limits. Return character id from mouse position (default 0).
---- Success return int ---textOffset can be used to set start position inside rectangle. Usefull to pass from previous
---DrawTextBoxedEx for continuous text.
---- Success return int, Vector2
---@param font any ---@param font any
---@param text string ---@param text string
---@param rec table ---@param rec table
---@param fontSize number ---@param fontSize number
---@param spacing number ---@param spacing number
---@param wordWrap boolean ---@param wordWrap boolean
---@param tints{} table ---@param tint table
---@param backTints{} table ---@param textOffset table
---@return any mouseCharId ---@return any mouseCharId
function RL.DrawTextBoxedTinted( font, text, rec, fontSize, spacing, wordWrap, tints{}, backTints{} ) end ---@return any textOffset
function RL.DrawTextBoxedEx( font, text, rec, fontSize, spacing, wordWrap, tint, textOffset ) end
-- Text - Text font info functions -- Text - Text font info functions

View File

@@ -55,6 +55,8 @@ DETAILED CHANGES:
- ADDED: Unload functions clear object to 0 so they would not be ready in Is*Ready. - ADDED: Unload functions clear object to 0 so they would not be ready in Is*Ready.
- CHANGE: DrawTextBoxed uses lineSpacing. - CHANGE: DrawTextBoxed uses lineSpacing.
- ADDED: GetTextLineSpacing. - ADDED: GetTextLineSpacing.
- REMOVED: DrawTextBoxedTinted. DrawTextBoxedEx can do same with much more efficiently.
- ADDED: DrawTextBoxedEx.
------------------------------------------------------------------------ ------------------------------------------------------------------------
Release: ReiLua version 0.7.0 Using Raylib 5.0 and Forked Raygui 4.0 Release: ReiLua version 0.7.0 Using Raylib 5.0 and Forked Raygui 4.0

View File

@@ -0,0 +1,60 @@
local textSize = 10
local spacing = 1
local rect = { 100, 64, 200, 200 }
local wordwrap = true
local linkColor = RL.BLUE
local mouseCharId = 0
local textOffset = { 0, 0 }
function RL.init()
RL.SetWindowTitle( "Text boxed" )
RL.SetWindowState( RL.FLAG_VSYNC_HINT )
end
function RL.update( delta )
if RL.IsMouseButtonDown( RL.MOUSE_BUTTON_RIGHT ) then
local mousePos = RL.GetMousePosition()
rect[3] = mousePos[1] - rect[1]
rect[4] = mousePos[2] - rect[2]
end
if RL.IsMouseButtonPressed( RL.MOUSE_BUTTON_LEFT ) and 0 < mouseCharId then
print( "Pressed link on char "..mouseCharId )
end
if RL.IsKeyPressed( RL.KEY_SPACE ) then
wordwrap = not wordwrap
end
end
function RL.draw()
RL.ClearBackground( RL.RAYWHITE )
RL.DrawRectangleLines( rect, RL.GREEN )
mouseCharId, textOffset = RL.DrawTextBoxedEx(
RL.GetFontDefault(),
"You can change the size of the box by pressing right mouse and toggle the wordwrap by pressing space. First we will write some text before the hyperlink to show that it is indeed is as powerful feature as adverticed.",
rect,
textSize, spacing, wordwrap, RL.RED, { 0, 0 }
)
mouseCharId, textOffset = RL.DrawTextBoxedEx(
RL.GetFontDefault(),
" Hyperlink.",
rect,
textSize, spacing, wordwrap, linkColor, textOffset
)
RL.DrawTextBoxedEx(
RL.GetFontDefault(),
" Then we demonstrate this further by writin more text after the link. Isn't this just amazing! Don't forget to press left mouse to print text to your console when hovering mouse over the hyperlink.",
rect,
textSize, spacing, wordwrap, RL.RED, textOffset
)
if 0 < mouseCharId then
linkColor = RL.GREEN
else
linkColor = RL.BLUE
end
end

View File

@@ -21,7 +21,7 @@ int ltextDrawTextPro( lua_State* L );
int ltextDrawTextCodepoint( lua_State* L ); int ltextDrawTextCodepoint( lua_State* L );
int ltextDrawTextCodepoints( lua_State* L ); int ltextDrawTextCodepoints( lua_State* L );
int ltextDrawTextBoxed( lua_State* L ); int ltextDrawTextBoxed( lua_State* L );
int ltextDrawTextBoxedTinted( lua_State* L ); int ltextDrawTextBoxedEx( lua_State* L );
/* Text font info functions. */ /* Text font info functions. */
int ltextSetTextLineSpacing( lua_State* L ); int ltextSetTextLineSpacing( lua_State* L );
int ltextGetTextLineSpacing( lua_State* L ); int ltextGetTextLineSpacing( lua_State* L );

View File

@@ -1838,7 +1838,7 @@ void luaRegister() {
assingGlobalFunction( "DrawTextCodepoint", ltextDrawTextCodepoint ); assingGlobalFunction( "DrawTextCodepoint", ltextDrawTextCodepoint );
assingGlobalFunction( "DrawTextCodepoints", ltextDrawTextCodepoints ); assingGlobalFunction( "DrawTextCodepoints", ltextDrawTextCodepoints );
assingGlobalFunction( "DrawTextBoxed", ltextDrawTextBoxed ); assingGlobalFunction( "DrawTextBoxed", ltextDrawTextBoxed );
assingGlobalFunction( "DrawTextBoxedTinted", ltextDrawTextBoxedTinted ); assingGlobalFunction( "DrawTextBoxedEx", ltextDrawTextBoxedEx );
/* Text font info functions. */ /* Text font info functions. */
assingGlobalFunction( "SetTextLineSpacing", ltextSetTextLineSpacing ); assingGlobalFunction( "SetTextLineSpacing", ltextSetTextLineSpacing );
assingGlobalFunction( "GetTextLineSpacing", ltextGetTextLineSpacing ); assingGlobalFunction( "GetTextLineSpacing", ltextGetTextLineSpacing );

View File

@@ -8,158 +8,82 @@ void unloadGlyphInfo( GlyphInfo* glyph ) {
UnloadImage( glyph->image ); UnloadImage( glyph->image );
} }
// DrawTextBoxed is modified DrawTextBoxedSelectable from raylib [text] example - Rectangle bounds static float measureWord( Font font, char* text, float fontSize, float spacing ) {
if ( text == NULL ) {
return 0.0;
}
int i = 0;
/* Find white scape and set it to null. */
for ( ; text[i] != '\0' && text[i] != ' ' && text[i] != '\n' && text[i] != '\t'; i++ ) {}
/* Replace whitespace with NULL for measuring. */
char letter = text[i];
text[i] = '\0';
Vector2 size = MeasureTextEx( font, text, fontSize, spacing );
text[i] = letter;
// Draw text using font inside rectangle limits return size.x;
static int DrawTextBoxed( Font font, const char* text, Rectangle rec, float fontSize, float spacing, }
bool wordWrap, Color* tints, int tintCount, Color* backTints, int backTintCount ) {
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') static int DrawTextBoxed( Font font, char* text, Rectangle rec, float fontSize,
float textOffsetX = 0.0f; // Offset X to next character to draw float spacing, bool wordWrap, Color tint, Vector2* textOffset, bool getMouseChar ) {
float scaleFactor = fontSize/(float)font.baseSize; // Character rectangle scaling factor
int lineSpacing = state->lineSpacing; int lineSpacing = state->lineSpacing;
// Word/character wrapping mechanism variables if ( rec.width <= 0 || rec.height <= ( textOffset->y + lineSpacing ) ) {
enum { MEASURE_STATE = 0, DRAW_STATE = 1 }; return 0;
int state = wordWrap ? MEASURE_STATE : DRAW_STATE; }
int startLine = -1; // Index where to begin drawing (where a line begins) int len = TextLength( text );
int endLine = -1; // Index where to stop drawing (where a line ends) float scaleFactor = fontSize / font.baseSize;
int lastk = -1; // Holds last value of the character position float wordWidth = 0.0;
Color tint = BLACK; bool measure = true;
Color backTint = BLANK;
Vector2 mousePos = GetMousePosition(); Vector2 mousePos = GetMousePosition();
int mouseChar = -1; int mouseChar = 0;
for ( int i = 0, k = 0; i < length; i++, k++) for ( int i = 0; i < len; ) {
{
if ( i < tintCount ) {
tint = tints[i];
}
if ( i < backTintCount ) {
backTint = backTints[i];
}
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0; int codepointByteCount = 0;
int codepoint = GetCodepoint(&text[i], &codepointByteCount); int codepoint = GetCodepointNext( &text[i], &codepointByteCount );
int index = GetGlyphIndex(font, codepoint); int index = GetGlyphIndex( font, codepoint );
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) float codepointWidth = font.glyphs[ index ].advanceX == 0
// but we need to draw all of the bad bytes using the '?' symbol moving one byte ? (float)font.recs[ index ].width * scaleFactor + spacing
if (codepoint == 0x3f) codepointByteCount = 1; : (float)font.glyphs[ index ].advanceX * scaleFactor + spacing;
i += (codepointByteCount - 1);
float glyphWidth = 0; if ( wordWrap && ( wordWidth < rec.width ) ) {
if (codepoint != '\n') if ( measure && codepoint != ' ' ) {
{ wordWidth = measureWord( font, &text[i], fontSize, spacing );
glyphWidth = (font.glyphs[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.glyphs[index].advanceX*scaleFactor; measure = false;
if (i + 1 < length) glyphWidth = glyphWidth + spacing; if ( rec.width < ( textOffset->x + wordWidth ) ) {
} textOffset->x = 0;
textOffset->y += lineSpacing;
// 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) else if ( codepoint == ' ' || codepoint == '\n' || codepoint == '\t' ) {
{ measure = true;
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 else {
{ if ( rec.width < ( textOffset->x + codepointWidth ) ) {
if (codepoint == '\n') textOffset->x = 0;
{ textOffset->y += lineSpacing;
if (!wordWrap)
{
// textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetY += lineSpacing;
textOffsetX = 0;
}
}
else
{
if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width))
{
// textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetY += lineSpacing;
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;
// }
if ( CheckCollisionPointRec( mousePos, (Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor } ) ) {
mouseChar = i;
}
if ( 0 < backTint.a ) {
DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, backTint);
}
// Draw current character glyph
if ((codepoint != ' ') && (codepoint != '\t'))
{
// DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
DrawTextCodepoint( font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, tint );
}
}
if (wordWrap && (i == endLine))
{
// textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetY += lineSpacing;
textOffsetX = 0;
startLine = endLine;
endLine = -1;
glyphWidth = 0;
// selectStart += lastk - k;
k = lastk;
state = !state;
} }
} }
if ((textOffsetX != 0) || (codepoint != ' ')) textOffsetX += glyphWidth; // avoid leading spaces if ( rec.height < ( textOffset->y + lineSpacing ) ) {
break;
}
if ( codepoint != '\n' && !( textOffset->x == 0 && codepoint == ' ' ) && codepointWidth < rec.width ) {
DrawTextCodepoint( font, codepoint, (Vector2){ rec.x + textOffset->x, rec.y + textOffset->y }, fontSize, tint );
if ( getMouseChar && CheckCollisionPointRec( mousePos, (Rectangle){ rec.x + textOffset->x - 1, rec.y + textOffset->y, codepointWidth, (float)font.baseSize * scaleFactor } ) ) {
mouseChar = i + 1;
}
textOffset->x += codepointWidth;
}
i += codepointByteCount;
} }
return mouseChar; return mouseChar;
} }
@@ -588,67 +512,48 @@ int ltextDrawTextCodepoints( lua_State* L ) {
} }
/* /*
> mouseCharId = RL.DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint ) > RL.DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint )
Draw text using font inside rectangle limits. Return character id from mouse position (default -1). Function from raylib [text] example - Rectangle bounds. Draw text using font inside rectangle limits.
- Success return int
*/ */
int ltextDrawTextBoxed( lua_State* L ) { int ltextDrawTextBoxed( lua_State* L ) {
Font* font = uluaGetFont( L, 1 ); Font* font = uluaGetFont( L, 1 );
const char* text = luaL_checkstring( L, 2 ); char* text = (char*)luaL_checkstring( L, 2 );
Rectangle rec = uluaGetRectangle( L, 3 ); Rectangle rec = uluaGetRectangle( L, 3 );
float fontSize = luaL_checknumber( L, 4 ); float fontSize = luaL_checknumber( L, 4 );
float spacing = luaL_checknumber( L, 5 ); float spacing = luaL_checknumber( L, 5 );
bool wordWrap = uluaGetBoolean( L, 6 ); bool wordWrap = uluaGetBoolean( L, 6 );
Color tint = uluaGetColor( L, 7 ); Color tint = uluaGetColor( L, 7 );
Vector2 textOffset = { 0, 0 };
DrawTextBoxed( *font, text, rec, fontSize, spacing, wordWrap, tint, &textOffset, false );
lua_pushinteger( L, DrawTextBoxed( *font, text, rec, fontSize, spacing, wordWrap, &tint, 1, NULL, 0 ) ); return 0;
return 1;
} }
/* /*
> mouseCharId = RL.DrawTextBoxedTinted( Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tints{}, Color backTints{} ) > mouseCharId, textOffset = RL.DrawTextBoxedEx( Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, Vector2 textOffset )
Draw text using font inside rectangle limits with support for tint and background tint for each character. Return character id from mouse position (default -1) Draw text using font inside rectangle limits. Return character id from mouse position (default 0).
textOffset can be used to set start position inside rectangle. Usefull to pass from previous
DrawTextBoxedEx for continuous text.
- Success return int - Success return int, Vector2
*/ */
int ltextDrawTextBoxedTinted( lua_State* L ) { int ltextDrawTextBoxedEx( lua_State* L ) {
Font* font = uluaGetFont( L, 1 ); Font* font = uluaGetFont( L, 1 );
const char* text = luaL_checkstring( L, 2 ); char* text = (char*)luaL_checkstring( L, 2 );
Rectangle rec = uluaGetRectangle( L, 3 ); Rectangle rec = uluaGetRectangle( L, 3 );
float fontSize = luaL_checknumber( L, 4 ); float fontSize = luaL_checknumber( L, 4 );
float spacing = luaL_checknumber( L, 5 ); float spacing = luaL_checknumber( L, 5 );
bool wordWrap = uluaGetBoolean( L, 6 ); bool wordWrap = uluaGetBoolean( L, 6 );
int tintCount = uluaGetTableLen( L, 7 ); Color tint = uluaGetColor( L, 7 );
int backTintCount = uluaGetTableLen( L, 8 ); Vector2 textOffset = uluaGetVector2( L, 8 );
Color tints[ tintCount ]; lua_pushinteger( L, DrawTextBoxed( *font, text, rec, fontSize, spacing, wordWrap, tint, &textOffset, true ) );
Color backTints[ backTintCount ]; uluaPushVector2( L, textOffset );
/* Tints. */ return 2;
int t = 7, i = 0;
lua_pushnil( L );
while ( lua_next( L, t ) != 0 ) {
tints[i] = uluaGetColor( L, lua_gettop( L ) );
i++;
lua_pop( L, 1 );
}
/* Back tints. */
t = 8; i = 0;
lua_pushnil( L );
while ( lua_next( L, t ) != 0 ) {
backTints[i] = uluaGetColor( L, lua_gettop( L ) );
i++;
lua_pop( L, 1 );
}
lua_pushinteger( L, DrawTextBoxed( *font, text, rec, fontSize, spacing, wordWrap, tints, tintCount, backTints, backTintCount ) );
return 1;
} }
/* /*