diff --git a/src/app_common/Model.zig b/src/app_common/Model.zig index 69365f8..3557170 100644 --- a/src/app_common/Model.zig +++ b/src/app_common/Model.zig @@ -1,14 +1,48 @@ -const std = @import("std"); -const models = @import("../core/models.zig"); -const Tab = @import("../core/tab/tab.zig").Tab; -const locked = @import("../core/sync.zig").locked; - running: bool = true, usage_number: locked(u16) = .{ .data = 0 }, current_items: locked(?[]*models.Item) = .{ .data = null }, current_items_allocator: std.heap.ArenaAllocator, -tab: *Tab, +appState: AppState, +_private: Private, + +const Private = struct { + preCurrentItemsUnload: Observer(*Tab), +}; + +pub fn init(model: *Self, currentItemsAllocator: std.heap.ArenaAllocator, appState: AppState) !void { + model.* = Self{ + .current_items_allocator = currentItemsAllocator, + .appState = appState, + ._private = .{ + .preCurrentItemsUnload = Observer(*Tab){ + .ctx = @ptrCast(@alignCast(model)), + .update = preCurrentItemsUnload, + }, + }, + }; + + try model.appState.tabPreCurrentItemsUnload.attach(&model._private.preCurrentItemsUnload); +} + +pub fn preCurrentItemsUnload(ctx: *anyopaque, tab: *Tab) void { + const self: *Self = @ptrCast(@alignCast(ctx)); + if (tab == self.appState.currentTab) { + // @panic("asdasdasd"); + self.current_items.mutex.lock(); + defer self.current_items.mutex.unlock(); + + self.current_items.data = null; + } +} pub fn deinit(self: *@This()) void { self.current_items_allocator.deinit(); } + +const std = @import("std"); +const models = @import("../core/models.zig"); +const Tab = @import("../core/tab/tab.zig").Tab; +const locked = @import("../core/sync.zig").locked; +const AppState = @import("../core/app_state.zig").AppState; +const Observer = @import("../core/observable.zig").Observer; +const Self = @This(); diff --git a/src/app_common/root.zig b/src/app_common/root.zig index 93e4f1c..d6ee808 100644 --- a/src/app_common/root.zig +++ b/src/app_common/root.zig @@ -11,18 +11,24 @@ pub fn data_loop(vm: *Model) void { vm.usage_number.data -= 1; } fn inner_loop(vm: *Model) !void { - if (vm.tab.currentItemsChanged) { + const tab = vm.appState.currentTab; + if (tab.currentItemsChanged) { std.Thread.sleep(10 * std.time.ns_per_ms); - vm.tab.currentItems.mutex.lock(); - defer vm.tab.currentItems.mutex.unlock(); + tab.currentItems.mutex.lock(); + defer tab.currentItems.mutex.unlock(); - if (vm.tab.currentItems.data) |current_items| { + if (tab.currentItems.data) |tab_current_items| { + { + vm.current_items.mutex.lock(); + defer vm.current_items.mutex.unlock(); + vm.current_items.data = null; + } _ = vm.current_items_allocator.reset(.retain_capacity); const allocator = vm.current_items_allocator.allocator(); - const items = try allocator.alloc(*models.Item, current_items.items.len); - for (current_items.items, 0..) |item, i| { + const items = try allocator.alloc(*models.Item, tab_current_items.items.len); + for (tab_current_items.items, 0..) |item, i| { items[i] = item; } @@ -37,8 +43,7 @@ fn inner_loop(vm: *Model) !void { vm.current_items.mutex.lock(); defer vm.current_items.mutex.unlock(); vm.current_items.data = items; - - vm.tab.currentItemsChanged = false; + tab.currentItemsChanged = false; } } } diff --git a/src/console/action_map.zig b/src/console/action_map.zig new file mode 100644 index 0000000..f7970f1 --- /dev/null +++ b/src/console/action_map.zig @@ -0,0 +1,11 @@ +pub fn handle_key(key: vaxis.Key, appState: *AppState) !void { + if (key.matches(vaxis.Key.left, .{})) { + try handle_action(Action.GoUp, appState); + } +} + +const vaxis = @import("vaxis"); +const handle_action = @import("../core/action/action_handler.zig").handle; +const Action = @import("../core/action/action.zig").Action; +const AppState = @import("../core/app_state.zig").AppState; +const RootProvider = @import("../core/provider/root.zig").RootProvider; diff --git a/src/console/main.zig b/src/console/main.zig index 2afdc16..ca14786 100644 --- a/src/console/main.zig +++ b/src/console/main.zig @@ -1,21 +1,10 @@ -const std = @import("std"); -const vaxis = @import("vaxis"); -const vxfw = vaxis.vxfw; - -const models = @import("../core/models.zig"); -const provider = @import("../core/provider/provider.zig"); -const local_provider = @import("../core/provider/local.zig"); -const Tab = @import("../core/tab/tab.zig").Tab; -const locked = @import("../core/sync.zig").locked; -const CoreModel = @import("../app_common/Model.zig"); -const core = @import("../app_common/root.zig"); - /// Our main application state const Model = struct { crash: bool = false, allocator: std.mem.Allocator, current_items_view: *vxfw.ListView, core_model: CoreModel, + root_provider: *RootProvider, /// Helper function to return a vxfw.Widget struct pub fn widget(self: *Model) vxfw.Widget { @@ -36,11 +25,17 @@ const Model = struct { ctx.quit = true; return; } - if (key.matches('r', .{ .ctrl = true })) { - vm.crash = true; + if (key.matches('r', .{})) { + // vm.crash = true; ctx.redraw = true; return ctx.requestFocus(vm.current_items_view.widget()); } + // if (key.matches(vaxis.Key.left, .{})) { + // ctx.redraw = true; + // return ctx.requestFocus(vm.current_items_view.widget()); + // } + + handle_key(key, &vm.core_model.appState) catch {}; }, .focus_in => return ctx.requestFocus(vm.current_items_view.widget()), else => {}, @@ -93,7 +88,7 @@ const Model = struct { }; try rootWidgets.append(list_surface); - const current_location_text = if (vm.core_model.tab.currentLocation) |loc| loc.item.fullName.path else ""; + const current_location_text = if (vm.core_model.appState.currentTab.currentLocation) |loc| loc.item.fullName.path else ""; const current_location_text_element = try ctx.arena.create(vxfw.Text); current_location_text_element.* = vxfw.Text{ .text = current_location_text, @@ -218,24 +213,26 @@ pub fn main() !void { defer pool.deinit(); var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool }; + var localContentProv = localContentProvider.provider(); - const homeFullName: models.FullName = .{ .path = "/home/adam" }; - const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator); - const c = switch (homeItem.item) { - .container => |c| c, - .element => unreachable, - }; + var rootProvider = RootProvider.init(allocator); + defer rootProvider.deinit(); + + try rootProvider.providers.append(&localContentProv); + + const current_path = try std.fs.cwd().realpathAlloc(allocator, "."); + defer allocator.free(current_path); + + const start_full_name = try local_provider.getFullNameByNativePath(allocator, models.NativePath{ .path = current_path }); + defer allocator.free(start_full_name.path); var tab1 = try allocator.create(Tab); defer allocator.destroy(tab1); - tab1.init(&pool, allocator); + tab1.init(&pool, &rootProvider, allocator); defer tab1.deinit(); - tab1.setCurrentLocation(c); - - const model = try allocator.create(Model); - defer allocator.destroy(model); + try tab1.setCurrentLocation(start_full_name); // TODO: remove std.Thread.sleep(1 * std.time.ns_per_s); @@ -260,13 +257,21 @@ pub fn main() !void { .draw_cursor = false, .children = .{ .slice = items }, }; + + var appState: AppState = AppState.init(allocator); + appState.currentTab = tab1; + + const model = try allocator.create(Model); + defer allocator.destroy(model); + + var core_model: CoreModel = undefined; + try CoreModel.init(&core_model, std.heap.ArenaAllocator.init(allocator), appState); + model.* = .{ .allocator = allocator, - .core_model = .{ - .current_items_allocator = std.heap.ArenaAllocator.init(allocator), - .tab = tab1, - }, + .core_model = core_model, .current_items_view = &list, + .root_provider = &rootProvider, }; defer model.core_model.deinit(); @@ -288,4 +293,17 @@ pub fn main() !void { } } -const asd1 = @import("../core/allocator.zig"); +const std = @import("std"); +const vaxis = @import("vaxis"); +const vxfw = vaxis.vxfw; + +const models = @import("../core/models.zig"); +const provider = @import("../core/provider/provider.zig"); +const RootProvider = @import("../core/provider/root.zig").RootProvider; +const local_provider = @import("../core/provider/local.zig"); +const Tab = @import("../core/tab/tab.zig").Tab; +const locked = @import("../core/sync.zig").locked; +const CoreModel = @import("../app_common/Model.zig"); +const core = @import("../app_common/root.zig"); +const AppState = @import("../core/app_state.zig").AppState; +const handle_key = @import("./action_map.zig").handle_key; diff --git a/src/core/action/action.zig b/src/core/action/action.zig new file mode 100644 index 0000000..0699e2f --- /dev/null +++ b/src/core/action/action.zig @@ -0,0 +1,4 @@ +pub const Action = union(enum) { + GoUp, + Enter, +}; diff --git a/src/core/action/action_handler.zig b/src/core/action/action_handler.zig new file mode 100644 index 0000000..e83e7cd --- /dev/null +++ b/src/core/action/action_handler.zig @@ -0,0 +1,17 @@ +pub fn handle(action: Action, appState: *AppState) !void { + switch (action) { + .GoUp => { + if (appState.currentTab.currentLocation) |currentLocation| { + const parent = currentLocation.item.parent; + if (parent) |p| { + try appState.currentTab.setCurrentLocation(.{ .path = p.path.path }); + } else return error.NoParent; + } else return error.NoCurrentLocation; + }, + .Enter => unreachable, + } +} + +const Action = @import("action.zig").Action; +const AppState = @import("../app_state.zig").AppState; +const RootProvider = @import("../provider/root.zig").RootProvider; diff --git a/src/core/app_state.zig b/src/core/app_state.zig new file mode 100644 index 0000000..44e584e --- /dev/null +++ b/src/core/app_state.zig @@ -0,0 +1,50 @@ +pub const UnknownTabError = error.UnknownTab; + +pub const AppState = struct { + currentTab: *Tab = undefined, + tabs: std.ArrayList(Tab), + currentTabChanged: Observable(*Tab), + tabPreCurrentItemsUnload: Observable(*Tab), + + pub fn init(allocator: std.mem.Allocator) AppState { + return .{ + .tabs = std.ArrayList(Tab).init(allocator), + .currentTabChanged = Observable(*Tab).init(allocator), + .tabPreCurrentItemsUnload = Observable(*Tab).init(allocator), + }; + } + + pub fn addTab(self: *AppState, tab: Tab) void { + self.tabs.append(tab); + // tab.preCurrentItemsUnload.attach(.{ + // .ctx = @ptrCast(@alignCast(self)), + // .update = preCurrentItemsUnload, + // }); + } + + pub fn setCurrentTab(self: *AppState, newTab: *Tab) !void { + blk: { + for (self.tabs.items) |item| { + if (item == newTab) break :blk; + } + return UnknownTabError; + } + + self.currentTab = newTab; + self.currentTabChanged.notify(newTab); + } + + pub fn deinit(self: *AppState) void { + self.currentTabChanged.deinit(); + self.tabs.deinit(); + } + + fn preCurrentItemsUnload(ctx: *anyopaque, tab: *Tab) void { + const self: AppState = @ptrCast(@alignCast(ctx)); + self.tabPreCurrentItemsUnload.notify(tab); + } +}; + +const std = @import("std"); +const Tab = @import("tab/tab.zig").Tab; +const Observable = @import("observable.zig").Observable; diff --git a/src/core/models.zig b/src/core/models.zig index d76c7c5..ed8c0bd 100644 --- a/src/core/models.zig +++ b/src/core/models.zig @@ -54,18 +54,26 @@ pub const Container = struct { self.item.allocator.free(itemFullName.path); } self.children.deinit(); - self.item.allocator.destroy(self); } }; pub const FullName = struct { path: []const u8, - pub fn getChild(self: *FullName, childName: []const u8, allocator: std.mem.Allocator) !FullName { + pub fn getChild(self: *const FullName, childName: []const u8, allocator: std.mem.Allocator) !FullName { const path = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ self.path, consts.PathSeparator, childName }); return FullName{ .path = path }; } + + pub fn getParent(self: *const FullName, allocator: std.mem.Allocator) !?FullName { + const lastIndex = std.mem.lastIndexOf(u8, self.path, consts.PathSeparator) orelse return null; + if (lastIndex == 0) return null; + + const path = try std.fmt.allocPrint(allocator, "{s}", .{self.path[0..lastIndex]}); + + return FullName{ .path = path }; + } }; pub const NativePath = struct { @@ -73,7 +81,7 @@ pub const NativePath = struct { }; pub const AbsolutePath = struct { - path: []const u8, + path: FullName, type: AbsolutePathType, }; @@ -81,7 +89,7 @@ pub const Error = struct { content: []const u8, }; -pub const AbsolutePathType = enum { - Container, - Item, +pub const AbsolutePathType = union(enum) { + container, + item, }; diff --git a/src/core/observable.zig b/src/core/observable.zig new file mode 100644 index 0000000..1c5df59 --- /dev/null +++ b/src/core/observable.zig @@ -0,0 +1,41 @@ +pub fn Observable(T: type) type { + return struct { + observers: std.ArrayList(*const Observer(T)), + + const Self = @This(); + pub fn init(allocator: std.mem.Allocator) Self { + return .{ + .observers = std.ArrayList(*const Observer(T)).init(allocator), + }; + } + + pub fn deinit(self: *Self) void { + self.observers.deinit(); + } + + pub fn attach(self: *Self, obs: *const Observer(T)) !void { + try self.observers.append(obs); + } + + pub fn detach(self: *Self, obs: *const Observer(T)) void { + if (std.mem.indexOfScalar(*const Observer, self.observers.items, obs)) |index| { + _ = self.observers.swapRemove(index); + } + } + + pub fn notify(self: Self, args: T) void { + for (self.observers.items) |obs| { + obs.update(obs.ctx, args); + } + } + }; +} + +pub fn Observer(T: type) type { + return struct { + ctx: *anyopaque, + update: *const fn (ctx: *anyopaque, args: T) void, + }; +} + +const std = @import("std"); diff --git a/src/core/provider/local.zig b/src/core/provider/local.zig index bcccf76..9bb1369 100644 --- a/src/core/provider/local.zig +++ b/src/core/provider/local.zig @@ -1,15 +1,4 @@ -const std = @import("std"); -const models = @import("../models.zig"); -const Provider = @import("provider.zig").Provider; -const ProviderVTable = @import("provider.zig").VTable; -const GetItemsError = @import("provider.zig").GetItemsError; -const InitContext = @import("provider.zig").InitContext; - -const FullName = models.FullName; -const Item = models.Item; -const ItemEnum = models.ItemEnum; -const Element = models.Element; -const Container = models.Container; +pub const LocalProviderId = "local"; // TODO: the container might be freed while this runs // Tab should hold something at pass it here @@ -18,6 +7,7 @@ fn loadChildren(container: *Container) void { container.childrenLoading = false; } + std.debug.print("load children {s}", .{container.item.nativePath.path}); var dir = std.fs.cwd().openDir(container.item.nativePath.path, .{ .iterate = true }) catch { // const errorContent = std.fmt.allocPrint(container.item.allocator, "Could not open directory '{s}'.", .{container.item.nativePath.path}) catch return; // container.item.errors.append(.{ .content = errorContent }) catch return; @@ -35,6 +25,8 @@ fn loadChildren(container: *Container) void { const VTable: ProviderVTable = .{ .getItemByFullName = getItemByFullNameGeneric, + .canHandle = canHandleGeneric, + .deinit = deinitGeneric, }; pub fn getItemByFullNameGeneric( @@ -47,6 +39,43 @@ pub fn getItemByFullNameGeneric( return self.getItemByFullName(fullName, initContext, allocator); } +pub fn canHandleGeneric( + context: *anyopaque, + fullName: FullName, +) bool { + const self: *LocalContentProvider = @ptrCast(@alignCast(context)); + return self.canHandle(fullName); +} + +pub fn deinitGeneric(_: *anyopaque) void {} + +pub fn getFullNameByNativePath(allocator: std.mem.Allocator, nativePath: NativePath) !FullName { + const correct_sep = try std.mem.replaceOwned(u8, allocator, nativePath.path, std.fs.path.sep_str, "/"); + defer allocator.free(correct_sep); + + const asd = if (std.mem.startsWith(u8, correct_sep, "/")) + correct_sep[1..] + else + correct_sep; + + const full_name_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ LocalProviderId, asd }); + + return .{ .path = full_name_path }; +} + +pub fn getNativePathByFullName(allocator: std.mem.Allocator, fullName: FullName) ![]u8 { + const fullNameWithoutId = fullName.path[LocalProviderId.len + 1 ..]; + var native_path = try std.mem.replaceOwned(u8, allocator, fullNameWithoutId, "/", std.fs.path.sep_str); + + if (native_os == .linux) { + const linux_native_path = try std.fmt.allocPrint(allocator, "/{s}", .{native_path}); + allocator.free(native_path); + native_path = linux_native_path; + } + + return native_path; +} + pub const LocalContentProvider = struct { threadPool: *std.Thread.Pool, @@ -56,7 +85,7 @@ pub const LocalContentProvider = struct { initContext: *const InitContext, allocator: std.mem.Allocator, ) GetItemsError!*Item { - const native_path = try std.mem.replaceOwned(u8, allocator, fullName.path, "/", std.fs.path.sep_str); + const native_path = try getNativePathByFullName(allocator, fullName); defer allocator.free(native_path); const kind: union(enum) { directory, file } = blk: { @@ -139,7 +168,10 @@ pub const LocalContentProvider = struct { const name = try allocator.dupe(u8, basename); const displayName = try allocator.dupe(u8, basename); const fullName2 = try allocator.dupe(u8, fullName.path); - const nativePath = try allocator.dupe(u8, fullName.path); + const nativePath = try getNativePathByFullName(allocator, fullName); + + const parentFullName = try fullName.getParent(allocator); + const parent = if (parentFullName) |p| models.AbsolutePath{ .path = p, .type = .container } else null; item.* = Item{ .allocator = allocator, @@ -148,12 +180,19 @@ pub const LocalContentProvider = struct { .displayName = displayName, .fullName = .{ .path = fullName2 }, .nativePath = models.NativePath{ .path = nativePath }, - .parent = null, + .parent = parent, .item = innerItem, .errors = std.ArrayList(models.Error).init(allocator), }; } + pub fn canHandle( + _: *LocalContentProvider, + fullName: FullName, + ) bool { + return std.mem.startsWith(u8, fullName.path, "local/"); + } + pub fn provider(self: *LocalContentProvider) Provider { return Provider{ .object = self, @@ -161,3 +200,18 @@ pub const LocalContentProvider = struct { }; } }; + +const std = @import("std"); +const models = @import("../models.zig"); +const Provider = @import("provider.zig").Provider; +const ProviderVTable = @import("provider.zig").VTable; +const GetItemsError = @import("provider.zig").GetItemsError; +const InitContext = @import("provider.zig").InitContext; + +const FullName = models.FullName; +const NativePath = models.NativePath; +const Item = models.Item; +const ItemEnum = models.ItemEnum; +const Element = models.Element; +const Container = models.Container; +const native_os = @import("builtin").os.tag; diff --git a/src/core/provider/provider.zig b/src/core/provider/provider.zig index 5f54285..26ad882 100644 --- a/src/core/provider/provider.zig +++ b/src/core/provider/provider.zig @@ -5,6 +5,13 @@ pub const VTable = struct { initContext: *const InitContext, allocator: std.mem.Allocator, ) GetItemsError!*Item, + + canHandle: *const fn ( + self: *anyopaque, + fullName: FullName, + ) bool, + + deinit: *const fn (self: *anyopaque) void, }; pub const GetItemsError = error{ @@ -25,6 +32,17 @@ pub const Provider = struct { ) GetItemsError!*Item { return self.vtable.getItemByFullName(self.object, fullName, initContext, allocator); } + + pub inline fn canHandle( + self: *const Provider, + fullName: FullName, + ) bool { + return self.vtable.canHandle(self.object, fullName); + } + + pub inline fn deinit(self: *const Provider) void { + return self.vtable.deinit(self.object); + } }; pub const InitContext = struct { diff --git a/src/core/provider/root.zig b/src/core/provider/root.zig new file mode 100644 index 0000000..9ec5a67 --- /dev/null +++ b/src/core/provider/root.zig @@ -0,0 +1,45 @@ +pub const RootGetItemsError = GetItemsError || error{ProviderNotFound}; +pub const RootProvider = struct { + providers: std.ArrayList(*Provider), + pub fn init(allocator: std.mem.Allocator) RootProvider { + return .{ + .providers = std.ArrayList(*Provider).init(allocator), + }; + } + + pub fn getItemByFullName( + self: *const RootProvider, + fullName: FullName, + initContext: *const InitContext, + allocator: std.mem.Allocator, + ) RootGetItemsError!*Item { + const provider = for (self.providers.items) |provider| { + if (provider.canHandle(fullName)) { + break provider; + } + } else return error.ProviderNotFound; + + return provider.getItemByFullName(fullName, initContext, allocator); + } + + pub fn addProvider(self: *Provider, provider: *Provider) !void { + try self.providers.append(provider); + } + + pub fn deinit( + self: *const RootProvider, + ) void { + for (self.providers.items) |p| { + p.deinit(); + } + self.providers.deinit(); + } +}; + +const std = @import("std"); +const Provider = @import("./provider.zig").Provider; +const InitContext = @import("./provider.zig").InitContext; +const GetItemsError = @import("./provider.zig").GetItemsError; +const models = @import("../models.zig"); +const FullName = models.FullName; +const Item = models.Item; diff --git a/src/core/tab/tab.zig b/src/core/tab/tab.zig index 139f82d..bd6a3a5 100644 --- a/src/core/tab/tab.zig +++ b/src/core/tab/tab.zig @@ -1,19 +1,12 @@ -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; - 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 { @@ -23,44 +16,67 @@ pub const Tab = struct { 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, newLocation: *Container) void { + pub fn setCurrentLocation(self: *Tab, newLocationFullName: models.FullName) !void { if (self.currentLocation) |c| { c.item.deinit(); + self.allocator.destroy(c); } - self.currentLocation = newLocation; + errdefer { + self.currentLocation = null; + self.currentLocationChanged.notify(null); + } + + const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator); + + const newLocationContainer = switch (newLocation.item) { + .container => |c| c, + .element => return error.NotContainer, + }; + + self.currentLocation = newLocationContainer; + self.currentLocationChanged.notify(newLocationContainer); //TODO: Proper error handling - std.Thread.Pool.spawn(self.threadPool, loadItemsWrapper, .{ self, newLocation }) catch unreachable; + 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._private.currentItemsAllocator.reset(.retain_capacity); - { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); + + self.preCurrentItemsUnload.notify(self); + if (self.currentItems.data) |items| { items.deinit(); } + + self.currentItems.data = null; } + _ = self._private.currentItemsAllocator.reset(.retain_capacity); + const arenaAllocator = &self._private.currentItemsAllocator; const arena = arenaAllocator.allocator(); @@ -90,7 +106,7 @@ pub const Tab = struct { 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}); + std.debug.print("error {} {s}\r\n", .{ e, item.path }); continue; }; @@ -98,6 +114,11 @@ pub const Tab = struct { defer self.currentItems.mutex.unlock(); try self.currentItems.data.?.append(resolvedItem); + } + + { + self.currentItems.mutex.lock(); + defer self.currentItems.mutex.unlock(); self.currentItemsChanged = true; } } @@ -109,3 +130,15 @@ pub const Tab = struct { 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; diff --git a/src/gui/main.zig b/src/gui/main.zig index 5d5f2cb..a79af7c 100644 --- a/src/gui/main.zig +++ b/src/gui/main.zig @@ -1,12 +1,3 @@ -const std = @import("std"); -const CoreModel = @import("../app_common/Model.zig"); -const models = @import("../core/models.zig"); -const provider = @import("../core/provider/provider.zig"); -const local_provider = @import("../core/provider/local.zig"); -const Tab = @import("../core/tab/tab.zig").Tab; -const core = @import("../app_common/root.zig"); - -const dvui = @import("dvui"); const RaylibBackend = dvui.backend; comptime { std.debug.assert(@hasDecl(RaylibBackend, "RaylibBackend")); @@ -46,31 +37,41 @@ pub fn main() !void { defer pool.deinit(); var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool }; + var localContentProv = localContentProvider.provider(); - const homeFullName: models.FullName = .{ .path = "/home/adam" }; - const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator); - const startLocation = switch (homeItem.item) { - .container => |c1| c1, - .element => unreachable, - }; + var rootProvider = RootProvider.init(allocator); + defer rootProvider.deinit(); + + try rootProvider.providers.append(&localContentProv); + + const home_path = try std.fs.cwd().realpathAlloc(allocator, "."); + defer allocator.free(home_path); + + const start_path = try std.fmt.allocPrint(allocator, "local/{s}", .{home_path}); + defer allocator.free(start_path); + + const homeFullName: models.FullName = .{ .path = start_path }; var tab1 = try allocator.create(Tab); defer allocator.destroy(tab1); - tab1.init(&pool, allocator); + tab1.init(&pool, &rootProvider, allocator); defer tab1.deinit(); - tab1.setCurrentLocation(startLocation); + try tab1.setCurrentLocation(homeFullName); + + var appState: AppState = AppState.init(allocator); + appState.currentTab = tab1; const model = try allocator.create(Model); defer allocator.destroy(model); + var core_model: CoreModel = undefined; + try CoreModel.init(&core_model, std.heap.ArenaAllocator.init(allocator), appState); + model.* = .{ .allocator = allocator, - .core_model = .{ - .current_items_allocator = std.heap.ArenaAllocator.init(allocator), - .tab = tab1, - }, + .core_model = core_model, }; defer model.core_model.deinit(); @@ -193,3 +194,15 @@ fn dvui_frame(model: *Model) !void { } } } + +const std = @import("std"); +const CoreModel = @import("../app_common/Model.zig"); +const models = @import("../core/models.zig"); +const provider = @import("../core/provider/provider.zig"); +const RootProvider = @import("../core/provider/root.zig").RootProvider; +const local_provider = @import("../core/provider/local.zig"); +const Tab = @import("../core/tab/tab.zig").Tab; +const core = @import("../app_common/root.zig"); +const AppState = @import("../core/app_state.zig").AppState; + +const dvui = @import("dvui");