use libgrapheme to interpret utf8 encoded strings

font_atlas
Alessandro Mauri 2 weeks ago
parent 8cf3881b6b
commit 0db858e814
  1. 2
      lib/libgrapheme.c3l/libgrapheme.c3i
  2. 7
      project.json
  3. 4
      src/main.c3
  4. 57
      src/ugui_cmd.c3
  5. 1
      src/ugui_data.c3
  6. 58
      src/ugui_text.c3

@ -1,6 +1,6 @@
module grapheme; module grapheme;
const uint GRAPHEME_INVALID_CODEPOINT UINT32_C = 0xFFFD; const uint GRAPHEME_INVALID_CODEPOINT = 0xFFFD;
enum BidirectionalDirection { enum BidirectionalDirection {
GRAPHEME_BIDIRECTIONAL_DIRECTION_NEUTRAL, GRAPHEME_BIDIRECTIONAL_DIRECTION_NEUTRAL,

@ -6,7 +6,7 @@
// Directories where C3 library files may be found. // Directories where C3 library files may be found.
"dependency-search-paths": [ "lib" ], "dependency-search-paths": [ "lib" ],
// Libraries to use for all targets. // Libraries to use for all targets.
"dependencies": [ "raylib", "schrift" ], "dependencies": [ "raylib", "schrift", "grapheme" ],
"features": [ "features": [
// See rcore.c3 // See rcore.c3
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT", //"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
@ -17,13 +17,12 @@
//"SUPPORT_TEXT_CODEPOINTS_MANAGEMENT", //"SUPPORT_TEXT_CODEPOINTS_MANAGEMENT",
//"SUPPORT_TEXT_C_STRING_MANAGEMENT", //"SUPPORT_TEXT_C_STRING_MANAGEMENT",
//"SUPPORT_RANDOM_GENERATION", //"SUPPORT_RANDOM_GENERATION",
//"SUPPORT_RAYGUI",
"SUPPORT_RAYGUI",
//"RAYGUI_NO_ICONS", //"RAYGUI_NO_ICONS",
//"RAYGUI_CUSTOM_ICONS", //"RAYGUI_CUSTOM_ICONS",
], ],
// Authors, optionally with email. // Authors, optionally with email.
"authors": [ "John Doe <john.doe@example.com>" ], "authors": [ "John Doe <ale@shitposting.expert>" ],
// Version using semantic versioning. // Version using semantic versioning.
"version": "0.1.0", "version": "0.1.0",
// Sources compiled for all targets. // Sources compiled for all targets.

@ -27,7 +27,7 @@ fn int main(String[] args)
{ {
ugui::Ctx ui; ugui::Ctx ui;
ui.init()!!; ui.init()!!;
ui.font.load("/usr/share/fonts/TTF/Hack-Regular.ttf", 16, scale: 1.5)!!; ui.font.load("/usr/share/fonts/NerdFonts/ttf/HackNerdFont-Regular.ttf", 16, scale: 1.5)!!;
short width = 800; short width = 800;
short height = 450; short height = 450;
@ -94,7 +94,7 @@ fn int main(String[] args)
io::printfn("slider: %f", e.slider.value); io::printfn("slider: %f", e.slider.value);
} }
ui.text_unbounded("text1", "Ciao Mamma\nSono a Casa")!!; ui.text_unbounded("text1", "Ciao Mamma\nAbilità ⚡")!!;
|}; |};
ui.div_end()!!; ui.div_end()!!;

@ -1,13 +1,15 @@
module ugui; module ugui;
import std::ascii;
// FIXME: is this really the best solution? // FIXME: is this really the best solution?
// "rect" is the bounding box of the element, which includes the border and the padding (so not just the content) // "rect" is the bounding box of the element, which includes the border and the padding (so not just the content)
fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false) fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false)
{ {
// FIXME: this should be culled higher up, maybe // FIXME: this should be culled higher up, maybe
if (rect.w <= 0 || rect.h <= 0) { if (rect.w <= 0 || rect.h <= 0) {
return; return;
} }
Rect border = ctx.style.border; Rect border = ctx.style.border;
Rect padding = ctx.style.padding; Rect padding = ctx.style.padding;
@ -48,3 +50,50 @@ fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture)
}; };
ctx.cmd_queue.enqueue(&cmd)!; ctx.cmd_queue.enqueue(&cmd)!;
} }
fn void! Ctx.push_string(&ctx, Rect bounds, String text)
{
if (text.len == 0) {
return;
}
short baseline = (short)ctx.font.ascender;
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
short line_gap = (short)ctx.font.linegap;
Point orig = {
.x = bounds.x,
.y = bounds.y,
};
short line_len;
Codepoint cp;
usz off, x;
while ((cp = str_to_codepoint(text[off..], &x)) != 0) {
off += x;
Glyph* gp;
if (!ascii::is_cntrl((char)cp)) {
gp = ctx.font.get_glyph(cp)!;
Rect gb = {
.x = orig.x + line_len + gp.ox,
.y = orig.y + gp.oy + baseline,
.w = gp.w,
.h = gp.h,
};
Rect gt = {
.x = gp.u,
.y = gp.v,
.w = gp.w,
.h = gp.h,
};
if (rect_collision(gb, bounds)) {
ctx.push_sprite(gb, gt)!;
}
line_len += gp.adv;
} else if (cp == '\n'){
orig.y += line_height + line_gap;
line_len = 0;
} else {
continue;
}
}
}

@ -1,6 +1,5 @@
module ugui; module ugui;
import std::io;
import std::core::string; import std::core::string;
import vtree; import vtree;

@ -1,6 +1,22 @@
module ugui; module ugui;
import std::io; import std::io;
import std::ascii;
import grapheme;
<*
@require off != null
*>
fn Codepoint str_to_codepoint(char[] str, usz* off)
{
Codepoint cp;
isz b = grapheme::decode_utf8(str, str.len, &cp);
if (b == 0 || b > str.len) {
return 0;
}
*off = b;
return cp;
}
fn Rect! Ctx.get_text_bounds(&ctx, String text, bool* update_atlas) fn Rect! Ctx.get_text_bounds(&ctx, String text, bool* update_atlas)
{ {
@ -12,16 +28,20 @@ fn Rect! Ctx.get_text_bounds(&ctx, String text, bool* update_atlas)
// TODO: account for unicode codepoints // TODO: account for unicode codepoints
short line_len; short line_len;
foreach (c: text) { Codepoint cp;
Codepoint cp = (Codepoint)c; usz off, x;
while ((cp = str_to_codepoint(text[off..], &x)) != 0) {
off += x;
bool n; bool n;
if (cp != '\n') { if (!ascii::is_cntrl((char)cp)) {
gp = ctx.font.get_glyph(cp, &n)!; gp = ctx.font.get_glyph(cp, &n)!;
line_len += gp.adv; line_len += gp.adv;
if (n) { *update_atlas = true; } if (n) { *update_atlas = true; }
} else { } else if (cp == '\n'){
text_bounds.h += line_height + line_gap; text_bounds.h += line_height + line_gap;
line_len = 0; line_len = 0;
} else {
continue;
} }
if (line_len > text_bounds.w) { if (line_len > text_bounds.w) {
text_bounds.w = line_len; text_bounds.w = line_len;
@ -79,33 +99,5 @@ fn void! Ctx.text_unbounded(&ctx, String label, String text)
}; };
ctx.cmd_queue.enqueue(&bounds)!; ctx.cmd_queue.enqueue(&bounds)!;
Point orig = { ctx.push_string(c_elem.bounds, text)!;
.x = c_elem.bounds.x,
.y = c_elem.bounds.y,
};
short line_len;
foreach (c: text) {
Glyph* gp;
Codepoint cp = (Codepoint)c;
if (cp != '\n') {
gp = ctx.font.get_glyph(cp)!;
Rect gb = {
.x = orig.x + line_len + gp.ox,
.y = orig.y + gp.oy + baseline,
.w = gp.w,
.h = gp.h,
};
Rect gt = {
.x = gp.u,
.y = gp.v,
.w = gp.w,
.h = gp.h,
};
ctx.push_sprite(gb, gt)!;
line_len += gp.adv;
} else {
orig.y += line_height + line_gap;
line_len = 0;
}
}
} }

Loading…
Cancel
Save