const std = @import("std"); const Font = @import("./font.zig"); const Align = Font.Align; const Context = @This(); const c = @cImport({ @cInclude("fontstash.h"); @cInclude("sokol/sokol_gfx.h"); @cInclude("sokol/sokol_gl.h"); @cInclude("sokol_fontstash.h"); }); pub const FONScontext = c.FONScontext; pub const Size = struct { width: u32, height: u32, }; pub const TextBounds = struct { advance: f32, min_x: f32, max_x: f32, min_y: f32, max_y: f32, }; pub const LineBounds = struct { min_y: f32, max_y: f32, }; pub const VertMetrics = struct { ascender: f32, descender: f32, lineh: f32 }; pub const Quad = struct { x0: f32, y0: f32, s0: f32, t0: f32, x1: f32, y1: f32, s1: f32, t1: f32, }; pub const TextIterator = struct { context: Context, iter: c.FONStextIter, pub fn init(ctx: Context, x: f32, y: f32, text: []const u8) TextIterator { var self = TextIterator{ .context = ctx, .iter = undefined }; const success = c.fonsTextIterInit( self.context.ctx, &self.iter, x, y, text.ptr, text.ptr + text.len ); if (success != 1) { return error.fonsTextIterInit; } return self; } pub fn next(self: TextIterator) ?Quad { var quad: c.FONSquad = undefined; const success = c.fonsTextIterNext(self.context, &self.iter, &quad); if (success != 1) { return null; } return Quad{ .x0 = quad.x0, .y0 = quad.y0, .s0 = quad.s0, .t0 = quad.t0, .x1 = quad.x0, .y1 = quad.y0, .s1 = quad.s0, .t1 = quad.t0, }; } }; ctx: *FONScontext, pub fn init(desc: c.sfons_desc_t) !Context { const ctx = c.sfons_create(&desc); if (ctx == null) { return error.sfons_create; } return Context{ .ctx = ctx.? }; } pub fn deinit(self: Context) void { c.sfons_destroy(self.ctx); } pub fn flush(self: Context) void { c.sfons_flush(self.ctx); } pub fn addFont(self: Context, name: [*c]const u8, data: []const u8) !Font.Id { const font_id = c.fonsAddFontMem( self.ctx, name, @constCast(data.ptr), @intCast(data.len), 0 ); if (font_id == c.FONS_INVALID) { return error.fonsAddFontMem; } return @enumFromInt(font_id); } pub fn addFallbackFont(self: Context, base: Font.Id, fallback: Font.Id) void { const success = c.fonsAddFallbackFont(self.ctx, @intFromEnum(base), @intFromEnum(fallback)); if (success != 1) { return error.fonsAddFallbackFont; } } pub fn getFontByName(self: Context, name: [*c]const u8) ?Font.Id { const font_id = c.fonsGetFontByName(self.ctx, name); if (font_id == c.FONS_INVALID) { return null; } return @enumFromInt(font_id); } // TODO: fonsSetErrorCallback pub fn getAtlasSize(self: Context) Size { var result: Size = .{ .width = 0, .height = 0 }; c.fonsGetAtlasSize(self.ctx, &result.width, &result.height); return result; } pub fn expandAtlas(self: Context, width: u32, height: u32) !void { const success = c.fonsExpandAtlas(self.ctx, @bitCast(width), @bitCast(height)); if (success != 1) { return error.fonsExpandAtlas; } } pub fn resetAtlas(self: Context) !void { const success = c.fonsResetAtlas(self.ctx); if (success != 1) { return error.fonsResetAtlas; } } pub fn pushState(self: Context) void { c.fonsPushState(self.ctx); } pub fn popState(self: Context) void { c.fonsPopState(self.ctx); } pub fn clearState(self: Context) void { c.fonsClearState(self.ctx); } pub fn setSize(self: Context, size: f32) void { c.fonsSetSize(self.ctx, size); } pub fn setColor(self: Context, color: u32) void { c.fonsSetColor(self.ctx, color); } pub fn setSpacing(self: Context, spacing: f32) void { c.fonsSetSpacing(self.ctx, spacing); } pub fn setBlur(self: Context, blur: f32) void { c.fonsSetSpacing(self.ctx, blur); } pub fn setAlign(self: Context, alignment: Align) void { c.fonsSetAlign(self.ctx, @intFromEnum(alignment.x) | @intFromEnum(alignment.y)); } pub fn setFont(self: Context, id: Font.Id) void { c.fonsSetFont(self.ctx, @intFromEnum(id)); } pub fn drawText(self: Context, x: f32, y: f32, text: []const u8) void { const advance = c.fonsDrawText(self.ctx, x, y, text.ptr, text.ptr + text.len); _ = advance; } pub fn textBounds(self: Context, x: f32, y: f32, text: []const u8) TextBounds { var bounds: f32[4] = undefined; const advance = c.fonsTextBounds(self.ctx, x, y, text.ptr, text.ptr + text.len, &bounds); return TextBounds{ .advance = advance, .min_x = bounds[0], .max_x = bounds[1], .min_y = bounds[2], .max_y = bounds[3] }; } pub fn lineBounds(self: Context, y: f32) LineBounds { var result: LineBounds = .{ .max_y = 0, .min_y = 0 }; c.fonsLineBounds(self.ctx, y, &result.min_y, &result.max_y); return result; } pub fn vertMetrics(self: Context) void { var result: VertMetrics = .{ .ascender = 0, .descender = 0, .lineh = 0 }; c.fonsVertMetrics(self.ctx, &result.ascender, &result.descender, &result.lineh); return result; } pub fn drawDebug(self: Context, x: f32, y: f32) void { c.fonsDrawDebug(self.ctx, x, y); }