mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #1204 from mitchellh/font-fallback
Ability to set preferred fallback fonts
This commit is contained in:
@ -342,7 +342,7 @@ pub fn init(
|
|||||||
// A buffer we use to store the font names for logging.
|
// A buffer we use to store the font names for logging.
|
||||||
var name_buf: [256]u8 = undefined;
|
var name_buf: [256]u8 = undefined;
|
||||||
|
|
||||||
if (config.@"font-family") |family| {
|
for (config.@"font-family".list.items) |family| {
|
||||||
var disco_it = try disco.discover(alloc, .{
|
var disco_it = try disco.discover(alloc, .{
|
||||||
.family = family,
|
.family = family,
|
||||||
.style = config.@"font-style".nameValue(),
|
.style = config.@"font-style".nameValue(),
|
||||||
@ -364,7 +364,7 @@ pub fn init(
|
|||||||
// a user says `font-style = italic` for the bold face for example,
|
// a user says `font-style = italic` for the bold face for example,
|
||||||
// no results would be found if we restrict to ALSO searching for
|
// no results would be found if we restrict to ALSO searching for
|
||||||
// italic.
|
// italic.
|
||||||
if (config.@"font-family-bold") |family| {
|
for (config.@"font-family-bold".list.items) |family| {
|
||||||
const style = config.@"font-style-bold".nameValue();
|
const style = config.@"font-style-bold".nameValue();
|
||||||
var disco_it = try disco.discover(alloc, .{
|
var disco_it = try disco.discover(alloc, .{
|
||||||
.family = family,
|
.family = family,
|
||||||
@ -379,7 +379,7 @@ pub fn init(
|
|||||||
_ = try group.addFace(.bold, .{ .deferred = face });
|
_ = try group.addFace(.bold, .{ .deferred = face });
|
||||||
} else log.warn("font-family-bold not found: {s}", .{family});
|
} else log.warn("font-family-bold not found: {s}", .{family});
|
||||||
}
|
}
|
||||||
if (config.@"font-family-italic") |family| {
|
for (config.@"font-family-italic".list.items) |family| {
|
||||||
const style = config.@"font-style-italic".nameValue();
|
const style = config.@"font-style-italic".nameValue();
|
||||||
var disco_it = try disco.discover(alloc, .{
|
var disco_it = try disco.discover(alloc, .{
|
||||||
.family = family,
|
.family = family,
|
||||||
@ -394,7 +394,7 @@ pub fn init(
|
|||||||
_ = try group.addFace(.italic, .{ .deferred = face });
|
_ = try group.addFace(.italic, .{ .deferred = face });
|
||||||
} else log.warn("font-family-italic not found: {s}", .{family});
|
} else log.warn("font-family-italic not found: {s}", .{family});
|
||||||
}
|
}
|
||||||
if (config.@"font-family-bold-italic") |family| {
|
for (config.@"font-family-bold-italic".list.items) |family| {
|
||||||
const style = config.@"font-style-bold-italic".nameValue();
|
const style = config.@"font-style-bold-italic".nameValue();
|
||||||
var disco_it = try disco.discover(alloc, .{
|
var disco_it = try disco.discover(alloc, .{
|
||||||
.family = family,
|
.family = family,
|
||||||
|
@ -29,15 +29,21 @@ const c = @cImport({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/// The font families to use.
|
/// The font families to use.
|
||||||
|
///
|
||||||
/// You can generate the list of valid values using the CLI:
|
/// You can generate the list of valid values using the CLI:
|
||||||
/// path/to/ghostty/cli +list-fonts
|
/// path/to/ghostty/cli +list-fonts
|
||||||
///
|
///
|
||||||
|
/// This configuration can be repeated multiple times to specify
|
||||||
|
/// preferred fallback fonts when the requested codepoint is not
|
||||||
|
/// available in the primary font. This is particularly useful for
|
||||||
|
/// multiple languages, symbolic fonts, etc.
|
||||||
|
///
|
||||||
/// Changing this configuration at runtime will only affect new terminals,
|
/// Changing this configuration at runtime will only affect new terminals,
|
||||||
/// i.e. new windows, tabs, etc.
|
/// i.e. new windows, tabs, etc.
|
||||||
@"font-family": ?[:0]const u8 = null,
|
@"font-family": RepeatableString = .{},
|
||||||
@"font-family-bold": ?[:0]const u8 = null,
|
@"font-family-bold": RepeatableString = .{},
|
||||||
@"font-family-italic": ?[:0]const u8 = null,
|
@"font-family-italic": RepeatableString = .{},
|
||||||
@"font-family-bold-italic": ?[:0]const u8 = null,
|
@"font-family-bold-italic": RepeatableString = .{},
|
||||||
|
|
||||||
/// The named font style to use for each of the requested terminal font
|
/// The named font style to use for each of the requested terminal font
|
||||||
/// styles. This looks up the style based on the font style string advertised
|
/// styles. This looks up the style based on the font style string advertised
|
||||||
@ -1623,15 +1629,15 @@ pub fn finalize(self: *Config) !void {
|
|||||||
// the others to the font family. This way, if someone does
|
// the others to the font family. This way, if someone does
|
||||||
// --font-family=foo, then we try to get the stylized versions of
|
// --font-family=foo, then we try to get the stylized versions of
|
||||||
// "foo" as well.
|
// "foo" as well.
|
||||||
if (self.@"font-family") |family| {
|
if (self.@"font-family".count() > 0) {
|
||||||
const fields = &[_][]const u8{
|
const fields = &[_][]const u8{
|
||||||
"font-family-bold",
|
"font-family-bold",
|
||||||
"font-family-italic",
|
"font-family-italic",
|
||||||
"font-family-bold-italic",
|
"font-family-bold-italic",
|
||||||
};
|
};
|
||||||
inline for (fields) |field| {
|
inline for (fields) |field| {
|
||||||
if (@field(self, field) == null) {
|
if (@field(self, field).count() == 0) {
|
||||||
@field(self, field) = family;
|
@field(self, field) = try self.@"font-family".clone(alloc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1984,9 +1990,9 @@ test "changed" {
|
|||||||
defer source.deinit();
|
defer source.deinit();
|
||||||
var dest = try source.clone(alloc);
|
var dest = try source.clone(alloc);
|
||||||
defer dest.deinit();
|
defer dest.deinit();
|
||||||
dest.@"font-family" = "something else";
|
dest.@"font-thicken" = true;
|
||||||
|
|
||||||
try testing.expect(source.changed(&dest, .@"font-family"));
|
try testing.expect(source.changed(&dest, .@"font-thicken"));
|
||||||
try testing.expect(!source.changed(&dest, .@"font-size"));
|
try testing.expect(!source.changed(&dest, .@"font-size"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2203,11 +2209,18 @@ pub const RepeatableString = struct {
|
|||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
// Allocator for the list is the arena for the parent config.
|
// Allocator for the list is the arena for the parent config.
|
||||||
list: std.ArrayListUnmanaged([]const u8) = .{},
|
list: std.ArrayListUnmanaged([:0]const u8) = .{},
|
||||||
|
|
||||||
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
||||||
const value = input orelse return error.ValueRequired;
|
const value = input orelse return error.ValueRequired;
|
||||||
const copy = try alloc.dupe(u8, value);
|
|
||||||
|
// Empty value resets the list
|
||||||
|
if (value.len == 0) {
|
||||||
|
self.list.clearRetainingCapacity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const copy = try alloc.dupeZ(u8, value);
|
||||||
try self.list.append(alloc, copy);
|
try self.list.append(alloc, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2218,6 +2231,11 @@ pub const RepeatableString = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The number of itemsin the list
|
||||||
|
pub fn count(self: Self) usize {
|
||||||
|
return self.list.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
/// Compare if two of our value are requal. Required by Config.
|
/// Compare if two of our value are requal. Required by Config.
|
||||||
pub fn equal(self: Self, other: Self) bool {
|
pub fn equal(self: Self, other: Self) bool {
|
||||||
const itemsA = self.list.items;
|
const itemsA = self.list.items;
|
||||||
@ -2237,8 +2255,10 @@ pub const RepeatableString = struct {
|
|||||||
var list: Self = .{};
|
var list: Self = .{};
|
||||||
try list.parseCLI(alloc, "A");
|
try list.parseCLI(alloc, "A");
|
||||||
try list.parseCLI(alloc, "B");
|
try list.parseCLI(alloc, "B");
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 2), list.list.items.len);
|
try testing.expectEqual(@as(usize, 2), list.list.items.len);
|
||||||
|
|
||||||
|
try list.parseCLI(alloc, "");
|
||||||
|
try testing.expectEqual(@as(usize, 0), list.list.items.len);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2284,7 +2304,8 @@ pub const RepeatablePath = struct {
|
|||||||
|
|
||||||
// If it isn't absolute, we need to make it absolute relative
|
// If it isn't absolute, we need to make it absolute relative
|
||||||
// to the base.
|
// to the base.
|
||||||
const abs = dir.realpathAlloc(alloc, path) catch |err| {
|
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||||
|
const abs = std.os.realpath(path, &buf) catch |err| {
|
||||||
try errors.add(alloc, .{
|
try errors.add(alloc, .{
|
||||||
.message = try std.fmt.allocPrintZ(
|
.message = try std.fmt.allocPrintZ(
|
||||||
alloc,
|
alloc,
|
||||||
@ -2300,7 +2321,7 @@ pub const RepeatablePath = struct {
|
|||||||
"expanding config-file path relative={s} abs={s}",
|
"expanding config-file path relative={s} abs={s}",
|
||||||
.{ path, abs },
|
.{ path, abs },
|
||||||
);
|
);
|
||||||
self.value.list.items[i] = abs;
|
self.value.list.items[i] = try alloc.dupeZ(u8, abs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,6 @@ pub fn Value(comptime key: Key) type {
|
|||||||
test "Value" {
|
test "Value" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
try testing.expectEqual(?[:0]const u8, Value(.@"font-family"));
|
try testing.expectEqual(Config.RepeatableString, Value(.@"font-family"));
|
||||||
try testing.expectEqual(?bool, Value(.@"cursor-style-blink"));
|
try testing.expectEqual(?bool, Value(.@"cursor-style-blink"));
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ pub const DerivedConfig = struct {
|
|||||||
arena: ArenaAllocator,
|
arena: ArenaAllocator,
|
||||||
|
|
||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
font_features: std.ArrayListUnmanaged([]const u8),
|
font_features: std.ArrayListUnmanaged([:0]const u8),
|
||||||
font_styles: font.Group.StyleStatus,
|
font_styles: font.Group.StyleStatus,
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
cursor_opacity: f64,
|
cursor_opacity: f64,
|
||||||
@ -154,7 +154,7 @@ pub const DerivedConfig = struct {
|
|||||||
selection_foreground: ?terminal.color.RGB,
|
selection_foreground: ?terminal.color.RGB,
|
||||||
invert_selection_fg_bg: bool,
|
invert_selection_fg_bg: bool,
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
custom_shaders: std.ArrayListUnmanaged([]const u8),
|
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
||||||
custom_shader_animation: bool,
|
custom_shader_animation: bool,
|
||||||
links: link.Set,
|
links: link.Set,
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ pub const DerivedConfig = struct {
|
|||||||
arena: ArenaAllocator,
|
arena: ArenaAllocator,
|
||||||
|
|
||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
font_features: std.ArrayListUnmanaged([]const u8),
|
font_features: std.ArrayListUnmanaged([:0]const u8),
|
||||||
font_styles: font.Group.StyleStatus,
|
font_styles: font.Group.StyleStatus,
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
cursor_text: ?terminal.color.RGB,
|
cursor_text: ?terminal.color.RGB,
|
||||||
@ -251,7 +251,7 @@ pub const DerivedConfig = struct {
|
|||||||
selection_foreground: ?terminal.color.RGB,
|
selection_foreground: ?terminal.color.RGB,
|
||||||
invert_selection_fg_bg: bool,
|
invert_selection_fg_bg: bool,
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
custom_shaders: std.ArrayListUnmanaged([]const u8),
|
custom_shaders: std.ArrayListUnmanaged([:0]const u8),
|
||||||
custom_shader_animation: bool,
|
custom_shader_animation: bool,
|
||||||
links: link.Set,
|
links: link.Set,
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user