From 63734ef81b888ef07282b28225d93fdd64c4954c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 19 Oct 2024 18:29:10 +0200 Subject: [PATCH] feat: add ModalBackground and make all palettes modal --- src/tui/ModalBackground.zig | 106 +++++++++++++++++++++++++++ src/tui/mode/overlay/open_recent.zig | 8 +- src/tui/mode/overlay/palette.zig | 8 +- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/tui/ModalBackground.zig diff --git a/src/tui/ModalBackground.zig b/src/tui/ModalBackground.zig new file mode 100644 index 0000000..d698593 --- /dev/null +++ b/src/tui/ModalBackground.zig @@ -0,0 +1,106 @@ +const std = @import("std"); +const tp = @import("thespian"); + +const tui = @import("tui.zig"); +const Widget = @import("Widget.zig"); +const EventHandler = @import("EventHandler.zig"); +const Plane = @import("renderer").Plane; +const key = @import("renderer").input.key; +const event_type = @import("renderer").input.event_type; + +pub fn Options(context: type) type { + return struct { + ctx: Context, + + on_click: *const fn (ctx: context, self: *State(Context)) void = on_click_exit_overlay_mode, + on_click2: *const fn (ctx: context, self: *State(Context)) void = do_nothing, + on_click3: *const fn (ctx: context, self: *State(Context)) void = do_nothing, + on_click4: *const fn (ctx: context, self: *State(Context)) void = do_nothing, + on_click5: *const fn (ctx: context, self: *State(Context)) void = do_nothing, + on_render: *const fn (ctx: context, self: *State(Context), theme: *const Widget.Theme) bool = on_render_default, + on_layout: *const fn (ctx: context) Widget.Layout = on_layout_default, + on_resize: *const fn (ctx: context, state: *State(Context), box: Widget.Box) void = on_resize_default, + + pub const Context = context; + pub fn do_nothing(_: context, _: *State(Context)) void {} + + fn on_click_exit_overlay_mode(_: context, _: *State(Context)) void { + tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch {}; + } + + pub fn on_render_default(_: context, _: *State(Context), _: *const Widget.Theme) bool { + return false; + } + + pub fn on_layout_default(_: context) Widget.Layout { + return .dynamic; + } + + pub fn on_resize_default(_: context, _: *State(Context), _: Widget.Box) void {} + }; +} + +pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Widget, opts: Options(ctx_type)) !*State(ctx_type) { + const self = try allocator.create(State(ctx_type)); + self.* = .{ + .allocator = allocator, + .plane = parent.plane.*, + .opts = opts, + }; + return self; +} + +pub fn State(ctx_type: type) type { + return struct { + allocator: std.mem.Allocator, + plane: Plane, + opts: options_type, + hover: bool = false, + + const Self = @This(); + const options_type = Options(ctx_type); + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.destroy(self); + } + + pub fn render(self: *Self, theme: *const Widget.Theme) bool { + return self.opts.on_render(self.opts.ctx, self, theme); + } + + pub fn widget(self: *Self) Widget { + return Widget.to(self); + } + + pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { + var btn: u32 = 0; + if (try m.match(.{ "B", event_type.PRESS, tp.more })) { + return true; + } else if (try m.match(.{ "B", event_type.RELEASE, tp.extract(&btn), tp.more })) { + self.call_click_handler(btn); + return true; + } else if (try m.match(.{ "D", event_type.PRESS, tp.extract(&btn), tp.more })) { + return true; + } else if (try m.match(.{ "D", event_type.RELEASE, tp.extract(&btn), tp.more })) { + self.call_click_handler(btn); + return true; + } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { + tui.current().rdr.request_mouse_cursor_pointer(self.hover); + return true; + } + return false; + } + + fn call_click_handler(self: *Self, btn: u32) void { + if (!self.hover) return; + switch (btn) { + key.BUTTON1 => self.opts.on_click(self.opts.ctx, self), + key.BUTTON2 => self.opts.on_click2(self.opts.ctx, self), + key.BUTTON3 => self.opts.on_click3(self.opts.ctx, self), + key.BUTTON4 => self.opts.on_click4(self.opts.ctx, self), + key.BUTTON5 => self.opts.on_click5(self.opts.ctx, self), + else => {}, + } + } + }; +} diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 59da250..dd70334 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -20,12 +20,14 @@ const InputBox = @import("../../InputBox.zig"); const Menu = @import("../../Menu.zig"); const Widget = @import("../../Widget.zig"); const mainview = @import("../../mainview.zig"); +const ModalBackground = @import("../../ModalBackground.zig"); const Self = @This(); const max_recent_files: usize = 25; allocator: std.mem.Allocator, f: usize = 0, +modal: *ModalBackground.State(*Self), menu: *Menu.State(*Self), inputbox: *InputBox.State(*Self), logger: log.Logger, @@ -40,6 +42,7 @@ pub fn create(allocator: std.mem.Allocator) !tui.Mode { const self: *Self = try allocator.create(Self); self.* = .{ .allocator = allocator, + .modal = try ModalBackground.create(*Self, allocator, tui.current().mainview, .{ .ctx = self }), .menu = try Menu.create(*Self, allocator, tui.current().mainview, .{ .ctx = self, .on_render = on_render_menu, @@ -56,6 +59,7 @@ pub fn create(allocator: std.mem.Allocator) !tui.Mode { self.query_pending = true; try project_manager.request_recent_files(max_recent_files); self.menu.resize(.{ .y = 0, .x = self.menu_pos_x(), .w = max_menu_width() + 2 }); + try mv.floating_views.add(self.modal.widget()); try mv.floating_views.add(self.menu.container_widget); return .{ .handler = EventHandler.to_owned(self), @@ -67,8 +71,10 @@ pub fn create(allocator: std.mem.Allocator) !tui.Mode { pub fn deinit(self: *Self) void { self.commands.deinit(); tui.current().message_filters.remove_ptr(self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv| + if (tui.current().mainview.dynamic_cast(mainview)) |mv| { mv.floating_views.remove(self.menu.container_widget); + mv.floating_views.remove(self.modal.widget()); + } self.logger.deinit(); self.allocator.destroy(self); } diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 2fd168a..0716c3d 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -18,6 +18,7 @@ const InputBox = @import("../../InputBox.zig"); const Widget = @import("../../Widget.zig"); const mainview = @import("../../mainview.zig"); const scrollbar_v = @import("../../scrollbar_v.zig"); +const ModalBackground = @import("../../ModalBackground.zig"); pub const Menu = @import("../../Menu.zig"); @@ -26,6 +27,7 @@ const max_menu_width = 80; pub fn Create(options: type) type { return struct { allocator: std.mem.Allocator, + modal: *ModalBackground.State(*Self), menu: *Menu.State(*Self), inputbox: *InputBox.State(*Self), logger: log.Logger, @@ -51,6 +53,7 @@ pub fn Create(options: type) type { const self: *Self = try allocator.create(Self); self.* = .{ .allocator = allocator, + .modal = try ModalBackground.create(*Self, allocator, tui.current().mainview, .{ .ctx = self }), .menu = try Menu.create(*Self, allocator, tui.current().mainview, .{ .ctx = self, .on_render = on_render_menu, @@ -78,6 +81,7 @@ pub fn Create(options: type) type { options.restore_state(self) catch {}; try self.commands.init(self); try self.start_query(); + try mv.floating_views.add(self.modal.widget()); try mv.floating_views.add(self.menu.container_widget); return .{ .handler = EventHandler.to_owned(self), @@ -92,8 +96,10 @@ pub fn Create(options: type) type { options.deinit(self); self.entries.deinit(); tui.current().message_filters.remove_ptr(self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv| + if (tui.current().mainview.dynamic_cast(mainview)) |mv| { mv.floating_views.remove(self.menu.container_widget); + mv.floating_views.remove(self.modal.widget()); + } self.logger.deinit(); self.allocator.destroy(self); }