feat: Add open changed files palette to commands
The palette shows modified and new files for the current project allowing to open one by one
This commit is contained in:
		
							parent
							
								
									81a6fef3b5
								
							
						
					
					
						commit
						b6010767f2
					
				
					 2 changed files with 471 additions and 0 deletions
				
			
		
							
								
								
									
										391
									
								
								src/tui/mode/overlay/open_changed.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								src/tui/mode/overlay/open_changed.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,391 @@ | |||
| const std = @import("std"); | ||||
| const tp = @import("thespian"); | ||||
| const log = @import("log"); | ||||
| const cbor = @import("cbor"); | ||||
| const file_type_config = @import("file_type_config"); | ||||
| 
 | ||||
| const Plane = @import("renderer").Plane; | ||||
| const input = @import("input"); | ||||
| const keybind = @import("keybind"); | ||||
| const project_manager = @import("project_manager"); | ||||
| const command = @import("command"); | ||||
| const EventHandler = @import("EventHandler"); | ||||
| const BufferManager = @import("Buffer").Manager; | ||||
| 
 | ||||
| const tui = @import("../../tui.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 ModalBackground = @import("../../ModalBackground.zig"); | ||||
| 
 | ||||
| const Self = @This(); | ||||
| const max_recent_files: usize = 25; | ||||
| const widget_type: Widget.Type = .palette; | ||||
| 
 | ||||
| allocator: std.mem.Allocator, | ||||
| f: usize = 0, | ||||
| modal: *ModalBackground.State(*Self), | ||||
| menu: *MenuType, | ||||
| inputbox: *InputBox.State(*Self), | ||||
| logger: log.Logger, | ||||
| query_pending: bool = false, | ||||
| need_reset: bool = false, | ||||
| need_select_first: bool = true, | ||||
| longest: usize, | ||||
| commands: Commands = undefined, | ||||
| buffer_manager: ?*BufferManager, | ||||
| 
 | ||||
| const inputbox_label = "Open changed files by name"; | ||||
| const MenuType = Menu.Options(*Self).MenuType; | ||||
| const ButtonType = MenuType.ButtonType; | ||||
| 
 | ||||
| pub fn create(allocator: std.mem.Allocator) !tui.Mode { | ||||
|     const mv = tui.mainview() orelse return error.NotFound; | ||||
|     const self = try allocator.create(Self); | ||||
|     errdefer allocator.destroy(self); | ||||
|     self.* = .{ | ||||
|         .allocator = allocator, | ||||
|         .modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ .ctx = self }), | ||||
|         .menu = try Menu.create(*Self, allocator, tui.plane(), .{ | ||||
|             .ctx = self, | ||||
|             .style = widget_type, | ||||
|             .on_render = on_render_menu, | ||||
|             .prepare_resize = prepare_resize_menu, | ||||
|         }), | ||||
|         .logger = log.logger(@typeName(Self)), | ||||
|         .inputbox = (try self.menu.add_header(try InputBox.create(*Self, self.allocator, self.menu.menu.parent, .{ | ||||
|             .ctx = self, | ||||
|             .label = inputbox_label, | ||||
|             .padding = 2, | ||||
|             .icon = "  ", | ||||
|         }))).dynamic_cast(InputBox.State(*Self)) orelse unreachable, | ||||
|         .buffer_manager = tui.get_buffer_manager(), | ||||
|         .longest = inputbox_label.len, | ||||
|     }; | ||||
|     try self.commands.init(self); | ||||
|     try tui.message_filters().add(MessageFilter.bind(self, receive_project_manager)); | ||||
|     self.query_pending = true; | ||||
|     try project_manager.request_new_or_modified_files(max_recent_files); | ||||
|     self.do_resize(); | ||||
|     try mv.floating_views.add(self.modal.widget()); | ||||
|     try mv.floating_views.add(self.menu.container_widget); | ||||
|     var mode = try keybind.mode("overlay/palette", allocator, .{ | ||||
|         .insert_command = "overlay_insert_bytes", | ||||
|     }); | ||||
|     mode.event_handler = EventHandler.to_owned(self); | ||||
|     mode.name = " open changed"; | ||||
|     return mode; | ||||
| } | ||||
| 
 | ||||
| pub fn deinit(self: *Self) void { | ||||
|     self.commands.deinit(); | ||||
|     tui.message_filters().remove_ptr(self); | ||||
|     if (tui.mainview()) |mv| { | ||||
|         mv.floating_views.remove(self.menu.container_widget); | ||||
|         mv.floating_views.remove(self.modal.widget()); | ||||
|     } | ||||
|     self.logger.deinit(); | ||||
|     self.allocator.destroy(self); | ||||
| } | ||||
| 
 | ||||
| inline fn menu_width(self: *Self) usize { | ||||
|     return @max(@min(self.longest + 3, max_menu_width()) + 5, inputbox_label.len + 2); | ||||
| } | ||||
| 
 | ||||
| inline fn menu_pos_x(self: *Self) usize { | ||||
|     const screen_width = tui.screen().w; | ||||
|     const width = self.menu_width(); | ||||
|     return if (screen_width <= width) 0 else (screen_width - width) / 2; | ||||
| } | ||||
| 
 | ||||
| inline fn max_menu_width() usize { | ||||
|     const width = tui.screen().w; | ||||
|     return @max(15, width - (width / 5)); | ||||
| } | ||||
| 
 | ||||
| fn on_render_menu(_: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { | ||||
|     return tui.render_file_vcs_item_cbor(&button.plane, button.opts.label, button.active, selected, button.hover, theme); | ||||
| } | ||||
| 
 | ||||
| fn prepare_resize_menu(self: *Self, _: *MenuType, _: Widget.Box) Widget.Box { | ||||
|     return self.prepare_resize(); | ||||
| } | ||||
| 
 | ||||
| fn prepare_resize(self: *Self) Widget.Box { | ||||
|     const w = self.menu_width(); | ||||
|     const x = self.menu_pos_x(); | ||||
|     const h = self.menu.menu.widgets.items.len; | ||||
|     return .{ .y = 0, .x = x, .w = w, .h = h }; | ||||
| } | ||||
| 
 | ||||
| fn do_resize(self: *Self) void { | ||||
|     self.menu.resize(self.prepare_resize()); | ||||
| } | ||||
| 
 | ||||
| fn menu_action_open_file(menu: **MenuType, button: *ButtonType, _: Widget.Pos) void { | ||||
|     var file_path: []const u8 = undefined; | ||||
|     var iter = button.opts.label; | ||||
|     if (!(cbor.matchString(&iter, &file_path) catch false)) return; | ||||
|     tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("navigate", e); | ||||
|     tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch |e| menu.*.opts.ctx.logger.err("navigate", e); | ||||
| } | ||||
| 
 | ||||
| fn add_item( | ||||
|     self: *Self, | ||||
|     file_name: []const u8, | ||||
|     file_icon: []const u8, | ||||
|     file_color: u24, | ||||
|     vcs_status: u8, | ||||
|     indicator: []const u8, | ||||
|     matches: ?[]const u8, | ||||
| ) !void { | ||||
|     var label: std.Io.Writer.Allocating = .init(self.allocator); | ||||
|     defer label.deinit(); | ||||
|     const writer = &label.writer; | ||||
|     try cbor.writeValue(writer, file_name); | ||||
|     try cbor.writeValue(writer, file_icon); | ||||
|     try cbor.writeValue(writer, file_color); | ||||
|     try cbor.writeValue(writer, indicator); | ||||
|     try cbor.writeValue(writer, vcs_status); | ||||
|     if (matches) |cb| _ = try writer.write(cb) else try cbor.writeValue(writer, &[_]usize{}); | ||||
|     try self.menu.add_item_with_handler(label.written(), menu_action_open_file); | ||||
| } | ||||
| 
 | ||||
| fn receive_project_manager(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { | ||||
|     if (cbor.match(m.buf, .{ "PRJ", tp.more }) catch false) { | ||||
|         try self.process_project_manager(m); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void { | ||||
|     var file_name: []const u8 = undefined; | ||||
|     var file_type: []const u8 = undefined; | ||||
|     var file_icon: []const u8 = undefined; | ||||
|     var file_color: u24 = undefined; | ||||
|     var vcs_status: u8 = undefined; | ||||
|     var matches: []const u8 = undefined; | ||||
|     var query: []const u8 = undefined; | ||||
|     if (try cbor.match(m.buf, .{ | ||||
|         "PRJ", | ||||
|         "new_or_modified_files", | ||||
|         tp.extract(&self.longest), | ||||
|         tp.extract(&file_name), | ||||
|         tp.extract(&file_type), | ||||
|         tp.extract(&file_icon), | ||||
|         tp.extract(&file_color), | ||||
|         tp.extract(&vcs_status), | ||||
|         tp.extract_cbor(&matches), | ||||
|     })) { | ||||
|         if (self.need_reset) self.reset_results(); | ||||
|         const indicator = if (self.buffer_manager) |bm| tui.get_file_state_indicator(bm, file_name) else ""; | ||||
|         try self.add_item(file_name, file_icon, file_color, vcs_status, indicator, matches); | ||||
|         self.do_resize(); | ||||
|         if (self.need_select_first) { | ||||
|             self.menu.select_down(); | ||||
|             self.need_select_first = false; | ||||
|         } | ||||
|         tui.need_render(); | ||||
|     } else if (try cbor.match(m.buf, .{ | ||||
|         "PRJ", | ||||
|         "new_or_modified_files", | ||||
|         tp.extract(&self.longest), | ||||
|         tp.extract(&file_name), | ||||
|         tp.extract(&file_type), | ||||
|         tp.extract(&file_icon), | ||||
|         tp.extract(&file_color), | ||||
|         tp.extract(&vcs_status), | ||||
|     })) { | ||||
|         if (self.need_reset) self.reset_results(); | ||||
|         const indicator = if (self.buffer_manager) |bm| tui.get_file_state_indicator(bm, file_name) else ""; | ||||
|         try self.add_item(file_name, file_icon, file_color, vcs_status, indicator, null); | ||||
|         self.do_resize(); | ||||
|         if (self.need_select_first) { | ||||
|             self.menu.select_down(); | ||||
|             self.need_select_first = false; | ||||
|         } | ||||
|         tui.need_render(); | ||||
|     } else if (try cbor.match(m.buf, .{ "PRJ", "new_or_modified_files_done", tp.extract(&self.longest), 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 if (try cbor.match(m.buf, .{ "PRJ", "open_done", tp.string, tp.extract(&self.longest), tp.any })) { | ||||
|         self.query_pending = false; | ||||
|         self.need_reset = true; | ||||
|         try self.start_query(); | ||||
|     } else { | ||||
|         self.logger.err("receive", tp.unexpected(m)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { | ||||
|     var text: []const u8 = undefined; | ||||
| 
 | ||||
|     if (try m.match(.{ "system_clipboard", tp.extract(&text) })) { | ||||
|         self.insert_bytes(text) catch |e| return tp.exit_error(e, @errorReturnTrace()); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 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) MessageFilter.Error!void { | ||||
|     if (self.query_pending) return; | ||||
|     self.query_pending = true; | ||||
|     try project_manager.query_new_or_modified_files(max_recent_files, self.inputbox.text.items); | ||||
| } | ||||
| 
 | ||||
| fn delete_word(self: *Self) !void { | ||||
|     if (std.mem.lastIndexOfAny(u8, self.inputbox.text.items, "/\\. -_")) |pos| { | ||||
|         self.inputbox.text.shrinkRetainingCapacity(pos); | ||||
|     } else { | ||||
|         self.inputbox.text.shrinkRetainingCapacity(0); | ||||
|     } | ||||
|     self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8); | ||||
|     return self.start_query(); | ||||
| } | ||||
| 
 | ||||
| fn delete_code_point(self: *Self) !void { | ||||
|     if (self.inputbox.text.items.len > 0) { | ||||
|         self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - tui.egc_last(self.inputbox.text.items).len); | ||||
|         self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8); | ||||
|     } | ||||
|     return self.start_query(); | ||||
| } | ||||
| 
 | ||||
| fn insert_code_point(self: *Self, c: u32) !void { | ||||
|     var buf: [6]u8 = undefined; | ||||
|     const bytes = try input.ucs32_to_utf8(&[_]u32{c}, &buf); | ||||
|     try self.inputbox.text.appendSlice(self.allocator, buf[0..bytes]); | ||||
|     self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8); | ||||
|     return self.start_query(); | ||||
| } | ||||
| 
 | ||||
| fn insert_bytes(self: *Self, bytes: []const u8) !void { | ||||
|     try self.inputbox.text.appendSlice(self.allocator, bytes); | ||||
|     self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8); | ||||
|     return self.start_query(); | ||||
| } | ||||
| 
 | ||||
| fn cmd(_: *Self, name_: []const u8, ctx: command.Context) tp.result { | ||||
|     try command.executeName(name_, ctx); | ||||
| } | ||||
| 
 | ||||
| fn msg(_: *Self, text: []const u8) tp.result { | ||||
|     return tp.self_pid().send(.{ "log", "home", text }); | ||||
| } | ||||
| 
 | ||||
| fn cmd_async(_: *Self, name_: []const u8) tp.result { | ||||
|     return tp.self_pid().send(.{ "cmd", name_ }); | ||||
| } | ||||
| 
 | ||||
| const Commands = command.Collection(cmds); | ||||
| const cmds = struct { | ||||
|     pub const Target = Self; | ||||
|     const Ctx = command.Context; | ||||
|     const Meta = command.Metadata; | ||||
|     const Result = command.Result; | ||||
| 
 | ||||
|     pub fn palette_menu_down(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_down(); | ||||
|     } | ||||
|     pub const palette_menu_down_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_up(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_up(); | ||||
|     } | ||||
|     pub const palette_menu_up_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_last(); | ||||
|     } | ||||
|     pub const palette_menu_pagedown_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_pageup(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_first(); | ||||
|     } | ||||
|     pub const palette_menu_pageup_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_bottom(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_last(); | ||||
|     } | ||||
|     pub const palette_menu_bottom_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_top(self: *Self, _: Ctx) Result { | ||||
|         self.menu.select_first(); | ||||
|     } | ||||
|     pub const palette_menu_top_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_activate(self: *Self, _: Ctx) Result { | ||||
|         self.menu.activate_selected(); | ||||
|     } | ||||
|     pub const palette_menu_activate_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result { | ||||
|         if (self.menu.selected orelse 0 > 0) self.menu.activate_selected(); | ||||
|     } | ||||
|     pub const palette_menu_activate_quick_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn palette_menu_cancel(self: *Self, _: Ctx) Result { | ||||
|         try self.cmd("exit_overlay_mode", .{}); | ||||
|     } | ||||
|     pub const palette_menu_cancel_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result { | ||||
|         self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace()); | ||||
|     } | ||||
|     pub const overlay_delete_word_left_meta: Meta = .{ .description = "Delete word to the left" }; | ||||
| 
 | ||||
|     pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result { | ||||
|         self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace()); | ||||
|     } | ||||
|     pub const overlay_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; | ||||
| 
 | ||||
|     pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result { | ||||
|         var egc: u32 = 0; | ||||
|         if (!try ctx.args.match(.{tp.extract(&egc)})) | ||||
|             return error.InvalidOpenRecentInsertCodePointArgument; | ||||
|         self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace()); | ||||
|     } | ||||
|     pub const overlay_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; | ||||
| 
 | ||||
|     pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result { | ||||
|         var bytes: []const u8 = undefined; | ||||
|         if (!try ctx.args.match(.{tp.extract(&bytes)})) | ||||
|             return error.InvalidOpenRecentInsertBytesArgument; | ||||
|         self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace()); | ||||
|     } | ||||
|     pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; | ||||
| 
 | ||||
|     pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { | ||||
|         return self.cmd_async("toggle_panel"); | ||||
|     } | ||||
|     pub const overlay_toggle_panel_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { | ||||
|         return self.cmd_async("toggle_inputview"); | ||||
|     } | ||||
|     pub const overlay_toggle_inputview_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { | ||||
|         tui.set_next_style(widget_type); | ||||
|         self.do_resize(); | ||||
|         tui.need_render(); | ||||
|         try tui.save_config(); | ||||
|     } | ||||
|     pub const overlay_next_widget_style_meta: Meta = .{}; | ||||
| 
 | ||||
|     pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { | ||||
|         return overlay_insert_bytes(self, ctx); | ||||
|     } | ||||
|     pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; | ||||
| }; | ||||
|  | @ -1032,6 +1032,11 @@ const cmds = struct { | |||
|     } | ||||
|     pub const open_recent_meta: Meta = .{ .description = "Open recent" }; | ||||
| 
 | ||||
|     pub fn open_changed_files(self: *Self, _: Ctx) Result { | ||||
|         return self.enter_overlay_mode(@import("mode/overlay/open_changed.zig")); | ||||
|     } | ||||
|     pub const open_changed_files_meta: Meta = .{ .description = "Open changed files (vcs)" }; | ||||
| 
 | ||||
|     pub fn open_recent_project(_: *Self, _: Ctx) Result { | ||||
|         try project_manager.request_recent_projects(); | ||||
|     } | ||||
|  | @ -1679,6 +1684,15 @@ pub fn render_pointer(self: *renderer.Plane, selected: bool) void { | |||
|     _ = self.print("{s}", .{pointer}) catch {}; | ||||
| } | ||||
| 
 | ||||
| pub fn render_pointer_vcs(self: *renderer.Plane, vcs_status: u8, selected: bool) void { | ||||
|     const pointer = "⏵"; | ||||
|     if (selected) { | ||||
|         _ = self.print("{s}{c}", .{ pointer, vcs_status }) catch {}; | ||||
|     } else { | ||||
|         _ = self.print("{c} ", .{vcs_status}) catch {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn render_file_item( | ||||
|     self: *renderer.Plane, | ||||
|     file_path_: []const u8, | ||||
|  | @ -1742,6 +1756,72 @@ pub fn render_file_item_cbor(self: *renderer.Plane, file_item_cbor: []const u8, | |||
|     return render_file_item(self, file_path_, icon, color, indicator, matches_cbor, active, selected, hover, theme_); | ||||
| } | ||||
| 
 | ||||
| pub fn render_file_vcs_item( | ||||
|     self: *renderer.Plane, | ||||
|     file_path_: []const u8, | ||||
|     icon: []const u8, | ||||
|     color: u24, | ||||
|     indicator: []const u8, | ||||
|     vcs_status: u8, | ||||
|     matches_cbor: []const u8, | ||||
|     active: bool, | ||||
|     selected: bool, | ||||
|     hover: bool, | ||||
|     theme_: *const Widget.Theme, | ||||
| ) bool { | ||||
|     const style_base = theme_.editor_widget; | ||||
|     const style_label = if (active) theme_.editor_cursor else if (hover or selected) theme_.editor_selection else theme_.editor_widget; | ||||
|     const style_hint = if (find_scope_style(theme_, "entity.name")) |sty| sty.style else style_label; | ||||
|     self.set_base_style(style_base); | ||||
|     self.erase(); | ||||
|     self.home(); | ||||
|     self.set_style(style_label); | ||||
|     if (active or hover or selected) { | ||||
|         self.fill(" "); | ||||
|         self.home(); | ||||
|     } | ||||
| 
 | ||||
|     self.set_style(style_hint); | ||||
|     render_pointer_vcs(self, vcs_status, selected); | ||||
| 
 | ||||
|     const icon_width = render_file_icon(self, icon, color); | ||||
| 
 | ||||
|     self.set_style(style_label); | ||||
|     _ = self.print("{s} ", .{file_path_}) catch {}; | ||||
| 
 | ||||
|     self.set_style(style_hint); | ||||
|     _ = self.print_aligned_right(0, "{s} ", .{indicator}) catch {}; | ||||
| 
 | ||||
|     var iter = matches_cbor; | ||||
|     var index: usize = 0; | ||||
|     var len = cbor.decodeArrayHeader(&iter) catch return false; | ||||
|     while (len > 0) : (len -= 1) { | ||||
|         if (cbor.matchValue(&iter, cbor.extract(&index)) catch break) { | ||||
|             render_match_cell(self, 0, index + 2 + icon_width, theme_) catch break; | ||||
|         } else break; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| pub fn render_file_vcs_item_cbor(self: *renderer.Plane, file_item_cbor: []const u8, active: bool, selected: bool, hover: bool, theme_: *const Widget.Theme) bool { | ||||
|     var iter = file_item_cbor; | ||||
|     var file_path_: []const u8 = undefined; | ||||
|     var icon: []const u8 = undefined; | ||||
|     var color: u24 = undefined; | ||||
|     var indicator: []const u8 = undefined; | ||||
|     var vcs_status: u8 = undefined; | ||||
|     var matches_cbor: []const u8 = undefined; | ||||
| 
 | ||||
|     if (!(cbor.matchString(&iter, &file_path_) catch false)) @panic("invalid buffer file path"); | ||||
|     if (!(cbor.matchString(&iter, &icon) catch false)) @panic("invalid buffer file type icon"); | ||||
|     if (!(cbor.matchInt(u24, &iter, &color) catch false)) @panic("invalid buffer file type color"); | ||||
|     if (!(cbor.matchString(&iter, &indicator) catch false)) indicator = ""; | ||||
|     if (!(cbor.matchInt(u8, &iter, &vcs_status) catch false)) indicator = " "; | ||||
| 
 | ||||
|     if (!(cbor.matchValue(&iter, cbor.extract_cbor(&matches_cbor)) catch false)) @panic("invalid matches cbor"); | ||||
|     return render_file_vcs_item(self, file_path_, icon, color, indicator, vcs_status, matches_cbor, active, selected, hover, theme_); | ||||
| } | ||||
| 
 | ||||
| fn get_or_create_theme_file(self: *Self, allocator: std.mem.Allocator) ![]const u8 { | ||||
|     const theme_name = self.current_theme().name; | ||||
|     if (root.read_theme(allocator, theme_name)) |content| { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Igor Támara
						Igor Támara