mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-19 02:06:20 +03:00

The inspector currently show colors as decimal r,g,b values. With this change, the hex format is used instead. The motivation for this is that references to color typically use the hex format. One example is the palette definitions in a color scheme. Using the inspector/cell picker to help create and debug color themes should be more convenient after this change. If there's a usecase for the decimal format, we could add a config option or maybe a switch in the inspector UI.
226 lines
8.0 KiB
Zig
226 lines
8.0 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
const cimgui = @import("cimgui");
|
|
const terminal = @import("../terminal/main.zig");
|
|
|
|
/// A cell being inspected. This duplicates much of the data in
|
|
/// the terminal data structure because we want the inspector to
|
|
/// not have a reference to the terminal state or to grab any
|
|
/// locks.
|
|
pub const Cell = struct {
|
|
/// The main codepoint for this cell.
|
|
codepoint: u21,
|
|
|
|
/// Codepoints for this cell to produce a single grapheme cluster.
|
|
/// This is only non-empty if the cell is part of a multi-codepoint
|
|
/// grapheme cluster. This does NOT include the primary codepoint.
|
|
cps: []const u21,
|
|
|
|
/// The style of this cell.
|
|
style: terminal.Style,
|
|
|
|
/// Wide state of the terminal cell
|
|
wide: terminal.Cell.Wide,
|
|
|
|
pub fn init(
|
|
alloc: Allocator,
|
|
pin: terminal.Pin,
|
|
) !Cell {
|
|
const cell = pin.rowAndCell().cell;
|
|
const style = pin.style(cell);
|
|
const cps: []const u21 = if (cell.hasGrapheme()) cps: {
|
|
const src = pin.grapheme(cell).?;
|
|
assert(src.len > 0);
|
|
break :cps try alloc.dupe(u21, src);
|
|
} else &.{};
|
|
errdefer if (cps.len > 0) alloc.free(cps);
|
|
|
|
return .{
|
|
.codepoint = cell.codepoint(),
|
|
.cps = cps,
|
|
.style = style,
|
|
.wide = cell.wide,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Cell, alloc: Allocator) void {
|
|
if (self.cps.len > 0) alloc.free(self.cps);
|
|
}
|
|
|
|
pub fn renderTable(
|
|
self: *const Cell,
|
|
t: *const terminal.Terminal,
|
|
x: usize,
|
|
y: usize,
|
|
) void {
|
|
// We have a selected cell, show information about it.
|
|
_ = cimgui.c.igBeginTable(
|
|
"table_cursor",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
.{ .x = 0, .y = 0 },
|
|
0,
|
|
);
|
|
defer cimgui.c.igEndTable();
|
|
|
|
{
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText("Grid Position");
|
|
}
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
cimgui.c.igText("row=%d col=%d", y, x);
|
|
}
|
|
}
|
|
|
|
// NOTE: we don't currently write the character itself because
|
|
// we haven't hooked up imgui to our font system. That's hard! We
|
|
// can/should instead hook up our renderer to imgui and just render
|
|
// the single glyph in an image view so it looks _identical_ to the
|
|
// terminal.
|
|
codepoint: {
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText("Codepoints");
|
|
}
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
if (cimgui.c.igBeginListBox("##codepoints", .{ .x = 0, .y = 0 })) {
|
|
defer cimgui.c.igEndListBox();
|
|
|
|
if (self.codepoint == 0) {
|
|
_ = cimgui.c.igSelectable_Bool("(empty)", false, 0, .{});
|
|
break :codepoint;
|
|
}
|
|
|
|
// Primary codepoint
|
|
var buf: [256]u8 = undefined;
|
|
{
|
|
const key = std.fmt.bufPrintZ(&buf, "U+{X}", .{self.codepoint}) catch
|
|
"<internal error>";
|
|
_ = cimgui.c.igSelectable_Bool(key.ptr, false, 0, .{});
|
|
}
|
|
|
|
// All extras
|
|
for (self.cps) |cp| {
|
|
const key = std.fmt.bufPrintZ(&buf, "U+{X}", .{cp}) catch
|
|
"<internal error>";
|
|
_ = cimgui.c.igSelectable_Bool(key.ptr, false, 0, .{});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Character width property
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText("Width Property");
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
cimgui.c.igText(@tagName(self.wide));
|
|
|
|
// If we have a color then we show the color
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText("Foreground Color");
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
switch (self.style.fg_color) {
|
|
.none => cimgui.c.igText("default"),
|
|
.palette => |idx| {
|
|
const rgb = t.color_palette.colors[idx];
|
|
cimgui.c.igValue_Int("Palette", idx);
|
|
var color: [3]f32 = .{
|
|
@as(f32, @floatFromInt(rgb.r)) / 255,
|
|
@as(f32, @floatFromInt(rgb.g)) / 255,
|
|
@as(f32, @floatFromInt(rgb.b)) / 255,
|
|
};
|
|
_ = cimgui.c.igColorEdit3(
|
|
"color_fg",
|
|
&color,
|
|
cimgui.c.ImGuiColorEditFlags_DisplayHex |
|
|
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
|
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
|
);
|
|
},
|
|
|
|
.rgb => |rgb| {
|
|
var color: [3]f32 = .{
|
|
@as(f32, @floatFromInt(rgb.r)) / 255,
|
|
@as(f32, @floatFromInt(rgb.g)) / 255,
|
|
@as(f32, @floatFromInt(rgb.b)) / 255,
|
|
};
|
|
_ = cimgui.c.igColorEdit3(
|
|
"color_fg",
|
|
&color,
|
|
cimgui.c.ImGuiColorEditFlags_DisplayHex |
|
|
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
|
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
|
);
|
|
},
|
|
}
|
|
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText("Background Color");
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
switch (self.style.bg_color) {
|
|
.none => cimgui.c.igText("default"),
|
|
.palette => |idx| {
|
|
const rgb = t.color_palette.colors[idx];
|
|
cimgui.c.igValue_Int("Palette", idx);
|
|
var color: [3]f32 = .{
|
|
@as(f32, @floatFromInt(rgb.r)) / 255,
|
|
@as(f32, @floatFromInt(rgb.g)) / 255,
|
|
@as(f32, @floatFromInt(rgb.b)) / 255,
|
|
};
|
|
_ = cimgui.c.igColorEdit3(
|
|
"color_bg",
|
|
&color,
|
|
cimgui.c.ImGuiColorEditFlags_DisplayHex |
|
|
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
|
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
|
);
|
|
},
|
|
|
|
.rgb => |rgb| {
|
|
var color: [3]f32 = .{
|
|
@as(f32, @floatFromInt(rgb.r)) / 255,
|
|
@as(f32, @floatFromInt(rgb.g)) / 255,
|
|
@as(f32, @floatFromInt(rgb.b)) / 255,
|
|
};
|
|
_ = cimgui.c.igColorEdit3(
|
|
"color_bg",
|
|
&color,
|
|
cimgui.c.ImGuiColorEditFlags_DisplayHex |
|
|
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
|
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
|
);
|
|
},
|
|
}
|
|
|
|
// Boolean styles
|
|
const styles = .{
|
|
"bold", "italic", "faint", "blink",
|
|
"inverse", "invisible", "strikethrough",
|
|
};
|
|
inline for (styles) |style| style: {
|
|
if (!@field(self.style.flags, style)) break :style;
|
|
|
|
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(0);
|
|
cimgui.c.igText(style.ptr);
|
|
}
|
|
{
|
|
_ = cimgui.c.igTableSetColumnIndex(1);
|
|
cimgui.c.igText("true");
|
|
}
|
|
}
|
|
|
|
cimgui.c.igTextDisabled("(Any styles not shown are not currently set)");
|
|
}
|
|
};
|