地圖 (Jido) is a lightweight Unix TUI file explorer designed for speed and simplicity.

refactor: `List` drawing logic is now handled by the `Drawer{}`.

+64 -64
-54
src/directories.zig
··· 106 } 107 } 108 109 - pub fn writeChildEntries( 110 - self: *Self, 111 - window: vaxis.Window, 112 - style: vaxis.Style, 113 - ) !void { 114 - for (self.child_entries.all(), 0..) |item, i| { 115 - if (std.mem.startsWith(u8, item, ".") and config.show_hidden == false) { 116 - continue; 117 - } 118 - 119 - if (i > window.height) continue; 120 - 121 - const w = window.child(.{ .y_off = @intCast(i), .height = 1 }); 122 - w.fill(vaxis.Cell{ .style = style }); 123 - 124 - _ = w.print(&.{.{ .text = item, .style = style }}, .{}); 125 - } 126 - } 127 - 128 pub fn populateEntries(self: *Self, fuzzy_search: []const u8) !void { 129 var it = self.dir.iterate(); 130 while (try it.next()) |entry| { ··· 145 146 if (config.sort_dirs == true) { 147 std.mem.sort(std.fs.Dir.Entry, self.entries.all(), {}, sortEntry); 148 - } 149 - } 150 - 151 - pub fn writeEntries( 152 - self: *Self, 153 - window: vaxis.Window, 154 - selected_list_item_style: vaxis.Style, 155 - list_item_style: vaxis.Style, 156 - ) !void { 157 - const win_height = window.height; 158 - var offset: usize = 0; 159 - 160 - while (self.entries.all()[offset..].len > win_height and 161 - self.entries.selected >= offset + (win_height / 2)) 162 - { 163 - offset += 1; 164 - } 165 - 166 - for (self.entries.all()[offset..], 0..) |item, i| { 167 - const selected = self.entries.selected - offset; 168 - const is_selected = selected == i; 169 - 170 - if (i > window.height) continue; 171 - 172 - const w = window.child(.{ .y_off = @intCast(i), .height = 1 }); 173 - w.fill(vaxis.Cell{ 174 - .style = if (is_selected) selected_list_item_style else list_item_style, 175 - }); 176 - 177 - _ = w.print(&.{ 178 - .{ 179 - .text = item.name, 180 - .style = if (is_selected) selected_list_item_style else list_item_style, 181 - }, 182 - }, .{}); 183 } 184 } 185
··· 106 } 107 } 108 109 pub fn populateEntries(self: *Self, fuzzy_search: []const u8) !void { 110 var it = self.dir.iterate(); 111 while (try it.next()) |entry| { ··· 126 127 if (config.sort_dirs == true) { 128 std.mem.sort(std.fs.Dir.Entry, self.entries.all(), {}, sortEntry); 129 } 130 } 131
+64 -10
src/drawer.zig
··· 5 const config = &@import("./config.zig").config; 6 const vaxis = @import("vaxis"); 7 const Git = @import("./git.zig"); 8 const inputToSlice = @import("./event_handlers.zig").inputToSlice; 9 const zeit = @import("zeit"); 10 ··· 27 const win = app.vx.window(); 28 win.clear(); 29 30 const abs_file_path_bar = try self.drawAbsFilePath(app.alloc, &app.directories, win); 31 const file_info_bar = try self.drawFileInfo(app.alloc, &app.directories, win); 32 app.last_known_height = try drawDirList( 33 - &app.directories, 34 win, 35 abs_file_path_bar, 36 file_info_bar, 37 ); 38 39 - if (config.preview_file == true) { 40 const file_name_bar = try self.drawFileName(&app.directories, win); 41 try self.drawFilePreview(app, win, file_name_bar); 42 } 43 44 const input = inputToSlice(app); 45 try drawUserInput(app.state, &app.text_input, input, win); 46 try drawNotification(&app.notification, win); 47 } 48 ··· 110 .directory => { 111 app.directories.clearChildEntries(); 112 if (app.directories.populateChildEntries(entry.name)) { 113 - try app.directories.writeChildEntries(preview_win, config.styles.list_item); 114 } else |err| { 115 switch (err) { 116 error.AccessDenied => try app.notification.writeErr(.PermissionDenied), ··· 392 } 393 394 fn drawDirList( 395 - directories: *Directories, 396 win: vaxis.Window, 397 abs_file_path: vaxis.Window, 398 file_information: vaxis.Window, 399 ) !u16 { ··· 405 .width = if (config.preview_file) win.width / 2 else win.width, 406 .height = win.height - (abs_file_path.height + file_information.height + top_div + bottom_div), 407 }); 408 - try directories.writeEntries( 409 - current_dir_list_win, 410 - config.styles.selected_list_item, 411 - config.styles.list_item, 412 - ); 413 414 - return current_dir_list_win.height; 415 } 416 417 fn drawAbsFilePath(
··· 5 const config = &@import("./config.zig").config; 6 const vaxis = @import("vaxis"); 7 const Git = @import("./git.zig"); 8 + const List = @import("./list.zig").List; 9 const inputToSlice = @import("./event_handlers.zig").inputToSlice; 10 const zeit = @import("zeit"); 11 ··· 28 const win = app.vx.window(); 29 win.clear(); 30 31 + if (app.state == .help_menu) { 32 + win.hideCursor(); 33 + const offset: usize = app.help_menu.selected; 34 + for (app.help_menu.all()[offset..], 0..) |item, i| { 35 + if (i > win.height) continue; 36 + 37 + const w = win.child(.{ .y_off = @intCast(i), .height = 1 }); 38 + w.fill(vaxis.Cell{ 39 + .style = config.styles.list_item, 40 + }); 41 + 42 + _ = w.print(&.{.{ 43 + .text = item, 44 + .style = config.styles.list_item, 45 + }}, .{}); 46 + } 47 + 48 + return; 49 + } 50 + 51 const abs_file_path_bar = try self.drawAbsFilePath(app.alloc, &app.directories, win); 52 const file_info_bar = try self.drawFileInfo(app.alloc, &app.directories, win); 53 app.last_known_height = try drawDirList( 54 win, 55 + app.directories.entries, 56 abs_file_path_bar, 57 file_info_bar, 58 ); 59 60 + if (config.preview_file) { 61 const file_name_bar = try self.drawFileName(&app.directories, win); 62 try self.drawFilePreview(app, win, file_name_bar); 63 } 64 65 const input = inputToSlice(app); 66 try drawUserInput(app.state, &app.text_input, input, win); 67 + 68 + // Notification should be drawn last. 69 try drawNotification(&app.notification, win); 70 } 71 ··· 133 .directory => { 134 app.directories.clearChildEntries(); 135 if (app.directories.populateChildEntries(entry.name)) { 136 + for (app.directories.child_entries.all(), 0..) |item, i| { 137 + if (std.mem.startsWith(u8, item, ".") and config.show_hidden == false) { 138 + continue; 139 + } 140 + if (i > preview_win.height) continue; 141 + const w = preview_win.child(.{ .y_off = @intCast(i), .height = 1 }); 142 + w.fill(vaxis.Cell{ .style = config.styles.list_item }); 143 + _ = w.print(&.{.{ .text = item, .style = config.styles.list_item }}, .{}); 144 + } 145 } else |err| { 146 switch (err) { 147 error.AccessDenied => try app.notification.writeErr(.PermissionDenied), ··· 423 } 424 425 fn drawDirList( 426 win: vaxis.Window, 427 + list: List(std.fs.Dir.Entry), 428 abs_file_path: vaxis.Window, 429 file_information: vaxis.Window, 430 ) !u16 { ··· 436 .width = if (config.preview_file) win.width / 2 else win.width, 437 .height = win.height - (abs_file_path.height + file_information.height + top_div + bottom_div), 438 }); 439 + 440 + const win_height = current_dir_list_win.height; 441 + var offset: usize = 0; 442 + 443 + while (list.all()[offset..].len > win_height and 444 + list.selected >= offset + (win_height / 2)) 445 + { 446 + offset += 1; 447 + } 448 + 449 + for (list.all()[offset..], 0..) |item, i| { 450 + const selected = list.selected - offset; 451 + const is_selected = selected == i; 452 + 453 + if (i > win_height) continue; 454 + 455 + const w = current_dir_list_win.child(.{ .y_off = @intCast(i), .height = 1 }); 456 + w.fill(vaxis.Cell{ 457 + .style = if (is_selected) config.styles.selected_list_item else config.styles.list_item, 458 + }); 459 460 + _ = w.print(&.{ 461 + .{ 462 + .text = item.name, 463 + .style = if (is_selected) config.styles.selected_list_item else config.styles.list_item, 464 + }, 465 + }, .{}); 466 + } 467 + 468 + return win_height; 469 } 470 471 fn drawAbsFilePath(