@@ -254,7 +254,7 @@ const TypingDisplay = struct {
254
254
return null ;
255
255
}
256
256
257
- pub fn measure (self : * TypingDisplay , font : rl.Font , font_size : f32 , max_width : f32 ) f32 {
257
+ pub fn measure (self : * TypingDisplay , fonts : Fonts , max_width : f32 ) f32 {
258
258
var codepoints : [max_string_len ]u21 = undefined ;
259
259
var num_codepoints : usize = 0 ;
260
260
@@ -270,7 +270,7 @@ const TypingDisplay = struct {
270
270
const res = std .fmt .bufPrintZ (& buf , "…{}×" , .{repeat }) catch max_repeat_indicator ;
271
271
272
272
num_codepoints = get_codepoints (res , & codepoints );
273
- width += measure_codepoints (font , font_size , codepoints [0.. num_codepoints ]);
273
+ width += measure_codepoints (fonts . subscript_font , codepoints [0.. num_codepoints ]);
274
274
if (width > max_width ) {
275
275
width = max_width ;
276
276
break ;
@@ -280,7 +280,7 @@ const TypingDisplay = struct {
280
280
}
281
281
282
282
num_codepoints = get_codepoints (repeated_string .string , & codepoints );
283
- width += @as (f32 , @floatFromInt (repeat )) * measure_codepoints (font , font_size , codepoints [0.. num_codepoints ]);
283
+ width += @as (f32 , @floatFromInt (repeat )) * measure_codepoints (fonts . regular_font , codepoints [0.. num_codepoints ]);
284
284
if (width > max_width ) {
285
285
width = max_width ;
286
286
break ;
@@ -290,9 +290,8 @@ const TypingDisplay = struct {
290
290
return width ;
291
291
}
292
292
293
- fn measure_codepoints (font : rl.Font , font_size : f32 , codepoints : []u21 ) f32 {
293
+ fn measure_codepoints (font : rl.Font , codepoints : []u21 ) f32 {
294
294
var width : f32 = 0 ;
295
- const scale_factor : f32 = font_size / @as (f32 , @floatFromInt (font .baseSize ));
296
295
297
296
var i : usize = codepoints .len ;
298
297
while (i > 0 ) {
@@ -301,16 +300,16 @@ const TypingDisplay = struct {
301
300
const glyph_index : usize = @intCast (rl .getGlyphIndex (font , cp ));
302
301
303
302
const advance : f32 = switch (font .glyphs [glyph_index ].advanceX ) {
304
- 0 = > font .recs [glyph_index ].width * scale_factor ,
305
- else = > @as (f32 , @floatFromInt (font .glyphs [glyph_index ].advanceX )) * scale_factor ,
303
+ 0 = > font .recs [glyph_index ].width ,
304
+ else = > @as (f32 , @floatFromInt (font .glyphs [glyph_index ].advanceX )),
306
305
};
307
306
308
307
width += advance ;
309
308
}
310
309
return width ;
311
310
}
312
311
313
- pub fn render (self : * TypingDisplay , position : rl.Vector2 , font : rl.Font , font_size : f32 , tint : rl.Color ) void {
312
+ pub fn render (self : * TypingDisplay , position : rl.Vector2 , fonts : Fonts , tint : rl.Color ) void {
314
313
var codepoints : [max_string_len ]u21 = undefined ;
315
314
var num_codepoints : usize = 0 ;
316
315
var rendered_codepoints : usize = 0 ;
@@ -327,15 +326,19 @@ const TypingDisplay = struct {
327
326
const res = std .fmt .bufPrintZ (& buf , "…{}×" , .{repeat }) catch max_repeat_indicator ;
328
327
329
328
num_codepoints = get_codepoints (res , & codepoints );
330
- rendered_codepoints = render_codepoints (font , position , font_size , tint , codepoints [0.. num_codepoints ], & offset );
329
+ const subscript_position = rl .Vector2 .init (
330
+ position .x ,
331
+ position .y + @as (f32 , @floatFromInt (fonts .regular_font .baseSize - fonts .subscript_font .baseSize ))
332
+ );
333
+ rendered_codepoints = render_codepoints (fonts .subscript_font , subscript_position , tint , codepoints [0.. num_codepoints ], & offset );
331
334
if (num_codepoints != rendered_codepoints ) break :string_render ; // no more fit on screen
332
335
333
336
repeat = repeat_indicator_threshold ;
334
337
}
335
338
336
339
num_codepoints = get_codepoints (repeated_string .string , & codepoints );
337
340
for (0.. repeat ) | _ | {
338
- rendered_codepoints = render_codepoints (font , position , font_size , tint , codepoints [0.. num_codepoints ], & offset );
341
+ rendered_codepoints = render_codepoints (fonts . regular_font , position , tint , codepoints [0.. num_codepoints ], & offset );
339
342
if (num_codepoints != rendered_codepoints ) break :string_render ; // no more fit on screen
340
343
}
341
344
}
@@ -352,9 +355,8 @@ const TypingDisplay = struct {
352
355
return i ;
353
356
}
354
357
355
- fn render_codepoints (font : rl.Font , position : rl.Vector2 , font_size : f32 , tint : rl.Color , codepoints : []u21 , offset : * f32 ) usize {
358
+ fn render_codepoints (font : rl.Font , position : rl.Vector2 , tint : rl.Color , codepoints : []u21 , offset : * f32 ) usize {
356
359
var number_of_rendered : usize = 0 ;
357
- const scale_factor : f32 = font_size / @as (f32 , @floatFromInt (font .baseSize ));
358
360
359
361
var i : usize = codepoints .len ;
360
362
while (i > 0 ) {
@@ -364,8 +366,8 @@ const TypingDisplay = struct {
364
366
const glyph_index : usize = @intCast (rl .getGlyphIndex (font , cp ));
365
367
366
368
const advance : f32 = switch (font .glyphs [glyph_index ].advanceX ) {
367
- 0 = > font .recs [glyph_index ].width * scale_factor ,
368
- else = > @as (f32 , @floatFromInt (font .glyphs [glyph_index ].advanceX )) * scale_factor ,
369
+ 0 = > font .recs [glyph_index ].width ,
370
+ else = > @as (f32 , @floatFromInt (font .glyphs [glyph_index ].advanceX )),
369
371
};
370
372
offset .* += advance ;
371
373
@@ -383,10 +385,10 @@ const TypingDisplay = struct {
383
385
.height = font .recs [glyph_index ].height ,
384
386
};
385
387
const dst_rec : rl.Rectangle = .{
386
- .x = glyph_position .x + offset_x * scale_factor ,
387
- .y = glyph_position .y + offset_y * scale_factor ,
388
- .width = font .recs [glyph_index ].width * scale_factor ,
389
- .height = font .recs [glyph_index ].height * scale_factor ,
388
+ .x = glyph_position .x + offset_x ,
389
+ .y = glyph_position .y + offset_y ,
390
+ .width = font .recs [glyph_index ].width ,
391
+ .height = font .recs [glyph_index ].height ,
390
392
};
391
393
392
394
if (dst_rec .x + dst_rec .width < 0 ) break ; // entire glyph out of screen
@@ -446,19 +448,66 @@ fn getDataForFrame(frame: usize) !?KeyData {
446
448
var key_data_producer : ? KeyDataProducer = null ;
447
449
pub var app_state : AppState = undefined ;
448
450
449
- // https://github.com/bits/UTF-8-Unicode-Test-Documents/blob/master/UTF-8_sequence_unseparated/utf8_sequence_0-0xfff_assigned_printable_unseparated.txt
450
- const text = @embedFile ("resources/utf8_sequence_0-0xfff_assigned_printable_unseparated.txt" );
451
-
452
- const symbols = "↚↹⏎␣⌫↑←→↓ᴷᴾ⏎…×" ;
453
- const all_text = text ++ symbols ;
454
-
455
451
// draw various lines helping with rendering debugging and position adjustments
456
452
const debug_lines = false ;
457
453
458
454
// it contains all current substitutions symbols:
459
455
// TODO: font discovery with fallback when glyph not found
460
- // TODO: support for non-monospaced fonts
461
- const font_data = @embedFile ("resources/DejaVuSansMono.ttf" );
456
+ const Fonts = struct {
457
+ regular_codepoints : []i32 ,
458
+ regular_font : rl.Font ,
459
+ subscript_codepoints : []i32 ,
460
+ subscript_font : rl.Font ,
461
+
462
+ const font_data = @embedFile ("resources/DejaVuSansMono.ttf" );
463
+
464
+ // https://github.com/bits/UTF-8-Unicode-Test-Documents/blob/master/UTF-8_sequence_unseparated/utf8_sequence_0-0xfff_assigned_printable_unseparated.txt
465
+ const text = @embedFile ("resources/utf8_sequence_0-0xfff_assigned_printable_unseparated.txt" );
466
+ const all_symbols = text ++ "↚↹⏎␣⌫↑←→↓ᴷᴾ⏎…×" ;
467
+
468
+ // letters are not used but are added to string to artificial increase atlas size,
469
+ // packing does not work well for small sizes,
470
+ // see: https://github.com/raysan5/raylib/issues/2550
471
+ const subscript_symbols : [:0 ]const u8 = "…0123456789×" ++ "abcdefghijklmnopqrstuvwyxz" ;
472
+
473
+ const subscript_scale_factor = 0.58 ;
474
+
475
+ pub fn init (font_size : i32 ) ! Fonts {
476
+ const regular_codepoints = try rl .loadCodepoints (all_symbols );
477
+ const subscript_codepoints = try rl .loadCodepoints (subscript_symbols );
478
+
479
+ return .{
480
+ .regular_codepoints = regular_codepoints ,
481
+ .regular_font = loadFont (font_size , regular_codepoints ),
482
+ .subscript_codepoints = subscript_codepoints ,
483
+ .subscript_font = loadFont (subscriptFontSize (font_size ), subscript_codepoints ),
484
+ };
485
+ }
486
+
487
+ fn loadFont (font_size : i32 , font_chars : []i32 ) rl.Font {
488
+ return rl .loadFontFromMemory (".ttf" , font_data , font_size , font_chars );
489
+ }
490
+
491
+ fn subscriptFontSize (font_size : i32 ) i32 {
492
+ return @intFromFloat (subscript_scale_factor * @as (f32 , @floatFromInt (font_size )));
493
+ }
494
+
495
+ pub fn updateSize (self : * Fonts , font_size : i32 ) void {
496
+ rl .unloadFont (self .regular_font );
497
+ rl .unloadFont (self .subscript_font );
498
+ self .regular_font = loadFont (font_size , self .regular_codepoints );
499
+ self .subscript_font = loadFont (subscriptFontSize (font_size ), self .subscript_codepoints );
500
+ }
501
+
502
+ pub fn deinit (self : * Fonts ) void {
503
+ rl .unloadCodepoints (self .regular_codepoints );
504
+ rl .unloadCodepoints (self .subscript_codepoints );
505
+ rl .unloadFont (self .regular_font );
506
+ rl .unloadFont (self .subscript_font );
507
+ self .* = undefined ;
508
+ }
509
+
510
+ };
462
511
463
512
pub const AppState = struct {
464
513
key_states : [MAX_KEYS ]KeyOnScreen ,
@@ -998,16 +1047,9 @@ pub fn main() !void {
998
1047
return ;
999
1048
}
1000
1049
1001
- // TODO: implement font discovery
1002
- // TODO: if not found fallback to default
1003
- const codepoints = try rl .loadCodepoints (all_text );
1004
- defer rl .unloadCodepoints (codepoints );
1005
-
1006
- std .debug .print ("Text contains {} codepoints\n " , .{codepoints .len });
1007
-
1008
1050
// TODO: font should be configurable
1009
- var font = rl . loadFontFromMemory ( ".ttf" , font_data , app_state .typing_font_size , codepoints );
1010
- defer rl . unloadFont ( font );
1051
+ var font = try Fonts . init ( app_state .typing_font_size );
1052
+ defer font . deinit ( );
1011
1053
1012
1054
var show_gui = false ;
1013
1055
@@ -1098,9 +1140,8 @@ pub fn main() !void {
1098
1140
}
1099
1141
},
1100
1142
.typing_font_size = > {
1101
- rl .unloadFont (font );
1102
1143
// TODO: sanitize, sizes <0 and larger than window height probably should be skipped
1103
- font = rl . loadFontFromMemory ( ".ttf" , font_data , app_config .data .typing_font_size , codepoints );
1144
+ font . updateSize ( app_config .data .typing_font_size );
1104
1145
app_state .typing_font_size = app_config .data .typing_font_size ;
1105
1146
},
1106
1147
inline .background_color , .typing_font_color , .typing_background_color , .key_tint_color = > | color | {
@@ -1162,7 +1203,7 @@ pub fn main() !void {
1162
1203
typing_display .update (k , app_state .backspace_mode );
1163
1204
1164
1205
const typing_max_width = 0.95 * @as (f32 , @floatFromInt (app_state .window_width ));
1165
- typing_display_width = typing_display .measure (font , @floatFromInt ( app_state . typing_font_size ), typing_max_width );
1206
+ typing_display_width = typing_display .measure (font , typing_max_width );
1166
1207
}
1167
1208
if (app_state .typing_persistance_sec > 0 and ! app_state .typingDisplayTimeElapsed ()) {
1168
1209
typing_display .clear ();
@@ -1221,7 +1262,7 @@ pub fn main() !void {
1221
1262
}
1222
1263
1223
1264
const position = rl .Vector2 .init (text_center_bounds .x + text_center_bounds .width , text_center_bounds .y );
1224
- typing_display .render (position , font , font_size , app_state .typing_font_color );
1265
+ typing_display .render (position , font , app_state .typing_font_color );
1225
1266
}
1226
1267
1227
1268
// button for closing application when window decorations disabled,
0 commit comments