mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge branch 'ghostty-org:main' into macos-window-deocrations-rework
This commit is contained in:
@ -43,17 +43,17 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
item.view = self.titleTextField
|
item.view = self.titleTextField
|
||||||
item.visibilityPriority = .user
|
item.visibilityPriority = .user
|
||||||
|
|
||||||
// NSToolbarItem.minSize and NSToolbarItem.maxSize are deprecated, and make big ugly
|
// This ensures the title text field doesn't disappear when shrinking the view
|
||||||
// warnings in Xcode when you use them, but I cannot for the life of me figure out
|
self.titleTextField.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// how to get this to work with constraints. The behavior isn't the same, instead of
|
self.titleTextField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
// shrinking the item and clipping the subview, it hides the item as soon as the
|
self.titleTextField.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
// intrinsic size of the subview gets too big for the toolbar width, regardless of
|
|
||||||
// whether I have constraints set on its width, height, or both :/
|
// Add constraints to the toolbar item's view
|
||||||
//
|
NSLayoutConstraint.activate([
|
||||||
// If someone can fix this so we don't have to use deprecated properties: Please do.
|
// Set the height constraint to match the toolbar's height
|
||||||
item.minSize = NSSize(width: 32, height: 1)
|
self.titleTextField.heightAnchor.constraint(equalToConstant: 22), // Adjust as needed
|
||||||
item.maxSize = NSSize(width: 1024, height: self.titleTextField.intrinsicContentSize.height)
|
])
|
||||||
|
|
||||||
item.isEnabled = true
|
item.isEnabled = true
|
||||||
case .resetZoom:
|
case .resetZoom:
|
||||||
item = NSToolbarItem(itemIdentifier: .resetZoom)
|
item = NSToolbarItem(itemIdentifier: .resetZoom)
|
||||||
@ -73,23 +73,27 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
// getting smaller than the max size so starts clipping. Lucky for us, two of the
|
// getting smaller than the max size so starts clipping. Lucky for us, two of the
|
||||||
// built-in spacers plus the un-zoom button item seems to exactly match the space
|
// built-in spacers plus the un-zoom button item seems to exactly match the space
|
||||||
// on the left that's reserved for the window buttons.
|
// on the left that's reserved for the window buttons.
|
||||||
return [.titleText, .flexibleSpace, .space, .space, .resetZoom]
|
return [.flexibleSpace, .titleText, .flexibleSpace]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A label that expands to fit whatever text you put in it and horizontally centers itself in the current window.
|
/// A label that expands to fit whatever text you put in it and horizontally centers itself in the current window.
|
||||||
fileprivate class CenteredDynamicLabel: NSTextField {
|
fileprivate class CenteredDynamicLabel: NSTextField {
|
||||||
override func viewDidMoveToSuperview() {
|
override func viewDidMoveToSuperview() {
|
||||||
// Truncate the title when it gets too long, cutting it off with an ellipsis.
|
// Configure the text field
|
||||||
|
isEditable = false
|
||||||
|
isBordered = false
|
||||||
|
drawsBackground = false
|
||||||
|
alignment = .center
|
||||||
|
lineBreakMode = .byTruncatingTail
|
||||||
cell?.truncatesLastVisibleLine = true
|
cell?.truncatesLastVisibleLine = true
|
||||||
cell?.lineBreakMode = .byCharWrapping
|
|
||||||
|
// Use Auto Layout
|
||||||
// Make the text field as small as possible while fitting its text.
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
setContentHuggingPriority(.required, for: .horizontal)
|
|
||||||
cell?.alignment = .center
|
// Set content hugging and compression resistance priorities
|
||||||
|
setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
// We've changed some alignment settings, make sure the layout is updated immediately.
|
setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
needsLayout = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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,
|
||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user