feat: add search box to open recent file mode
This commit is contained in:
parent
f88adf9a9d
commit
744c6012a7
2 changed files with 86 additions and 15 deletions
|
@ -8,6 +8,7 @@ const command = @import("../../command.zig");
|
|||
const EventHandler = @import("../../EventHandler.zig");
|
||||
const MessageFilter = @import("../../MessageFilter.zig");
|
||||
const Button = @import("../../Button.zig");
|
||||
const InputBox = @import("../../InputBox.zig");
|
||||
const Menu = @import("../../Menu.zig");
|
||||
const Widget = @import("../../Widget.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
@ -19,8 +20,11 @@ const max_recent_files: usize = 25;
|
|||
a: std.mem.Allocator,
|
||||
f: usize = 0,
|
||||
menu: *Menu.State(*Self),
|
||||
inputbox: *InputBox.State(*Self),
|
||||
logger: log.Logger,
|
||||
count: usize = 0,
|
||||
query_pending: bool = false,
|
||||
need_reset: bool = false,
|
||||
need_select_first: bool = true,
|
||||
longest: usize = 0,
|
||||
commands: Commands = undefined,
|
||||
|
||||
|
@ -31,9 +35,14 @@ pub fn create(a: std.mem.Allocator) !tui.Mode {
|
|||
.a = a,
|
||||
.menu = try Menu.create(*Self, a, tui.current().mainview, .{ .ctx = self, .on_render = on_render_menu, .on_resize = on_resize_menu }),
|
||||
.logger = log.logger(@typeName(Self)),
|
||||
.inputbox = (try self.menu.add_header(try InputBox.create(*Self, self.a, self.menu.menu.parent, .{
|
||||
.ctx = self,
|
||||
.label = "Search files by name",
|
||||
}))).dynamic_cast(InputBox.State(*Self)) orelse unreachable,
|
||||
};
|
||||
try self.commands.init(self);
|
||||
try tui.current().message_filters.add(MessageFilter.bind(self, receive_project_manager));
|
||||
self.query_pending = true;
|
||||
try project_manager.request_recent_files(max_recent_files);
|
||||
self.menu.resize(.{ .y = 0, .x = 25, .w = 32 });
|
||||
try mv.floating_views.add(self.menu.menu_widget);
|
||||
|
@ -87,14 +96,22 @@ fn receive_project_manager(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit
|
|||
|
||||
fn process_project_manager(self: *Self, m: tp.message) tp.result {
|
||||
var file_name: []const u8 = undefined;
|
||||
var query: []const u8 = undefined;
|
||||
if (try m.match(.{ "PRJ", "recent", tp.extract(&file_name) })) {
|
||||
if (self.count < max_recent_files) {
|
||||
self.count += 1;
|
||||
self.longest = @max(self.longest, file_name.len);
|
||||
self.menu.add_item_with_handler(file_name, menu_action_open_file) catch |e| return tp.exit_error(e);
|
||||
self.menu.resize(.{ .y = 0, .x = 25, .w = @min(self.longest, 80) + 2 });
|
||||
tui.need_render();
|
||||
if (self.need_reset) self.reset_results();
|
||||
self.longest = @max(self.longest, file_name.len);
|
||||
self.menu.add_item_with_handler(file_name, menu_action_open_file) catch |e| return tp.exit_error(e);
|
||||
self.menu.resize(.{ .y = 0, .x = 25, .w = @min(self.longest, 80) + 2 });
|
||||
if (self.need_select_first) {
|
||||
self.menu.select_down();
|
||||
self.need_select_first = false;
|
||||
}
|
||||
tui.need_render();
|
||||
} else if (try m.match(.{ "PRJ", "recent_done", tp.extract(&query) })) {
|
||||
self.query_pending = false;
|
||||
self.need_reset = true;
|
||||
if (!std.mem.eql(u8, self.inputbox.text.items, query))
|
||||
try self.start_query();
|
||||
} else {
|
||||
self.logger.err("receive", tp.unexpected(m));
|
||||
}
|
||||
|
@ -103,24 +120,25 @@ fn process_project_manager(self: *Self, m: tp.message) tp.result {
|
|||
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
var evtype: u32 = undefined;
|
||||
var keypress: u32 = undefined;
|
||||
var egc: u32 = undefined;
|
||||
var modifiers: u32 = undefined;
|
||||
|
||||
if (try m.match(.{ "I", tp.extract(&evtype), tp.extract(&keypress), tp.any, tp.string, tp.extract(&modifiers) })) {
|
||||
try self.mapEvent(evtype, keypress, modifiers);
|
||||
if (try m.match(.{ "I", tp.extract(&evtype), tp.extract(&keypress), tp.extract(&egc), tp.string, tp.extract(&modifiers) })) {
|
||||
try self.mapEvent(evtype, keypress, egc, modifiers);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, modifiers: u32) tp.result {
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, modifiers),
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
||||
fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
const keynormal = if ('a' <= keypress and keypress <= 'z') keypress - ('a' - 'A') else keypress;
|
||||
return switch (modifiers) {
|
||||
nc.mod.CTRL => switch (keynormal) {
|
||||
|
@ -128,10 +146,13 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
'Q' => self.cmd("quit", .{}),
|
||||
'W' => self.cmd("close_file", .{}),
|
||||
'E' => self.cmd("open_recent_menu_down", .{}),
|
||||
'P' => self.cmd("open_recent_menu_up", .{}),
|
||||
'N' => self.cmd("open_recent_menu_down", .{}),
|
||||
nc.key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
nc.key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
nc.key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
nc.key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
nc.key.BACKSPACE => self.delete_word(),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.CTRL | nc.mod.SHIFT => switch (keynormal) {
|
||||
|
@ -147,6 +168,11 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
'I' => self.cmd("toggle_inputview", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.SHIFT => switch (keypress) {
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
0 => switch (keypress) {
|
||||
nc.key.F09 => self.cmd("theme_prev", .{}),
|
||||
nc.key.F10 => self.cmd("theme_next", .{}),
|
||||
|
@ -156,7 +182,10 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
nc.key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
nc.key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
nc.key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
else => {},
|
||||
nc.key.BACKSPACE => self.delete_code_point(),
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
@ -164,11 +193,50 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
|
||||
fn mapRelease(self: *Self, keypress: u32, _: u32) tp.result {
|
||||
return switch (keypress) {
|
||||
nc.key.LCTRL, nc.key.RCTRL => self.cmd("open_recent_menu_activate", .{}),
|
||||
nc.key.LCTRL, nc.key.RCTRL => if (self.menu.selected orelse 0 > 0) return self.cmd("open_recent_menu_activate", .{}),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn reset_results(self: *Self) void {
|
||||
self.need_reset = false;
|
||||
self.menu.reset_items();
|
||||
self.menu.selected = null;
|
||||
self.need_select_first = true;
|
||||
}
|
||||
|
||||
fn start_query(self: *Self) tp.result {
|
||||
if (self.query_pending) return;
|
||||
self.query_pending = true;
|
||||
try project_manager.query_recent_files(max_recent_files, self.inputbox.text.items);
|
||||
}
|
||||
|
||||
fn delete_word(self: *Self) tp.result {
|
||||
if (std.mem.lastIndexOfAny(u8, self.inputbox.text.items, "/\\. -_")) |pos| {
|
||||
self.inputbox.text.shrinkRetainingCapacity(pos);
|
||||
} else {
|
||||
self.inputbox.text.shrinkRetainingCapacity(0);
|
||||
}
|
||||
self.inputbox.cursor = self.inputbox.text.items.len;
|
||||
return self.start_query();
|
||||
}
|
||||
|
||||
fn delete_code_point(self: *Self) tp.result {
|
||||
if (self.inputbox.text.items.len > 0) {
|
||||
self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - 1);
|
||||
self.inputbox.cursor = self.inputbox.text.items.len;
|
||||
}
|
||||
return self.start_query();
|
||||
}
|
||||
|
||||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.text.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.cursor = self.inputbox.text.items.len;
|
||||
return self.start_query();
|
||||
}
|
||||
|
||||
fn cmd(_: *Self, name_: []const u8, ctx: command.Context) tp.result {
|
||||
try command.executeName(name_, ctx);
|
||||
}
|
||||
|
|
|
@ -299,6 +299,9 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
|
|||
if (try m.match(.{ "exit", "timeout_error", 125, "Operation aborted." }))
|
||||
return;
|
||||
|
||||
if (try m.match(.{ "PRJ", tp.more })) // drop late project manager query responses
|
||||
return;
|
||||
|
||||
return tp.unexpected(m);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue