Something went wrong. Try again.
A GPU terminal emulator for Wayland and Windows on libghostty-vt, drawing with Vulkan, running a program on a pty or a serial port.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! The button that says whether programs are reporting their progress or//! status, and opens the status window when pressed.//!//! It is the most pressing of the reports' marks, in its color, and how many//! there are: `✗ 3` when one of three has failed. With none it is still//! there, a mark alone, greyed out. The title bar, the tab//! bar or a badge over the terminal carries it, whichever the window shows;//! each says where it is, and this draws it there.
const std = @import("std");const vt = @import("ghostty-vt");
const Grid = @import("aerie").text.Family;const Glyphs = @import("aerie").text.Glyphs;const frame = @import("../renderer/frame.zig");
/// What the button shows.pub const Indicator = struct { /// The mark of the most pressing report, and its color. mark: u21, color: vt.color.RGB, /// How many reports there are. With none, the mark is shown alone. count: u32,};
/// The digits of `n`, at most four of them: more is shown as `999+`.fn digits(n: u32, buf: []u21) []const u21 { var tmp: [8]u8 = undefined; const text = if (n > 999) "999+" else std.fmt.bufPrint(&tmp, "{d}", .{n}) catch unreachable; for (text, 0..) |c, i| buf[i] = c; return buf[0..text.len];}
/// Cells the button's content takes: the mark, a space, the count; or the/// mark alone, with no reports.pub fn cells(ind: Indicator) u32 { if (ind.count == 0) return 1; var buf: [4]u21 = undefined; return 2 + @as(u32, @intCast(digits(ind.count, &buf).len));}
/// The button's width in physical pixels, with `pad` either side.pub fn width(ind: Indicator, cell_width: u32, pad: u32) u32 { return cells(ind) * cell_width + 2 * pad;}
/// Draws the button in the rectangle `x`, `y`, `w`, `h`: its background/// when `fill` is given, and its content centered, the count in `text`.pub fn build( b: *frame.Builder, grid: *Grid, glyphs: *Glyphs, x: u32, y: u32, w: u32, h: u32, ind: Indicator, text: vt.color.RGB, fill: ?vt.color.RGB,) !void { if (fill) |f| try b.rect(@floatFromInt(x), @floatFromInt(y), @floatFromInt(w), @floatFromInt(h), .{ f.r, f.g, f.b, 255 }); const m = grid.metrics; const content = cells(ind) * m.cell_width; const left = x + (w -| content) / 2; const top = y + (h -| m.cell_height) / 2; try b.text(grid, glyphs, @floatFromInt(left), @floatFromInt(top), &.{ind.mark}, .{ ind.color.r, ind.color.g, ind.color.b, 255 }); if (ind.count == 0) return; var buf: [4]u21 = undefined; try b.text(grid, glyphs, @floatFromInt(left + 2 * m.cell_width), @floatFromInt(top), digits(ind.count, &buf), .{ text.r, text.g, text.b, 255 });}
test "a count past three digits is cut short" { const ind: Indicator = .{ .mark = 0x25cf, .color = .{}, .count = 7 }; try std.testing.expectEqual(@as(u32, 3), cells(ind)); try std.testing.expectEqual(@as(u32, 6), cells(.{ .mark = 0x25cf, .color = .{}, .count = 12345 })); try std.testing.expectEqual(@as(u32, 3 * 10 + 8), width(ind, 10, 4)); // With nothing reported, the mark alone. try std.testing.expectEqual(@as(u32, 1), cells(.{ .mark = 0x25cb, .color = .{}, .count = 0 }));}