feat(core): appstate

This commit is contained in:
2025-05-19 08:48:50 +02:00
parent 4eda4d335b
commit cbeed4003a
14 changed files with 454 additions and 103 deletions

View File

@@ -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, running: bool = true,
usage_number: locked(u16) = .{ .data = 0 }, usage_number: locked(u16) = .{ .data = 0 },
current_items: locked(?[]*models.Item) = .{ .data = null }, current_items: locked(?[]*models.Item) = .{ .data = null },
current_items_allocator: std.heap.ArenaAllocator, 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 { pub fn deinit(self: *@This()) void {
self.current_items_allocator.deinit(); 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();

View File

@@ -11,18 +11,24 @@ pub fn data_loop(vm: *Model) void {
vm.usage_number.data -= 1; vm.usage_number.data -= 1;
} }
fn inner_loop(vm: *Model) !void { 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); std.Thread.sleep(10 * std.time.ns_per_ms);
vm.tab.currentItems.mutex.lock(); tab.currentItems.mutex.lock();
defer vm.tab.currentItems.mutex.unlock(); 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); _ = vm.current_items_allocator.reset(.retain_capacity);
const allocator = vm.current_items_allocator.allocator(); const allocator = vm.current_items_allocator.allocator();
const items = try allocator.alloc(*models.Item, current_items.items.len); const items = try allocator.alloc(*models.Item, tab_current_items.items.len);
for (current_items.items, 0..) |item, i| { for (tab_current_items.items, 0..) |item, i| {
items[i] = item; items[i] = item;
} }
@@ -37,8 +43,7 @@ fn inner_loop(vm: *Model) !void {
vm.current_items.mutex.lock(); vm.current_items.mutex.lock();
defer vm.current_items.mutex.unlock(); defer vm.current_items.mutex.unlock();
vm.current_items.data = items; vm.current_items.data = items;
tab.currentItemsChanged = false;
vm.tab.currentItemsChanged = false;
} }
} }
} }

View File

@@ -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;

View File

@@ -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 /// Our main application state
const Model = struct { const Model = struct {
crash: bool = false, crash: bool = false,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
current_items_view: *vxfw.ListView, current_items_view: *vxfw.ListView,
core_model: CoreModel, core_model: CoreModel,
root_provider: *RootProvider,
/// Helper function to return a vxfw.Widget struct /// Helper function to return a vxfw.Widget struct
pub fn widget(self: *Model) vxfw.Widget { pub fn widget(self: *Model) vxfw.Widget {
@@ -36,11 +25,17 @@ const Model = struct {
ctx.quit = true; ctx.quit = true;
return; return;
} }
if (key.matches('r', .{ .ctrl = true })) { if (key.matches('r', .{})) {
vm.crash = true; // vm.crash = true;
ctx.redraw = true; ctx.redraw = true;
return ctx.requestFocus(vm.current_items_view.widget()); 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()), .focus_in => return ctx.requestFocus(vm.current_items_view.widget()),
else => {}, else => {},
@@ -93,7 +88,7 @@ const Model = struct {
}; };
try rootWidgets.append(list_surface); 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); const current_location_text_element = try ctx.arena.create(vxfw.Text);
current_location_text_element.* = vxfw.Text{ current_location_text_element.* = vxfw.Text{
.text = current_location_text, .text = current_location_text,
@@ -218,24 +213,26 @@ pub fn main() !void {
defer pool.deinit(); defer pool.deinit();
var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool }; var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool };
var localContentProv = localContentProvider.provider();
const homeFullName: models.FullName = .{ .path = "/home/adam" }; var rootProvider = RootProvider.init(allocator);
const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator); defer rootProvider.deinit();
const c = switch (homeItem.item) {
.container => |c| c, try rootProvider.providers.append(&localContentProv);
.element => unreachable,
}; 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); var tab1 = try allocator.create(Tab);
defer allocator.destroy(tab1); defer allocator.destroy(tab1);
tab1.init(&pool, allocator); tab1.init(&pool, &rootProvider, allocator);
defer tab1.deinit(); defer tab1.deinit();
tab1.setCurrentLocation(c); try tab1.setCurrentLocation(start_full_name);
const model = try allocator.create(Model);
defer allocator.destroy(model);
// TODO: remove // TODO: remove
std.Thread.sleep(1 * std.time.ns_per_s); std.Thread.sleep(1 * std.time.ns_per_s);
@@ -260,13 +257,21 @@ pub fn main() !void {
.draw_cursor = false, .draw_cursor = false,
.children = .{ .slice = items }, .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.* = .{ model.* = .{
.allocator = allocator, .allocator = allocator,
.core_model = .{ .core_model = core_model,
.current_items_allocator = std.heap.ArenaAllocator.init(allocator),
.tab = tab1,
},
.current_items_view = &list, .current_items_view = &list,
.root_provider = &rootProvider,
}; };
defer model.core_model.deinit(); 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;

View File

@@ -0,0 +1,4 @@
pub const Action = union(enum) {
GoUp,
Enter,
};

View File

@@ -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;

50
src/core/app_state.zig Normal file
View File

@@ -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;

View File

@@ -54,18 +54,26 @@ pub const Container = struct {
self.item.allocator.free(itemFullName.path); self.item.allocator.free(itemFullName.path);
} }
self.children.deinit(); self.children.deinit();
self.item.allocator.destroy(self);
} }
}; };
pub const FullName = struct { pub const FullName = struct {
path: []const u8, 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 }); const path = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ self.path, consts.PathSeparator, childName });
return FullName{ .path = path }; 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 { pub const NativePath = struct {
@@ -73,7 +81,7 @@ pub const NativePath = struct {
}; };
pub const AbsolutePath = struct { pub const AbsolutePath = struct {
path: []const u8, path: FullName,
type: AbsolutePathType, type: AbsolutePathType,
}; };
@@ -81,7 +89,7 @@ pub const Error = struct {
content: []const u8, content: []const u8,
}; };
pub const AbsolutePathType = enum { pub const AbsolutePathType = union(enum) {
Container, container,
Item, item,
}; };

41
src/core/observable.zig Normal file
View File

@@ -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");

View File

@@ -1,15 +1,4 @@
const std = @import("std"); pub const LocalProviderId = "local";
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;
// TODO: the container might be freed while this runs // TODO: the container might be freed while this runs
// Tab should hold something at pass it here // Tab should hold something at pass it here
@@ -18,6 +7,7 @@ fn loadChildren(container: *Container) void {
container.childrenLoading = false; 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 { 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; // 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; // container.item.errors.append(.{ .content = errorContent }) catch return;
@@ -35,6 +25,8 @@ fn loadChildren(container: *Container) void {
const VTable: ProviderVTable = .{ const VTable: ProviderVTable = .{
.getItemByFullName = getItemByFullNameGeneric, .getItemByFullName = getItemByFullNameGeneric,
.canHandle = canHandleGeneric,
.deinit = deinitGeneric,
}; };
pub fn getItemByFullNameGeneric( pub fn getItemByFullNameGeneric(
@@ -47,6 +39,43 @@ pub fn getItemByFullNameGeneric(
return self.getItemByFullName(fullName, initContext, allocator); 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 { pub const LocalContentProvider = struct {
threadPool: *std.Thread.Pool, threadPool: *std.Thread.Pool,
@@ -56,7 +85,7 @@ pub const LocalContentProvider = struct {
initContext: *const InitContext, initContext: *const InitContext,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
) GetItemsError!*Item { ) 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); defer allocator.free(native_path);
const kind: union(enum) { directory, file } = blk: { const kind: union(enum) { directory, file } = blk: {
@@ -139,7 +168,10 @@ pub const LocalContentProvider = struct {
const name = try allocator.dupe(u8, basename); const name = try allocator.dupe(u8, basename);
const displayName = try allocator.dupe(u8, basename); const displayName = try allocator.dupe(u8, basename);
const fullName2 = try allocator.dupe(u8, fullName.path); 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{ item.* = Item{
.allocator = allocator, .allocator = allocator,
@@ -148,12 +180,19 @@ pub const LocalContentProvider = struct {
.displayName = displayName, .displayName = displayName,
.fullName = .{ .path = fullName2 }, .fullName = .{ .path = fullName2 },
.nativePath = models.NativePath{ .path = nativePath }, .nativePath = models.NativePath{ .path = nativePath },
.parent = null, .parent = parent,
.item = innerItem, .item = innerItem,
.errors = std.ArrayList(models.Error).init(allocator), .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 { pub fn provider(self: *LocalContentProvider) Provider {
return Provider{ return Provider{
.object = self, .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;

View File

@@ -5,6 +5,13 @@ pub const VTable = struct {
initContext: *const InitContext, initContext: *const InitContext,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
) GetItemsError!*Item, ) GetItemsError!*Item,
canHandle: *const fn (
self: *anyopaque,
fullName: FullName,
) bool,
deinit: *const fn (self: *anyopaque) void,
}; };
pub const GetItemsError = error{ pub const GetItemsError = error{
@@ -25,6 +32,17 @@ pub const Provider = struct {
) GetItemsError!*Item { ) GetItemsError!*Item {
return self.vtable.getItemByFullName(self.object, fullName, initContext, allocator); 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 { pub const InitContext = struct {

View File

@@ -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;

View File

@@ -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 { pub const Tab = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
currentLocation: ?*Container, currentLocation: ?*Container,
currentItems: locked(?std.ArrayList(*Item)), currentItems: locked(?std.ArrayList(*Item)),
currentLocationChanged: Observable(?*Container),
currentItemsChanged: bool = false, currentItemsChanged: bool = false,
preCurrentItemsUnload: Observable(*Tab),
threadPool: *std.Thread.Pool, threadPool: *std.Thread.Pool,
rootProvider: *RootProvider,
_private: Private, _private: Private,
const Private = struct { const Private = struct {
@@ -23,44 +16,67 @@ pub const Tab = struct {
pub fn init( pub fn init(
self: *Tab, self: *Tab,
threadPool: *std.Thread.Pool, threadPool: *std.Thread.Pool,
rootProvider: *RootProvider,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
) void { ) void {
self.* = Tab{ self.* = Tab{
.allocator = allocator, .allocator = allocator,
.currentItems = .{ .data = null }, .currentItems = .{ .data = null },
.currentLocationChanged = Observable(?*Container).init(allocator),
.currentLocation = null, .currentLocation = null,
.threadPool = threadPool, .threadPool = threadPool,
.rootProvider = rootProvider,
.preCurrentItemsUnload = Observable(*Tab).init(allocator),
._private = .{ ._private = .{
.currentItemsAllocator = std.heap.ArenaAllocator.init(allocator), .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| { if (self.currentLocation) |c| {
c.item.deinit(); 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 //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 { fn loadItemsWrapper(self: *Tab, location: *Container) void {
loadItems(self, location) catch return; loadItems(self, location) catch return;
} }
fn loadItems(self: *Tab, location: *Container) !void { fn loadItems(self: *Tab, location: *Container) !void {
_ = self._private.currentItemsAllocator.reset(.retain_capacity);
{ {
self.currentItems.mutex.lock(); self.currentItems.mutex.lock();
defer self.currentItems.mutex.unlock(); defer self.currentItems.mutex.unlock();
self.preCurrentItemsUnload.notify(self);
if (self.currentItems.data) |items| { if (self.currentItems.data) |items| {
items.deinit(); items.deinit();
} }
self.currentItems.data = null;
} }
_ = self._private.currentItemsAllocator.reset(.retain_capacity);
const arenaAllocator = &self._private.currentItemsAllocator; const arenaAllocator = &self._private.currentItemsAllocator;
const arena = arenaAllocator.allocator(); const arena = arenaAllocator.allocator();
@@ -90,7 +106,7 @@ pub const Tab = struct {
for (location.children.items) |item| { for (location.children.items) |item| {
const resolvedItem = location.item.provider.getItemByFullName(item, &.{ .skipChildInit = true }, allocator) catch |e| { const resolvedItem = location.item.provider.getItemByFullName(item, &.{ .skipChildInit = true }, allocator) catch |e| {
//TODO: save error to container errors //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; continue;
}; };
@@ -98,6 +114,11 @@ pub const Tab = struct {
defer self.currentItems.mutex.unlock(); defer self.currentItems.mutex.unlock();
try self.currentItems.data.?.append(resolvedItem); try self.currentItems.data.?.append(resolvedItem);
}
{
self.currentItems.mutex.lock();
defer self.currentItems.mutex.unlock();
self.currentItemsChanged = true; self.currentItemsChanged = true;
} }
} }
@@ -109,3 +130,15 @@ pub const Tab = struct {
self._private.currentItemsAllocator.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;

View File

@@ -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; const RaylibBackend = dvui.backend;
comptime { comptime {
std.debug.assert(@hasDecl(RaylibBackend, "RaylibBackend")); std.debug.assert(@hasDecl(RaylibBackend, "RaylibBackend"));
@@ -46,31 +37,41 @@ pub fn main() !void {
defer pool.deinit(); defer pool.deinit();
var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool }; var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool };
var localContentProv = localContentProvider.provider();
const homeFullName: models.FullName = .{ .path = "/home/adam" }; var rootProvider = RootProvider.init(allocator);
const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator); defer rootProvider.deinit();
const startLocation = switch (homeItem.item) {
.container => |c1| c1, try rootProvider.providers.append(&localContentProv);
.element => unreachable,
}; 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); var tab1 = try allocator.create(Tab);
defer allocator.destroy(tab1); defer allocator.destroy(tab1);
tab1.init(&pool, allocator); tab1.init(&pool, &rootProvider, allocator);
defer tab1.deinit(); 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); const model = try allocator.create(Model);
defer allocator.destroy(model); defer allocator.destroy(model);
var core_model: CoreModel = undefined;
try CoreModel.init(&core_model, std.heap.ArenaAllocator.init(allocator), appState);
model.* = .{ model.* = .{
.allocator = allocator, .allocator = allocator,
.core_model = .{ .core_model = core_model,
.current_items_allocator = std.heap.ArenaAllocator.init(allocator),
.tab = tab1,
},
}; };
defer model.core_model.deinit(); 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");