From c103a278f1a890282283a6b147e54944eb7c6235 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 Oct 2022 14:47:51 -0700 Subject: [PATCH] render font info in dev mode --- pkg/imgui/core.zig | 144 +++++++++++++++++++++++++++++++++++++++++---- src/DevMode.zig | 86 ++++++++++++++++++++++++++- src/Window.zig | 29 ++++++++- src/config.zig | 2 +- src/input/key.zig | 1 + 5 files changed, 245 insertions(+), 17 deletions(-) diff --git a/pkg/imgui/core.zig b/pkg/imgui/core.zig index 259350c20..2b5d78cf9 100644 --- a/pkg/imgui/core.zig +++ b/pkg/imgui/core.zig @@ -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()); +} diff --git a/src/DevMode.zig b/src/DevMode.zig index 9f26c6f44..08cca5d4e 100644 --- a/src/DevMode.zig +++ b/src/DevMode.zig @@ -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 }, + ); + } +} diff --git a/src/Window.zig b/src/Window.zig index 14004e6b9..d05d83b91 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -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); } diff --git a/src/config.zig b/src/config.zig index 6bd7a3225..acdab5a4b 100644 --- a/src/config.zig +++ b/src/config.zig @@ -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 }, ); diff --git a/src/input/key.zig b/src/input/key.zig index dda6d6704..8445547b4 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -61,6 +61,7 @@ pub const Key = enum { end, page_up, page_down, + escape, f1, f2,