mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-17 04:52:47 +00:00
inspector: remove cell picker
This commit is contained in:
@@ -3874,36 +3874,8 @@ pub fn mouseButtonCallback(
|
||||
// log.debug("mouse action={} button={} mods={}", .{ action, button, mods });
|
||||
|
||||
// If we have an inspector, we always queue a render
|
||||
if (self.inspector) |insp| {
|
||||
if (self.inspector != null) {
|
||||
defer self.queueRender() catch {};
|
||||
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
|
||||
// If the inspector is requesting a cell, then we intercept
|
||||
// left mouse clicks and send them to the inspector.
|
||||
if (insp.cell == .requested and
|
||||
button == .left and
|
||||
action == .press)
|
||||
{
|
||||
const pos = try self.rt_surface.getCursorPos();
|
||||
const point = self.posToViewport(pos.x, pos.y);
|
||||
const screen: *terminal.Screen = self.renderer_state.terminal.screens.active;
|
||||
const p = screen.pages.pin(.{ .viewport = point }) orelse {
|
||||
log.warn("failed to get pin for clicked point", .{});
|
||||
return false;
|
||||
};
|
||||
|
||||
insp.cell.select(
|
||||
self.alloc,
|
||||
p,
|
||||
point.x,
|
||||
point.y,
|
||||
) catch |err| {
|
||||
log.warn("error selecting cell for inspector err={}", .{err});
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Always record our latest mouse state
|
||||
|
||||
@@ -14,76 +14,13 @@ const terminal = @import("../terminal/main.zig");
|
||||
const inspector = @import("main.zig");
|
||||
const widgets = @import("widgets.zig");
|
||||
|
||||
/// The window names. These are used with docking so we need to have access.
|
||||
const window_cell = "Cell";
|
||||
const window_termio = "Terminal IO";
|
||||
const window_imgui_demo = "Dear ImGui Demo";
|
||||
|
||||
/// Mouse state that we track in addition to normal mouse states that
|
||||
/// Ghostty always knows about.
|
||||
mouse: widgets.surface.Mouse = .{},
|
||||
|
||||
/// A selected cell.
|
||||
cell: CellInspect = .{ .idle = {} },
|
||||
|
||||
// ImGui state
|
||||
gui: widgets.surface.Inspector,
|
||||
|
||||
const CellInspect = union(enum) {
|
||||
/// Idle, no cell inspection is requested
|
||||
idle: void,
|
||||
|
||||
/// Requested, a cell is being picked.
|
||||
requested: void,
|
||||
|
||||
/// The cell has been picked and set to this. This is a copy so that
|
||||
/// if the cell contents change we still have the original cell.
|
||||
selected: Selected,
|
||||
|
||||
const Selected = struct {
|
||||
alloc: Allocator,
|
||||
row: usize,
|
||||
col: usize,
|
||||
cell: inspector.Cell,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *CellInspect) void {
|
||||
switch (self.*) {
|
||||
.idle, .requested => {},
|
||||
.selected => |*v| v.cell.deinit(v.alloc),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(self: *CellInspect) void {
|
||||
switch (self.*) {
|
||||
.idle => self.* = .requested,
|
||||
.selected => |*v| {
|
||||
v.cell.deinit(v.alloc);
|
||||
self.* = .requested;
|
||||
},
|
||||
.requested => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(
|
||||
self: *CellInspect,
|
||||
alloc: Allocator,
|
||||
pin: terminal.Pin,
|
||||
x: usize,
|
||||
y: usize,
|
||||
) !void {
|
||||
assert(self.* == .requested);
|
||||
const cell = try inspector.Cell.init(alloc, pin);
|
||||
errdefer cell.deinit(alloc);
|
||||
self.* = .{ .selected = .{
|
||||
.alloc = alloc,
|
||||
.row = y,
|
||||
.col = x,
|
||||
.cell = cell,
|
||||
} };
|
||||
}
|
||||
};
|
||||
|
||||
/// Setup the ImGui state. This requires an ImGui context to be set.
|
||||
pub fn setup() void {
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO();
|
||||
@@ -126,7 +63,6 @@ pub fn init(alloc: Allocator) !Inspector {
|
||||
|
||||
pub fn deinit(self: *Inspector, alloc: Allocator) void {
|
||||
self.gui.deinit(alloc);
|
||||
self.cell.deinit();
|
||||
}
|
||||
|
||||
/// Record a keyboard event.
|
||||
@@ -179,66 +115,3 @@ pub fn render(
|
||||
self.mouse,
|
||||
);
|
||||
}
|
||||
|
||||
/// TODO: OLD, REMOVE EVENTUALLY ONCE WE MIGRATE FUNCTIONALITY
|
||||
fn renderCellWindow(self: *Inspector) void {
|
||||
// Start our window. If we're collapsed we do nothing.
|
||||
defer cimgui.c.ImGui_End();
|
||||
if (!cimgui.c.ImGui_Begin(
|
||||
window_cell,
|
||||
null,
|
||||
cimgui.c.ImGuiWindowFlags_NoFocusOnAppearing,
|
||||
)) return;
|
||||
|
||||
// Our popup for the picker
|
||||
const popup_picker = "Cell Picker";
|
||||
|
||||
if (cimgui.c.ImGui_Button("Picker")) {
|
||||
// Request a cell
|
||||
self.cell.request();
|
||||
|
||||
cimgui.c.ImGui_OpenPopup(
|
||||
popup_picker,
|
||||
cimgui.c.ImGuiPopupFlags_None,
|
||||
);
|
||||
}
|
||||
|
||||
if (cimgui.c.ImGui_BeginPopupModal(
|
||||
popup_picker,
|
||||
null,
|
||||
cimgui.c.ImGuiWindowFlags_AlwaysAutoResize,
|
||||
)) popup: {
|
||||
defer cimgui.c.ImGui_EndPopup();
|
||||
|
||||
// Once we select a cell, close this popup.
|
||||
if (self.cell == .selected) {
|
||||
cimgui.c.ImGui_CloseCurrentPopup();
|
||||
break :popup;
|
||||
}
|
||||
|
||||
cimgui.c.ImGui_Text(
|
||||
"Click on a cell in the terminal to inspect it.\n" ++
|
||||
"The click will be intercepted by the picker, \n" ++
|
||||
"so it won't be sent to the terminal.",
|
||||
);
|
||||
cimgui.c.ImGui_Separator();
|
||||
|
||||
if (cimgui.c.ImGui_Button("Cancel")) {
|
||||
cimgui.c.ImGui_CloseCurrentPopup();
|
||||
}
|
||||
} // cell pick popup
|
||||
|
||||
cimgui.c.ImGui_Separator();
|
||||
|
||||
if (self.cell != .selected) {
|
||||
cimgui.c.ImGui_Text("No cell selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
const selected = self.cell.selected;
|
||||
selected.cell.renderTable(
|
||||
self.surface.renderer_state.terminal,
|
||||
selected.col,
|
||||
selected.row,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
const std = @import("std");
|
||||
const assert = @import("../quirks.zig").inlineAssert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const cimgui = @import("dcimgui");
|
||||
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.ImGui_BeginTable(
|
||||
"table_cursor",
|
||||
2,
|
||||
cimgui.c.ImGuiTableFlags_None,
|
||||
);
|
||||
defer cimgui.c.ImGui_EndTable();
|
||||
|
||||
{
|
||||
cimgui.c.ImGui_TableNextRow();
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text("Grid Position");
|
||||
}
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
cimgui.c.ImGui_Text("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.ImGui_TableNextRow();
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text("Codepoints");
|
||||
}
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
if (cimgui.c.ImGui_BeginListBox("##codepoints", .{ .x = 0, .y = 0 })) {
|
||||
defer cimgui.c.ImGui_EndListBox();
|
||||
|
||||
if (self.codepoint == 0) {
|
||||
_ = cimgui.c.ImGui_SelectableEx("(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.ImGui_SelectableEx(key.ptr, false, 0, .{});
|
||||
}
|
||||
|
||||
// All extras
|
||||
for (self.cps) |cp| {
|
||||
const key = std.fmt.bufPrintZ(&buf, "U+{X}", .{cp}) catch
|
||||
"<internal error>";
|
||||
_ = cimgui.c.ImGui_SelectableEx(key.ptr, false, 0, .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Character width property
|
||||
cimgui.c.ImGui_TableNextRow();
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text("Width Property");
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
cimgui.c.ImGui_Text(@tagName(self.wide));
|
||||
|
||||
// If we have a color then we show the color
|
||||
cimgui.c.ImGui_TableNextRow();
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text("Foreground Color");
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
switch (self.style.fg_color) {
|
||||
.none => cimgui.c.ImGui_Text("default"),
|
||||
.palette => |idx| {
|
||||
const rgb = t.colors.palette.current[idx];
|
||||
cimgui.c.ImGui_Text("Palette %d", 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.ImGui_ColorEdit3(
|
||||
"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.ImGui_ColorEdit3(
|
||||
"color_fg",
|
||||
&color,
|
||||
cimgui.c.ImGuiColorEditFlags_DisplayHex |
|
||||
cimgui.c.ImGuiColorEditFlags_NoPicker |
|
||||
cimgui.c.ImGuiColorEditFlags_NoLabel,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
cimgui.c.ImGui_TableNextRow();
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text("Background Color");
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
switch (self.style.bg_color) {
|
||||
.none => cimgui.c.ImGui_Text("default"),
|
||||
.palette => |idx| {
|
||||
const rgb = t.colors.palette.current[idx];
|
||||
cimgui.c.ImGui_Text("Palette %d", 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.ImGui_ColorEdit3(
|
||||
"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.ImGui_ColorEdit3(
|
||||
"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.ImGui_TableNextRow();
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
||||
cimgui.c.ImGui_Text(style.ptr);
|
||||
}
|
||||
{
|
||||
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
||||
cimgui.c.ImGui_Text("true");
|
||||
}
|
||||
}
|
||||
|
||||
cimgui.c.ImGui_TextDisabled("(Any styles not shown are not currently set)");
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,3 @@
|
||||
// TODO: Remove
|
||||
pub const cell = @import("cell.zig");
|
||||
pub const Cell = cell.Cell;
|
||||
|
||||
pub const widgets = @import("widgets.zig");
|
||||
pub const Inspector = @import("Inspector.zig");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user