ADDED: DrawTextCodepoint()
- Renamed GetGlyphIndex() parameter - Review DrawTextEx() implementation - Review DrawTextRecEx() implementation
This commit is contained in:
parent
416a52b5bc
commit
3ffe34f9bb
3 changed files with 94 additions and 75 deletions
|
@ -22,9 +22,9 @@ int main(void)
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle");
|
InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle");
|
||||||
|
|
||||||
const char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's\
|
const 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\
|
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.";
|
tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget.";
|
||||||
|
|
||||||
bool resizing = false;
|
bool resizing = false;
|
||||||
bool wordWrap = true;
|
bool wordWrap = true;
|
||||||
|
@ -97,15 +97,17 @@ int main(void)
|
||||||
|
|
||||||
DrawRectangleRec(resizer, borderColor); // Draw the resize box
|
DrawRectangleRec(resizer, borderColor); // Draw the resize box
|
||||||
|
|
||||||
// Draw info
|
// Draw bottom info
|
||||||
|
DrawRectangle(0, screenHeight - 54, screenWidth, 54, GRAY);
|
||||||
|
DrawRectangleRec((Rectangle){ 382, screenHeight - 34, 12, 12 }, MAROON);
|
||||||
|
|
||||||
DrawText("Word Wrap: ", 313, screenHeight-115, 20, BLACK);
|
DrawText("Word Wrap: ", 313, screenHeight-115, 20, BLACK);
|
||||||
if (wordWrap) DrawText("ON", 447, screenHeight - 115, 20, RED);
|
if (wordWrap) DrawText("ON", 447, screenHeight - 115, 20, RED);
|
||||||
else DrawText("OFF", 447, screenHeight - 115, 20, BLACK);
|
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("Press [SPACE] to toggle word wrap", 218, screenHeight - 86, 20, GRAY);
|
||||||
|
|
||||||
DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, RAYWHITE);
|
DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, RAYWHITE);
|
||||||
DrawRectangleRec((Rectangle){ 382, screenHeight - 34, 12, 12 }, MAROON);
|
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1185,12 +1185,13 @@ RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color co
|
||||||
RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
|
RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
|
||||||
RLAPI void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits
|
RLAPI void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits
|
||||||
RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint,
|
RLAPI void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint,
|
||||||
int selectStart, int selectLength, Color selectText, Color selectBack); // Draw text using font inside rectangle limits with support for text selection
|
int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection
|
||||||
|
RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float scale, Color tint); // Draw one character (codepoint)
|
||||||
|
|
||||||
// Text misc. functions
|
// Text misc. functions
|
||||||
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
|
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
|
||||||
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
|
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
|
||||||
RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font
|
RLAPI int GetGlyphIndex(Font font, int codepoint); // Get index position for a unicode character on font
|
||||||
|
|
||||||
// Text strings management functions (no utf8 strings, only byte chars)
|
// Text strings management functions (no utf8 strings, only byte chars)
|
||||||
// NOTE: Some strings allocate memory internally for returned strings, just be careful!
|
// NOTE: Some strings allocate memory internally for returned strings, just be careful!
|
||||||
|
|
136
src/text.c
136
src/text.c
|
@ -127,7 +127,7 @@ extern void LoadFontDefault(void)
|
||||||
#define BIT_CHECK(a,b) ((a) & (1u << (b)))
|
#define BIT_CHECK(a,b) ((a) & (1u << (b)))
|
||||||
|
|
||||||
// NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
|
// NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
|
||||||
// http://www.utf8-chartable.de/unicode-utf8-table.pl
|
// Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
|
||||||
|
|
||||||
defaultFont.charsCount = 224; // Number of chars included in our default font
|
defaultFont.charsCount = 224; // Number of chars included in our default font
|
||||||
|
|
||||||
|
@ -795,51 +795,66 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw one character (codepoint)
|
||||||
|
void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float scale, Color tint)
|
||||||
|
{
|
||||||
|
// Character index position in sprite font
|
||||||
|
// NOTE: In case a codepoint is not available in the font, index returned points to '?'
|
||||||
|
int index = GetGlyphIndex(font, codepoint);
|
||||||
|
|
||||||
|
// Character rectangle on screen
|
||||||
|
// NOTE: Quad is scaled proportionally to base character width-height
|
||||||
|
Rectangle rec = { position.x, position.y, font.recs[index].width*scale, font.recs[index].height*scale };
|
||||||
|
|
||||||
|
DrawTexturePro(font.texture, font.recs[index], rec, (Vector2){ 0, 0 }, 0.0f, tint);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw text using Font
|
// Draw text using Font
|
||||||
// NOTE: chars spacing is NOT proportional to fontSize
|
// NOTE: chars spacing is NOT proportional to fontSize
|
||||||
void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
|
void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
|
||||||
{
|
{
|
||||||
int length = strlen(text);
|
int length = strlen(text); // Total length in bytes of the text, scanned by codepoints in loop
|
||||||
int textOffsetY = 0; // Required for line break!
|
|
||||||
float textOffsetX = 0.0f; // Offset between characters
|
|
||||||
float scaleFactor = 0.0f;
|
|
||||||
|
|
||||||
int letter = 0; // Current character
|
int textOffsetY = 0; // Offset between lines (on line break '\n')
|
||||||
int index = 0; // Index position in sprite font
|
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||||
|
|
||||||
scaleFactor = fontSize/font.baseSize;
|
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
int next = 0;
|
// Get next codepoint from byte string and glyph index in font
|
||||||
letter = GetNextCodepoint(&text[i], &next);
|
int codepointByteCount = 0;
|
||||||
index = GetGlyphIndex(font, letter);
|
int codepoint = GetNextCodepoint(&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)
|
// 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 so to not skip any we set 'next = 1'
|
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
||||||
if (letter == 0x3f) next = 1;
|
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||||
i += (next - 1);
|
|
||||||
|
|
||||||
if (letter == '\n')
|
if (codepoint == '\n')
|
||||||
{
|
{
|
||||||
// NOTE: Fixed line spacing of 1.5 lines
|
// NOTE: Fixed line spacing of 1.5 line-height
|
||||||
|
// TODO: Support custom line spacing defined by user
|
||||||
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
|
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
|
||||||
textOffsetX = 0.0f;
|
textOffsetX = 0.0f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (letter != ' ')
|
if ((codepoint != ' ') && (codepoint != '\t'))
|
||||||
{
|
{
|
||||||
DrawTexturePro(font.texture, font.recs[index],
|
Rectangle rec = { position.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
|
||||||
(Rectangle){ position.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
|
|
||||||
position.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
position.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
||||||
font.recs[index].width*scaleFactor,
|
font.recs[index].width*scaleFactor,
|
||||||
font.recs[index].height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint);
|
font.recs[index].height*scaleFactor };
|
||||||
|
|
||||||
|
DrawTexturePro(font.texture, font.recs[index], rec, (Vector2){ 0, 0 }, 0.0f, tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
|
if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
|
||||||
else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
|
else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -850,37 +865,37 @@ void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, flo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw text using font inside rectangle limits with support for text selection
|
// Draw text using font inside rectangle limits with support for text selection
|
||||||
void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectText, Color selectBack)
|
void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
|
||||||
{
|
{
|
||||||
int length = strlen(text);
|
int length = strlen(text); // Total length in bytes of the text, scanned by codepoints in loop
|
||||||
int textOffsetX = 0; // Offset between characters
|
|
||||||
int textOffsetY = 0; // Required for line break!
|
|
||||||
float scaleFactor = 0.0f;
|
|
||||||
|
|
||||||
int letter = 0; // Current character
|
int textOffsetY = 0; // Offset between lines (on line break '\n')
|
||||||
int index = 0; // Index position in sprite font
|
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||||
|
|
||||||
scaleFactor = fontSize/font.baseSize;
|
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
|
||||||
|
|
||||||
|
// Word/character wrapping mechanism variables
|
||||||
enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
|
enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
|
||||||
int state = wordWrap? MEASURE_STATE : DRAW_STATE;
|
int state = wordWrap? MEASURE_STATE : DRAW_STATE;
|
||||||
|
|
||||||
int startLine = -1; // Index where to begin drawing (where a line begins)
|
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 endLine = -1; // Index where to stop drawing (where a line ends)
|
||||||
int lastk = -1; // Holds last value of the character position
|
int lastk = -1; // Holds last value of the character position
|
||||||
|
|
||||||
for (int i = 0, k = 0; i < length; i++, k++)
|
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 = GetNextCodepoint(&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);
|
||||||
|
|
||||||
int glyphWidth = 0;
|
int glyphWidth = 0;
|
||||||
int next = 0;
|
if (codepoint != '\n')
|
||||||
letter = GetNextCodepoint(&text[i], &next);
|
|
||||||
index = GetGlyphIndex(font, letter);
|
|
||||||
|
|
||||||
// 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 so to not skip any we set next = 1
|
|
||||||
if (letter == 0x3f) next = 1;
|
|
||||||
i += next - 1;
|
|
||||||
|
|
||||||
if (letter != '\n')
|
|
||||||
{
|
{
|
||||||
glyphWidth = (font.chars[index].advanceX == 0)?
|
glyphWidth = (font.chars[index].advanceX == 0)?
|
||||||
(int)(font.recs[index].width*scaleFactor + spacing):
|
(int)(font.recs[index].width*scaleFactor + spacing):
|
||||||
|
@ -894,26 +909,25 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
// and begin drawing on the next line before we can get outside the container.
|
// and begin drawing on the next line before we can get outside the container.
|
||||||
if (state == MEASURE_STATE)
|
if (state == MEASURE_STATE)
|
||||||
{
|
{
|
||||||
// TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
|
// TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
|
||||||
// See: http://jkorpela.fi/chars/spaces.html
|
// Ref: http://jkorpela.fi/chars/spaces.html
|
||||||
if ((letter == ' ') || (letter == '\t') || (letter == '\n')) endLine = i;
|
if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
|
||||||
|
|
||||||
if ((textOffsetX + glyphWidth + 1) >= rec.width)
|
if ((textOffsetX + glyphWidth + 1) >= rec.width)
|
||||||
{
|
{
|
||||||
endLine = (endLine < 1)? i : endLine;
|
endLine = (endLine < 1)? i : endLine;
|
||||||
if (i == endLine) endLine -= next;
|
if (i == endLine) endLine -= codepointByteCount;
|
||||||
if ((startLine + next) == endLine) endLine = i - next;
|
if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
|
||||||
|
|
||||||
state = !state;
|
state = !state;
|
||||||
}
|
}
|
||||||
else if ((i + 1) == length)
|
else if ((i + 1) == length)
|
||||||
{
|
{
|
||||||
endLine = i;
|
endLine = i;
|
||||||
|
|
||||||
state = !state;
|
state = !state;
|
||||||
}
|
}
|
||||||
else if (letter == '\n')
|
else if (codepoint == '\n') state = !state;
|
||||||
{
|
|
||||||
state = !state;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == DRAW_STATE)
|
if (state == DRAW_STATE)
|
||||||
{
|
{
|
||||||
|
@ -929,7 +943,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (letter == '\n')
|
if (codepoint == '\n')
|
||||||
{
|
{
|
||||||
if (!wordWrap)
|
if (!wordWrap)
|
||||||
{
|
{
|
||||||
|
@ -945,26 +959,25 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
textOffsetX = 0;
|
textOffsetX = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When text overflows rectangle height limit, just stop drawing
|
||||||
if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
|
if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
|
||||||
|
|
||||||
// Draw selected
|
// Draw selection background
|
||||||
bool isGlyphSelected = false;
|
bool isGlyphSelected = false;
|
||||||
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
|
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
|
||||||
{
|
{
|
||||||
Rectangle strec = {rec.x + textOffsetX-1, rec.y + textOffsetY, glyphWidth, font.baseSize*scaleFactor };
|
DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, font.baseSize*scaleFactor }, selectBackTint);
|
||||||
DrawRectangleRec(strec, selectBack);
|
|
||||||
isGlyphSelected = true;
|
isGlyphSelected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw glyph
|
// Draw current chracter glyph
|
||||||
if ((letter != ' ') && (letter != '\t'))
|
if ((codepoint != ' ') && (codepoint != '\t'))
|
||||||
{
|
{
|
||||||
DrawTexturePro(font.texture, font.recs[index],
|
DrawTexturePro(font.texture, font.recs[index],
|
||||||
(Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
|
(Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
|
||||||
rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
|
||||||
font.recs[index].width*scaleFactor,
|
font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor },
|
||||||
font.recs[index].height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f,
|
(Vector2){ 0, 0 }, 0.0f, (!isGlyphSelected)? tint : selectTint);
|
||||||
(!isGlyphSelected)? tint : selectText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,6 +989,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
|
||||||
endLine = -1;
|
endLine = -1;
|
||||||
glyphWidth = 0;
|
glyphWidth = 0;
|
||||||
k = lastk;
|
k = lastk;
|
||||||
|
|
||||||
state = !state;
|
state = !state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1057,15 +1071,17 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns index position for a unicode character on spritefont
|
// Returns index position for a unicode character on spritefont
|
||||||
int GetGlyphIndex(Font font, int character)
|
int GetGlyphIndex(Font font, int codepoint)
|
||||||
{
|
{
|
||||||
|
#define TEXT_CHARACTER_NOTFOUND 63 // Character: '?'
|
||||||
|
|
||||||
#define UNORDERED_CHARSET
|
#define UNORDERED_CHARSET
|
||||||
#if defined(UNORDERED_CHARSET)
|
#if defined(UNORDERED_CHARSET)
|
||||||
int index = 0;
|
int index = TEXT_CHARACTER_NOTFOUND;
|
||||||
|
|
||||||
for (int i = 0; i < font.charsCount; i++)
|
for (int i = 0; i < font.charsCount; i++)
|
||||||
{
|
{
|
||||||
if (font.chars[i].value == character)
|
if (font.chars[i].value == codepoint)
|
||||||
{
|
{
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
|
@ -1074,7 +1090,7 @@ int GetGlyphIndex(Font font, int character)
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
#else
|
#else
|
||||||
return (character - 32);
|
return (codepoint - 32);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue