From fab0ff00da2086e6cdd59d1de6371181abe2bc4d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 7 Mar 2025 21:31:07 +0100 Subject: [PATCH 1/2] refactor: move home fire to seperate file --- src/tui/Fire.zig | 126 +++++++++++++++++++++++++++++++++++++++++++++++ src/tui/home.zig | 123 +-------------------------------------------- 2 files changed, 127 insertions(+), 122 deletions(-) create mode 100644 src/tui/Fire.zig diff --git a/src/tui/Fire.zig b/src/tui/Fire.zig new file mode 100644 index 0000000..4216dd6 --- /dev/null +++ b/src/tui/Fire.zig @@ -0,0 +1,126 @@ +const std = @import("std"); +const Plane = @import("renderer").Plane; +const Widget = @import("Widget.zig"); + +const px = "▀"; + +const Fire = @This(); + +allocator: std.mem.Allocator, +plane: Plane, +prng: std.Random.DefaultPrng, + +//scope cache - spread fire +spread_px: u8 = 0, +spread_rnd_idx: u8 = 0, +spread_dst: usize = 0, + +FIRE_H: u16, +FIRE_W: u16, +FIRE_SZ: usize, +FIRE_LAST_ROW: usize, + +screen_buf: []u8, + +const MAX_COLOR = 256; +const LAST_COLOR = MAX_COLOR - 1; + +pub fn init(allocator: std.mem.Allocator, plane: Plane) !Fire { + const pos = Widget.Box.from(plane); + const FIRE_H = @as(u16, @intCast(pos.h)) * 2; + const FIRE_W = @as(u16, @intCast(pos.w)); + var self: Fire = .{ + .allocator = allocator, + .plane = plane, + .prng = std.Random.DefaultPrng.init(blk: { + var seed: u64 = undefined; + try std.posix.getrandom(std.mem.asBytes(&seed)); + break :blk seed; + }), + .FIRE_H = FIRE_H, + .FIRE_W = FIRE_W, + .FIRE_SZ = @as(usize, @intCast(FIRE_H)) * FIRE_W, + .FIRE_LAST_ROW = @as(usize, @intCast(FIRE_H - 1)) * FIRE_W, + .screen_buf = try allocator.alloc(u8, @as(usize, @intCast(FIRE_H)) * FIRE_W), + }; + + var buf_idx: usize = 0; + while (buf_idx < self.FIRE_SZ) : (buf_idx += 1) { + self.screen_buf[buf_idx] = fire_black; + } + + // last row is white...white is "fire source" + buf_idx = 0; + while (buf_idx < self.FIRE_W) : (buf_idx += 1) { + self.screen_buf[self.FIRE_LAST_ROW + buf_idx] = fire_white; + } + return self; +} + +pub fn deinit(self: *Fire) void { + self.allocator.free(self.screen_buf); +} + +const fire_palette = [_]u8{ 0, 233, 234, 52, 53, 88, 89, 94, 95, 96, 130, 131, 132, 133, 172, 214, 215, 220, 220, 221, 3, 226, 227, 230, 195, 230 }; +const fire_black: u8 = 0; +const fire_white: u8 = fire_palette.len - 1; + +pub fn render(self: *Fire) void { + self.plane.home(); + var rand = self.prng.random(); + + //update fire buf + var doFire_x: u16 = 0; + while (doFire_x < self.FIRE_W) : (doFire_x += 1) { + var doFire_y: u16 = 0; + while (doFire_y < self.FIRE_H) : (doFire_y += 1) { + const doFire_idx = @as(usize, @intCast(doFire_y)) * self.FIRE_W + doFire_x; + + //spread fire + self.spread_px = self.screen_buf[doFire_idx]; + + //bounds checking + if ((self.spread_px == 0) and (doFire_idx >= self.FIRE_W)) { + self.screen_buf[doFire_idx - self.FIRE_W] = 0; + } else { + self.spread_rnd_idx = rand.intRangeAtMost(u8, 0, 3); + if (doFire_idx >= (self.spread_rnd_idx + 1)) { + self.spread_dst = doFire_idx - self.spread_rnd_idx + 1; + } else { + self.spread_dst = doFire_idx; + } + if (self.spread_dst >= self.FIRE_W) { + if (self.spread_px > (self.spread_rnd_idx & 1)) { + self.screen_buf[self.spread_dst - self.FIRE_W] = self.spread_px - (self.spread_rnd_idx & 1); + } else { + self.screen_buf[self.spread_dst - self.FIRE_W] = 0; + } + } + } + } + } + + //scope cache - fire 2 screen buffer + var frame_x: u16 = 0; + var frame_y: u16 = 0; + + // for each row + frame_y = 0; + while (frame_y < self.FIRE_H) : (frame_y += 2) { // 'paint' two rows at a time because of half height char + // for each col + frame_x = 0; + while (frame_x < self.FIRE_W) : (frame_x += 1) { + //each character rendered is actually to rows of 'pixels' + // - "hi" (current px row => fg char) + // - "low" (next row => bg color) + const px_hi = self.screen_buf[@as(usize, @intCast(frame_y)) * self.FIRE_W + frame_x]; + const px_lo = self.screen_buf[@as(usize, @intCast(frame_y + 1)) * self.FIRE_W + frame_x]; + + self.plane.set_fg_palindex(fire_palette[px_hi]) catch {}; + self.plane.set_bg_palindex(fire_palette[px_lo]) catch {}; + _ = self.plane.putstr(px) catch {}; + } + self.plane.cursor_move_yx(-1, 0) catch {}; + self.plane.cursor_move_rel(1, 0) catch {}; + } +} diff --git a/src/tui/home.zig b/src/tui/home.zig index ed5bd84..5db94dd 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -285,125 +285,4 @@ const cmds = struct { pub const home_sheeran_meta = .{}; }; -const Fire = struct { - const px = "▀"; - - allocator: std.mem.Allocator, - plane: Plane, - prng: std.Random.DefaultPrng, - - //scope cache - spread fire - spread_px: u8 = 0, - spread_rnd_idx: u8 = 0, - spread_dst: usize = 0, - - FIRE_H: u16, - FIRE_W: u16, - FIRE_SZ: usize, - FIRE_LAST_ROW: usize, - - screen_buf: []u8, - - const MAX_COLOR = 256; - const LAST_COLOR = MAX_COLOR - 1; - - fn init(allocator: std.mem.Allocator, plane: Plane) !Fire { - const pos = Widget.Box.from(plane); - const FIRE_H = @as(u16, @intCast(pos.h)) * 2; - const FIRE_W = @as(u16, @intCast(pos.w)); - var self: Fire = .{ - .allocator = allocator, - .plane = plane, - .prng = std.Random.DefaultPrng.init(blk: { - var seed: u64 = undefined; - try std.posix.getrandom(std.mem.asBytes(&seed)); - break :blk seed; - }), - .FIRE_H = FIRE_H, - .FIRE_W = FIRE_W, - .FIRE_SZ = @as(usize, @intCast(FIRE_H)) * FIRE_W, - .FIRE_LAST_ROW = @as(usize, @intCast(FIRE_H - 1)) * FIRE_W, - .screen_buf = try allocator.alloc(u8, @as(usize, @intCast(FIRE_H)) * FIRE_W), - }; - - var buf_idx: usize = 0; - while (buf_idx < self.FIRE_SZ) : (buf_idx += 1) { - self.screen_buf[buf_idx] = fire_black; - } - - // last row is white...white is "fire source" - buf_idx = 0; - while (buf_idx < self.FIRE_W) : (buf_idx += 1) { - self.screen_buf[self.FIRE_LAST_ROW + buf_idx] = fire_white; - } - return self; - } - - fn deinit(self: *Fire) void { - self.allocator.free(self.screen_buf); - } - - const fire_palette = [_]u8{ 0, 233, 234, 52, 53, 88, 89, 94, 95, 96, 130, 131, 132, 133, 172, 214, 215, 220, 220, 221, 3, 226, 227, 230, 195, 230 }; - const fire_black: u8 = 0; - const fire_white: u8 = fire_palette.len - 1; - - fn render(self: *Fire) void { - self.plane.home(); - var rand = self.prng.random(); - - //update fire buf - var doFire_x: u16 = 0; - while (doFire_x < self.FIRE_W) : (doFire_x += 1) { - var doFire_y: u16 = 0; - while (doFire_y < self.FIRE_H) : (doFire_y += 1) { - const doFire_idx = @as(usize, @intCast(doFire_y)) * self.FIRE_W + doFire_x; - - //spread fire - self.spread_px = self.screen_buf[doFire_idx]; - - //bounds checking - if ((self.spread_px == 0) and (doFire_idx >= self.FIRE_W)) { - self.screen_buf[doFire_idx - self.FIRE_W] = 0; - } else { - self.spread_rnd_idx = rand.intRangeAtMost(u8, 0, 3); - if (doFire_idx >= (self.spread_rnd_idx + 1)) { - self.spread_dst = doFire_idx - self.spread_rnd_idx + 1; - } else { - self.spread_dst = doFire_idx; - } - if (self.spread_dst >= self.FIRE_W) { - if (self.spread_px > (self.spread_rnd_idx & 1)) { - self.screen_buf[self.spread_dst - self.FIRE_W] = self.spread_px - (self.spread_rnd_idx & 1); - } else { - self.screen_buf[self.spread_dst - self.FIRE_W] = 0; - } - } - } - } - } - - //scope cache - fire 2 screen buffer - var frame_x: u16 = 0; - var frame_y: u16 = 0; - - // for each row - frame_y = 0; - while (frame_y < self.FIRE_H) : (frame_y += 2) { // 'paint' two rows at a time because of half height char - // for each col - frame_x = 0; - while (frame_x < self.FIRE_W) : (frame_x += 1) { - //each character rendered is actually to rows of 'pixels' - // - "hi" (current px row => fg char) - // - "low" (next row => bg color) - const px_hi = self.screen_buf[@as(usize, @intCast(frame_y)) * self.FIRE_W + frame_x]; - const px_lo = self.screen_buf[@as(usize, @intCast(frame_y + 1)) * self.FIRE_W + frame_x]; - - self.plane.set_fg_palindex(fire_palette[px_hi]) catch {}; - self.plane.set_bg_palindex(fire_palette[px_lo]) catch {}; - _ = self.plane.putstr(px) catch {}; - } - self.plane.cursor_move_yx(-1, 0) catch {}; - self.plane.cursor_move_rel(1, 0) catch {}; - } - } -}; +const Fire = @import("Fire.zig"); From 3da2f8d4844c9de0b5e53dfd7f2edff39d26ce21 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 8 Mar 2025 00:59:23 +0100 Subject: [PATCH 2/2] feat: add some home screen customization --- src/tui/home.zig | 284 +++++++++++++++++++++++++++++-------------- src/tui/mainview.zig | 44 ++++--- 2 files changed, 220 insertions(+), 108 deletions(-) diff --git a/src/tui/home.zig b/src/tui/home.zig index 5db94dd..8d41d7b 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -1,6 +1,8 @@ const std = @import("std"); const build_options = @import("build_options"); const tp = @import("thespian"); +const log = @import("log"); +const cbor = @import("cbor"); const Plane = @import("renderer").Plane; const root = @import("root"); @@ -14,6 +16,52 @@ const keybind = @import("keybind"); const fonts = @import("fonts.zig"); +const style = struct { + title: []const u8 = root.application_title, + subtext: []const u8 = root.application_subtext, + + centered: bool = false, + + menu_commands: []const u8 = splice(if (build_options.gui) + \\find_file + \\create_new_file + \\open_file + \\open_recent_project + \\find_in_files + \\open_command_palette + \\select_task + \\add_task + \\open_config + \\open_gui_config + \\change_fontface + \\open_keybind_config + \\toggle_input_mode + \\change_theme + \\open_help + \\open_version_info + \\quit + else + \\find_file + \\create_new_file + \\open_file + \\open_recent_project + \\find_in_files + \\open_command_palette + \\select_task + \\add_task + \\open_config + \\open_keybind_config + \\toggle_input_mode + \\change_theme + \\open_help + \\open_version_info + \\quit + ), + + include_files: []const u8 = "", +}; +pub const Style = style; + allocator: std.mem.Allocator, plane: Plane, parent: Plane, @@ -21,54 +69,24 @@ fire: ?Fire = null, commands: Commands = undefined, menu: *Menu.State(*Self), menu_w: usize = 0, +menu_len: usize = 0, max_desc_len: usize = 0, input_namespace: []const u8, +home_style: style, +home_style_bufs: [][]const u8, + const Self = @This(); -const menu_commands = if (build_options.gui) &[_][]const u8{ - "find_file", - "create_new_file", - "open_file", - "open_recent_project", - "find_in_files", - "open_command_palette", - "select_task", - "add_task", - "open_config", - "open_gui_config", - "change_fontface", - "open_keybind_config", - "toggle_input_mode", - "change_theme", - "open_help", - "open_version_info", - "quit", -} else &[_][]const u8{ - "find_file", - "create_new_file", - "open_file", - "open_recent_project", - "find_in_files", - "open_command_palette", - "select_task", - "add_task", - "open_config", - "open_keybind_config", - "toggle_input_mode", - "change_theme", - "open_help", - "open_version_info", - "quit", -}; - pub fn create(allocator: std.mem.Allocator, parent: Widget) !Widget { + const logger = log.logger("home"); const self: *Self = try allocator.create(Self); var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*); errdefer n.deinit(); command.executeName("enter_mode", command.Context.fmt(.{"home"})) catch {}; const keybind_mode = tui.get_keybind_mode() orelse @panic("no active keybind mode"); + const home_style, const home_style_bufs = root.read_config(style, allocator); const w = Widget.to(self); self.* = .{ @@ -77,15 +95,32 @@ pub fn create(allocator: std.mem.Allocator, parent: Widget) !Widget { .plane = n, .menu = try Menu.create(*Self, allocator, w.plane.*, .{ .ctx = self, .on_render = menu_on_render }), .input_namespace = keybind.get_namespace(), + .home_style = home_style, + .home_style_bufs = home_style_bufs, }; try self.commands.init(self); - self.get_max_desc_len(keybind_mode.keybind_hints); - inline for (menu_commands) |command_name| try self.add_menu_command(command_name, self.menu, keybind_mode.keybind_hints); + var it = std.mem.splitAny(u8, self.home_style.menu_commands, "\n "); + while (it.next()) |command_name| { + self.menu_len += 1; + const id = command.get_id(command_name) orelse { + logger.print("{s} is not defined", .{command_name}); + continue; + }; + const description = command.get_description(id) orelse { + logger.print("{s} has no description", .{command_name}); + continue; + }; + var hints = std.mem.splitScalar(u8, keybind_mode.keybind_hints.get(command_name) orelse "", ','); + const hint = hints.first(); + self.max_desc_len = @max(self.max_desc_len, description.len + hint.len + 5); + try self.add_menu_command(command_name, description, hint, self.menu); + } self.position_menu(15, 9); return w; } pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + root.free_config(self.allocator, self.home_style_bufs); self.menu.deinit(allocator); self.commands.deinit(); self.plane.deinit(); @@ -93,36 +128,32 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.destroy(self); } -fn get_max_desc_len(self: *Self, hints_map: anytype) void { - inline for (menu_commands) |command_name| { - const id = command.get_id(command_name) orelse @panic(command_name ++ " is not defined"); - const description = command.get_description(id) orelse @panic(command_name ++ " has no description"); - var hints = std.mem.splitScalar(u8, hints_map.get(command_name) orelse "", ','); - const hint = hints.first(); - self.max_desc_len = @max(self.max_desc_len, description.len + hint.len + 5); - } -} - -fn add_menu_command(self: *Self, comptime command_name: []const u8, menu: anytype, hints_map: anytype) !void { - const id = command.get_id(command_name) orelse @panic(command_name ++ " is not defined"); - const description = command.get_description(id) orelse @panic(command_name ++ " has no description"); - var hints = std.mem.splitScalar(u8, hints_map.get(command_name) orelse "", ','); - const hint = hints.first(); +fn add_menu_command(self: *Self, command_name: []const u8, description: []const u8, hint: []const u8, menu: anytype) !void { const label_len = description.len + hint.len; var buf: [64]u8 = undefined; - var fis = std.io.fixedBufferStream(&buf); - const writer = fis.writer(); - const leader = if (hint.len > 0) "." else " "; - _ = try writer.write(description); - _ = try writer.write(" "); - _ = try writer.write(leader); - _ = try writer.write(leader); - for (0..(self.max_desc_len - label_len - 5)) |_| + { + var fis = std.io.fixedBufferStream(&buf); + const writer = fis.writer(); + const leader = if (hint.len > 0) "." else " "; + _ = try writer.write(description); + _ = try writer.write(" "); _ = try writer.write(leader); - try writer.print(" :{s}", .{hint}); - const label = fis.getWritten(); - try menu.add_item_with_handler(label, menu_action(command_name)); - self.menu_w = @max(self.menu_w, label.len + 1); + _ = try writer.write(leader); + for (0..(self.max_desc_len - label_len - 5)) |_| + _ = try writer.write(leader); + try writer.print(" :{s}", .{hint}); + const label = fis.getWritten(); + self.menu_w = @max(self.menu_w, label.len + 1); + } + + var value = std.ArrayList(u8).init(self.allocator); + defer value.deinit(); + const writer = value.writer(); + try cbor.writeValue(writer, description); + try cbor.writeValue(writer, hint); + try cbor.writeValue(writer, command_name); + + try menu.add_item_with_handler(value.items, menu_action); } pub fn update(self: *Self) void { @@ -143,7 +174,33 @@ pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { return false; } -fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { +fn menu_on_render(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { + var description: []const u8 = undefined; + var hint: []const u8 = undefined; + var command_name: []const u8 = undefined; + var iter = button.opts.label; // label contains cbor + if (!(cbor.matchString(&iter, &description) catch false)) + description = "#ERROR#"; + if (!(cbor.matchString(&iter, &hint) catch false)) + hint = ""; + if (!(cbor.matchString(&iter, &command_name) catch false)) + command_name = ""; + + const label_len = description.len + hint.len; + var buf: [64]u8 = undefined; + const leader = blk: { + var fis = std.io.fixedBufferStream(&buf); + const writer = fis.writer(); + const leader = if (hint.len > 0) "." else " "; + _ = writer.write(" ") catch return false; + _ = writer.write(leader) catch return false; + _ = writer.write(leader) catch return false; + for (0..(self.max_desc_len - label_len - 5)) |_| + _ = writer.write(leader) catch return false; + writer.print(" ", .{}) catch return false; + break :blk fis.getWritten(); + }; + const style_base = theme.editor; const style_label = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else style_base; if (button.active or button.hover or selected) { @@ -159,8 +216,9 @@ fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c button.plane.home(); } const style_text = if (tui.find_scope_style(theme, "keyword")) |sty| sty.style else style_label; + const style_leader = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor; const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else style_label; - const sep = std.mem.indexOfScalar(u8, button.opts.label, ':') orelse button.opts.label.len; + if (button.active) { button.plane.set_style(style_label); } else if (button.hover or selected) { @@ -169,22 +227,35 @@ fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c button.plane.set_style_bg_transparent(style_text); } const pointer = if (selected) "⏵" else " "; - _ = button.plane.print("{s}{s}", .{ pointer, button.opts.label[0..sep] }) catch {}; + _ = button.plane.print("{s}{s}", .{ pointer, description }) catch {}; + if (button.active or button.hover or selected) { + button.plane.set_style(style_leader); + } else { + button.plane.set_style_bg_transparent(style_leader); + } + _ = button.plane.print("{s}", .{leader}) catch {}; if (button.active or button.hover or selected) { button.plane.set_style(style_keybind); } else { button.plane.set_style_bg_transparent(style_keybind); } - _ = button.plane.print("{s}", .{button.opts.label[sep + 1 ..]}) catch {}; + _ = button.plane.print("{s}", .{hint}) catch {}; return false; } -fn menu_action(comptime command_name: []const u8) *const fn (_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { - return struct { - fn action(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { - command.executeName(command_name, .{}) catch {}; - } - }.action; +fn menu_action(_: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { + var description: []const u8 = undefined; + var hint: []const u8 = undefined; + var command_name: []const u8 = undefined; + var iter = button.opts.label; // label contains cbor + if (!(cbor.matchString(&iter, &description) catch false)) + description = "#ERROR#"; + if (!(cbor.matchString(&iter, &hint) catch false)) + hint = ""; + if (!(cbor.matchString(&iter, &command_name) catch false)) + command_name = ""; + + command.executeName(command_name, .{}) catch {}; } pub fn render(self: *Self, theme: *const Widget.Theme) bool { @@ -199,35 +270,35 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor; if (self.plane.dim_x() > 120 and self.plane.dim_y() > 22) { - self.plane.cursor_move_yx(2, 4) catch return false; - fonts.print_string_large(&self.plane, root.application_title, style_title) catch return false; + self.plane.cursor_move_yx(2, self.centerI(4, self.home_style.title.len * 8)) catch return false; + fonts.print_string_large(&self.plane, self.home_style.title, style_title) catch return false; - self.plane.cursor_move_yx(10, 8) catch return false; - fonts.print_string_medium(&self.plane, root.application_subtext, style_subtext) catch return false; + self.plane.cursor_move_yx(10, self.centerI(8, self.home_style.subtext.len * 4)) catch return false; + fonts.print_string_medium(&self.plane, self.home_style.subtext, style_subtext) catch return false; - self.position_menu(15, 10); + self.position_menu(self.v_center(15, self.menu_len, 15), self.center(10, self.menu_w)); } else if (self.plane.dim_x() > 55 and self.plane.dim_y() > 16) { - self.plane.cursor_move_yx(2, 4) catch return false; - fonts.print_string_medium(&self.plane, root.application_title, style_title) catch return false; + self.plane.cursor_move_yx(2, self.centerI(4, self.home_style.title.len * 4)) catch return false; + fonts.print_string_medium(&self.plane, self.home_style.title, style_title) catch return false; self.plane.set_style_bg_transparent(style_subtext); - self.plane.cursor_move_yx(7, 6) catch return false; - _ = self.plane.print(root.application_subtext, .{}) catch {}; + self.plane.cursor_move_yx(7, self.centerI(6, self.home_style.subtext.len)) catch return false; + _ = self.plane.print("{s}", .{self.home_style.subtext}) catch {}; self.plane.set_style(theme.editor); - self.position_menu(9, 8); + self.position_menu(self.v_center(9, self.menu_len, 9), self.center(8, self.menu_w)); } else { self.plane.set_style_bg_transparent(style_title); - self.plane.cursor_move_yx(1, 4) catch return false; - _ = self.plane.print(root.application_title, .{}) catch return false; + self.plane.cursor_move_yx(1, self.centerI(4, self.home_style.title.len)) catch return false; + _ = self.plane.print("{s}", .{self.home_style.title}) catch return false; self.plane.set_style_bg_transparent(style_subtext); - self.plane.cursor_move_yx(3, 6) catch return false; - _ = self.plane.print(root.application_subtext, .{}) catch {}; + self.plane.cursor_move_yx(3, self.centerI(6, self.home_style.subtext.len)) catch return false; + _ = self.plane.print("{s}", .{self.home_style.subtext}) catch {}; self.plane.set_style(theme.editor); const x = @min(self.plane.dim_x() -| 32, 8); - self.position_menu(5, x); + self.position_menu(self.v_center(5, self.menu_len, 5), self.center(x, self.menu_w)); } const more = self.menu.render(theme); @@ -239,6 +310,24 @@ fn position_menu(self: *Self, y: usize, x: usize) void { self.menu.resize(.{ .y = box.y + y, .x = box.x + x, .w = self.menu_w }); } +fn center(self: *Self, non_centered: usize, w: usize) usize { + if (!self.home_style.centered) return non_centered; + const box = Widget.Box.from(self.plane); + const x = if (box.w > w) (box.w - w) / 2 else 0; + return box.x + x; +} + +fn centerI(self: *Self, non_centered: usize, w: usize) c_int { + return @intCast(self.center(non_centered, w)); +} + +fn v_center(self: *Self, non_centered: usize, h: usize, minoffset: usize) usize { + if (!self.home_style.centered) return non_centered; + const box = Widget.Box.from(self.plane); + const y = if (box.h > h) (box.h - h) / 2 else 0; + return box.y + @max(y, minoffset); +} + pub fn handle_resize(self: *Self, pos: Widget.Box) void { self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return; self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return; @@ -286,3 +375,18 @@ const cmds = struct { }; const Fire = @import("Fire.zig"); + +fn splice(in: []const u8) []const u8 { + var out: []const u8 = ""; + var it = std.mem.splitAny(u8, in, "\n "); + var first = true; + while (it.next()) |item| { + if (first) { + first = false; + } else { + out = out ++ " "; + } + out = out ++ item; + } + return out; +} diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index eb0e0f7..d25cbd9 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -230,6 +230,26 @@ fn check_all_not_dirty(self: *const Self) command.Result { return tp.exit("unsaved changes"); } +fn open_style_config(self: *Self, Style: type) command.Result { + const file_name = try root.get_config_file_name(Style); + const style, const style_bufs: [][]const u8 = if (root.exists_config(Style)) blk: { + const style, const style_bufs = root.read_config(Style, self.allocator); + break :blk .{ style, style_bufs }; + } else .{ Style{}, &.{} }; + defer root.free_config(self.allocator, style_bufs); + var conf = std.ArrayList(u8).init(self.allocator); + defer conf.deinit(); + root.write_config_to_writer(Style, style, conf.writer()) catch {}; + tui.reset_drag_context(); + try self.create_editor(); + try command.executeName("open_scratch_buffer", command.fmt(.{ + file_name[0 .. file_name.len - ".json".len], + conf.items, + "conf", + })); + if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral(); +} + const cmds = struct { pub const Target = Self; const Ctx = command.Context; @@ -414,27 +434,15 @@ const cmds = struct { pub const open_gui_config_meta = .{ .description = "Edit gui configuration" }; pub fn open_tabs_style_config(self: *Self, _: Ctx) Result { - const Style = @import("status/tabs.zig").Style; - const file_name = try root.get_config_file_name(Style); - const tab_style, const tab_style_bufs: [][]const u8 = if (root.exists_config(Style)) blk: { - const tab_style, const tab_style_bufs = root.read_config(Style, self.allocator); - break :blk .{ tab_style, tab_style_bufs }; - } else .{ Style{}, &.{} }; - defer root.free_config(self.allocator, tab_style_bufs); - var conf = std.ArrayList(u8).init(self.allocator); - defer conf.deinit(); - root.write_config_to_writer(Style, tab_style, conf.writer()) catch {}; - tui.reset_drag_context(); - try self.create_editor(); - try command.executeName("open_scratch_buffer", command.fmt(.{ - file_name[0 .. file_name.len - ".json".len], - conf.items, - "conf", - })); - if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral(); + try self.open_style_config(@import("status/tabs.zig").Style); } pub const open_tabs_style_config_meta: Meta = .{ .description = "Edit tab style" }; + pub fn open_home_style_config(self: *Self, _: Ctx) Result { + try self.open_style_config(@import("home.zig").Style); + } + pub const open_home_style_config_meta: Meta = .{ .description = "Edit home screen" }; + pub fn create_scratch_buffer(self: *Self, ctx: Ctx) Result { const args = try ctx.args.clone(self.allocator); defer self.allocator.free(args.buf);