diff --git a/src/Inspector.zig b/src/Inspector.zig index f7c2885c1..fcfa8aa99 100644 --- a/src/Inspector.zig +++ b/src/Inspector.zig @@ -7,6 +7,7 @@ const std = @import("std"); const builtin = @import("builtin"); const cimgui = @import("cimgui"); const Surface = @import("Surface.zig"); +const input = @import("input.zig"); const terminal = @import("terminal/main.zig"); /// The window names. These are used with docking so we need to have access. @@ -22,10 +23,22 @@ surface: *Surface, /// is used to set up the initial window positions. first_render: bool = true, +/// Window show states show_modes_window: bool = true, show_screen_window: bool = true, show_size_window: bool = true, +/// Mouse state that we track in addition to normal mouse states that +/// Ghostty always knows about. +mouse: struct { + /// Last hovered x/y + last_xpos: f64 = 0, + last_ypos: f64 = 0, + + /// Last hovered screen point + last_point: terminal.point.ScreenPoint = .{}, +} = .{}, + /// Setup the ImGui state. This requires an ImGui context to be set. pub fn setup() void { const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); @@ -440,7 +453,7 @@ fn renderSizeWindow(self: *Inspector) void { { _ = cimgui.c.igTableSetColumnIndex(1); cimgui.c.igText( - "%d x %d", + "%dpx x %dpx", self.surface.screen_size.width, self.surface.screen_size.height, ); @@ -457,7 +470,7 @@ fn renderSizeWindow(self: *Inspector) void { { _ = cimgui.c.igTableSetColumnIndex(1); cimgui.c.igText( - "%d x %d", + "%dc x %dr", self.surface.grid_size.columns, self.surface.grid_size.rows, ); @@ -474,7 +487,7 @@ fn renderSizeWindow(self: *Inspector) void { { _ = cimgui.c.igTableSetColumnIndex(1); cimgui.c.igText( - "%d x %d", + "%dpx x %dpx", self.surface.cell_size.width, self.surface.cell_size.height, ); @@ -491,7 +504,7 @@ fn renderSizeWindow(self: *Inspector) void { { _ = cimgui.c.igTableSetColumnIndex(1); cimgui.c.igText( - "T=%d B=%d L=%d R=%d", + "T=%d B=%d L=%d R=%d px", self.surface.padding.top, self.surface.padding.bottom, self.surface.padding.left, @@ -543,4 +556,125 @@ fn renderSizeWindow(self: *Inspector) void { } } } + + cimgui.c.igSeparatorText("Mouse"); + + { + _ = cimgui.c.igBeginTable( + "table_mouse", + 2, + cimgui.c.ImGuiTableFlags_None, + .{ .x = 0, .y = 0 }, + 0, + ); + defer cimgui.c.igEndTable(); + + const mouse = &self.surface.mouse; + const t = self.surface.renderer_state.terminal; + + { + const hover_point = self.mouse.last_point.toViewport(&t.screen); + cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); + { + _ = cimgui.c.igTableSetColumnIndex(0); + cimgui.c.igText("Hover Grid"); + } + { + _ = cimgui.c.igTableSetColumnIndex(1); + cimgui.c.igText( + "row=%d, col=%d", + hover_point.y, + hover_point.x, + ); + } + } + + { + cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); + { + _ = cimgui.c.igTableSetColumnIndex(0); + cimgui.c.igText("Hover Point"); + } + { + _ = cimgui.c.igTableSetColumnIndex(1); + cimgui.c.igText( + "(%dpx, %dpx)", + @as(u32, @intFromFloat(self.mouse.last_xpos)), + @as(u32, @intFromFloat(self.mouse.last_ypos)), + ); + } + } + + const any_click = for (mouse.click_state) |state| { + if (state == .press) break true; + } else false; + + click: { + cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); + { + _ = cimgui.c.igTableSetColumnIndex(0); + cimgui.c.igText("Click State"); + } + { + _ = cimgui.c.igTableSetColumnIndex(1); + if (!any_click) { + cimgui.c.igText("none"); + break :click; + } + + for (mouse.click_state, 0..) |state, i| { + if (state != .press) continue; + const button: input.MouseButton = @enumFromInt(i); + cimgui.c.igSameLine(0, 0); + cimgui.c.igText("%s", (switch (button) { + .unknown => "?", + .left => "L", + .middle => "M", + .right => "R", + .four => "{4}", + .five => "{5}", + .six => "{6}", + .seven => "{7}", + .eight => "{8}", + .nine => "{9}", + .ten => "{10}", + .eleven => "{11}", + }).ptr); + } + } + } + + { + const left_click_point = mouse.left_click_point.toViewport(&t.screen); + cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); + { + _ = cimgui.c.igTableSetColumnIndex(0); + cimgui.c.igText("Click Grid"); + } + { + _ = cimgui.c.igTableSetColumnIndex(1); + cimgui.c.igText( + "row=%d, col=%d", + left_click_point.y, + left_click_point.x, + ); + } + } + + { + cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); + { + _ = cimgui.c.igTableSetColumnIndex(0); + cimgui.c.igText("Click Point"); + } + { + _ = cimgui.c.igTableSetColumnIndex(1); + cimgui.c.igText( + "(%dpx, %dpx)", + @as(u32, @intFromFloat(mouse.left_click_xpos)), + @as(u32, @intFromFloat(mouse.left_click_ypos)), + ); + } + } + } } diff --git a/src/Surface.zig b/src/Surface.zig index b59a90daf..0da68d1f9 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1615,6 +1615,9 @@ pub fn mouseButtonCallback( self.mouse.click_state[@intCast(@intFromEnum(button))] = action; self.mouse.mods = @bitCast(mods); + // If we have an inspector, we always queue a render + if (self.inspector != null) try self.queueRender(); + // Always show the mouse again if it is hidden if (self.mouse.hidden) self.showMouse(); @@ -1788,6 +1791,17 @@ pub fn cursorPosCallback( self.renderer_state.mutex.lock(); defer self.renderer_state.mutex.unlock(); + // If we have an inspector, we need to always record position information + if (self.inspector) |insp| { + insp.mouse.last_xpos = pos.x; + insp.mouse.last_ypos = pos.y; + + const point = self.posToViewport(pos.x, pos.y); + insp.mouse.last_point = point.toScreen(&self.io.terminal.screen); + + try self.queueRender(); + } + // Do a mouse report if (self.io.terminal.flags.mouse_event != .none) report: { // Shift overrides mouse "grabbing" in the window, taken from Kitty.