ghostty/src/inspector/cell.zig
cryptocode dbe6d6c29c Inspector: change color picker to use hex values
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.
2024-08-28 17:55:59 +02:00

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)");
}
};