mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
render font info in dev mode
This commit is contained in:
@ -3,18 +3,140 @@ const c = @import("c.zig");
|
||||
const imgui = @import("main.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn newFrame() void {
|
||||
c.igNewFrame();
|
||||
}
|
||||
|
||||
pub fn endFrame() void {
|
||||
c.igEndFrame();
|
||||
}
|
||||
|
||||
pub fn render() void {
|
||||
c.igRender();
|
||||
}
|
||||
pub const newFrame = c.igNewFrame;
|
||||
pub const endFrame = c.igEndFrame;
|
||||
pub const render = c.igRender;
|
||||
pub const end = c.igEnd;
|
||||
pub const beginTooltip = c.igBeginTooltip;
|
||||
pub const endTooltip = c.igEndTooltip;
|
||||
pub const spacing = c.igSpacing;
|
||||
pub const text = c.igText;
|
||||
pub const textDisabled = c.igTextDisabled;
|
||||
pub const textWrapped = c.igTextWrapped;
|
||||
pub const button = c.igButton;
|
||||
pub const sameLine = c.igSameLine;
|
||||
pub const getFontSize = c.igGetFontSize;
|
||||
pub const pushTextWrapPos = c.igPushTextWrapPos;
|
||||
pub const popTextWrapPos = c.igPopTextWrapPos;
|
||||
pub const treePop = c.igTreePop;
|
||||
|
||||
pub fn showDemoWindow(open: ?*bool) void {
|
||||
c.igShowDemoWindow(@ptrCast([*c]bool, if (open) |v| v else null));
|
||||
}
|
||||
|
||||
pub fn begin(name: [:0]const u8, open: ?*bool, flags: WindowFlags) bool {
|
||||
return c.igBegin(
|
||||
name.ptr,
|
||||
@ptrCast([*c]bool, if (open) |v| v else null),
|
||||
@bitCast(c_int, flags),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn collapsingHeader(
|
||||
label: [:0]const u8,
|
||||
visible: ?*bool,
|
||||
flags: TreeNodeFlags,
|
||||
) bool {
|
||||
return c.igCollapsingHeader_BoolPtr(
|
||||
label.ptr,
|
||||
@ptrCast([*c]bool, if (visible) |v| v else null),
|
||||
@bitCast(c_int, flags),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn isItemHovered(flags: HoveredFlags) bool {
|
||||
return c.igIsItemHovered(
|
||||
@bitCast(c_int, flags),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn treeNode(
|
||||
label: [:0]const u8,
|
||||
flags: TreeNodeFlags,
|
||||
) bool {
|
||||
return c.igTreeNodeEx_Str(
|
||||
label.ptr,
|
||||
@bitCast(c_int, flags),
|
||||
);
|
||||
}
|
||||
|
||||
pub const WindowFlags = packed struct {
|
||||
no_title_bar: bool = false,
|
||||
no_resize: bool = false,
|
||||
no_move: bool = false,
|
||||
no_scrollbar: bool = false,
|
||||
no_scrollbar_with_mouse: bool = false,
|
||||
no_collapse: bool = false,
|
||||
always_auto_resize: bool = false,
|
||||
no_background: bool = false,
|
||||
no_saved_settings: bool = false,
|
||||
no_mouse_inputs: bool = false,
|
||||
menu_bar: bool = false,
|
||||
horizontal_scroll_bar: bool = false,
|
||||
no_focus_on_appearing: bool = false,
|
||||
no_bring_to_front_on_focus: bool = false,
|
||||
always_vertical_scrollbar: bool = false,
|
||||
always_horizontal_scrollbar: bool = false,
|
||||
always_use_window_padding: bool = false,
|
||||
no_nav_inputs: bool = false,
|
||||
no_nav_focus: bool = false,
|
||||
unsaved_document: bool = false,
|
||||
no_docking: bool = false,
|
||||
_unusued_1: u1 = 0,
|
||||
nav_flattened: bool = false,
|
||||
child_window: bool = false,
|
||||
tooltip: bool = false,
|
||||
popup: bool = false,
|
||||
modal: bool = false,
|
||||
child_menu: bool = false,
|
||||
dock_node_host: bool = false,
|
||||
_padding: u3 = 0,
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(@bitSizeOf(c_int), @bitSizeOf(WindowFlags));
|
||||
}
|
||||
};
|
||||
|
||||
pub const TreeNodeFlags = packed struct {
|
||||
selected: bool = false,
|
||||
framed: bool = false,
|
||||
allow_item_overlap: bool = false,
|
||||
no_tree_push_on_open: bool = false,
|
||||
no_auto_open_on_log: bool = false,
|
||||
default_open: bool = false,
|
||||
open_on_double_click: bool = false,
|
||||
open_on_arrow: bool = false,
|
||||
leaf: bool = false,
|
||||
bullet: bool = false,
|
||||
frame_padding: bool = false,
|
||||
span_avail_width: bool = false,
|
||||
span_full_width: bool = false,
|
||||
nav_left_jumps_back_here: bool = false,
|
||||
_padding: u18 = 0,
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(@bitSizeOf(c_int), @bitSizeOf(TreeNodeFlags));
|
||||
}
|
||||
};
|
||||
|
||||
pub const HoveredFlags = packed struct {
|
||||
child_windows: bool = false,
|
||||
root_window: bool = false,
|
||||
any_window: bool = false,
|
||||
no_popup_hierarchy: bool = false,
|
||||
dock_hierarchy: bool = false,
|
||||
allow_when_blocked_by_popup: bool = false,
|
||||
allow_when_blocked_by_active_item: bool = false,
|
||||
allow_when_overlapped: bool = false,
|
||||
allow_when_disabled: bool = false,
|
||||
no_nav_override: bool = false,
|
||||
_padding: u22 = 0,
|
||||
|
||||
test {
|
||||
try std.testing.expectEqual(@bitSizeOf(c_int), @bitSizeOf(HoveredFlags));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
|
@ -2,7 +2,13 @@
|
||||
//! includes state managements and rendering.
|
||||
const DevMode = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const imgui = @import("imgui");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Atlas = @import("Atlas.zig");
|
||||
const Window = @import("Window.zig");
|
||||
|
||||
/// If this is false, the rest of the terminal will be compiled without
|
||||
/// dev mode support at all.
|
||||
@ -15,17 +21,44 @@ pub var instance: DevMode = .{};
|
||||
/// Whether to show the dev mode UI currently.
|
||||
visible: bool = false,
|
||||
|
||||
/// The window we're tracking.
|
||||
window: ?*Window = null,
|
||||
|
||||
/// Update the state associated with the dev mode. This should generally
|
||||
/// only be called paired with a render since it otherwise wastes CPU
|
||||
/// cycles.
|
||||
pub fn update(self: DevMode) void {
|
||||
_ = self;
|
||||
pub fn update(self: *DevMode) !void {
|
||||
imgui.ImplOpenGL3.newFrame();
|
||||
imgui.ImplGlfw.newFrame();
|
||||
imgui.newFrame();
|
||||
|
||||
if (imgui.begin("dev mode", null, .{})) {
|
||||
defer imgui.end();
|
||||
|
||||
if (self.window) |window| {
|
||||
if (imgui.collapsingHeader("Font Manager", null, .{})) {
|
||||
imgui.text("Glyphs: %d", window.font_group.glyphs.count());
|
||||
imgui.sameLine(0, -1);
|
||||
helpMarker("The number of glyphs loaded and rendered into a " ++
|
||||
"font atlas currently.");
|
||||
|
||||
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
|
||||
defer imgui.treePop();
|
||||
const atlas = &window.font_group.atlas_greyscale;
|
||||
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture.id));
|
||||
}
|
||||
|
||||
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
|
||||
defer imgui.treePop();
|
||||
const atlas = &window.font_group.atlas_color;
|
||||
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture_color.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just demo for now
|
||||
imgui.showDemoWindow(null);
|
||||
//imgui.showDemoWindow(null);
|
||||
}
|
||||
|
||||
/// Render the scene and return the draw data. The caller must be imgui-aware
|
||||
@ -36,3 +69,50 @@ pub fn render(self: DevMode) !*imgui.DrawData {
|
||||
imgui.render();
|
||||
return try imgui.DrawData.get();
|
||||
}
|
||||
|
||||
/// Helper to render a tooltip.
|
||||
fn helpMarker(desc: [:0]const u8) void {
|
||||
imgui.textDisabled("(?)");
|
||||
if (imgui.isItemHovered(.{})) {
|
||||
imgui.beginTooltip();
|
||||
defer imgui.endTooltip();
|
||||
imgui.pushTextWrapPos(imgui.getFontSize() * 35);
|
||||
defer imgui.popTextWrapPos();
|
||||
imgui.text(desc.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
fn atlasInfo(self: *DevMode, atlas: *Atlas, tex: ?usize) !void {
|
||||
_ = self;
|
||||
|
||||
imgui.text("Dimensions: %d x %d", atlas.size, atlas.size);
|
||||
imgui.sameLine(0, -1);
|
||||
helpMarker("The pixel dimensions of the atlas texture.");
|
||||
|
||||
imgui.text("Size: %d KB", atlas.data.len >> 10);
|
||||
imgui.sameLine(0, -1);
|
||||
helpMarker("The byte size of the atlas texture.");
|
||||
|
||||
var buf: [1024]u8 = undefined;
|
||||
imgui.text(
|
||||
"Format: %s (depth = %d)",
|
||||
(try std.fmt.bufPrintZ(&buf, "{}", .{atlas.format})).ptr,
|
||||
atlas.format.depth(),
|
||||
);
|
||||
imgui.sameLine(0, -1);
|
||||
helpMarker("The internal storage format of this atlas.");
|
||||
|
||||
if (tex) |id| {
|
||||
imgui.c.igImage(
|
||||
@intToPtr(*anyopaque, id),
|
||||
.{
|
||||
.x = @intToFloat(f32, atlas.size),
|
||||
.y = @intToFloat(f32, atlas.size),
|
||||
},
|
||||
.{ .x = 0, .y = 0 },
|
||||
.{ .x = 1, .y = 1 },
|
||||
.{ .x = 1, .y = 1, .z = 1, .w = 1 },
|
||||
.{ .x = 0, .y = 0, .z = 0, .w = 0 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -515,16 +515,24 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
|
||||
|
||||
// Add our built-in fonts so it looks slightly better
|
||||
const dev_atlas = @ptrCast(*imgui.FontAtlas, io.cval().Fonts);
|
||||
dev_atlas.addFontFromMemoryTTF(face_ttf, @intToFloat(f32, font_size.pixels()));
|
||||
dev_atlas.addFontFromMemoryTTF(
|
||||
face_ttf,
|
||||
@intToFloat(f32, font_size.pixels()),
|
||||
);
|
||||
|
||||
// Default dark style
|
||||
const style = try imgui.Style.get();
|
||||
style.colorsDark();
|
||||
|
||||
// Initialize for our window
|
||||
assert(imgui.ImplGlfw.initForOpenGL(
|
||||
@ptrCast(*imgui.ImplGlfw.GLFWWindow, window.handle),
|
||||
true,
|
||||
));
|
||||
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
||||
|
||||
// Add our window to the instance
|
||||
DevMode.instance.window = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -532,6 +540,9 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
|
||||
|
||||
pub fn destroy(self: *Window) void {
|
||||
if (DevMode.enabled) {
|
||||
// Clear the window
|
||||
DevMode.instance.window = null;
|
||||
|
||||
// Uninitialize imgui
|
||||
imgui.ImplOpenGL3.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
@ -792,6 +803,7 @@ fn keyCallback(
|
||||
.end => .end,
|
||||
.page_up => .page_up,
|
||||
.page_down => .page_down,
|
||||
.escape => .escape,
|
||||
.F1 => .f1,
|
||||
.F2 => .f2,
|
||||
.F3 => .f3,
|
||||
@ -809,6 +821,7 @@ fn keyCallback(
|
||||
},
|
||||
};
|
||||
|
||||
//log.warn("BINDING TRIGGER={}", .{trigger});
|
||||
if (win.config.keybind.set.get(trigger)) |binding_action| {
|
||||
//log.warn("BINDING ACTION={}", .{binding_action});
|
||||
|
||||
@ -964,6 +977,18 @@ fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void {
|
||||
|
||||
const win = window.getUserPointer(Window) orelse return;
|
||||
|
||||
// If our dev mode window is visible then we always schedule a render on
|
||||
// cursor move because the cursor might touch our windows.
|
||||
if (DevMode.enabled and DevMode.instance.visible) {
|
||||
win.render_timer.schedule() catch |err|
|
||||
log.err("error scheduling render timer err={}", .{err});
|
||||
|
||||
// If the mouse event was handled by imgui, ignore it.
|
||||
if (imgui.IO.get()) |io| {
|
||||
if (io.cval().WantCaptureMouse) return;
|
||||
} else |_| {}
|
||||
}
|
||||
|
||||
// If we're scrolling up or down, then send a mouse event
|
||||
if (yoff != 0) {
|
||||
const pos = window.getCursorPos() catch |err| {
|
||||
@ -1613,7 +1638,7 @@ fn renderTimerCallback(t: *libuv.Timer) void {
|
||||
};
|
||||
|
||||
if (DevMode.enabled and DevMode.instance.visible) {
|
||||
DevMode.instance.update();
|
||||
DevMode.instance.update() catch unreachable;
|
||||
const data = DevMode.instance.render() catch unreachable;
|
||||
imgui.ImplOpenGL3.renderDrawData(data);
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ pub const Config = struct {
|
||||
// Dev Mode
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .grave_accent, .mods = .{ .shift = true, .super = true } },
|
||||
.{ .key = .down, .mods = .{ .shift = true, .super = true } },
|
||||
.{ .toggle_dev_mode = 0 },
|
||||
);
|
||||
|
||||
|
@ -61,6 +61,7 @@ pub const Key = enum {
|
||||
end,
|
||||
page_up,
|
||||
page_down,
|
||||
escape,
|
||||
|
||||
f1,
|
||||
f2,
|
||||
|
Reference in New Issue
Block a user