diff --git a/src/tui/Button.zig b/src/tui/Button.zig index e5cbe81..85d51ce 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -12,14 +12,17 @@ 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_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_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_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, pub const Context = context; - pub fn do_nothing(_: context, _: *State(Context)) void {} + pub fn do_nothing(_: *context, _: *State(Context)) void {} - pub fn on_render_default(_: context, self: *State(Context), theme: *const Widget.Theme) bool { + pub fn on_render_default(_: *context, self: *State(Context), theme: *const Widget.Theme) bool { tui.set_base_style(&self.plane, " ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar); self.plane.erase(); self.plane.home(); @@ -27,13 +30,17 @@ 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: *State(Context)) 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 { + return false; + } }; } -pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !Widget { +pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !*State(ctx_type) { const Self = State(ctx_type); const self = try a.create(Self); var n = try nc.Plane.init(&opts.pos.opts(@typeName(Self)), parent); @@ -46,7 +53,11 @@ pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Opti }; try self.label.appendSlice(self.opts.label); self.opts.label = self.label.items; - return Widget.to(self); + return self; +} + +pub fn create_widget(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !Widget { + return Widget.to(try create(ctx_type, a, parent, opts)); } pub fn State(ctx_type: type) type { @@ -68,25 +79,26 @@ pub fn State(ctx_type: type) type { } pub fn layout(self: *Self) Widget.Layout { - return self.opts.on_layout(self.opts.ctx, self); + return self.opts.on_layout(&self.opts.ctx, self); } pub fn render(self: *Self, theme: *const Widget.Theme) bool { - return self.opts.on_render(self.opts.ctx, self, theme); + return self.opts.on_render(&self.opts.ctx, self, theme); } - pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { - if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) { + pub fn receive(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { + var btn: u32 = 0; + if (try m.match(.{ "B", nc.event_type.PRESS, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) { self.active = true; tui.need_render(); return true; - } else if (try m.match(.{ "B", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) { - self.opts.on_click(self.opts.ctx, self); + } else if (try m.match(.{ "B", nc.event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) { + self.call_click_handler(btn); self.active = false; tui.need_render(); return true; - } else if (try m.match(.{ "D", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) { - self.opts.on_click(self.opts.ctx, self); + } else if (try m.match(.{ "D", nc.event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) { + self.call_click_handler(btn); self.active = false; tui.need_render(); return true; @@ -95,7 +107,16 @@ pub fn State(ctx_type: type) type { tui.need_render(); return true; } - return false; + return self.opts.on_receive(&self.opts.ctx, self, from, m); + } + + fn call_click_handler(self: *Self, btn: u32) void { + switch (btn) { + nc.key.BUTTON1 => self.opts.on_click(&self.opts.ctx, self), + nc.key.BUTTON2 => self.opts.on_click2(&self.opts.ctx, self), + nc.key.BUTTON3 => self.opts.on_click3(&self.opts.ctx, self), + else => {}, + } } }; } diff --git a/src/tui/Menu.zig b/src/tui/Menu.zig index dece251..0ec05fe 100644 --- a/src/tui/Menu.zig +++ b/src/tui/Menu.zig @@ -91,8 +91,8 @@ 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 { - try self.menu.add(try Button.create(*Self, self.a, self.menu.parent, .{ + pub fn add_item_with_handler(self: *Self, label: []const u8, on_click: *const fn (_: **Self, _: *Button.State(*Self)) void) !void { + try self.menu.add(try Button.create_widget(*Self, self.a, self.menu.parent, .{ .ctx = self, .on_layout = on_layout, .label = label, @@ -117,14 +117,14 @@ pub fn State(ctx_type: type) type { self.render_idx = 0; } - pub fn on_layout(self: *Self, button: *Button.State(*Self)) Widget.Layout { - return self.opts.on_layout(self.opts.ctx, button); + pub fn on_layout(self: **Self, button: *Button.State(*Self)) 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 { - 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); + pub fn on_render(self: **Self, button: *Button.State(*Self), 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); } fn on_resize_menu_widgetlist(ctx: ?*anyopaque, _: *WidgetList, box: Widget.Box) void { @@ -168,7 +168,7 @@ pub fn State(ctx_type: type) type { self.selected_active = true; const pos = selected + self.header_count; const button = self.menu.widgets.items[pos].widget.dynamic_cast(button_type) orelse return; - button.opts.on_click(button.opts.ctx, button); + button.opts.on_click(&button.opts.ctx, button); } }; } diff --git a/src/tui/home.zig b/src/tui/home.zig index efa36d2..8ea1b6c 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -114,31 +114,31 @@ fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c return false; } -fn menu_action_help(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_help(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { command.executeName("open_help", .{}) catch {}; } -fn menu_action_open_file(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_open_file(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { command.executeName("enter_open_file_mode", .{}) catch {}; } -fn menu_action_open_recent_file(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_open_recent_file(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { command.executeName("enter_overlay_mode", command.fmt(.{"open_recent"})) catch {}; } -fn menu_action_open_recent_project(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_open_recent_project(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { tp.self_pid().send(.{ "log", "home", "open recent project not implemented" }) catch {}; } -fn menu_action_show_commands(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_show_commands(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { tp.self_pid().send(.{ "log", "home", "open command palette not implemented" }) catch {}; } -fn menu_action_open_config(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_open_config(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { command.executeName("open_config", .{}) catch {}; } -fn menu_action_quit(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { +fn menu_action_quit(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void { command.executeName("quit", .{}) catch {}; } diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index dc7de9c..ddf4d11 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -87,9 +87,9 @@ fn on_resize_menu(self: *Self, state: *Menu.State(*Self), box: Widget.Box) void }); } -fn menu_action_open_file(menu: *Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { - tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.opts.ctx.logger.err("navigate", e); - tp.self_pid().send(.{ "cmd", "navigate", .{ .file = button.label.items } }) catch |e| menu.opts.ctx.logger.err("navigate", e); +fn menu_action_open_file(menu: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void { + tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("navigate", e); + tp.self_pid().send(.{ "cmd", "navigate", .{ .file = button.label.items } }) catch |e| menu.*.opts.ctx.logger.err("navigate", e); } fn shorten_path(buf: []u8, path: []const u8) []const u8 { diff --git a/src/tui/status/modestate.zig b/src/tui/status/modestate.zig index 87d7347..caf6308 100644 --- a/src/tui/status/modestate.zig +++ b/src/tui/status/modestate.zig @@ -14,7 +14,7 @@ const ed = @import("../editor.zig"); const tui = @import("../tui.zig"); pub fn create(a: Allocator, parent: nc.Plane) !Widget { - return Button.create(void, a, parent, .{ + return Button.create_widget(void, a, parent, .{ .ctx = {}, .label = tui.get_mode(), .on_click = on_click, @@ -23,7 +23,7 @@ pub fn create(a: Allocator, parent: nc.Plane) !Widget { }); } -pub fn layout(_: void, _: *Button.State(void)) Widget.Layout { +pub fn layout(_: *void, _: *Button.State(void)) Widget.Layout { const name = tui.get_mode(); const width = Buffer.egc_chunk_width(name, 0); const padding: usize = if (is_mini_mode()) 3 else 2; @@ -34,7 +34,7 @@ fn is_mini_mode() bool { return if (tui.current().mini_mode) |_| true else false; } -pub fn render(_: void, self: *Button.State(void), theme: *const Widget.Theme) bool { +pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) bool { tui.set_base_style(&self.plane, " ", if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover); self.plane.on_styles(nc.style.bold); self.plane.erase(); @@ -52,6 +52,6 @@ fn render_separator(self: *Button.State(void), theme: *const Widget.Theme) void _ = self.plane.putstr("") catch {}; } -fn on_click(_: void, _: *Button.State(void)) void { +fn on_click(_: *void, _: *Button.State(void)) void { command.executeName("toggle_input_mode", .{}) catch {}; }