From ce87dcfa2b66b1cfcde8b0c91306f5e058ec530f Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 9 Oct 2025 19:11:25 +0200 Subject: [PATCH] refactor: pass relative click position to button click handlers This a big refactor just to clean-up type definitions used by Button and Menu. The goals is to pass the click position as a cursor object. --- src/tui/Button.zig | 65 +++++++++++-------- src/tui/Menu.zig | 48 +++++++------- src/tui/filelist_view.zig | 12 ++-- src/tui/home.zig | 6 +- src/tui/mode/overlay/buffer_palette.zig | 6 +- src/tui/mode/overlay/command_palette.zig | 2 +- src/tui/mode/overlay/completion_palette.zig | 6 +- src/tui/mode/overlay/file_type_palette.zig | 4 +- src/tui/mode/overlay/fontface_palette.zig | 4 +- .../overlay/list_all_commands_palette.zig | 2 +- src/tui/mode/overlay/open_recent.zig | 10 +-- src/tui/mode/overlay/open_recent_project.zig | 4 +- src/tui/mode/overlay/palette.zig | 11 ++-- src/tui/mode/overlay/task_palette.zig | 6 +- src/tui/mode/overlay/theme_palette.zig | 4 +- src/tui/status/branch.zig | 9 +-- src/tui/status/diagstate.zig | 9 +-- src/tui/status/filestate.zig | 13 ++-- src/tui/status/linenumstate.zig | 9 +-- src/tui/status/modestate.zig | 14 ++-- src/tui/status/tabs.zig | 22 ++++--- 21 files changed, 148 insertions(+), 118 deletions(-) diff --git a/src/tui/Button.zig b/src/tui/Button.zig index 1ca9704..be81a00 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -4,6 +4,7 @@ const tp = @import("thespian"); const EventHandler = @import("EventHandler"); const Plane = @import("renderer").Plane; const input = @import("input"); +pub const Cursor = @import("Buffer").Cursor; const Widget = @import("Widget.zig"); const tui = @import("tui.zig"); @@ -14,20 +15,23 @@ pub fn Options(context: type) type { pos: Widget.Box = .{ .y = 0, .x = 0, .w = 8, .h = 1 }, ctx: Context, - on_click: *const fn (ctx: *context, button: *State(Context)) void = do_nothing, - on_click2: *const fn (ctx: *context, button: *State(Context)) void = do_nothing, - on_click3: *const fn (ctx: *context, button: *State(Context)) void = do_nothing, - on_click4: *const fn (ctx: *context, button: *State(Context)) void = do_nothing, - on_click5: *const fn (ctx: *context, button: *State(Context)) void = do_nothing, - on_render: *const fn (ctx: *context, button: *State(Context), theme: *const Widget.Theme) bool = on_render_default, - on_layout: *const fn (ctx: *context, button: *State(Context)) Widget.Layout = on_layout_default, - on_receive: *const fn (ctx: *context, button: *State(Context), from: tp.pid_ref, m: tp.message) error{Exit}!bool = on_receive_default, + on_click: ClickHandler = do_nothing, + on_click2: ClickHandler = do_nothing, + on_click3: ClickHandler = do_nothing, + on_click4: ClickHandler = do_nothing, + on_click5: ClickHandler = do_nothing, + on_render: *const fn (ctx: *context, button: *ButtonType, theme: *const Widget.Theme) bool = on_render_default, + on_layout: *const fn (ctx: *context, button: *ButtonType) Widget.Layout = on_layout_default, + on_receive: *const fn (ctx: *context, button: *ButtonType, from: tp.pid_ref, m: tp.message) error{Exit}!bool = on_receive_default, on_event: ?EventHandler = null, + pub const ButtonType = State(Context); pub const Context = context; - pub fn do_nothing(_: *context, _: *State(Context)) void {} + pub const ClickHandler = *const fn (ctx: *context, button: *ButtonType, pos: Cursor) void; - pub fn on_render_default(_: *context, self: *State(Context), theme: *const Widget.Theme) bool { + pub fn do_nothing(_: *context, _: *ButtonType, _: Cursor) void {} + + pub fn on_render_default(_: *context, self: *ButtonType, theme: *const Widget.Theme) bool { self.plane.set_base_style(if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar); self.plane.erase(); self.plane.home(); @@ -35,18 +39,18 @@ pub fn Options(context: type) type { return false; } - pub fn on_layout_default(_: *context, self: *State(Context)) Widget.Layout { + pub fn on_layout_default(_: *context, self: *ButtonType) Widget.Layout { return .{ .static = self.opts.label.len + 2 }; } - pub fn on_receive_default(_: *context, _: *State(Context), _: tp.pid_ref, _: tp.message) error{Exit}!bool { + pub fn on_receive_default(_: *context, _: *ButtonType, _: tp.pid_ref, _: tp.message) error{Exit}!bool { return false; } }; } -pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) error{OutOfMemory}!*State(ctx_type) { - const Self = State(ctx_type); +pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) error{OutOfMemory}!*Options(ctx_type).ButtonType { + const Self = Options(ctx_type).ButtonType; const self = try allocator.create(Self); errdefer allocator.destroy(self); var n = try Plane.init(&opts.pos.opts(@typeName(Self)), parent); @@ -67,7 +71,7 @@ pub fn create_widget(ctx_type: type, allocator: std.mem.Allocator, parent: Plane return Widget.to(try create(ctx_type, allocator, parent, opts)); } -pub fn State(ctx_type: type) type { +fn State(ctx_type: type) type { return struct { allocator: std.mem.Allocator, parent: Plane, @@ -110,7 +114,9 @@ pub fn State(ctx_type: type) type { pub fn receive(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { var btn: input.MouseType = 0; - if (try m.match(.{ "B", input.event.press, tp.extract(&btn), tp.more })) { + var x: c_int = undefined; + var y: c_int = undefined; + if (try m.match(.{ "B", input.event.press, tp.extract(&btn), tp.any, tp.extract(&x), tp.extract(&y), tp.any, tp.any })) { const btn_enum: input.Mouse = @enumFromInt(btn); switch (btn_enum) { input.mouse.BUTTON1 => { @@ -118,14 +124,14 @@ pub fn State(ctx_type: type) type { tui.need_render(); }, input.mouse.BUTTON4, input.mouse.BUTTON5 => { - self.call_click_handler(btn_enum); + self.call_click_handler(btn_enum, self.to_rel_cursor(x, y)); return true; }, else => {}, } return true; - } else if (try m.match(.{ "B", input.event.release, tp.extract(&btn), tp.more })) { - self.call_click_handler(@enumFromInt(btn)); + } else if (try m.match(.{ "B", input.event.release, tp.extract(&btn), tp.any, tp.extract(&x), tp.extract(&y), tp.any, tp.any })) { + self.call_click_handler(@enumFromInt(btn), self.to_rel_cursor(x, y)); tui.need_render(); return true; } else if (try m.match(.{ "D", input.event.press, tp.extract(&btn), tp.more })) { @@ -134,12 +140,12 @@ pub fn State(ctx_type: type) type { h.send(from, m) catch {}; } return true; - } else if (try m.match(.{ "D", input.event.release, tp.extract(&btn), tp.more })) { + } else if (try m.match(.{ "D", input.event.release, tp.extract(&btn), tp.any, tp.extract(&x), tp.extract(&y), tp.any, tp.any })) { if (self.opts.on_event) |h| { self.active = false; h.send(from, m) catch {}; } - self.call_click_handler(@enumFromInt(btn)); + self.call_click_handler(@enumFromInt(btn), self.to_rel_cursor(x, y)); tui.need_render(); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { @@ -150,18 +156,23 @@ pub fn State(ctx_type: type) type { return self.opts.on_receive(&self.opts.ctx, self, from, m); } - fn call_click_handler(self: *Self, btn: input.Mouse) void { + fn to_rel_cursor(self: *const Self, abs_x: c_int, abs_y: c_int) Cursor { + const rel_y, const rel_x = self.plane.abs_yx_to_rel(abs_y, abs_x); + return .{ .row = @intCast(rel_y), .col = @intCast(rel_x) }; + } + + fn call_click_handler(self: *Self, btn: input.Mouse, pos: Cursor) void { if (btn == input.mouse.BUTTON1) { if (!self.active) return; self.active = false; } if (!self.hover) return; switch (btn) { - input.mouse.BUTTON1 => self.opts.on_click(&self.opts.ctx, self), - input.mouse.BUTTON2 => self.opts.on_click2(&self.opts.ctx, self), - input.mouse.BUTTON3 => self.opts.on_click3(&self.opts.ctx, self), - input.mouse.BUTTON4 => self.opts.on_click4(&self.opts.ctx, self), - input.mouse.BUTTON5 => self.opts.on_click5(&self.opts.ctx, self), + input.mouse.BUTTON1 => self.opts.on_click(&self.opts.ctx, self, pos), + input.mouse.BUTTON2 => self.opts.on_click2(&self.opts.ctx, self, pos), + input.mouse.BUTTON3 => self.opts.on_click3(&self.opts.ctx, self, pos), + input.mouse.BUTTON4 => self.opts.on_click4(&self.opts.ctx, self, pos), + input.mouse.BUTTON5 => self.opts.on_click5(&self.opts.ctx, self, pos), else => {}, } } diff --git a/src/tui/Menu.zig b/src/tui/Menu.zig index 7d51ab2..701291a 100644 --- a/src/tui/Menu.zig +++ b/src/tui/Menu.zig @@ -16,20 +16,24 @@ pub fn Options(context: type) type { ctx: Context, style: Widget.Type, - on_click: *const fn (ctx: context, button: *Button.State(*State(Context))) void = do_nothing, - on_click4: *const fn (menu: **State(Context), button: *Button.State(*State(Context))) void = do_nothing_click, - on_click5: *const fn (menu: **State(Context), button: *Button.State(*State(Context))) void = do_nothing_click, - on_render: *const fn (ctx: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme, selected: bool) bool = on_render_default, - on_layout: *const fn (ctx: context, button: *Button.State(*State(Context))) Widget.Layout = on_layout_default, - prepare_resize: *const fn (ctx: context, menu: *State(Context), box: Widget.Box) Widget.Box = prepare_resize_default, - after_resize: *const fn (ctx: context, menu: *State(Context), box: Widget.Box) void = after_resize_default, + on_click: ClickHandler = do_nothing, + on_click4: ButtonClickHandler = do_nothing_click, + on_click5: ButtonClickHandler = do_nothing_click, + on_render: *const fn (ctx: context, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool = on_render_default, + on_layout: *const fn (ctx: context, button: *ButtonType) Widget.Layout = on_layout_default, + prepare_resize: *const fn (ctx: context, menu: *MenuType, box: Widget.Box) Widget.Box = prepare_resize_default, + after_resize: *const fn (ctx: context, menu: *MenuType, box: Widget.Box) void = after_resize_default, on_scroll: ?EventHandler = null, pub const Context = context; - pub fn do_nothing(_: context, _: *Button.State(*State(Context))) void {} - pub fn do_nothing_click(_: **State(Context), _: *Button.State(*State(Context))) void {} + pub const MenuType = State(Context); + pub const ButtonType = Button.Options(*MenuType).ButtonType; + pub const ButtonClickHandler = Button.Options(*MenuType).ClickHandler; + pub const ClickHandler = *const fn (ctx: context, button: *ButtonType) void; + pub fn do_nothing(_: context, _: *ButtonType) void {} + pub fn do_nothing_click(_: **MenuType, _: *ButtonType, _: Button.Cursor) void {} - pub fn on_render_default(_: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme, selected: bool) bool { + pub fn on_render_default(_: context, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { 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; button.plane.set_base_style(style_base); @@ -44,17 +48,17 @@ pub fn Options(context: type) type { return false; } - pub fn on_layout_default(_: context, _: *Button.State(*State(Context))) Widget.Layout { + pub fn on_layout_default(_: context, _: *ButtonType) Widget.Layout { return .{ .static = 1 }; } - pub fn prepare_resize_default(_: context, state: *State(Context), box_: Widget.Box) Widget.Box { + pub fn prepare_resize_default(_: context, state: *MenuType, box_: Widget.Box) Widget.Box { var box = box_; box.h = if (box_.h == 0) state.menu.widgets.items.len else box_.h; return box; } - pub fn after_resize_default(_: context, _: *State(Context), _: Widget.Box) void {} + pub fn after_resize_default(_: context, _: *MenuType, _: Widget.Box) void {} }; } @@ -92,15 +96,15 @@ pub fn State(ctx_type: type) type { container_widget: Widget, frame_widget: ?Widget, scrollbar: ?*scrollbar_v, - opts: options_type, + opts: OptionsType, selected: ?usize = null, render_idx: usize = 0, selected_active: bool = false, header_count: usize = 0, const Self = @This(); - const options_type = Options(ctx_type); - const button_type = Button.State(*Self); + pub const OptionsType = Options(ctx_type); + pub const ButtonType = Button.Options(*Self).ButtonType; pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { self.menu.deinit(allocator); @@ -125,7 +129,7 @@ pub fn State(ctx_type: type) type { })); } - pub fn add_item_with_handler(self: *Self, label: []const u8, on_click: *const fn (_: **Self, _: *Button.State(*Self)) void) !void { + pub fn add_item_with_handler(self: *Self, label: []const u8, on_click: OptionsType.ButtonClickHandler) !void { try self.menu.add(try Button.create_widget(*Self, self.allocator, self.menu.parent, .{ .ctx = self, .on_layout = on_layout, @@ -163,11 +167,11 @@ pub fn State(ctx_type: type) type { self.opts.after_resize(self.*.opts.ctx, self, box); } - pub fn on_layout(self: **Self, button: *Button.State(*Self)) Widget.Layout { + pub fn on_layout(self: **Self, button: *ButtonType) Widget.Layout { return self.*.opts.on_layout(self.*.opts.ctx, button); } - pub fn on_render(self: **Self, button: *Button.State(*Self), theme: *const Widget.Theme) bool { + pub fn on_render(self: **Self, button: *ButtonType, theme: *const Widget.Theme) bool { defer self.*.render_idx += 1; std.debug.assert(self.*.render_idx < self.*.menu.widgets.items.len); return self.*.opts.on_render(self.*.opts.ctx, button, theme, self.*.render_idx == self.*.selected); @@ -217,15 +221,15 @@ pub fn State(ctx_type: type) type { pub fn activate_selected(self: *Self) void { const button = self.get_selected() orelse return; - button.opts.on_click(&button.opts.ctx, button); + button.opts.on_click(&button.opts.ctx, button, .{}); } - pub fn get_selected(self: *Self) ?*button_type { + pub fn get_selected(self: *Self) ?*ButtonType { const selected = self.selected orelse return null; self.selected_active = true; const pos = selected + self.header_count; return if (pos < self.menu.widgets.items.len) - self.menu.widgets.items[pos].widget.dynamic_cast(button_type) + self.menu.widgets.items[pos].widget.dynamic_cast(ButtonType) else null; } diff --git a/src/tui/filelist_view.zig b/src/tui/filelist_view.zig index f0062f4..15c2e1f 100644 --- a/src/tui/filelist_view.zig +++ b/src/tui/filelist_view.zig @@ -23,7 +23,7 @@ const Commands = command.Collection(cmds); allocator: std.mem.Allocator, plane: Plane, -menu: *Menu.State(*Self), +menu: *MenuType, logger: log.Logger, commands: Commands = undefined, @@ -35,6 +35,8 @@ entries: std.ArrayList(Entry) = undefined, selected: ?usize = null, box: Widget.Box = .{}, +const MenuType = Menu.Options(*Self).MenuType; +const ButtonType = MenuType.ButtonType; const path_column_ratio = 4; const widget_type: Widget.Type = .panel; @@ -136,7 +138,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { return self.menu.container_widget.render(theme); } -fn handle_render_menu(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { +fn handle_render_menu(self: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { const style_base = theme.panel; const style_label = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.panel; const style_hint: Widget.Theme.Style = .{ .fg = theme.editor_hint.fg, .fs = theme.editor_hint.fs, .bg = style_label.bg }; @@ -195,7 +197,7 @@ fn update_scrollbar(self: *Self) void { scrollbar.set(@intCast(self.entries.items.len), @intCast(self.view_rows), @intCast(self.view_pos)); } -fn mouse_click_button4(menu: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn mouse_click_button4(menu: **MenuType, _: *ButtonType, _: Button.Cursor) void { const self = &menu.*.opts.ctx.*; self.selected = if (self.menu.selected) |sel_| sel_ + self.view_pos else self.selected; if (self.view_pos < Menu.scroll_lines) { @@ -207,7 +209,7 @@ fn mouse_click_button4(menu: **Menu.State(*Self), _: *Button.State(*Menu.State(* self.update_scrollbar(); } -fn mouse_click_button5(menu: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn mouse_click_button5(menu: **MenuType, _: *ButtonType, _: Button.Cursor) void { const self = &menu.*.opts.ctx.*; self.selected = if (self.menu.selected) |sel_| sel_ + self.view_pos else self.selected; if (self.view_pos < @max(self.entries.items.len, self.view_rows) - self.view_rows) @@ -226,7 +228,7 @@ fn update_selected(self: *Self) void { } } -fn handle_menu_action(menu: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { +fn handle_menu_action(menu: **MenuType, button: *ButtonType, _: Button.Cursor) void { const self = menu.*.opts.ctx; var idx: usize = undefined; var iter = button.opts.label; diff --git a/src/tui/home.zig b/src/tui/home.zig index 52529b6..e516d1c 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -82,6 +82,8 @@ home_style_bufs: [][]const u8, const Self = @This(); const widget_type: Widget.Type = .home; +const MenuType = Menu.Options(*Self).MenuType; +const ButtonType = MenuType.ButtonType; pub fn create(allocator: std.mem.Allocator, parent: Widget) !Widget { const logger = log.logger("home"); @@ -188,7 +190,7 @@ pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { return false; } -fn menu_on_render(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { +fn menu_on_render(self: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { var description: []const u8 = undefined; var hint: []const u8 = undefined; var command_name: []const u8 = undefined; @@ -257,7 +259,7 @@ fn menu_on_render(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: return false; } -fn menu_action(_: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { +fn menu_action(_: **Menu.State(*Self), button: *ButtonType, _: Button.Cursor) void { var description: []const u8 = undefined; var hint: []const u8 = undefined; var command_name: []const u8 = undefined; diff --git a/src/tui/mode/overlay/buffer_palette.zig b/src/tui/mode/overlay/buffer_palette.zig index f66abba..9e97f81 100644 --- a/src/tui/mode/overlay/buffer_palette.zig +++ b/src/tui/mode/overlay/buffer_palette.zig @@ -54,11 +54,11 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -pub fn on_render_menu(_: *Type, button: *Type.ButtonState, theme: *const Widget.Theme, selected: bool) bool { +pub fn on_render_menu(_: *Type, button: *Type.ButtonType, theme: *const Widget.Theme, selected: bool) bool { return tui.render_file_item_cbor(&button.plane, button.opts.label, button.active, selected, button.hover, theme); } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var file_path: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &file_path) catch false)) return; @@ -66,7 +66,7 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); } -pub fn delete_item(menu: *Type.MenuState, button: *Type.ButtonState) bool { +pub fn delete_item(menu: *Type.MenuType, button: *Type.ButtonType) bool { var file_path: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &file_path) catch false)) return false; diff --git a/src/tui/mode/overlay/command_palette.zig b/src/tui/mode/overlay/command_palette.zig index 4b107a0..24b26d1 100644 --- a/src/tui/mode/overlay/command_palette.zig +++ b/src/tui/mode/overlay/command_palette.zig @@ -52,7 +52,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var unused: []const u8 = undefined; var command_id: command.ID = undefined; var iter = button.opts.label; diff --git a/src/tui/mode/overlay/completion_palette.zig b/src/tui/mode/overlay/completion_palette.zig index 132df46..4985446 100644 --- a/src/tui/mode/overlay/completion_palette.zig +++ b/src/tui/mode/overlay/completion_palette.zig @@ -83,7 +83,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -pub fn on_render_menu(_: *Type, button: *Type.ButtonState, theme: *const Widget.Theme, selected: bool) bool { +pub fn on_render_menu(_: *Type, button: *Type.ButtonType, theme: *const Widget.Theme, selected: bool) bool { var item_cbor: []const u8 = undefined; var matches_cbor: []const u8 = undefined; @@ -146,13 +146,13 @@ fn get_replace_selection(replace: Buffer.Selection) ?Buffer.Selection { replace; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { const label_, _, _, _, _ = get_values(button.opts.label); tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); tp.self_pid().send(.{ "cmd", "insert_chars", .{label_} }) catch |e| menu.*.opts.ctx.logger.err(module_name, e); } -pub fn updated(palette: *Type, button_: ?*Type.ButtonState) !void { +pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(palette); _, _, _, const replace, _ = get_values(button.opts.label); const editor = tui.get_active_editor() orelse return error.NotFound; diff --git a/src/tui/mode/overlay/file_type_palette.zig b/src/tui/mode/overlay/file_type_palette.zig index c59e12c..1bc112a 100644 --- a/src/tui/mode/overlay/file_type_palette.zig +++ b/src/tui/mode/overlay/file_type_palette.zig @@ -71,7 +71,7 @@ pub fn Variant(comptime command: []const u8, comptime label_: []const u8, allow_ palette.items += 1; } - pub fn on_render_menu(_: *Type, button: *Type.ButtonState, theme: *const Widget.Theme, selected: bool) bool { + pub fn on_render_menu(_: *Type, button: *Type.ButtonType, theme: *const Widget.Theme, selected: bool) bool { const style_base = theme.editor_widget; const style_label = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor_widget; const style_hint = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else style_label; @@ -116,7 +116,7 @@ pub fn Variant(comptime command: []const u8, comptime label_: []const u8, allow_ return false; } - fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { + fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var description_: []const u8 = undefined; var icon_: []const u8 = undefined; var color: u24 = undefined; diff --git a/src/tui/mode/overlay/fontface_palette.zig b/src/tui/mode/overlay/fontface_palette.zig index 895968c..743996a 100644 --- a/src/tui/mode/overlay/fontface_palette.zig +++ b/src/tui/mode/overlay/fontface_palette.zig @@ -56,7 +56,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var label_: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &label_) catch false)) return; @@ -64,7 +64,7 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { tp.self_pid().send(.{ "cmd", "set_fontface", .{label_} }) catch |e| menu.*.opts.ctx.logger.err("fontface_palette", e); } -pub fn updated(palette: *Type, button_: ?*Type.ButtonState) !void { +pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(palette); var label_: []const u8 = undefined; var iter = button.opts.label; diff --git a/src/tui/mode/overlay/list_all_commands_palette.zig b/src/tui/mode/overlay/list_all_commands_palette.zig index bd19c27..8966e55 100644 --- a/src/tui/mode/overlay/list_all_commands_palette.zig +++ b/src/tui/mode/overlay/list_all_commands_palette.zig @@ -69,7 +69,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var unused: []const u8 = undefined; var command_id: command.ID = undefined; var iter = button.opts.label; diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index ba3409a..4b7d96c 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -28,7 +28,7 @@ const widget_type: Widget.Type = .palette; allocator: std.mem.Allocator, f: usize = 0, modal: *ModalBackground.State(*Self), -menu: *Menu.State(*Self), +menu: *MenuType, inputbox: *InputBox.State(*Self), logger: log.Logger, query_pending: bool = false, @@ -39,6 +39,8 @@ commands: Commands = undefined, buffer_manager: ?*BufferManager, const inputbox_label = "Search files by name"; +const MenuType = Menu.Options(*Self).MenuType; +const ButtonType = MenuType.ButtonType; pub fn create(allocator: std.mem.Allocator) !tui.Mode { const mv = tui.mainview() orelse return error.NotFound; @@ -104,11 +106,11 @@ inline fn max_menu_width() usize { return @max(15, width - (width / 5)); } -fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { +fn on_render_menu(_: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { return tui.render_file_item_cbor(&button.plane, button.opts.label, button.active, selected, button.hover, theme); } -fn prepare_resize_menu(self: *Self, _: *Menu.State(*Self), _: Widget.Box) Widget.Box { +fn prepare_resize_menu(self: *Self, _: *MenuType, _: Widget.Box) Widget.Box { return self.prepare_resize(); } @@ -123,7 +125,7 @@ fn do_resize(self: *Self) void { self.menu.resize(self.prepare_resize()); } -fn menu_action_open_file(menu: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { +fn menu_action_open_file(menu: **MenuType, button: *ButtonType, _: Button.Cursor) void { var file_path: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &file_path) catch false)) return; diff --git a/src/tui/mode/overlay/open_recent_project.zig b/src/tui/mode/overlay/open_recent_project.zig index 94cba5e..c6c91d4 100644 --- a/src/tui/mode/overlay/open_recent_project.zig +++ b/src/tui/mode/overlay/open_recent_project.zig @@ -63,7 +63,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var name_: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &name_) catch false)) return; @@ -71,7 +71,7 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { tp.self_pid().send(.{ "cmd", "change_project", .{name_} }) catch |e| menu.*.opts.ctx.logger.err("open_recent_project", e); } -pub fn delete_item(menu: *Type.MenuState, button: *Type.ButtonState) bool { +pub fn delete_item(menu: *Type.MenuType, button: *Type.ButtonType) bool { var name_: []const u8 = undefined; var iter = button.opts.label; if (!(cbor.matchString(&iter, &name_) catch false)) return false; diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 28b2b5b..a749b11 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -46,8 +46,9 @@ pub fn Create(options: type) type { const Self = @This(); const ValueType = if (@hasDecl(options, "ValueType")) options.ValueType else void; - pub const MenuState = Menu.State(*Self); - pub const ButtonState = Button.State(*Menu.State(*Self)); + pub const MenuType = Menu.Options(*Self).MenuType; + pub const ButtonType = MenuType.ButtonType; + pub const Cursor = Button.Cursor; pub fn create(allocator: std.mem.Allocator) !tui.Mode { return create_with_args(allocator, .{}); @@ -131,7 +132,7 @@ pub fn Create(options: type) type { .{ .fg = theme.scrollbar.fg, .bg = theme.editor_widget.bg }; } - fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { + fn on_render_menu(_: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { const style_base = theme.editor_widget; const style_label = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor_widget; const style_hint = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else style_label; @@ -211,7 +212,7 @@ pub fn Create(options: type) type { scrollbar.set(@intCast(@max(self.total_items, 1) - 1), @intCast(self.view_rows), @intCast(self.view_pos)); } - fn mouse_click_button4(menu: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { + fn mouse_click_button4(menu: **Menu.State(*Self), _: *ButtonType, _: Button.Cursor) void { const self = &menu.*.opts.ctx.*; if (self.view_pos < Menu.scroll_lines) { self.view_pos = 0; @@ -222,7 +223,7 @@ pub fn Create(options: type) type { self.start_query(0) catch {}; } - fn mouse_click_button5(menu: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { + fn mouse_click_button5(menu: **Menu.State(*Self), _: *ButtonType, _: Button.Cursor) void { const self = &menu.*.opts.ctx.*; if (self.view_pos < @max(self.total_items, self.view_rows) - self.view_rows) self.view_pos += Menu.scroll_lines; diff --git a/src/tui/mode/overlay/task_palette.zig b/src/tui/mode/overlay/task_palette.zig index 9c5c30b..aedd7a2 100644 --- a/src/tui/mode/overlay/task_palette.zig +++ b/src/tui/mode/overlay/task_palette.zig @@ -58,7 +58,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -pub fn on_render_menu(palette: *Type, button: *Type.ButtonState, theme: *const Widget.Theme, selected: bool) bool { +pub fn on_render_menu(palette: *Type, button: *Type.ButtonType, theme: *const Widget.Theme, selected: bool) bool { var entry: Entry = undefined; var iter = button.opts.label; // label contains cbor entry object and matches if (!(cbor.matchValue(&iter, cbor.extract(&entry)) catch false)) @@ -116,7 +116,7 @@ pub fn on_render_menu(palette: *Type, button: *Type.ButtonState, theme: *const W return false; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var entry: Entry = undefined; var iter = button.opts.label; if (!(cbor.matchValue(&iter, cbor.extract(&entry)) catch false)) return; @@ -130,7 +130,7 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { } } -pub fn delete_item(menu: *Type.MenuState, button: *Type.ButtonState) bool { +pub fn delete_item(menu: *Type.MenuType, button: *Type.ButtonType) bool { var entry: Entry = undefined; var iter = button.opts.label; if (!(cbor.matchValue(&iter, cbor.extract(&entry)) catch false)) return false; diff --git a/src/tui/mode/overlay/theme_palette.zig b/src/tui/mode/overlay/theme_palette.zig index 3be9cf6..3aaa695 100644 --- a/src/tui/mode/overlay/theme_palette.zig +++ b/src/tui/mode/overlay/theme_palette.zig @@ -53,7 +53,7 @@ pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !v palette.items += 1; } -fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { +fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Cursor) void { var description_: []const u8 = undefined; var name_: []const u8 = undefined; var iter = button.opts.label; @@ -65,7 +65,7 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { tp.self_pid().send(.{ "cmd", "set_theme", .{name_} }) catch |e| menu.*.opts.ctx.logger.err("theme_palette", e); } -pub fn updated(palette: *Type, button_: ?*Type.ButtonState) !void { +pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(palette); var description_: []const u8 = undefined; var name_: []const u8 = undefined; diff --git a/src/tui/status/branch.zig b/src/tui/status/branch.zig index 940699d..af433a5 100644 --- a/src/tui/status/branch.zig +++ b/src/tui/status/branch.zig @@ -29,6 +29,7 @@ untracked: usize = 0, done: bool = true, const Self = @This(); +const ButtonType = Button.Options(Self).ButtonType; pub fn create( allocator: std.mem.Allocator, @@ -61,7 +62,7 @@ pub fn ctx_deinit(self: *Self) void { if (self.behind) |p| self.allocator.free(p); } -fn on_click(self: *Self, _: *Button.State(Self)) void { +fn on_click(self: *Self, _: *ButtonType, _: Button.Cursor) void { self.refresh_git_status(); command.executeName("show_git_status", .{}) catch {}; } @@ -70,7 +71,7 @@ fn refresh_git_status(self: *Self) void { if (self.workspace_path) |_| git.status(0) catch {}; } -pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn receive(self: *Self, _: *ButtonType, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "E", tp.more })) return self.process_event(m); if (try m.match(.{ "PRJ", "open" })) @@ -195,14 +196,14 @@ fn format(self: *Self, buf: []u8) []const u8 { return fbs.getWritten(); } -pub fn layout(self: *Self, btn: *Button.State(Self)) Widget.Layout { +pub fn layout(self: *Self, btn: *ButtonType) Widget.Layout { var buf: [256]u8 = undefined; const text = self.format(&buf); const len = btn.plane.egc_chunk_width(text, 0, 1); return .{ .static = len }; } -pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool { +pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { var buf: [256]u8 = undefined; const text = self.format(&buf); if (text.len == 0) return false; diff --git a/src/tui/status/diagstate.zig b/src/tui/status/diagstate.zig index 5ddb304..37a81b0 100644 --- a/src/tui/status/diagstate.zig +++ b/src/tui/status/diagstate.zig @@ -17,6 +17,7 @@ buf: [256]u8 = undefined, rendered: [:0]const u8 = "", const Self = @This(); +const ButtonType = Button.Options(Self).ButtonType; pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { return Button.create_widget(Self, allocator, parent, .{ @@ -30,15 +31,15 @@ pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, }); } -fn on_click(_: *Self, _: *Button.State(Self)) void { +fn on_click(_: *Self, _: *ButtonType, _: Button.Cursor) void { command.executeName("show_diagnostics", .{}) catch {}; } -pub fn layout(self: *Self, _: *Button.State(Self)) Widget.Layout { +pub fn layout(self: *Self, _: *ButtonType) Widget.Layout { return .{ .static = self.rendered.len }; } -pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool { +pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { const bg_style = if (btn.active) theme.editor_cursor else if (btn.hover) theme.statusbar_hover else theme.statusbar; btn.plane.set_base_style(theme.editor); btn.plane.erase(); @@ -61,7 +62,7 @@ fn format(self: *Self) void { self.buf[self.rendered.len] = 0; } -pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn receive(self: *Self, _: *ButtonType, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "E", "diag", tp.extract(&self.errors), tp.extract(&self.warnings), tp.extract(&self.info), tp.extract(&self.hints) })) self.format(); return false; diff --git a/src/tui/status/filestate.zig b/src/tui/status/filestate.zig index 52ffb08..f9e39ac 100644 --- a/src/tui/status/filestate.zig +++ b/src/tui/status/filestate.zig @@ -40,6 +40,7 @@ indent_mode: config.IndentMode = .spaces, const project_icon = ""; const Self = @This(); +const ButtonType = Button.Options(Self).ButtonType; pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { const btn = try Button.create(Self, allocator, parent, .{ @@ -64,23 +65,23 @@ pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, return Widget.to(btn); } -fn on_click(_: *Self, _: *Button.State(Self)) void { +fn on_click(_: *Self, _: *ButtonType, _: Button.Cursor) void { command.executeName("open_recent", .{}) catch {}; } -fn on_click2(_: *Self, _: *Button.State(Self)) void { +fn on_click2(_: *Self, _: *ButtonType, _: Button.Cursor) void { command.executeName("close_file", .{}) catch {}; } -fn on_click3(self: *Self, _: *Button.State(Self)) void { +fn on_click3(self: *Self, _: *ButtonType, _: Button.Cursor) void { self.detailed = !self.detailed; } -pub fn layout(_: *Self, _: *Button.State(Self)) Widget.Layout { +pub fn layout(_: *Self, _: *ButtonType) Widget.Layout { return .dynamic; } -pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool { +pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { const style_base = theme.statusbar; const style_label = if (btn.active) theme.editor_cursor else style_base; btn.plane.set_base_style(theme.editor); @@ -205,7 +206,7 @@ fn render_terminal_title(self: *Self) void { tui.rdr().set_terminal_title(new_title); } -pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn receive(self: *Self, _: *ButtonType, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "E", tp.more })) return self.process_event(m); if (try m.match(.{ "PRJ", "open" })) { diff --git a/src/tui/status/linenumstate.zig b/src/tui/status/linenumstate.zig index 9c6813c..1339db2 100644 --- a/src/tui/status/linenumstate.zig +++ b/src/tui/status/linenumstate.zig @@ -33,6 +33,7 @@ const Leader = enum { zero, }; const Self = @This(); +const ButtonType = Button.Options(Self).ButtonType; pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @import("widget.zig").CreateError!Widget { const padding: ?usize, const leader: ?Leader, const style: ?DigitStyle = if (arg) |fmt| blk: { @@ -59,17 +60,17 @@ pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, }); } -fn on_click(_: *Self, _: *Button.State(Self)) void { +fn on_click(_: *Self, _: *ButtonType, _: Button.Cursor) void { command.executeName("goto", .{}) catch {}; } -pub fn layout(self: *Self, btn: *Button.State(Self)) Widget.Layout { +pub fn layout(self: *Self, btn: *ButtonType) Widget.Layout { const warn_len = if (self.utf8_sanitized) btn.plane.egc_chunk_width(utf8_sanitized_warning, 0, 1) else 0; const len = btn.plane.egc_chunk_width(self.rendered, 0, 1) + warn_len; return .{ .static = len }; } -pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool { +pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { btn.plane.set_base_style(theme.editor); btn.plane.erase(); btn.plane.home(); @@ -120,7 +121,7 @@ fn format_count(self: *Self, writer: anytype, value: usize, width: usize) !void for (value_str, 0..) |_, i| try writer.writeAll(fonts.get_digit_ascii(value_str[i .. i + 1], self.style orelse .ascii)); } -pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn receive(self: *Self, _: *ButtonType, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "E", "pos", tp.extract(&self.lines), tp.extract(&self.line), tp.extract(&self.column) })) { self.format(); } else if (try m.match(.{ "E", "eol_mode", tp.extract(&self.eol_mode), tp.extract(&self.utf8_sanitized), tp.extract(&self.indent_mode) })) { diff --git a/src/tui/status/modestate.zig b/src/tui/status/modestate.zig index bc91fcd..be8f9f8 100644 --- a/src/tui/status/modestate.zig +++ b/src/tui/status/modestate.zig @@ -18,6 +18,8 @@ const Style = enum { }; const default_style = .fancy; +const ButtonType = Button.Options(Style).ButtonType; + pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) CreateError!Widget { const style_ = if (arg) |str_style| std.meta.stringToEnum(Style, str_style) orelse default_style else default_style; return Button.create_widget(Style, allocator, parent, .{ @@ -32,7 +34,7 @@ pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, }); } -pub fn layout(_: *Style, btn: *Button.State(Style)) Widget.Layout { +pub fn layout(_: *Style, btn: *ButtonType) Widget.Layout { const name = btn.plane.egc_chunk_width(tui.get_mode(), 0, 1); const logo = if (is_mini_mode() or is_overlay_mode()) 1 else btn.plane.egc_chunk_width(left ++ symbol ++ right, 0, 1); const padding: usize = 2; @@ -48,7 +50,7 @@ fn is_overlay_mode() bool { return tui.input_mode_outer() != null; } -pub fn render(ctx: *Style, self: *Button.State(Style), theme: *const Widget.Theme) bool { +pub fn render(ctx: *Style, self: *ButtonType, theme: *const Widget.Theme) bool { const style_base = theme.statusbar; const style_label = switch (ctx.*) { .fancy => if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover, @@ -78,7 +80,7 @@ pub fn render(ctx: *Style, self: *Button.State(Style), theme: *const Widget.Them return false; } -fn render_separator(self: *Button.State(Style), theme: *const Widget.Theme) void { +fn render_separator(self: *ButtonType, theme: *const Widget.Theme) void { self.plane.reverse_style(); self.plane.set_base_style(.{ .bg = theme.editor.bg }); if (theme.statusbar.bg) |bg| self.plane.set_style(.{ .bg = bg }); @@ -89,7 +91,7 @@ const left = " "; const symbol = "󱞏"; const right = " "; -fn render_logo(self: *Button.State(Style), theme: *const Widget.Theme, style_label: Widget.Theme.Style) void { +fn render_logo(self: *ButtonType, theme: *const Widget.Theme, style_label: Widget.Theme.Style) void { const style_braces: Widget.Theme.Style = if (tui.find_scope_style(theme, "punctuation")) |sty| .{ .fg = sty.style.fg, .bg = style_label.bg, .fs = style_label.fs } else style_label; if (left.len > 0) { self.plane.set_style(style_braces); @@ -103,7 +105,7 @@ fn render_logo(self: *Button.State(Style), theme: *const Widget.Theme, style_lab } } -fn on_click(_: *Style, _: *Button.State(Style)) void { +fn on_click(_: *Style, _: *ButtonType, _: Button.Cursor) void { if (is_mini_mode()) { command.executeName("exit_mini_mode", .{}) catch {}; } else if (is_overlay_mode()) { @@ -113,6 +115,6 @@ fn on_click(_: *Style, _: *Button.State(Style)) void { } } -fn toggle_panel(_: *Style, _: *Button.State(Style)) void { +fn toggle_panel(_: *Style, _: *ButtonType, _: Button.Cursor) void { command.executeName("toggle_panel", .{}) catch {}; } diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 9427894..46491c3 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -184,7 +184,7 @@ pub const TabBar = struct { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); try self.update_tab_buffers(); const prev_widget_count = self.widget_list.widgets.items.len; - while (self.widget_list.pop()) |widget| if (widget.dynamic_cast(Button.State(Tab)) == null) + while (self.widget_list.pop()) |widget| if (widget.dynamic_cast(Tab.ButtonType) == null) widget.deinit(self.widget_list.allocator); var first = true; for (self.tabs) |tab| { @@ -194,7 +194,7 @@ pub const TabBar = struct { try self.widget_list.add(try self.make_spacer()); } try self.widget_list.add(tab.widget); - if (tab.widget.dynamic_cast(Button.State(Tab))) |btn| { + if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn| { if (buffer_manager.buffer_from_ref(tab.buffer_ref)) |buffer| try btn.update_label(Tab.name_from_buffer(buffer)); } @@ -327,6 +327,8 @@ const Tab = struct { const Mode = enum { active, inactive, selected }; + const ButtonType = Button.Options(@This()).ButtonType; + fn create( tabbar: *TabBar, buffer_ref: usize, @@ -346,19 +348,19 @@ const Tab = struct { }); } - fn on_click(self: *@This(), _: *Button.State(@This())) void { + fn on_click(self: *@This(), _: *ButtonType, _: Button.Cursor) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); if (buffer_manager.buffer_from_ref(self.buffer_ref)) |buffer| tp.self_pid().send(.{ "cmd", "navigate", .{ .file = buffer.get_file_path() } }) catch {}; } - fn on_click2(self: *@This(), _: *Button.State(@This())) void { + fn on_click2(self: *@This(), _: *ButtonType, _: Button.Cursor) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); if (buffer_manager.buffer_from_ref(self.buffer_ref)) |buffer| tp.self_pid().send(.{ "cmd", "close_buffer", .{buffer.get_file_path()} }) catch {}; } - fn render(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme) bool { + fn render(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) bool { const active = self.tabbar.active_buffer_ref == self.buffer_ref; const mode: Mode = if (btn.hover) .selected else if (active) .active else .inactive; switch (mode) { @@ -369,7 +371,7 @@ const Tab = struct { return false; } - fn render_selected(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme, active: bool) void { + fn render_selected(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme, active: bool) void { btn.plane.set_base_style(theme.editor); btn.plane.erase(); btn.plane.home(); @@ -407,7 +409,7 @@ const Tab = struct { _ = btn.plane.putstr(self.tab_style.selected_right) catch {}; } - fn render_active(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme) void { + fn render_active(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) void { btn.plane.set_base_style(theme.editor); btn.plane.erase(); btn.plane.home(); @@ -443,7 +445,7 @@ const Tab = struct { _ = btn.plane.putstr(self.tab_style.active_right) catch {}; } - fn render_inactive(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme) void { + fn render_inactive(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) void { btn.plane.set_base_style(theme.editor); btn.plane.erase(); btn.plane.home(); @@ -473,7 +475,7 @@ const Tab = struct { _ = btn.plane.putstr(self.tab_style.inactive_right) catch {}; } - fn render_content(self: *@This(), btn: *Button.State(@This()), fg: ?Widget.Theme.Color, theme: *const Widget.Theme) void { + fn render_content(self: *@This(), btn: *ButtonType, fg: ?Widget.Theme.Color, theme: *const Widget.Theme) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); const buffer_ = buffer_manager.buffer_from_ref(self.buffer_ref); const is_dirty = if (buffer_) |buffer| buffer.is_dirty() else false; @@ -513,7 +515,7 @@ const Tab = struct { while (padding > 0) : (padding -= 1) _ = plane.putstr(self.tab_style.padding) catch {}; } - fn layout(self: *@This(), btn: *Button.State(@This())) Widget.Layout { + fn layout(self: *@This(), btn: *ButtonType) Widget.Layout { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); const is_dirty = if (buffer_manager.buffer_from_ref(self.buffer_ref)) |buffer| buffer.is_dirty() else false; const active = self.tabbar.active_buffer_ref == self.buffer_ref;