implement overline decoration (SGR 53/55)

This commit is contained in:
Marijn Besseling
2024-10-20 12:51:18 +02:00
committed by Mitchell Hashimoto
parent 1134a9cbea
commit 4496e7d314
9 changed files with 134 additions and 0 deletions

View File

@ -27,6 +27,8 @@ pub const Sprite = enum(u32) {
strikethrough, strikethrough,
overline,
cursor_rect, cursor_rect,
cursor_hollow_rect, cursor_hollow_rect,
cursor_bar, cursor_bar,

View File

@ -150,6 +150,16 @@ pub fn renderGlyph(
self.thickness, self.thickness,
), ),
.overline => try underline.renderGlyph(
alloc,
atlas,
@enumFromInt(cp),
width,
self.height,
0,
self.thickness,
),
.powerline => powerline: { .powerline => powerline: {
const f: Powerline = .{ const f: Powerline = .{
.width = width, .width = width,
@ -166,6 +176,7 @@ pub fn renderGlyph(
const Kind = enum { const Kind = enum {
box, box,
underline, underline,
overline,
strikethrough, strikethrough,
powerline, powerline,
@ -179,6 +190,9 @@ const Kind = enum {
.underline_curly, .underline_curly,
=> .underline, => .underline,
.overline,
=> .overline,
.strikethrough, .strikethrough,
=> .strikethrough, => .strikethrough,

View File

@ -34,6 +34,7 @@ pub fn renderGlyph(
.underline_dotted => try drawDotted(alloc, width, line_thickness), .underline_dotted => try drawDotted(alloc, width, line_thickness),
.underline_dashed => try drawDashed(alloc, width, line_thickness), .underline_dashed => try drawDashed(alloc, width, line_thickness),
.underline_curly => try drawCurly(alloc, width, line_thickness), .underline_curly => try drawCurly(alloc, width, line_thickness),
.overline => try drawSingle(alloc, width, line_thickness),
.strikethrough => try drawSingle(alloc, width, line_thickness), .strikethrough => try drawSingle(alloc, width, line_thickness),
else => unreachable, else => unreachable,
}; };

View File

@ -2492,6 +2492,18 @@ fn rebuildCells(
); );
}; };
if (style.flags.overline) self.addOverline(
@intCast(x),
@intCast(y),
fg,
alpha
) catch |err| {
log.warn(
"error adding overline to cell, will be invalid x={} y={}, err={}",
.{ x, y, err },
);
};
// If we're at or past the end of our shaper run then // If we're at or past the end of our shaper run then
// we need to get the next run from the run iterator. // we need to get the next run from the run iterator.
if (shaper_cells != null and shaper_cells_i >= shaper_cells.?.len) { if (shaper_cells != null and shaper_cells_i >= shaper_cells.?.len) {
@ -2709,6 +2721,38 @@ fn addUnderline(
}); });
} }
/// Add a overline decoration to the specified cell
fn addOverline(
self: *Metal,
x: terminal.size.CellCountInt,
y: terminal.size.CellCountInt,
color: terminal.color.RGB,
alpha: u8,
) !void {
const render = try self.font_grid.renderGlyph(
self.alloc,
font.sprite_index,
@intFromEnum(font.Sprite.overline),
.{
.cell_width = 1,
.grid_metrics = self.grid_metrics,
},
);
try self.cells.add(self.alloc, .overline, .{
.mode = .fg,
.grid_pos = .{ @intCast(x), @intCast(y) },
.constraint_width = 1,
.color = .{ color.r, color.g, color.b, alpha },
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
.glyph_size = .{ render.glyph.width, render.glyph.height },
.bearings = .{
@intCast(render.glyph.offset_x),
@intCast(render.glyph.offset_y),
},
});
}
/// Add a strikethrough decoration to the specified cell /// Add a strikethrough decoration to the specified cell
fn addStrikethrough( fn addStrikethrough(
self: *Metal, self: *Metal,

View File

@ -1586,6 +1586,19 @@ pub fn rebuildCells(
); );
}; };
if (style.flags.overline) self.addOverline(
@intCast(x),
@intCast(y),
fg,
alpha,
bg_color,
) catch |err| {
log.warn(
"error adding overline to cell, will be invalid x={} y={}, err={}",
.{ x, y, err },
);
};
// If we're at or past the end of our shaper run then // If we're at or past the end of our shaper run then
// we need to get the next run from the run iterator. // we need to get the next run from the run iterator.
if (shaper_cells != null and shaper_cells_i >= shaper_cells.?.len) { if (shaper_cells != null and shaper_cells_i >= shaper_cells.?.len) {
@ -1955,6 +1968,47 @@ fn addUnderline(
}); });
} }
/// Add an overline decoration to the specified cell
fn addOverline(
self: *OpenGL,
x: terminal.size.CellCountInt,
y: terminal.size.CellCountInt,
color: terminal.color.RGB,
alpha: u8,
bg: [4]u8,
) !void {
const render = try self.font_grid.renderGlyph(
self.alloc,
font.sprite_index,
@intFromEnum(font.Sprite.overline),
.{
.cell_width = 1,
.grid_metrics = self.grid_metrics,
},
);
try self.cells.append(self.alloc, .{
.mode = .fg,
.grid_col = @intCast(x),
.grid_row = @intCast(y),
.grid_width = 1,
.glyph_x = render.glyph.atlas_x,
.glyph_y = render.glyph.atlas_y,
.glyph_width = render.glyph.width,
.glyph_height = render.glyph.height,
.glyph_offset_x = render.glyph.offset_x,
.glyph_offset_y = render.glyph.offset_y,
.r = color.r,
.g = color.g,
.b = color.b,
.a = alpha,
.bg_r = bg[0],
.bg_g = bg[1],
.bg_b = bg[2],
.bg_a = bg[3],
});
}
/// Add a strikethrough decoration to the specified cell /// Add a strikethrough decoration to the specified cell
fn addStrikethrough( fn addStrikethrough(
self: *OpenGL, self: *OpenGL,

View File

@ -12,6 +12,7 @@ pub const Key = enum {
text, text,
underline, underline,
strikethrough, strikethrough,
overline,
/// Returns the GPU vertex type for this key. /// Returns the GPU vertex type for this key.
pub fn CellType(self: Key) type { pub fn CellType(self: Key) type {
@ -21,6 +22,7 @@ pub const Key = enum {
.text, .text,
.underline, .underline,
.strikethrough, .strikethrough,
.overline,
=> mtl_shaders.CellText, => mtl_shaders.CellText,
}; };
} }
@ -196,6 +198,7 @@ pub const Contents = struct {
.text, .text,
.underline, .underline,
.strikethrough, .strikethrough,
.overline,
// We have a special list containing the cursor cell at the start // We have a special list containing the cursor cell at the start
// of our fg row pool, so we need to add 1 to the y to get the // of our fg row pool, so we need to add 1 to the y to get the
// correct index. // correct index.

View File

@ -1618,6 +1618,14 @@ pub fn setAttribute(self: *Screen, attr: sgr.Attribute) !void {
self.cursor.style.underline_color = .none; self.cursor.style.underline_color = .none;
}, },
.overline => {
self.cursor.style.flags.overline = true;
},
.reset_overline => {
self.cursor.style.flags.overline = false;
},
.blink => { .blink => {
self.cursor.style.flags.blink = true; self.cursor.style.flags.blink = true;
}, },

View File

@ -37,6 +37,10 @@ pub const Attribute = union(enum) {
@"256_underline_color": u8, @"256_underline_color": u8,
reset_underline_color: void, reset_underline_color: void,
// Overline the text
overline: void,
reset_overline: void,
/// Blink the text /// Blink the text
blink: void, blink: void,
reset_blink: void, reset_blink: void,
@ -237,6 +241,9 @@ pub const Parser = struct {
49 => return Attribute{ .reset_bg = {} }, 49 => return Attribute{ .reset_bg = {} },
53 => return Attribute{ .overline = {} },
55 => return Attribute{ .reset_overline = {} },
58 => if (slice.len >= 5 and slice[1] == 2) { 58 => if (slice.len >= 5 and slice[1] == 2) {
self.idx += 4; self.idx += 4;

View File

@ -35,6 +35,7 @@ pub const Style = struct {
inverse: bool = false, inverse: bool = false,
invisible: bool = false, invisible: bool = false,
strikethrough: bool = false, strikethrough: bool = false,
overline: bool = false,
underline: sgr.Attribute.Underline = .none, underline: sgr.Attribute.Underline = .none,
} = .{}, } = .{},