const std = @import("std"); const rl = @import("raylib"); font: rl.Font, spacing: ?f32 = null, line_height: f32 = 1.4, pub fn getSpacing(self: @This()) f32 { if (self.spacing) |spacing| { return spacing; } else { return self.getSize() / 10; } } pub fn getSize(self: @This()) f32 { return @floatFromInt(self.font.baseSize); } pub fn drawTextLines(self: @This(), lines: []const []const u8, position: rl.Vector2, tint: rl.Color) void { var offset_y: f32 = 0; const font_size = self.getSize(); for (lines) |line| { self.drawText(line, position.add(.{ .x = 0, .y = offset_y }), tint); const line_size = self.measureText(line); offset_y += line_size.y + font_size * (self.line_height - 1); } } pub fn measureTextLines(self: @This(), lines: []const []const u8) rl.Vector2 { var text_size = rl.Vector2.zero(); const font_size = self.getSize(); for (lines) |line| { const line_size = self.measureText(line); text_size.x = @max(text_size.x, line_size.x); text_size.y += line_size.y; } text_size.y += (self.line_height - 1) * font_size * @as(f32, @floatFromInt(@max(lines.len - 1, 0))); return text_size; } pub fn drawTextEx(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color, offset: *rl.Vector2) void { if (self.font.texture.id == 0) return; const font_size = self.getSize(); const spacing = self.getSpacing(); var iter = std.unicode.Utf8Iterator{ .bytes = text, .i = 0 }; while (iter.nextCodepoint()) |codepoint| { if (codepoint == '\n') { offset.x = 0; offset.y += font_size * self.line_height; } else { if (!(codepoint <= 255 and std.ascii.isWhitespace(@intCast(codepoint)))) { var codepoint_position = position.add(offset.*); codepoint_position.x = @round(codepoint_position.x); codepoint_position.y = @round(codepoint_position.y); rl.drawTextCodepoint(self.font, codepoint, codepoint_position, font_size, tint); } const index: u32 = @intCast(rl.getGlyphIndex(self.font, codepoint)); if (self.font.glyphs[index].advanceX != 0) { offset.x += @floatFromInt(self.font.glyphs[index].advanceX); } else { offset.x += self.font.recs[index].width; offset.x += @floatFromInt(self.font.glyphs[index].offsetX); } offset.x += spacing; } } } pub fn drawText(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color) void { var offset = rl.Vector2.init(0, 0); self.drawTextEx(text, position, tint, &offset); } pub fn drawTextAlloc(self: @This(), allocator: std.mem.Allocator, comptime fmt: []const u8, args: anytype, position: rl.Vector2, tint: rl.Color) !void { const text = try std.fmt.allocPrint(allocator, fmt, args); defer allocator.free(text); self.drawText(text, position, tint); } pub fn measureText(self: @This(), text: []const u8) rl.Vector2 { var text_size = rl.Vector2.zero(); if (self.font.texture.id == 0) return text_size; // Security check if (text.len == 0) return text_size; const font_size = self.getSize(); const spacing = self.getSpacing(); var line_width: f32 = 0; text_size.y = font_size; var iter = std.unicode.Utf8Iterator{ .bytes = text, .i = 0 }; while (iter.nextCodepoint()) |codepoint| { if (codepoint == '\n') { text_size.y += font_size * self.line_height; line_width = 0; } else { if (line_width > 0) { line_width += spacing; } const index: u32 = @intCast(rl.getGlyphIndex(self.font, codepoint)); if (self.font.glyphs[index].advanceX != 0) { line_width += @floatFromInt(self.font.glyphs[index].advanceX); } else { line_width += self.font.recs[index].width; line_width += @floatFromInt(self.font.glyphs[index].offsetX); } text_size.x = @max(text_size.x, line_width); } } return text_size; } pub fn drawTextCenter(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color) void { const text_size = self.measureText(text); const adjusted_position = rl.Vector2{ .x = position.x - text_size.x / 2, .y = position.y - text_size.y / 2, }; self.drawText(text, adjusted_position, tint); }