font/coretext: allow setting a non-monospace font explicitly

This changes our font discovery to not filter out monospace and updates
our scoring mechanism to prefer monospace.
This commit is contained in:
Mitchell Hashimoto
2023-10-05 08:39:48 -07:00
parent 7a0b8a6781
commit 224b39b86e
2 changed files with 40 additions and 9 deletions

View File

@ -90,6 +90,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
.style = config.style, .style = config.style,
.bold = config.bold, .bold = config.bold,
.italic = config.italic, .italic = config.italic,
.monospace = config.family == null,
}); });
defer disco_it.deinit(); defer disco_it.deinit();
while (try disco_it.next()) |face| { while (try disco_it.next()) |face| {

View File

@ -50,7 +50,7 @@ pub const Descriptor = struct {
/// specific styles. /// specific styles.
bold: bool = false, bold: bool = false,
italic: bool = false, italic: bool = false,
monospace: bool = true, monospace: bool = false,
/// Variation axes to apply to the font. This also impacts searching /// Variation axes to apply to the font. This also impacts searching
/// for fonts since fonts with the ability to set these variations /// for fonts since fonts with the ability to set these variations
@ -387,27 +387,57 @@ pub const CoreText = struct {
const lhs_score = score(desc_inner, lhs); const lhs_score = score(desc_inner, lhs);
const rhs_score = score(desc_inner, rhs); const rhs_score = score(desc_inner, rhs);
// Higher score is "less" (earlier) // Higher score is "less" (earlier)
return lhs_score > rhs_score; return lhs_score.int() > rhs_score.int();
} }
}.lessThan); }.lessThan);
} }
fn score(desc: *const Descriptor, ct_desc: *const macos.text.FontDescriptor) i32 { /// We represent our sorting score as a packed struct so that we can
var score_acc: i32 = 0; /// compare scores numerically but build scores symbolically.
const Score = packed struct {
const Backing = @typeInfo(@This()).Struct.backing_integer.?;
score_acc += if (desc.style) |desired_style| style: { style: Style = .unmatched,
monospace: bool = false,
const Style = enum(u8) { unmatched = 0, match = 0xFF, _ };
pub fn int(self: Score) Backing {
return @bitCast(self);
}
};
fn score(desc: *const Descriptor, ct_desc: *const macos.text.FontDescriptor) Score {
var score_acc: Score = .{};
// Get our symbolic traits for the descriptor so we can compare
// boolean attributes like bold, monospace, etc.
const symbolic_traits: macos.text.FontSymbolicTraits = traits: {
const traits = ct_desc.copyAttribute(.traits);
defer traits.release();
const key = macos.text.FontTraitKey.symbolic.key();
const symbolic = traits.getValue(macos.foundation.Number, key) orelse
break :traits .{};
break :traits macos.text.FontSymbolicTraits.init(symbolic);
};
score_acc.monospace = symbolic_traits.monospace;
score_acc.style = if (desc.style) |desired_style| style: {
const style = ct_desc.copyAttribute(.style_name); const style = ct_desc.copyAttribute(.style_name);
defer style.release(); defer style.release();
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
const style_str = style.cstring(&buf, .utf8) orelse break :style 0; const style_str = style.cstring(&buf, .utf8) orelse break :style .unmatched;
// Matching style string gets highest score // Matching style string gets highest score
if (std.mem.eql(u8, desired_style, style_str)) break :style 100; if (std.mem.eql(u8, desired_style, style_str)) break :style .match;
// Otherwise the score is based on the length of the style string. // Otherwise the score is based on the length of the style string.
// Shorter styles are scored higher. // Shorter styles are scored higher.
break :style -1 * @as(i32, @intCast(style_str.len)); break :style @enumFromInt(100 -| style_str.len);
} else 0; } else .unmatched;
return score_acc; return score_acc;
} }