font: font_wrap support

main
Dominik Madarász 2024-02-10 21:28:48 +01:00
parent 3a853d09d7
commit 77012ae90d
7 changed files with 709 additions and 479 deletions

View File

@ -681,6 +681,7 @@ typedef struct font_metrics_t {
void font_goto(float x, float y);
vec2 font_print(const char *text);
vec2 font_clip(const char *text, vec4 rect);
const char* font_wrap(const char *text, float max_width);
vec2 font_rect(const char *text);
font_metrics_t font_metrics(const char *text);
void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords);

View File

@ -132,8 +132,22 @@ int main() {
font_goto(pos.x, pos.y);
vec2 size=font_rect(txt);
font_metrics_t m=font_metrics(txt);
ddraw_aabb(vec3(pos.x,pos.y,0), vec3(pos.x+size.x,pos.y+size.y-m.descent+m.linegap+100,0));
font_clip(txt, vec4(pos.x, pos.y, size.x, size.y+100));
ddraw_aabb(vec3(pos.x,pos.y,0), vec3(pos.x+size.x,pos.y+size.y-m.descent+m.linegap,0));
font_clip(txt, vec4(pos.x, pos.y, size.x, size.y));
ddraw_pop_2d();
}
static float max_width = 300.0f;
{
vec2 pos = vec2(2000,240);
ddraw_push_2d();
char *txt = "This is the first line.\n \nAnd now the second line.\n \nYou can do a third great line, too!\n \nNow this is a very long line aaaaaaaaaa!\n";
font_goto(pos.x, pos.y);
const char *wrapped_text = font_wrap(txt, max_width);
vec2 size=font_rect(wrapped_text);
font_metrics_t m=font_metrics(wrapped_text);
ddraw_aabb(vec3(pos.x,pos.y,0), vec3(pos.x+size.x,pos.y+size.y-m.descent+m.linegap,0));
font_clip(wrapped_text, vec4(pos.x, pos.y, size.x, size.y));
ddraw_pop_2d();
}
@ -207,5 +221,10 @@ int main() {
ui_font();
ui_panel_end();
}
if (ui_panel("Textbox", 0)) {
ui_float("Max width", &max_width);
ui_panel_end();
}
}
}

View File

@ -16147,9 +16147,9 @@ API vec2 font_xy();
API void font_goto(float x, float y);
API vec2 font_print(const char *text);
API vec2 font_clip(const char *text, vec4 rect);
API const char* font_wrap(const char *text, float max_width);
API vec2 font_rect(const char *text);
API font_metrics_t font_metrics(const char *text);
// void font_wrap(vec2 topleft, vec2 bottomright);
// syntax highlighting
API void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.
@ -363702,6 +363702,234 @@ vec2 font_draw_ex(const char *text, vec2 offset, vec4 rect, const char *col, voi
return abs2(vec2(W*W > X*X ? W : X, Y*Y > LL*LL ? Y : LL));
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
const char *font_wrap(const char *text, float max_width) {
// return early if the text fits the max_width already
if (font_rect(text).x <= max_width) {
return text;
}
// skip alignment flags and collect tags
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) has_set=1;
if (text[1] == FONT_CENTER[1]) has_set=1;
if (text[1] == FONT_RIGHT[1]) has_set=1;
if (text[1] == FONT_JUSTIFY[1]) has_set=1;
if (text[1] == FONT_TOP[1]) has_set=1;
if (text[1] == FONT_BOTTOM[1]) has_set=1;
if (text[1] == FONT_MIDDLE[1]) has_set=1;
if (text[1] == FONT_BASELINE[1]) has_set=1;
if (!has_set) break;
else text += 2;
}
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char*) words = strsplit(text, " ");
static __thread int slot = 0;
static __thread char *buf[16] = {0};
int len = strlen(text) + array_count(words);
slot = (slot+1) % 16;
buf[slot] = REALLOC(buf[slot], len+1);
memset(buf[slot], 0, len+1);
char *out = buf[slot];
float width = 0.0f;
for (int i = 0; i < array_count(words); ++i) {
char *word = words[i];
float word_width = font_rect(va("%s%s ", tags, word)).x;
if (strstr(word, "\n")) {
width = word_width;
strcat(out, va("%s ", word));
} else {
if (width+word_width > max_width) {
width = 0.0f;
strcat(out, "\n");
}
width += word_width;
strcat(out, va("%s ", word));
}
}
// get rid of the space added at the end
out[strlen(out)] = 0;
return out;
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}
void *font_colorize(const char *text, const char *comma_types, const char *comma_keywords) {
// reallocate memory
static __thread int slot = 0;
@ -363924,164 +364152,6 @@ void *font_colorize(const char *text, const char *comma_types, const char *comma
return col;
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}
#line 0
#line 1 "v4k_gui.c"

View File

@ -2145,6 +2145,234 @@ vec2 font_draw_ex(const char *text, vec2 offset, vec4 rect, const char *col, voi
return abs2(vec2(W*W > X*X ? W : X, Y*Y > LL*LL ? Y : LL));
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
const char *font_wrap(const char *text, float max_width) {
// return early if the text fits the max_width already
if (font_rect(text).x <= max_width) {
return text;
}
// skip alignment flags and collect tags
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) has_set=1;
if (text[1] == FONT_CENTER[1]) has_set=1;
if (text[1] == FONT_RIGHT[1]) has_set=1;
if (text[1] == FONT_JUSTIFY[1]) has_set=1;
if (text[1] == FONT_TOP[1]) has_set=1;
if (text[1] == FONT_BOTTOM[1]) has_set=1;
if (text[1] == FONT_MIDDLE[1]) has_set=1;
if (text[1] == FONT_BASELINE[1]) has_set=1;
if (!has_set) break;
else text += 2;
}
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char*) words = strsplit(text, " ");
static __thread int slot = 0;
static __thread char *buf[16] = {0};
int len = strlen(text) + array_count(words);
slot = (slot+1) % 16;
buf[slot] = REALLOC(buf[slot], len+1);
memset(buf[slot], 0, len+1);
char *out = buf[slot];
float width = 0.0f;
for (int i = 0; i < array_count(words); ++i) {
char *word = words[i];
float word_width = font_rect(va("%s%s ", tags, word)).x;
if (strstr(word, "\n")) {
width = word_width;
strcat(out, va("%s ", word));
} else {
if (width+word_width > max_width) {
width = 0.0f;
strcat(out, "\n");
}
width += word_width;
strcat(out, va("%s ", word));
}
}
// get rid of the space added at the end
out[strlen(out)] = 0;
return out;
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}
void *font_colorize(const char *text, const char *comma_types, const char *comma_keywords) {
// reallocate memory
static __thread int slot = 0;
@ -2367,161 +2595,3 @@ void *font_colorize(const char *text, const char *comma_types, const char *comma
return col;
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}

View File

@ -90,9 +90,9 @@ API vec2 font_xy();
API void font_goto(float x, float y);
API vec2 font_print(const char *text);
API vec2 font_clip(const char *text, vec4 rect);
API const char* font_wrap(const char *text, float max_width);
API vec2 font_rect(const char *text);
API font_metrics_t font_metrics(const char *text);
// void font_wrap(vec2 topleft, vec2 bottomright);
// syntax highlighting
API void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.

View File

@ -10843,6 +10843,234 @@ vec2 font_draw_ex(const char *text, vec2 offset, vec4 rect, const char *col, voi
return abs2(vec2(W*W > X*X ? W : X, Y*Y > LL*LL ? Y : LL));
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
const char *font_wrap(const char *text, float max_width) {
// return early if the text fits the max_width already
if (font_rect(text).x <= max_width) {
return text;
}
// skip alignment flags and collect tags
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) has_set=1;
if (text[1] == FONT_CENTER[1]) has_set=1;
if (text[1] == FONT_RIGHT[1]) has_set=1;
if (text[1] == FONT_JUSTIFY[1]) has_set=1;
if (text[1] == FONT_TOP[1]) has_set=1;
if (text[1] == FONT_BOTTOM[1]) has_set=1;
if (text[1] == FONT_MIDDLE[1]) has_set=1;
if (text[1] == FONT_BASELINE[1]) has_set=1;
if (!has_set) break;
else text += 2;
}
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char*) words = strsplit(text, " ");
static __thread int slot = 0;
static __thread char *buf[16] = {0};
int len = strlen(text) + array_count(words);
slot = (slot+1) % 16;
buf[slot] = REALLOC(buf[slot], len+1);
memset(buf[slot], 0, len+1);
char *out = buf[slot];
float width = 0.0f;
for (int i = 0; i < array_count(words); ++i) {
char *word = words[i];
float word_width = font_rect(va("%s%s ", tags, word)).x;
if (strstr(word, "\n")) {
width = word_width;
strcat(out, va("%s ", word));
} else {
if (width+word_width > max_width) {
width = 0.0f;
strcat(out, "\n");
}
width += word_width;
strcat(out, va("%s ", word));
}
}
// get rid of the space added at the end
out[strlen(out)] = 0;
return out;
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}
void *font_colorize(const char *text, const char *comma_types, const char *comma_keywords) {
// reallocate memory
static __thread int slot = 0;
@ -11065,164 +11293,6 @@ void *font_colorize(const char *text, const char *comma_types, const char *comma
return col;
}
static vec2 gotoxy = {0};
// Return cursor
vec2 font_xy() {
return gotoxy;
}
// Relocate cursor
void font_goto(float x, float y) {
gotoxy = vec2(x, y);
}
// Print and linefeed. Text may include markup code
vec2 font_clip(const char *text, vec4 rect) {
int l=0,c=0,r=0,j=0,t=0,b=0,m=0,B=0;
while ( text[0] == FONT_LEFT[0] ) {
int has_set=0;
if (text[1] == FONT_LEFT[1]) l = 1, has_set=1;
if (text[1] == FONT_CENTER[1]) c = 1, has_set=1;
if (text[1] == FONT_RIGHT[1]) r = 1, has_set=1;
if (text[1] == FONT_JUSTIFY[1]) j = 1, has_set=1;
if (text[1] == FONT_TOP[1]) t = 1, has_set=1;
if (text[1] == FONT_BOTTOM[1]) b = 1, has_set=1;
if (text[1] == FONT_MIDDLE[1]) m = 1, has_set=1;
if (text[1] == FONT_BASELINE[1]) B = 1, has_set=1;
if (!has_set) break;
else text += 2;
}
int num_newlines = 0;
for (int i = 0, end = strlen(text); i < end; ++i) {
if (text[i] == '\n') ++num_newlines;
}
if (num_newlines > 1) {
vec2 text_dims = font_rect(text);
char tags[4] = {0};
int t=0;
while (*text) {
char ch = *text;
if( (ch >= 1 && ch <= 6) ||
(ch >= 0x1a && ch <= 0x1f) ||
(ch >= 0x10 && ch <= 0x19)) {
if (t < sizeof(tags)) tags[t++] = ch;
}
else break;
++text;
}
array(char *) lines = strsplit(text, "\n");
if (b) {
gotoxy.y += (rect.w - text_dims.y);
}
if (m) {
gotoxy.y += (rect.w/2. - text_dims.y/2.);
}
if (B) {
gotoxy.y += (rect.w/2. - text_dims.y/1.);
}
for (int i = 0; i < array_count(lines); i++) {
char *line = va("%s%s\n", tags, lines[i]);
vec2 text_rect = font_rect(line);
if( l || c || r ) {
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
} else if (j) {
float words_space = 0.0f;
array(char *) words = strsplit(lines[i], " ");
for (int k = 0; k < array_count(words); ++k) {
words_space += font_rect(words[k]).x;
}
if (array_count(words) == 0) {
gotoxy.y += text_rect.y;
continue;
}
float extra_space = rect.z - words_space;
int gaps = array_count(words) - 1;
float space_offset = gaps > 0 ? extra_space / (float)gaps : 0;
for (int k = 0; k < array_count(words); ++k) {
vec2 dims = font_draw_ex(va("%s%s", tags, words[k]), gotoxy, rect, NULL, font_draw_cmd);
gotoxy.x += dims.x + space_offset;
}
gotoxy.x = rect.x;
}
if (!j) {
font_draw_ex(line, gotoxy, rect, NULL, font_draw_cmd);
}
gotoxy.y += text_rect.y;
}
return text_dims;
} else {
if( l || c || r ) {
vec2 text_rect = font_rect(text);
gotoxy.x = l ? rect.x : r ? ((rect.x+rect.z) - text_rect.x) : rect.x+rect.z/2. - text_rect.x/2.;
}
if( t || b || m || B ) {
vec2 text_rect = font_rect(text);
gotoxy.y = t ? rect.y : b ? ((rect.y+rect.w) - text_rect.y) : m ? rect.y+rect.w/2.-text_rect.y/2. : rect.y+rect.w/2.-text_rect.y/1;
}
vec2 dims = font_draw_ex(text, gotoxy, rect, NULL, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? rect.x : gotoxy.x + dims.x;
return dims;
}
}
vec2 font_print(const char *text) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_clip(text, dims);
}
// Print a code snippet with syntax highlighting
vec2 font_highlight(const char *text, const void *colors) {
vec4 screen_dim = {0, 0, window_width(), window_height()};
vec2 dims = font_draw_ex(text, gotoxy, screen_dim, (const char *)colors, font_draw_cmd);
gotoxy.y += strchr(text, '\n') ? dims.y : 0;
gotoxy.x = strchr(text, '\n') ? 0 : gotoxy.x + dims.x;
return dims;
}
// Calculate the size of a string, in the pixel size specified. Count stray newlines too.
vec2 font_rect(const char *str) {
vec4 dims = {0, 0, window_width(), window_height()};
return font_draw_ex(str, gotoxy, dims, NULL, NULL);
}
font_metrics_t font_metrics(const char *text) {
font_metrics_t m={0};
int S = 3;
font_t *f = &fonts[0];
// utf8 to utf32
array(uint32_t) unicode = string32(text);
// parse string
for( int i = 0, end = array_count(unicode); i < end; ++i ) {
uint32_t ch = unicode[i];
if( ch >= 1 && ch <= 6 ) {
S = ch;
continue;
}
if( ch >= 0x1a && ch <= 0x1f ) {
if( fonts[ ch - 0x1a ].initialized) {
// change face
f = &fonts[ ch - 0x1a ];
}
continue;
}
}
m.ascent = f->ascent*f->factor*f->scale[S];
m.descent = f->descent*f->factor*f->scale[S];
m.linegap = f->linegap*f->factor*f->scale[S];
m.linedist = f->linedist*f->factor*f->scale[S];
return m;
}
#line 0
#line 1 "v4k_gui.c"

View File

@ -2214,9 +2214,9 @@ API vec2 font_xy();
API void font_goto(float x, float y);
API vec2 font_print(const char *text);
API vec2 font_clip(const char *text, vec4 rect);
API const char* font_wrap(const char *text, float max_width);
API vec2 font_rect(const char *text);
API font_metrics_t font_metrics(const char *text);
// void font_wrap(vec2 topleft, vec2 bottomright);
// syntax highlighting
API void* font_colorize(const char *text, const char *comma_types, const char *comma_keywords); // comma separated tokens. expensive, please cache result.