mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
font/shaper: new API
This commit is contained in:
@ -1047,103 +1047,133 @@ test "shape cursor boundary and colored emoji" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test "shape cell attribute change" {
|
test "shape cell attribute change" {
|
||||||
// const testing = std.testing;
|
const testing = std.testing;
|
||||||
// const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
//
|
|
||||||
// var testdata = try testShaper(alloc);
|
var testdata = try testShaper(alloc);
|
||||||
// defer testdata.deinit();
|
defer testdata.deinit();
|
||||||
//
|
|
||||||
// // Plain >= should shape into 1 run
|
// Plain >= should shape into 1 run
|
||||||
// {
|
{
|
||||||
// var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
var screen = try terminal.Screen.init(alloc, 10, 3, 0);
|
||||||
// defer screen.deinit();
|
defer screen.deinit();
|
||||||
// try screen.testWriteString(">=");
|
try screen.testWriteString(">=");
|
||||||
//
|
|
||||||
// var shaper = &testdata.shaper;
|
var shaper = &testdata.shaper;
|
||||||
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null);
|
var it = shaper.runIterator(
|
||||||
// var count: usize = 0;
|
testdata.cache,
|
||||||
// while (try it.next(alloc)) |run| {
|
&screen,
|
||||||
// count += 1;
|
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||||
// _ = try shaper.shape(run);
|
null,
|
||||||
// }
|
null,
|
||||||
// try testing.expectEqual(@as(usize, 1), count);
|
);
|
||||||
// }
|
var count: usize = 0;
|
||||||
//
|
while (try it.next(alloc)) |run| {
|
||||||
// // Bold vs regular should split
|
count += 1;
|
||||||
// {
|
_ = try shaper.shape(run);
|
||||||
// var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
}
|
||||||
// defer screen.deinit();
|
try testing.expectEqual(@as(usize, 1), count);
|
||||||
// try screen.testWriteString(">");
|
}
|
||||||
// screen.cursor.pen.attrs.bold = true;
|
|
||||||
// try screen.testWriteString("=");
|
// Bold vs regular should split
|
||||||
//
|
{
|
||||||
// var shaper = &testdata.shaper;
|
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||||
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null);
|
defer screen.deinit();
|
||||||
// var count: usize = 0;
|
try screen.testWriteString(">");
|
||||||
// while (try it.next(alloc)) |run| {
|
try screen.setAttribute(.{ .bold = {} });
|
||||||
// count += 1;
|
try screen.testWriteString("=");
|
||||||
// _ = try shaper.shape(run);
|
|
||||||
// }
|
var shaper = &testdata.shaper;
|
||||||
// try testing.expectEqual(@as(usize, 2), count);
|
var it = shaper.runIterator(
|
||||||
// }
|
testdata.cache,
|
||||||
//
|
&screen,
|
||||||
// // Changing fg color should split
|
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||||
// {
|
null,
|
||||||
// var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
null,
|
||||||
// defer screen.deinit();
|
);
|
||||||
// screen.cursor.pen.fg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
var count: usize = 0;
|
||||||
// try screen.testWriteString(">");
|
while (try it.next(alloc)) |run| {
|
||||||
// screen.cursor.pen.fg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } };
|
count += 1;
|
||||||
// try screen.testWriteString("=");
|
_ = try shaper.shape(run);
|
||||||
//
|
}
|
||||||
// var shaper = &testdata.shaper;
|
try testing.expectEqual(@as(usize, 2), count);
|
||||||
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null);
|
}
|
||||||
// var count: usize = 0;
|
|
||||||
// while (try it.next(alloc)) |run| {
|
// Changing fg color should split
|
||||||
// count += 1;
|
{
|
||||||
// _ = try shaper.shape(run);
|
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||||
// }
|
defer screen.deinit();
|
||||||
// try testing.expectEqual(@as(usize, 2), count);
|
try screen.setAttribute(.{ .direct_color_fg = .{ .r = 1, .g = 2, .b = 3 } });
|
||||||
// }
|
try screen.testWriteString(">");
|
||||||
//
|
try screen.setAttribute(.{ .direct_color_fg = .{ .r = 3, .g = 2, .b = 1 } });
|
||||||
// // Changing bg color should split
|
try screen.testWriteString("=");
|
||||||
// {
|
|
||||||
// var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
var shaper = &testdata.shaper;
|
||||||
// defer screen.deinit();
|
var it = shaper.runIterator(
|
||||||
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
testdata.cache,
|
||||||
// try screen.testWriteString(">");
|
&screen,
|
||||||
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } };
|
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||||
// try screen.testWriteString("=");
|
null,
|
||||||
//
|
null,
|
||||||
// var shaper = &testdata.shaper;
|
);
|
||||||
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null);
|
var count: usize = 0;
|
||||||
// var count: usize = 0;
|
while (try it.next(alloc)) |run| {
|
||||||
// while (try it.next(alloc)) |run| {
|
count += 1;
|
||||||
// count += 1;
|
_ = try shaper.shape(run);
|
||||||
// _ = try shaper.shape(run);
|
}
|
||||||
// }
|
try testing.expectEqual(@as(usize, 2), count);
|
||||||
// try testing.expectEqual(@as(usize, 2), count);
|
}
|
||||||
// }
|
|
||||||
//
|
// Changing bg color should split
|
||||||
// // Same bg color should not split
|
{
|
||||||
// {
|
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||||
// var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
defer screen.deinit();
|
||||||
// defer screen.deinit();
|
try screen.setAttribute(.{ .direct_color_bg = .{ .r = 1, .g = 2, .b = 3 } });
|
||||||
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } };
|
try screen.testWriteString(">");
|
||||||
// try screen.testWriteString(">");
|
try screen.setAttribute(.{ .direct_color_bg = .{ .r = 3, .g = 2, .b = 1 } });
|
||||||
// try screen.testWriteString("=");
|
try screen.testWriteString("=");
|
||||||
//
|
|
||||||
// var shaper = &testdata.shaper;
|
var shaper = &testdata.shaper;
|
||||||
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null);
|
var it = shaper.runIterator(
|
||||||
// var count: usize = 0;
|
testdata.cache,
|
||||||
// while (try it.next(alloc)) |run| {
|
&screen,
|
||||||
// count += 1;
|
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||||
// _ = try shaper.shape(run);
|
null,
|
||||||
// }
|
null,
|
||||||
// try testing.expectEqual(@as(usize, 1), count);
|
);
|
||||||
// }
|
var count: usize = 0;
|
||||||
// }
|
while (try it.next(alloc)) |run| {
|
||||||
|
count += 1;
|
||||||
|
_ = try shaper.shape(run);
|
||||||
|
}
|
||||||
|
try testing.expectEqual(@as(usize, 2), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same bg color should not split
|
||||||
|
{
|
||||||
|
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||||
|
defer screen.deinit();
|
||||||
|
try screen.setAttribute(.{ .direct_color_bg = .{ .r = 1, .g = 2, .b = 3 } });
|
||||||
|
try screen.testWriteString(">");
|
||||||
|
try screen.testWriteString("=");
|
||||||
|
|
||||||
|
var shaper = &testdata.shaper;
|
||||||
|
var it = shaper.runIterator(
|
||||||
|
testdata.cache,
|
||||||
|
&screen,
|
||||||
|
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
var count: usize = 0;
|
||||||
|
while (try it.next(alloc)) |run| {
|
||||||
|
count += 1;
|
||||||
|
_ = try shaper.shape(run);
|
||||||
|
}
|
||||||
|
try testing.expectEqual(@as(usize, 1), count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const TestShaper = struct {
|
const TestShaper = struct {
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
@ -54,6 +54,9 @@ pub const RunIterator = struct {
|
|||||||
// Allow the hook to prepare
|
// Allow the hook to prepare
|
||||||
try self.hooks.prepare();
|
try self.hooks.prepare();
|
||||||
|
|
||||||
|
// Let's get our style that we'll expect for the run.
|
||||||
|
const style = self.row.style(&cells[0]);
|
||||||
|
|
||||||
// Go through cell by cell and accumulate while we build our run.
|
// Go through cell by cell and accumulate while we build our run.
|
||||||
var j: usize = self.i;
|
var j: usize = self.i;
|
||||||
while (j < max) : (j += 1) {
|
while (j < max) : (j += 1) {
|
||||||
@ -92,14 +95,13 @@ pub const RunIterator = struct {
|
|||||||
|
|
||||||
// Text runs break when font styles change so we need to get
|
// Text runs break when font styles change so we need to get
|
||||||
// the proper style.
|
// the proper style.
|
||||||
const style: font.Style = style: {
|
const font_style: font.Style = style: {
|
||||||
// TODO(paged-terminal)
|
if (style.flags.bold) {
|
||||||
// if (cell.attrs.bold) {
|
if (style.flags.italic) break :style .bold_italic;
|
||||||
// if (cell.attrs.italic) break :style .bold_italic;
|
break :style .bold;
|
||||||
// break :style .bold;
|
}
|
||||||
// }
|
|
||||||
//
|
if (style.flags.italic) break :style .italic;
|
||||||
// if (cell.attrs.italic) break :style .italic;
|
|
||||||
break :style .regular;
|
break :style .regular;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,7 +168,7 @@ pub const RunIterator = struct {
|
|||||||
if (try self.indexForCell(
|
if (try self.indexForCell(
|
||||||
alloc,
|
alloc,
|
||||||
cell,
|
cell,
|
||||||
style,
|
font_style,
|
||||||
presentation,
|
presentation,
|
||||||
)) |idx| break :font_info .{ .idx = idx };
|
)) |idx| break :font_info .{ .idx = idx };
|
||||||
|
|
||||||
@ -175,7 +177,7 @@ pub const RunIterator = struct {
|
|||||||
if (try self.group.indexForCodepoint(
|
if (try self.group.indexForCodepoint(
|
||||||
alloc,
|
alloc,
|
||||||
0xFFFD, // replacement char
|
0xFFFD, // replacement char
|
||||||
style,
|
font_style,
|
||||||
presentation,
|
presentation,
|
||||||
)) |idx| break :font_info .{ .idx = idx, .fallback = 0xFFFD };
|
)) |idx| break :font_info .{ .idx = idx, .fallback = 0xFFFD };
|
||||||
|
|
||||||
@ -183,7 +185,7 @@ pub const RunIterator = struct {
|
|||||||
if (try self.group.indexForCodepoint(
|
if (try self.group.indexForCodepoint(
|
||||||
alloc,
|
alloc,
|
||||||
' ',
|
' ',
|
||||||
style,
|
font_style,
|
||||||
presentation,
|
presentation,
|
||||||
)) |idx| break :font_info .{ .idx = idx, .fallback = ' ' };
|
)) |idx| break :font_info .{ .idx = idx, .fallback = ' ' };
|
||||||
|
|
||||||
|
@ -2059,6 +2059,15 @@ pub const Pin = struct {
|
|||||||
return self.page.data.lookupGrapheme(cell);
|
return self.page.data.lookupGrapheme(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the style for the given cell in this pin.
|
||||||
|
pub fn style(self: Pin, cell: *pagepkg.Cell) stylepkg.Style {
|
||||||
|
if (cell.style_id == stylepkg.default_id) return .{};
|
||||||
|
return self.page.data.styles.lookupId(
|
||||||
|
self.page.data.memory,
|
||||||
|
cell.style_id,
|
||||||
|
).?.*;
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterators. These are the same as PageList iterator funcs but operate
|
/// Iterators. These are the same as PageList iterator funcs but operate
|
||||||
/// on pins rather than points. This is MUCH more efficient than calling
|
/// on pins rather than points. This is MUCH more efficient than calling
|
||||||
/// pointFromPin and building up the iterator from points.
|
/// pointFromPin and building up the iterator from points.
|
||||||
|
Reference in New Issue
Block a user