Merge branch 'tabs'
This commit is contained in:
		
						commit
						d6f785a564
					
				
					 12 changed files with 340 additions and 37 deletions
				
			
		| 
						 | 
					@ -18,8 +18,8 @@
 | 
				
			||||||
            .hash = "1220220dbc7fe91c1c54438193ca765cebbcb7d58f35cdcaee404a9d2245a42a4362",
 | 
					            .hash = "1220220dbc7fe91c1c54438193ca765cebbcb7d58f35cdcaee404a9d2245a42a4362",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .thespian = .{
 | 
					        .thespian = .{
 | 
				
			||||||
            .url = "https://github.com/neurocyte/thespian/archive/d2b65df7c59b58f5e86b1ce3403cd3589d06a367.tar.gz",
 | 
					            .url = "https://github.com/neurocyte/thespian/archive/fdf7a0c3bb9738b68895cbde63dbae00b74f5a73.tar.gz",
 | 
				
			||||||
            .hash = "122040a2dcb9569ee26ae9c205f944db4ee9e3eb7c9d5ec610c7d84cf82ab6909fa9",
 | 
					            .hash = "1220042d96513aec54ec3c78dcb28e2529d5f45d0c37bf676860f77f26dce590f9f8",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .themes = .{
 | 
					        .themes = .{
 | 
				
			||||||
            .url = "https://github.com/neurocyte/flow-themes/releases/download/master-770924d1aba9cc33440b66e865bd4f7dca871074/flow-themes.tar.gz",
 | 
					            .url = "https://github.com/neurocyte/flow-themes/releases/download/master-770924d1aba9cc33440b66e865bd4f7dca871074/flow-themes.tar.gz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ file_exists: bool = true,
 | 
				
			||||||
file_eol_mode: EolMode = .lf,
 | 
					file_eol_mode: EolMode = .lf,
 | 
				
			||||||
last_save_eol_mode: EolMode = .lf,
 | 
					last_save_eol_mode: EolMode = .lf,
 | 
				
			||||||
file_utf8_sanitized: bool = false,
 | 
					file_utf8_sanitized: bool = false,
 | 
				
			||||||
 | 
					hidden: bool = false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
undo_history: ?*UndoNode = null,
 | 
					undo_history: ?*UndoNode = null,
 | 
				
			||||||
redo_history: ?*UndoNode = null,
 | 
					redo_history: ?*UndoNode = null,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ pub fn open_file(self: *Self, file_path: []const u8) Buffer.LoadFromFileError!*B
 | 
				
			||||||
        break :blk buffer;
 | 
					        break :blk buffer;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    buffer.update_last_used_time();
 | 
					    buffer.update_last_used_time();
 | 
				
			||||||
 | 
					    buffer.hidden = false;
 | 
				
			||||||
    return buffer;
 | 
					    return buffer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +45,7 @@ pub fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) Buf
 | 
				
			||||||
        break :blk buffer;
 | 
					        break :blk buffer;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    buffer.update_last_used_time();
 | 
					    buffer.update_last_used_time();
 | 
				
			||||||
 | 
					    buffer.hidden = false;
 | 
				
			||||||
    return buffer;
 | 
					    return buffer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,17 +62,22 @@ pub fn delete_buffer(self: *Self, file_path: []const u8) bool {
 | 
				
			||||||
pub fn retire(_: *Self, _: *Buffer) void {}
 | 
					pub fn retire(_: *Self, _: *Buffer) void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn list_most_recently_used(self: *Self, allocator: std.mem.Allocator) error{OutOfMemory}![]*Buffer {
 | 
					pub fn list_most_recently_used(self: *Self, allocator: std.mem.Allocator) error{OutOfMemory}![]*Buffer {
 | 
				
			||||||
    var buffers: std.ArrayListUnmanaged(*Buffer) = .{};
 | 
					    const result = try self.list_unordered(allocator);
 | 
				
			||||||
    var i = self.buffers.iterator();
 | 
					 | 
				
			||||||
    while (i.next()) |kv|
 | 
					 | 
				
			||||||
        (try buffers.addOne(allocator)).* = kv.value_ptr.*;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std.mem.sort(*Buffer, buffers.items, {}, struct {
 | 
					    std.mem.sort(*Buffer, result, {}, struct {
 | 
				
			||||||
        fn less_fn(_: void, lhs: *Buffer, rhs: *Buffer) bool {
 | 
					        fn less_fn(_: void, lhs: *Buffer, rhs: *Buffer) bool {
 | 
				
			||||||
            return lhs.utime > rhs.utime;
 | 
					            return lhs.utime > rhs.utime;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }.less_fn);
 | 
					    }.less_fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn list_unordered(self: *Self, allocator: std.mem.Allocator) error{OutOfMemory}![]*Buffer {
 | 
				
			||||||
 | 
					    var buffers = try std.ArrayListUnmanaged(*Buffer).initCapacity(allocator, self.buffers.size);
 | 
				
			||||||
 | 
					    var i = self.buffers.iterator();
 | 
				
			||||||
 | 
					    while (i.next()) |kv|
 | 
				
			||||||
 | 
					        (try buffers.addOne(allocator)).* = kv.value_ptr.*;
 | 
				
			||||||
    return buffers.toOwnedSlice(allocator);
 | 
					    return buffers.toOwnedSlice(allocator);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,3 +100,12 @@ pub fn save_all(self: *const Self) Buffer.StoreToFileError!void {
 | 
				
			||||||
        try buffer.store_to_file_and_clean(buffer.file_path);
 | 
					        try buffer.store_to_file_and_clean(buffer.file_path);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn delete_all(self: *Self) void {
 | 
				
			||||||
 | 
					    var i = self.buffers.iterator();
 | 
				
			||||||
 | 
					    while (i.next()) |p| {
 | 
				
			||||||
 | 
					        self.allocator.free(p.key_ptr.*);
 | 
				
			||||||
 | 
					        p.value_ptr.*.deinit();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self.buffers.clearRetainingCapacity();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "project": {
 | 
					    "project": {
 | 
				
			||||||
        "press": [
 | 
					        "press": [
 | 
				
			||||||
 | 
					            ["ctrl+tab", "next_tab"],
 | 
				
			||||||
 | 
					            ["ctrl+shift+tab", "previous_tab"],
 | 
				
			||||||
            ["ctrl+shift+e", "switch_buffers"],
 | 
					            ["ctrl+shift+e", "switch_buffers"],
 | 
				
			||||||
            ["ctrl+0", "reset_fontsize"],
 | 
					            ["ctrl+0", "reset_fontsize"],
 | 
				
			||||||
            ["ctrl+plus", "adjust_fontsize", 1.0],
 | 
					            ["ctrl+plus", "adjust_fontsize", 1.0],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,6 +104,10 @@ pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
 | 
				
			||||||
    return .{ y - self.abs_y(), x - self.abs_x() };
 | 
					    return .{ y - self.abs_y(), x - self.abs_x() };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn abs_y_to_rel(self: Plane, y: c_int) c_int {
 | 
				
			||||||
 | 
					    return y - self.abs_y();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
 | 
					pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } {
 | 
				
			||||||
    return .{ self.abs_y() + y, self.abs_x() + x };
 | 
					    return .{ self.abs_y() + y, self.abs_x() + x };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,16 @@ pub fn remove(self: *Self, w: Widget) void {
 | 
				
			||||||
        self.widgets.orderedRemove(i).widget.deinit(self.allocator);
 | 
					        self.widgets.orderedRemove(i).widget.deinit(self.allocator);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn remove_all(self: *Self) void {
 | 
				
			||||||
 | 
					    for (self.widgets.items) |*w|
 | 
				
			||||||
 | 
					        w.widget.deinit(self.allocator);
 | 
				
			||||||
 | 
					    self.widgets.clearRetainingCapacity();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn pop(self: *Self) ?Widget {
 | 
				
			||||||
 | 
					    return if (self.widgets.popOrNull()) |ws| ws.widget else null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn empty(self: *const Self) bool {
 | 
					pub fn empty(self: *const Self) bool {
 | 
				
			||||||
    return self.widgets.items.len == 0;
 | 
					    return self.widgets.items.len == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -150,6 +160,9 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
 | 
				
			||||||
fn on_render_default(_: ?*anyopaque, _: *const Widget.Theme) void {}
 | 
					fn on_render_default(_: ?*anyopaque, _: *const Widget.Theme) void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
 | 
					pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
 | 
				
			||||||
 | 
					    if (try m.match(.{ "H", tp.more }))
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (self.widgets.items) |*w|
 | 
					    for (self.widgets.items) |*w|
 | 
				
			||||||
        if (try w.widget.send(from_, m))
 | 
					        if (try w.widget.send(from_, m))
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3786,13 +3786,21 @@ pub const Editor = struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn close_file(self: *Self, _: Context) Result {
 | 
					    pub fn close_file(self: *Self, _: Context) Result {
 | 
				
			||||||
        self.cancel_all_selections();
 | 
					        self.cancel_all_selections();
 | 
				
			||||||
 | 
					        if (self.buffer) |buffer| {
 | 
				
			||||||
 | 
					            if (buffer.is_dirty())
 | 
				
			||||||
 | 
					                return tp.exit("unsaved changes");
 | 
				
			||||||
 | 
					            buffer.hidden = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        try self.close();
 | 
					        try self.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub const close_file_meta = .{ .description = "Close file" };
 | 
					    pub const close_file_meta = .{ .description = "Close file" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn close_file_without_saving(self: *Self, _: Context) Result {
 | 
					    pub fn close_file_without_saving(self: *Self, _: Context) Result {
 | 
				
			||||||
        self.cancel_all_selections();
 | 
					        self.cancel_all_selections();
 | 
				
			||||||
        if (self.buffer) |buffer| buffer.reset_to_last_saved();
 | 
					        if (self.buffer) |buffer| {
 | 
				
			||||||
 | 
					            buffer.reset_to_last_saved();
 | 
				
			||||||
 | 
					            buffer.hidden = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        try self.close();
 | 
					        try self.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub const close_file_without_saving_meta = .{ .description = "Close file without saving" };
 | 
					    pub const close_file_without_saving_meta = .{ .description = "Close file without saving" };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -273,7 +273,8 @@ fn render_diagnostic(self: *Self, diag: *const ed.Diagnostic, theme: *const Widg
 | 
				
			||||||
    _ = self.plane.putc(&cell) catch {};
 | 
					    _ = self.plane.putc(&cell) catch {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn primary_click(self: *const Self, y: i32) error{Exit}!bool {
 | 
					fn primary_click(self: *const Self, y_: i32) error{Exit}!bool {
 | 
				
			||||||
 | 
					    const y = self.editor.plane.abs_y_to_rel(y_);
 | 
				
			||||||
    var line = self.row + 1;
 | 
					    var line = self.row + 1;
 | 
				
			||||||
    line += @intCast(y);
 | 
					    line += @intCast(y);
 | 
				
			||||||
    if (line > self.lines) line = self.lines;
 | 
					    if (line > self.lines) line = self.lines;
 | 
				
			||||||
| 
						 | 
					@ -284,7 +285,8 @@ fn primary_click(self: *const Self, y: i32) error{Exit}!bool {
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn primary_drag(_: *const Self, y: i32) error{Exit}!bool {
 | 
					fn primary_drag(self: *const Self, y_: i32) error{Exit}!bool {
 | 
				
			||||||
 | 
					    const y = self.editor.plane.abs_y_to_rel(y_);
 | 
				
			||||||
    try command.executeName("drag_to", command.fmt(.{ y + 1, 0 }));
 | 
					    try command.executeName("drag_to", command.fmt(.{ y + 1, 0 }));
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,6 @@ panels: ?*WidgetList = null,
 | 
				
			||||||
last_match_text: ?[]const u8 = null,
 | 
					last_match_text: ?[]const u8 = null,
 | 
				
			||||||
location_history: location_history,
 | 
					location_history: location_history,
 | 
				
			||||||
buffer_manager: BufferManager,
 | 
					buffer_manager: BufferManager,
 | 
				
			||||||
file_stack: std.ArrayList([]const u8),
 | 
					 | 
				
			||||||
find_in_files_state: enum { init, adding, done } = .done,
 | 
					find_in_files_state: enum { init, adding, done } = .done,
 | 
				
			||||||
file_list_type: FileListType = .find_in_files,
 | 
					file_list_type: FileListType = .find_in_files,
 | 
				
			||||||
panel_height: ?usize = null,
 | 
					panel_height: ?usize = null,
 | 
				
			||||||
| 
						 | 
					@ -69,7 +68,6 @@ pub fn create(allocator: std.mem.Allocator) !Widget {
 | 
				
			||||||
        .widgets_widget = undefined,
 | 
					        .widgets_widget = undefined,
 | 
				
			||||||
        .floating_views = WidgetStack.init(allocator),
 | 
					        .floating_views = WidgetStack.init(allocator),
 | 
				
			||||||
        .location_history = try location_history.create(),
 | 
					        .location_history = try location_history.create(),
 | 
				
			||||||
        .file_stack = std.ArrayList([]const u8).init(allocator),
 | 
					 | 
				
			||||||
        .views = undefined,
 | 
					        .views = undefined,
 | 
				
			||||||
        .views_widget = undefined,
 | 
					        .views_widget = undefined,
 | 
				
			||||||
        .buffer_manager = BufferManager.init(allocator),
 | 
					        .buffer_manager = BufferManager.init(allocator),
 | 
				
			||||||
| 
						 | 
					@ -102,8 +100,6 @@ pub fn create(allocator: std.mem.Allocator) !Widget {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
 | 
					pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
 | 
				
			||||||
    self.close_all_panel_views();
 | 
					    self.close_all_panel_views();
 | 
				
			||||||
    self.clear_file_stack();
 | 
					 | 
				
			||||||
    self.file_stack.deinit();
 | 
					 | 
				
			||||||
    self.commands.deinit();
 | 
					    self.commands.deinit();
 | 
				
			||||||
    self.widgets.deinit(allocator);
 | 
					    self.widgets.deinit(allocator);
 | 
				
			||||||
    self.floating_views.deinit();
 | 
					    self.floating_views.deinit();
 | 
				
			||||||
| 
						 | 
					@ -286,7 +282,7 @@ const cmds = struct {
 | 
				
			||||||
            editor.clear_diagnostics();
 | 
					            editor.clear_diagnostics();
 | 
				
			||||||
            try editor.close_file(.{});
 | 
					            try editor.close_file(.{});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.clear_file_stack();
 | 
					        self.delete_all_buffers();
 | 
				
			||||||
        self.clear_find_in_files_results(.diagnostics);
 | 
					        self.clear_find_in_files_results(.diagnostics);
 | 
				
			||||||
        if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view))
 | 
					        if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view))
 | 
				
			||||||
            try self.toggle_panel_view(filelist_view, false);
 | 
					            try self.toggle_panel_view(filelist_view, false);
 | 
				
			||||||
| 
						 | 
					@ -644,8 +640,7 @@ const cmds = struct {
 | 
				
			||||||
    pub const show_diagnostics_meta = .{ .description = "Show diagnostics panel" };
 | 
					    pub const show_diagnostics_meta = .{ .description = "Show diagnostics panel" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn open_previous_file(self: *Self, _: Ctx) Result {
 | 
					    pub fn open_previous_file(self: *Self, _: Ctx) Result {
 | 
				
			||||||
        const file_path = try project_manager.request_n_most_recent_file(self.allocator, 1);
 | 
					        self.show_file_async(self.get_next_mru_buffer() orelse return error.Stop);
 | 
				
			||||||
        self.show_file_async_and_free(file_path orelse return error.Stop);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub const open_previous_file_meta = .{ .description = "Open the previous file" };
 | 
					    pub const open_previous_file_meta = .{ .description = "Open the previous file" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -749,6 +744,16 @@ const cmds = struct {
 | 
				
			||||||
            tui.rdr().reset_fontface();
 | 
					            tui.rdr().reset_fontface();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub const reset_fontface_meta = .{ .description = "Reset font to configured face" };
 | 
					    pub const reset_fontface_meta = .{ .description = "Reset font to configured face" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn next_tab(self: *Self, _: Ctx) Result {
 | 
				
			||||||
 | 
					        _ = try self.widgets_widget.msg(.{"next_tab"});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const next_tab_meta = .{ .description = "Switch to next tab" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn previous_tab(self: *Self, _: Ctx) Result {
 | 
				
			||||||
 | 
					        _ = try self.widgets_widget.msg(.{"previous_tab"});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const previous_tab_meta = .{ .description = "Switch to previous tab" };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {
 | 
					pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {
 | 
				
			||||||
| 
						 | 
					@ -759,8 +764,8 @@ pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result
 | 
				
			||||||
        return self.location_update(m);
 | 
					        return self.location_update(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (try m.match(.{ "E", "close" })) {
 | 
					    if (try m.match(.{ "E", "close" })) {
 | 
				
			||||||
        if (self.pop_file_stack(editor.file_path)) |file_path|
 | 
					        if (self.get_next_mru_buffer()) |file_path|
 | 
				
			||||||
            self.show_file_async_and_free(file_path)
 | 
					            self.show_file_async(file_path)
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            self.show_home_async();
 | 
					            self.show_home_async();
 | 
				
			||||||
        self.active_editor = null;
 | 
					        self.active_editor = null;
 | 
				
			||||||
| 
						 | 
					@ -873,7 +878,6 @@ fn replace_active_view(self: *Self, widget: Widget) !void {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn create_editor(self: *Self) !void {
 | 
					fn create_editor(self: *Self) !void {
 | 
				
			||||||
    if (self.get_active_file_path()) |file_path| self.push_file_stack(file_path) catch {};
 | 
					 | 
				
			||||||
    try self.delete_active_view();
 | 
					    try self.delete_active_view();
 | 
				
			||||||
    command.executeName("enter_mode_default", .{}) catch {};
 | 
					    command.executeName("enter_mode_default", .{}) catch {};
 | 
				
			||||||
    var editor_widget = try ed.create(self.allocator, self.plane, &self.buffer_manager);
 | 
					    var editor_widget = try ed.create(self.allocator, self.plane, &self.buffer_manager);
 | 
				
			||||||
| 
						 | 
					@ -898,6 +902,10 @@ fn toggle_inputview_async(_: *Self) void {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn show_file_async_and_free(self: *Self, file_path: []const u8) void {
 | 
					fn show_file_async_and_free(self: *Self, file_path: []const u8) void {
 | 
				
			||||||
    defer self.allocator.free(file_path);
 | 
					    defer self.allocator.free(file_path);
 | 
				
			||||||
 | 
					    self.show_file_async(file_path);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn show_file_async(_: *Self, file_path: []const u8) void {
 | 
				
			||||||
    tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch return;
 | 
					    tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -943,24 +951,22 @@ fn read_restore_info(self: *Self) !void {
 | 
				
			||||||
    try editor.extract_state(buf[0..size]);
 | 
					    try editor.extract_state(buf[0..size]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn push_file_stack(self: *Self, file_path: []const u8) !void {
 | 
					fn get_next_mru_buffer(self: *Self) ?[]const u8 {
 | 
				
			||||||
    for (self.file_stack.items, 0..) |file_path_, i|
 | 
					    const buffers = self.buffer_manager.list_most_recently_used(self.allocator) catch return null;
 | 
				
			||||||
        if (std.mem.eql(u8, file_path, file_path_))
 | 
					    defer self.allocator.free(buffers);
 | 
				
			||||||
            self.allocator.free(self.file_stack.orderedRemove(i));
 | 
					    const active_file_path = self.get_active_file_path();
 | 
				
			||||||
    (try self.file_stack.addOne()).* = try self.allocator.dupe(u8, file_path);
 | 
					    for (buffers) |buffer| {
 | 
				
			||||||
 | 
					        if (active_file_path) |fp| if (std.mem.eql(u8, fp, buffer.file_path))
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        if (buffer.hidden)
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        return buffer.file_path;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn pop_file_stack(self: *Self, closed: ?[]const u8) ?[]const u8 {
 | 
					fn delete_all_buffers(self: *Self) void {
 | 
				
			||||||
    if (closed) |file_path|
 | 
					    self.buffer_manager.delete_all();
 | 
				
			||||||
        for (self.file_stack.items, 0..) |file_path_, i|
 | 
					 | 
				
			||||||
            if (std.mem.eql(u8, file_path, file_path_))
 | 
					 | 
				
			||||||
                self.allocator.free(self.file_stack.orderedRemove(i));
 | 
					 | 
				
			||||||
    return self.file_stack.popOrNull();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn clear_file_stack(self: *Self) void {
 | 
					 | 
				
			||||||
    for (self.file_stack.items) |file_path| self.allocator.free(file_path);
 | 
					 | 
				
			||||||
    self.file_stack.clearRetainingCapacity();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn add_find_in_files_result(
 | 
					fn add_find_in_files_result(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,6 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void {
 | 
				
			||||||
    var iter = button.opts.label;
 | 
					    var iter = button.opts.label;
 | 
				
			||||||
    if (!(cbor.matchString(&iter, &file_path) catch false)) return;
 | 
					    if (!(cbor.matchString(&iter, &file_path) catch false)) return;
 | 
				
			||||||
    tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
 | 
					    tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
 | 
				
			||||||
    tp.self_pid().send(.{ "cmd", "navigate", .{} }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
 | 
					 | 
				
			||||||
    tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
 | 
					    tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										251
									
								
								src/tui/status/tabs.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								src/tui/status/tabs.zig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,251 @@
 | 
				
			||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					const tp = @import("thespian");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const EventHandler = @import("EventHandler");
 | 
				
			||||||
 | 
					const Plane = @import("renderer").Plane;
 | 
				
			||||||
 | 
					const Buffer = @import("Buffer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tui = @import("../tui.zig");
 | 
				
			||||||
 | 
					const Widget = @import("../Widget.zig");
 | 
				
			||||||
 | 
					const WidgetList = @import("../WidgetList.zig");
 | 
				
			||||||
 | 
					const Button = @import("../Button.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dirty_indicator = " ";
 | 
				
			||||||
 | 
					const padding = " ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
 | 
				
			||||||
 | 
					    const self = try allocator.create(TabBar);
 | 
				
			||||||
 | 
					    self.* = try TabBar.init(allocator, parent, event_handler);
 | 
				
			||||||
 | 
					    return Widget.to(self);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TabBar = struct {
 | 
				
			||||||
 | 
					    allocator: std.mem.Allocator,
 | 
				
			||||||
 | 
					    plane: Plane,
 | 
				
			||||||
 | 
					    widget_list: *WidgetList,
 | 
				
			||||||
 | 
					    widget_list_widget: Widget,
 | 
				
			||||||
 | 
					    event_handler: ?EventHandler,
 | 
				
			||||||
 | 
					    tabs: []TabBarTab = &[_]TabBarTab{},
 | 
				
			||||||
 | 
					    active_buffer: ?*Buffer = null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Self = @This();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const TabBarTab = struct {
 | 
				
			||||||
 | 
					        buffer: *Buffer,
 | 
				
			||||||
 | 
					        widget: Widget,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn init(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) !Self {
 | 
				
			||||||
 | 
					        var w = try WidgetList.createH(allocator, parent, "tabs", .dynamic);
 | 
				
			||||||
 | 
					        w.ctx = w;
 | 
				
			||||||
 | 
					        return .{
 | 
				
			||||||
 | 
					            .allocator = allocator,
 | 
				
			||||||
 | 
					            .plane = w.plane,
 | 
				
			||||||
 | 
					            .widget_list = w,
 | 
				
			||||||
 | 
					            .widget_list_widget = w.widget(),
 | 
				
			||||||
 | 
					            .event_handler = event_handler,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
 | 
				
			||||||
 | 
					        self.allocator.free(self.tabs);
 | 
				
			||||||
 | 
					        self.widget_list_widget.deinit(allocator);
 | 
				
			||||||
 | 
					        allocator.destroy(self);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn layout(self: *Self) Widget.Layout {
 | 
				
			||||||
 | 
					        return self.widget_list_widget.layout();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update(self: *Self) void {
 | 
				
			||||||
 | 
					        self.update_tabs() catch {};
 | 
				
			||||||
 | 
					        self.widget_list_widget.resize(Widget.Box.from(self.plane));
 | 
				
			||||||
 | 
					        self.widget_list_widget.update();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn render(self: *Self, theme: *const Widget.Theme) bool {
 | 
				
			||||||
 | 
					        self.plane.set_base_style(theme.statusbar);
 | 
				
			||||||
 | 
					        self.plane.erase();
 | 
				
			||||||
 | 
					        self.plane.home();
 | 
				
			||||||
 | 
					        return self.widget_list_widget.render(theme);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
 | 
				
			||||||
 | 
					        tp.trace(tp.channel.widget, .{"receive"});
 | 
				
			||||||
 | 
					        tp.trace(tp.channel.widget, m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
 | 
				
			||||||
 | 
					        var file_path: []const u8 = undefined;
 | 
				
			||||||
 | 
					        if (try m.match(.{"next_tab"})) {
 | 
				
			||||||
 | 
					            self.select_next_tab();
 | 
				
			||||||
 | 
					        } else if (try m.match(.{"previous_tab"})) {
 | 
				
			||||||
 | 
					            self.select_previous_tab();
 | 
				
			||||||
 | 
					        } else if (try m.match(.{ "E", "open", tp.extract(&file_path), tp.more })) {
 | 
				
			||||||
 | 
					            self.active_buffer = buffer_manager.get_buffer_for_file(file_path);
 | 
				
			||||||
 | 
					        } else if (try m.match(.{ "E", "close" })) {
 | 
				
			||||||
 | 
					            self.active_buffer = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn handle_resize(self: *Self, pos: Widget.Box) void {
 | 
				
			||||||
 | 
					        self.widget_list_widget.resize(pos);
 | 
				
			||||||
 | 
					        self.plane = self.widget_list.plane;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn get(self: *Self, name: []const u8) ?*Widget {
 | 
				
			||||||
 | 
					        return self.widget_list_widget.get(name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn walk(self: *Self, ctx: *anyopaque, f: Widget.WalkFn, _: *Widget) bool {
 | 
				
			||||||
 | 
					        return self.widget_list_widget.walk(ctx, f);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn hover(self: *Self) bool {
 | 
				
			||||||
 | 
					        return self.widget_list_widget.hover();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn update_tabs(self: *Self) !void {
 | 
				
			||||||
 | 
					        try self.update_tab_buffers();
 | 
				
			||||||
 | 
					        while (self.widget_list.pop()) |widget| if (widget.dynamic_cast(Button.State(Tab)) == null)
 | 
				
			||||||
 | 
					            widget.deinit(self.widget_list.allocator);
 | 
				
			||||||
 | 
					        var first = true;
 | 
				
			||||||
 | 
					        for (self.tabs) |tab| {
 | 
				
			||||||
 | 
					            if (first) {
 | 
				
			||||||
 | 
					                first = false;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                try self.widget_list.add(try self.make_spacer(1));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try self.widget_list.add(tab.widget);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn update_tab_buffers(self: *Self) !void {
 | 
				
			||||||
 | 
					        const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
 | 
				
			||||||
 | 
					        const buffers = try buffer_manager.list_unordered(self.allocator);
 | 
				
			||||||
 | 
					        defer self.allocator.free(buffers);
 | 
				
			||||||
 | 
					        const existing_tabs = self.tabs;
 | 
				
			||||||
 | 
					        defer self.allocator.free(existing_tabs);
 | 
				
			||||||
 | 
					        var result: std.ArrayListUnmanaged(TabBarTab) = .{};
 | 
				
			||||||
 | 
					        errdefer result.deinit(self.allocator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // add existing tabs in original order if they still exist
 | 
				
			||||||
 | 
					        outer: for (existing_tabs) |existing_tab|
 | 
				
			||||||
 | 
					            for (buffers) |buffer| if (existing_tab.buffer == buffer) {
 | 
				
			||||||
 | 
					                if (!buffer.hidden)
 | 
				
			||||||
 | 
					                    (try result.addOne(self.allocator)).* = existing_tab;
 | 
				
			||||||
 | 
					                continue :outer;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // add new tabs
 | 
				
			||||||
 | 
					        outer: for (buffers) |buffer| {
 | 
				
			||||||
 | 
					            for (result.items) |result_tab| if (result_tab.buffer == buffer)
 | 
				
			||||||
 | 
					                continue :outer;
 | 
				
			||||||
 | 
					            if (!buffer.hidden)
 | 
				
			||||||
 | 
					                (try result.addOne(self.allocator)).* = .{
 | 
				
			||||||
 | 
					                    .buffer = buffer,
 | 
				
			||||||
 | 
					                    .widget = try Tab.create(self, buffer, self.event_handler),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.tabs = try result.toOwnedSlice(self.allocator);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn make_spacer(self: @This(), comptime size: usize) !Widget {
 | 
				
			||||||
 | 
					        return @import("blank.zig").Create(.{ .static = size })(self.allocator, self.widget_list.plane, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn select_next_tab(self: *Self) void {
 | 
				
			||||||
 | 
					        tp.trace(tp.channel.widget, .{"select_next_tab"});
 | 
				
			||||||
 | 
					        var activate_next = false;
 | 
				
			||||||
 | 
					        var first: ?*const TabBarTab = null;
 | 
				
			||||||
 | 
					        for (self.tabs) |*tab| {
 | 
				
			||||||
 | 
					            if (first == null)
 | 
				
			||||||
 | 
					                first = tab;
 | 
				
			||||||
 | 
					            if (activate_next)
 | 
				
			||||||
 | 
					                return navigate_to_tab(tab);
 | 
				
			||||||
 | 
					            if (tab.buffer == self.active_buffer)
 | 
				
			||||||
 | 
					                activate_next = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (first) |tab|
 | 
				
			||||||
 | 
					            navigate_to_tab(tab);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn select_previous_tab(self: *Self) void {
 | 
				
			||||||
 | 
					        tp.trace(tp.channel.widget, .{"select_previous_tab"});
 | 
				
			||||||
 | 
					        var goto: ?*const TabBarTab = if (self.tabs.len > 0) &self.tabs[self.tabs.len - 1] else null;
 | 
				
			||||||
 | 
					        for (self.tabs) |*tab| {
 | 
				
			||||||
 | 
					            if (tab.buffer == self.active_buffer)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            goto = tab;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (goto) |tab| navigate_to_tab(tab);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn navigate_to_tab(tab: *const TabBarTab) void {
 | 
				
			||||||
 | 
					        tp.self_pid().send(.{ "cmd", "navigate", .{ .file = tab.buffer.file_path } }) catch {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Tab = struct {
 | 
				
			||||||
 | 
					    tabbar: *TabBar,
 | 
				
			||||||
 | 
					    buffer: *Buffer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn create(
 | 
				
			||||||
 | 
					        tabbar: *TabBar,
 | 
				
			||||||
 | 
					        buffer: *Buffer,
 | 
				
			||||||
 | 
					        event_handler: ?EventHandler,
 | 
				
			||||||
 | 
					    ) !Widget {
 | 
				
			||||||
 | 
					        return Button.create_widget(Tab, tabbar.allocator, tabbar.widget_list.plane, .{
 | 
				
			||||||
 | 
					            .ctx = .{ .tabbar = tabbar, .buffer = buffer },
 | 
				
			||||||
 | 
					            .label = name_from_buffer(buffer),
 | 
				
			||||||
 | 
					            .on_click = Tab.on_click,
 | 
				
			||||||
 | 
					            .on_click2 = Tab.on_click2,
 | 
				
			||||||
 | 
					            .on_layout = Tab.layout,
 | 
				
			||||||
 | 
					            .on_render = Tab.render,
 | 
				
			||||||
 | 
					            .on_event = event_handler,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_click(self: *@This(), _: *Button.State(@This())) void {
 | 
				
			||||||
 | 
					        tp.self_pid().send(.{ "cmd", "navigate", .{ .file = self.buffer.file_path } }) catch {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_click2(self: *@This(), _: *Button.State(@This())) void {
 | 
				
			||||||
 | 
					        tp.self_pid().send(.{ "cmd", "delete_buffer", .{self.buffer.file_path} }) catch {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme) bool {
 | 
				
			||||||
 | 
					        const active = self.tabbar.active_buffer == self.buffer;
 | 
				
			||||||
 | 
					        btn.plane.set_base_style(theme.statusbar);
 | 
				
			||||||
 | 
					        btn.plane.erase();
 | 
				
			||||||
 | 
					        btn.plane.home();
 | 
				
			||||||
 | 
					        btn.plane.set_style(if (active) theme.editor else if (btn.hover) theme.statusbar_hover else theme.statusbar);
 | 
				
			||||||
 | 
					        btn.plane.fill(" ");
 | 
				
			||||||
 | 
					        btn.plane.home();
 | 
				
			||||||
 | 
					        btn.plane.set_style(if (active) theme.editor else if (btn.hover) theme.statusbar_hover else theme.statusbar);
 | 
				
			||||||
 | 
					        _ = btn.plane.putstr(" ") catch {};
 | 
				
			||||||
 | 
					        if (self.buffer.is_dirty())
 | 
				
			||||||
 | 
					            _ = btn.plane.putstr(dirty_indicator) catch {};
 | 
				
			||||||
 | 
					        _ = btn.plane.putstr(btn.opts.label) catch {};
 | 
				
			||||||
 | 
					        _ = btn.plane.putstr(" ") catch {};
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn layout(self: *@This(), btn: *Button.State(@This())) Widget.Layout {
 | 
				
			||||||
 | 
					        const len = btn.plane.egc_chunk_width(btn.opts.label, 0, 1);
 | 
				
			||||||
 | 
					        const len_dirty_indicator = btn.plane.egc_chunk_width(dirty_indicator, 0, 1);
 | 
				
			||||||
 | 
					        const len_padding = btn.plane.egc_chunk_width(padding, 0, 1);
 | 
				
			||||||
 | 
					        return if (self.buffer.is_dirty())
 | 
				
			||||||
 | 
					            .{ .static = len + (2 * len_padding) + len_dirty_indicator }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            .{ .static = len + (2 * len_padding) };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn name_from_buffer(buffer: *Buffer) []const u8 {
 | 
				
			||||||
 | 
					        const file_path = buffer.file_path;
 | 
				
			||||||
 | 
					        const basename_begin = std.mem.lastIndexOfScalar(u8, file_path, std.fs.path.sep);
 | 
				
			||||||
 | 
					        const basename = if (basename_begin) |begin| file_path[begin + 1 ..] else file_path;
 | 
				
			||||||
 | 
					        return basename;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ const widgets = std.static_string_map.StaticStringMap(CreateFunction).initCompti
 | 
				
			||||||
    .{ "spacer", @import("blank.zig").Create(.{ .static = 1 }) },
 | 
					    .{ "spacer", @import("blank.zig").Create(.{ .static = 1 }) },
 | 
				
			||||||
    .{ "clock", @import("clock.zig").create },
 | 
					    .{ "clock", @import("clock.zig").create },
 | 
				
			||||||
    .{ "keybind", @import("keybindstate.zig").create },
 | 
					    .{ "keybind", @import("keybindstate.zig").create },
 | 
				
			||||||
 | 
					    .{ "tabs", @import("tabs.zig").create },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
pub const CreateError = error{ OutOfMemory, Exit };
 | 
					pub const CreateError = error{ OutOfMemory, Exit };
 | 
				
			||||||
pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget;
 | 
					pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue