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.
This commit is contained in:
CJ van den Berg 2025-10-09 19:11:25 +02:00
parent 35ccf7f1df
commit ce87dcfa2b
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
21 changed files with 148 additions and 118 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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" })) {

View file

@ -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) })) {

View file

@ -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 {};
}

View file

@ -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;