feat: cleanup appstate, move sort to tab

This commit is contained in:
2025-05-26 17:22:23 +02:00
parent c4c901f1d4
commit 095b8659d6
9 changed files with 270 additions and 159 deletions

View File

@@ -1,4 +1,11 @@
pub const ActionResult = union(enum) {
None,
Handled,
};
pub const Action = union(enum) {
GoUp,
Enter,
SelectNext,
SelectPrevious,
};

View File

@@ -1,4 +1,4 @@
pub fn handle(action: Action, appState: *AppState, arena: std.mem.Allocator) !void {
pub fn handle(action: Action, appState: *AppState, arena: std.mem.Allocator) !ActionResult {
switch (action) {
.GoUp => {
if (appState.currentTab.currentLocation) |currentLocation| {
@@ -6,14 +6,76 @@ pub fn handle(action: Action, appState: *AppState, arena: std.mem.Allocator) !vo
if (parent) |p| {
const path = try arena.dupe(u8, p.path.path);
try appState.currentTab.setCurrentLocation(.{ .path = path });
return ActionResult.Handled;
} else return error.NoParent;
} else return error.NoCurrentLocation;
},
.Enter => unreachable,
.Enter => {},
.SelectNext => {
return selectNextIndex(appState.currentTab, .Next);
},
.SelectPrevious => {
return selectNextIndex(appState.currentTab, .Previous);
},
}
return ActionResult.None;
}
const SelectStep = enum {
Next,
Previous,
NextPage,
PreviousPage,
};
fn selectNextIndex(currentTab: *Tab, step: SelectStep) ActionResult {
const index = blk: {
currentTab.currentItems.mutex.lock();
defer currentTab.currentItems.mutex.unlock();
const currentItems = currentTab.currentItems.data orelse return ActionResult.None;
if (currentTab.currentItem) |*currentItem| {
const index = indexOf(Arc(*models.Item), models.FullName, currentItems.items, currentItem, arcFullNameEql);
if (index) |i| {
break :blk switch (step) {
.Next => i +| 1,
.Previous => i -| 1,
.NextPage => i +| 8,
.PreviousPage => i +| 8,
};
}
}
break :blk 0;
};
currentTab.selectItem(index) catch {
//TODO
};
return ActionResult.Handled;
}
fn arcFullNameEql(arcLhs: *const Arc(*models.Item), rhs: *const models.FullName) bool {
const arcLhs2 = arcLhs.retain();
defer if (arcLhs2.releaseUnwrap()) |i| i.deinit();
return models.FullName.eql(&arcLhs2.value.*.fullName, rhs);
}
fn indexOf(comptime TSlice: type, comptime TItem: type, slice: []const TSlice, value: *const TItem, compare: *const fn (a: *const TSlice, b: *const TItem) bool) ?usize {
for (0.., slice) |i, *item| {
if (compare(item, value)) return i;
}
return null;
}
const std = @import("std");
const Action = @import("action.zig").Action;
const ActionResult = @import("action.zig").ActionResult;
const AppState = @import("../app_state.zig").AppState;
const RootProvider = @import("../provider/root.zig").RootProvider;
const models = @import("../models.zig");
const Arc = @import("zigrc").Arc;
const Tab = @import("../tab/tab.zig").Tab;

View File

@@ -80,6 +80,10 @@ pub const FullName = struct {
return FullName{ .path = path };
}
pub fn eql(self: *const FullName, value: *const FullName) bool {
return std.mem.eql(u8, self.path, value.path);
}
};
pub const NativePath = struct {

View File

@@ -3,7 +3,7 @@ pub const Tab = struct {
currentLocation: ?*Container,
currentItems: locked(?std.ArrayList(Arc(*Item))),
currentLocationChanged: Observable(?*Container),
currentItemsChanged: bool = false,
currentItem: ?models.FullName,
threadPool: *std.Thread.Pool,
rootProvider: *RootProvider,
@@ -18,6 +18,7 @@ pub const Tab = struct {
.currentItems = .{ .data = null },
.currentLocationChanged = Observable(?*Container).init(allocator),
.currentLocation = null,
.currentItem = null,
.threadPool = threadPool,
.rootProvider = rootProvider,
};
@@ -28,26 +29,54 @@ pub const Tab = struct {
c.item.deinit();
}
errdefer {
self.currentLocation = null;
self.currentLocationChanged.notify(null);
if (self.currentItem) |i| {
self.allocator.free(i.path);
self.currentItem = null;
}
const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator);
errdefer {
newLocation.deinit();
}
const newLocationContainer = blk: {
const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator);
errdefer {
newLocation.deinit();
self.currentLocation = null;
self.currentLocationChanged.notify(null);
}
const newLocationContainer = switch (newLocation.item) {
.container => |c| c,
.element => return error.NotContainer,
const newLocationContainer = switch (newLocation.item) {
.container => |c| c,
.element => return error.NotContainer,
};
self.currentLocation = newLocationContainer;
break :blk newLocationContainer;
};
self.currentLocation = newLocationContainer;
self.currentLocationChanged.notify(newLocationContainer);
//TODO: Proper error handling
std.Thread.Pool.spawn(self.threadPool, loadItemsWrapper, .{ self, newLocationContainer }) catch unreachable;
self.selectItem(0) catch {
self.currentItem = null;
};
}
pub fn selectItem(self: *Tab, index: usize) !void {
self.currentItems.mutex.lock();
defer self.currentItems.mutex.unlock();
const currentItems = self.currentItems.data orelse return error.Error;
if (index >= currentItems.items.len) {
return error.OutOfRange;
}
if (self.currentItem) |currentItem| {
self.allocator.free(currentItem.path);
}
self.currentItem = models.FullName{
.path = try self.allocator.dupe(u8, currentItems.items[index].value.*.fullName.path),
};
}
fn loadItemsWrapper(self: *Tab, location: *Container) void {
@@ -84,25 +113,25 @@ pub const Tab = struct {
std.Thread.sleep(1 * std.time.ns_per_ms);
}
for (location.children.items) |item| {
const resolvedItem = location.item.provider.getItemByFullName(item, &.{ .skipChildInit = true }, self.allocator) catch |e| {
for (location.children.items) |item_fullname| {
const resolvedItem = location.item.provider.getItemByFullName(item_fullname, &.{ .skipChildInit = true }, self.allocator) catch |e| {
//TODO: save error to container errors
std.debug.print("error {} {s}\r\n", .{ e, item.path });
std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path });
continue;
};
self.currentItems.mutex.lock();
defer self.currentItems.mutex.unlock();
// const arc = try Arc(*Item).init(self.allocator, resolvedItem);
// try self.currentItems.data.?.append(arc);
try self.currentItems.data.?.append(try Arc(*Item).init(self.allocator, resolvedItem));
}
{
self.currentItems.mutex.lock();
defer self.currentItems.mutex.unlock();
self.currentItemsChanged = true;
const index = for (self.currentItems.data.?.items, 0..) |item, i| {
if (compareByDisplayName(resolvedItem, item.value.*)) break i;
} else null;
const arc = try Arc(*Item).init(self.allocator, resolvedItem);
if (index) |i| {
try self.currentItems.data.?.insert(i, arc);
} else {
try self.currentItems.data.?.append(arc);
}
}
}
@@ -122,8 +151,18 @@ pub const Tab = struct {
currentItems.deinit();
}
if (self.currentItem) |currentItem| {
self.allocator.free(currentItem.path);
}
self.allocator.destroy(self);
}
fn compareByDisplayName(lhs: *models.Item, rhs: *models.Item) bool {
if (lhs.item == .container and rhs.item == .element) return true;
if (lhs.item == .element and rhs.item == .container) return false;
return std.mem.order(u8, lhs.displayName, rhs.displayName) == .lt;
}
};
const std = @import("std");