pub const Tab = struct { allocator: std.mem.Allocator, currentLocation: ?*Container, currentItems: locked(?std.ArrayList(*Item)), currentLocationChanged: Observable(?*Container), currentItemsChanged: bool = false, preCurrentItemsUnload: Observable(*Tab), threadPool: *std.Thread.Pool, rootProvider: *RootProvider, _private: Private, const Private = struct { currentItemsAllocator: std.heap.ArenaAllocator, }; pub fn init( self: *Tab, threadPool: *std.Thread.Pool, rootProvider: *RootProvider, allocator: std.mem.Allocator, ) void { self.* = Tab{ .allocator = allocator, .currentItems = .{ .data = null }, .currentLocationChanged = Observable(?*Container).init(allocator), .currentLocation = null, .threadPool = threadPool, .rootProvider = rootProvider, .preCurrentItemsUnload = Observable(*Tab).init(allocator), ._private = .{ .currentItemsAllocator = std.heap.ArenaAllocator.init(allocator), }, }; } pub fn setCurrentLocation(self: *Tab, newLocationFullName: models.FullName) !void { if (self.currentLocation) |c| { c.item.deinit(); } errdefer { self.currentLocation = null; self.currentLocationChanged.notify(null); } const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator); errdefer { newLocation.deinit(); } const newLocationContainer = switch (newLocation.item) { .container => |c| c, .element => return error.NotContainer, }; self.currentLocation = newLocationContainer; self.currentLocationChanged.notify(newLocationContainer); std.debug.print("\nASD1\n", .{}); //TODO: Proper error handling std.Thread.Pool.spawn(self.threadPool, loadItemsWrapper, .{ self, newLocationContainer }) catch unreachable; } fn loadItemsWrapper(self: *Tab, location: *Container) void { loadItems(self, location) catch return; } fn loadItems(self: *Tab, location: *Container) !void { { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); self.preCurrentItemsUnload.notify(self); self.currentItems.data = null; } //TODO: half the capacity to prevent "leaking" after a huuuge container has been opened once _ = self._private.currentItemsAllocator.reset(.retain_capacity); const arena = self._private.currentItemsAllocator.allocator(); var threadSafeAllocator = std.heap.ThreadSafeAllocator{ .child_allocator = arena }; const allocator = threadSafeAllocator.allocator(); errdefer { _ = self._private.currentItemsAllocator.reset(.free_all); } { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); self.currentItems.data = std.ArrayList(*Item).init(allocator); } errdefer { self.currentItems.data.?.deinit(); self.currentItems.data = null; } //TODO: add async while (location.childrenLoading) { std.Thread.sleep(1 * std.time.ns_per_ms); } for (location.children.items) |item| { const resolvedItem = location.item.provider.getItemByFullName(item, &.{ .skipChildInit = true }, allocator) catch |e| { //TODO: save error to container errors std.debug.print("error {} {s}\r\n", .{ e, item.path }); continue; }; self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); try self.currentItems.data.?.append(resolvedItem); } { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); self.currentItemsChanged = true; } std.debug.print("\nASDX\n", .{}); } pub fn deinit(self: *Tab) void { if (self.currentLocation) |c| { c.item.deinit(); } self._private.currentItemsAllocator.deinit(); } }; const std = @import("std"); const assert = std.debug.assert; const Mutex = std.Thread.Mutex; const models = @import("../models.zig"); const Container = models.Container; const Item = models.Item; const locked = @import("../sync.zig").locked; const Observable = @import("../observable.zig").Observable; const RootProvider = @import("../provider/root.zig").RootProvider;