feat: selected's children

This commit is contained in:
2025-05-28 18:07:25 +02:00
parent a884c2066f
commit 53b93bad64
4 changed files with 229 additions and 52 deletions

View File

@@ -2,7 +2,8 @@ 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,
current_items_view_widget: vxfw.Widget, parent_items_view: *vxfw.ListView,
current_children_view: *vxfw.ListView,
app_common_model: *AppCommonModel, app_common_model: *AppCommonModel,
root_provider: *RootProvider, root_provider: *RootProvider,
@@ -19,7 +20,7 @@ const Model = struct {
fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
const vm: *Model = @ptrCast(@alignCast(ptr)); const vm: *Model = @ptrCast(@alignCast(ptr));
switch (event) { switch (event) {
.init => return ctx.requestFocus(vm.current_items_view_widget), .init => return ctx.requestFocus(vm.current_items_view.widget()),
.key_press => |key| { .key_press => |key| {
if (key.matches('c', .{ .ctrl = true })) { if (key.matches('c', .{ .ctrl = true })) {
ctx.quit = true; ctx.quit = true;
@@ -28,10 +29,10 @@ const Model = struct {
if (key.matches('r', .{})) { 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());
} }
}, },
.focus_in => return ctx.requestFocus(vm.current_items_view_widget), .focus_in => return ctx.requestFocus(vm.current_items_view.widget()),
else => {}, else => {},
} }
} }
@@ -61,6 +62,8 @@ const Model = struct {
const max_size = ctx.max.size(); const max_size = ctx.max.size();
try createCurrentItems(ctx, vm); try createCurrentItems(ctx, vm);
try createParentItems(ctx, vm);
try createChildrenItems(ctx, vm);
{ {
if (vm.current_items_view.children.slice.len == 0) { if (vm.current_items_view.children.slice.len == 0) {
@@ -85,18 +88,46 @@ const Model = struct {
const parentItemsWidth = max_size.width / 9; const parentItemsWidth = max_size.width / 9;
const childrenWidth = max_size.width * 4 / 9; const childrenWidth = max_size.width * 4 / 9;
const currentItemsWidth = max_size.width - parentItemsWidth - childrenWidth; const currentItemsWidth = max_size.width - parentItemsWidth - childrenWidth;
const currentItemsTop = 1; const currentItemsTop = 1;
const list_surface: vxfw.SubSurface = .{
.origin = .{ .row = currentItemsTop, .col = parentItemsWidth }, {
.surface = try vm.current_items_view_widget const parent_items_list_surface: vxfw.SubSurface = .{
.draw(ctx.withConstraints(ctx.min, .{ .origin = .{ .row = currentItemsTop, .col = 0 },
.width = currentItemsWidth, .surface = try vm.parent_items_view
.height = ctx.max.height.? - currentItemsTop, .widget()
})), .draw(ctx.withConstraints(ctx.min, .{
// .surface = try vm.current_items.widget().draw(ctx), .width = parentItemsWidth,
}; .height = ctx.max.height.? - currentItemsTop,
try rootWidgets.append(list_surface); })),
};
try rootWidgets.append(parent_items_list_surface);
}
{
const current_items_list_surface: vxfw.SubSurface = .{
.origin = .{ .row = currentItemsTop, .col = parentItemsWidth + 1 },
.surface = try vm.current_items_view
.widget()
.draw(ctx.withConstraints(ctx.min, .{
.width = currentItemsWidth - 1,
.height = ctx.max.height.? - currentItemsTop,
})),
};
try rootWidgets.append(current_items_list_surface);
}
{
const current_children_list_surface: vxfw.SubSurface = .{
.origin = .{ .row = currentItemsTop, .col = parentItemsWidth + currentItemsWidth + 1 },
.surface = try vm.current_children_view
.widget()
.draw(ctx.withConstraints(ctx.min, .{
.width = childrenWidth - 1,
.height = ctx.max.height.? - currentItemsTop,
})),
};
try rootWidgets.append(current_children_list_surface);
}
const header = try ctx.arena.create(std.ArrayList(vxfw.Widget)); const header = try ctx.arena.create(std.ArrayList(vxfw.Widget));
header.* = std.ArrayList(vxfw.Widget).init(ctx.arena); header.* = std.ArrayList(vxfw.Widget).init(ctx.arena);
@@ -182,20 +213,49 @@ const Model = struct {
} }
fn createCurrentItems(ctx: vxfw.DrawContext, vm: *Model) !void { fn createCurrentItems(ctx: vxfw.DrawContext, vm: *Model) !void {
if (vm.crash) @panic("asd123"); const currentTab = vm.app_common_model.appState.currentTab;
const current_full_name = if (currentTab.currentItem) |currentItem| blk: {
const arc_copy = currentItem.retain();
defer if (arc_copy.releaseUnwrap()) |item| item.deinit();
break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) };
} else null;
const widgets = try createItems(ctx, &currentTab.currentItems, current_full_name);
if (widgets) |w| {
vm.current_items_view.children.slice = w;
} else {
vm.current_items_view.children.slice = &.{};
}
}
fn createParentItems(ctx: vxfw.DrawContext, vm: *Model) !void {
const currentTab = vm.app_common_model.appState.currentTab;
//TODO: current fullname
const widgets = try createItems(ctx, &currentTab.currentParentItems, null);
if (widgets) |w| {
vm.parent_items_view.children.slice = w;
}
}
fn createChildrenItems(ctx: vxfw.DrawContext, vm: *Model) !void {
const currentTab = vm.app_common_model.appState.currentTab;
//TODO: current fullname
const widgets = try createItems(ctx, &currentTab.currentChildren, null);
if (widgets) |w| {
vm.current_children_view.children.slice = w;
}
}
fn createItems(
ctx: vxfw.DrawContext,
items1: *locked(?std.ArrayList(Arc(*models.Item))),
current_full_name: ?models.FullName,
) !?[]vxfw.Widget {
const text_items = blk2: { const text_items = blk2: {
const currentTab = vm.app_common_model.appState.currentTab; items1.mutex.lock();
const current_full_name = if (currentTab.currentItem) |currentItem| blk: { defer items1.mutex.unlock();
const arc_copy = currentItem.retain();
defer if (arc_copy.releaseUnwrap()) |item| item.deinit();
break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) };
} else null;
currentTab.currentItems.mutex.lock(); break :blk2 if (items1.data) |items| blk: {
defer currentTab.currentItems.mutex.unlock();
break :blk2 if (currentTab.currentItems.data) |items| blk: {
const children = try ctx.arena.create(std.ArrayList(*vxfw.Text)); const children = try ctx.arena.create(std.ArrayList(*vxfw.Text));
children.* = std.ArrayList(*vxfw.Text).init(ctx.arena); children.* = std.ArrayList(*vxfw.Text).init(ctx.arena);
@@ -231,7 +291,8 @@ const Model = struct {
break :colors .{ fg, bg }; break :colors .{ fg, bg };
}; };
const text = try ctx.arena.dupe(u8, child.displayName); //NOTE: the right padding is wrong, if the text is too long, the remainder of the text and also the space will be clipped
const text = try std.fmt.allocPrint(ctx.arena, " {s} ", .{child.displayName});
const text_element = try ctx.arena.create(vxfw.Text); const text_element = try ctx.arena.create(vxfw.Text);
text_element.* = vxfw.Text{ text_element.* = vxfw.Text{
.text = text, .text = text,
@@ -243,13 +304,11 @@ const Model = struct {
}, },
}; };
// children[i] = text_element;
try children.append(text_element); try children.append(text_element);
} }
break :blk children; break :blk children;
} else { } else {
vm.current_items_view.children.slice = &.{}; return null;
return;
}; };
}; };
@@ -258,7 +317,7 @@ const Model = struct {
widgets[i] = t.widget(); widgets[i] = t.widget();
} }
vm.current_items_view.children.slice = widgets; return widgets;
} }
}; };
@@ -313,14 +372,20 @@ pub fn main() !void {
defer allocator.free(items); defer allocator.free(items);
items[0] = empty_text_element.widget(); items[0] = empty_text_element.widget();
var list: vxfw.ListView = .{ var current_items_list: vxfw.ListView = .{
.draw_cursor = false,
.children = .{ .slice = items },
};
var parent_items_list: vxfw.ListView = .{
.draw_cursor = false,
.children = .{ .slice = items },
};
var current_children_view: vxfw.ListView = .{
.draw_cursor = false, .draw_cursor = false,
.children = .{ .slice = items }, .children = .{ .slice = items },
}; };
var list_widget = list.widget();
list_widget.eventHandler = struct {
fn a(_: *anyopaque, _: *vxfw.EventContext, _: vxfw.Event) anyerror!void {}
}.a;
var appState: AppState = AppState.init(allocator); var appState: AppState = AppState.init(allocator);
try appState.addTab(tab1); try appState.addTab(tab1);
@@ -333,11 +398,13 @@ pub fn main() !void {
try AppCommonModel.init(&app_common_model, allocator, appState); try AppCommonModel.init(&app_common_model, allocator, appState);
defer app_common_model.deinit(); defer app_common_model.deinit();
model.* = .{ model.* = .{
.allocator = allocator, .allocator = allocator,
.app_common_model = &app_common_model, .app_common_model = &app_common_model,
.current_items_view = &list, .current_items_view = &current_items_list,
.current_items_view_widget = list_widget, .parent_items_view = &parent_items_list,
.current_children_view = &current_children_view,
.root_provider = &rootProvider, .root_provider = &rootProvider,
}; };
@@ -346,6 +413,16 @@ pub fn main() !void {
var app = try vxfw.App.init(allocator); var app = try vxfw.App.init(allocator);
defer app.deinit(); defer app.deinit();
const asd = Observer(*Tab){
.ctx = @ptrCast(@alignCast(&app)),
.update = (struct{fn update(ctx: *anyopaque, _: *Tab)void {
const app1: *vxfw.App = @ptrCast(@alignCast(ctx));
_ = app1;
}}).update,
};
try app_common_model.appState.tabChildrenLoaded.attach(&asd);
try app.run(model.widget(), .{}); try app.run(model.widget(), .{});
model.app_common_model.usage_number.data -= 1; model.app_common_model.usage_number.data -= 1;
@@ -370,3 +447,5 @@ const locked = @import("../core/sync.zig").locked;
const AppCommonModel = @import("../app_common/Model.zig"); const AppCommonModel = @import("../app_common/Model.zig");
const AppState = @import("../core/app_state.zig").AppState; const AppState = @import("../core/app_state.zig").AppState;
const handle_key = @import("./action_map.zig").handle_key; const handle_key = @import("./action_map.zig").handle_key;
const Arc = @import("zigrc").Arc;
const Observer = @import("../core/observable.zig").Observer;

View File

@@ -1,25 +1,30 @@
pub const UnknownTabError = error.UnknownTab; pub const UnknownTabError = error.UnknownTab;
pub const AppState = struct { pub const AppState = struct {
allocator: std.mem.Allocator,
currentTab: *Tab = undefined, currentTab: *Tab = undefined,
tabs: std.ArrayList(*Tab), tabs: std.ArrayList(*Tab),
currentTabChanged: Observable(*Tab), currentTabChanged: Observable(*Tab),
tabPreCurrentItemsUnload: Observable(*Tab), tabChildrenLoaded: Observable(*Tab),
pub fn init(allocator: std.mem.Allocator) AppState { pub fn init(allocator: std.mem.Allocator) AppState {
return .{ return .{
.allocator = allocator,
.tabs = std.ArrayList(*Tab).init(allocator), .tabs = std.ArrayList(*Tab).init(allocator),
.currentTabChanged = Observable(*Tab).init(allocator), .currentTabChanged = Observable(*Tab).init(allocator),
.tabPreCurrentItemsUnload = Observable(*Tab).init(allocator), .tabChildrenLoaded = Observable(*Tab).init(allocator),
}; };
} }
pub fn addTab(self: *AppState, tab: *Tab) !void { pub fn addTab(self: *AppState, tab: *Tab) !void {
try self.tabs.append(tab); try self.tabs.append(tab);
// tab.preCurrentItemsUnload.attach(.{ const tabChildrenLoadedHandlerObserver = try tab.childrenLoaded.allocator.create(Observer(*Tab));
// .ctx = @ptrCast(@alignCast(self)), tabChildrenLoadedHandlerObserver.* = Observer(*Tab){
// .update = preCurrentItemsUnload, .ctx = @ptrCast(@alignCast(self)),
// }); .update = tabChildrenLoadedHandler,
};
try tab.childrenLoaded.attach(tabChildrenLoadedHandlerObserver);
} }
pub fn setCurrentTab(self: *AppState, newTab: *Tab) !void { pub fn setCurrentTab(self: *AppState, newTab: *Tab) !void {
@@ -34,8 +39,20 @@ pub const AppState = struct {
self.currentTabChanged.notify(newTab); self.currentTabChanged.notify(newTab);
} }
pub fn removeTab(self: *AppState, tab: *Tab) void {
const index = for (tab.childrenLoaded.observers, 0..) |observer, i| {
if (observer.ctx == self) break i;
} else null;
if (index) |i| {
tab.childrenLoaded.observers.swapRemove(i);
}
//TODO: remove from tabs
}
pub fn deinit(self: *AppState) void { pub fn deinit(self: *AppState) void {
self.tabPreCurrentItemsUnload.deinit(); self.tabChildrenLoaded.deinit();
self.currentTabChanged.deinit(); self.currentTabChanged.deinit();
for (self.tabs.items) |tab| { for (self.tabs.items) |tab| {
tab.deinit(); tab.deinit();
@@ -43,12 +60,13 @@ pub const AppState = struct {
self.tabs.deinit(); self.tabs.deinit();
} }
fn preCurrentItemsUnload(ctx: *anyopaque, tab: *Tab) void { fn tabChildrenLoadedHandler(ctx: *anyopaque, tab: *Tab) void {
const self: AppState = @ptrCast(@alignCast(ctx)); const self: *AppState = @ptrCast(@alignCast(ctx));
self.tabPreCurrentItemsUnload.notify(tab); self.tabChildrenLoaded.notify(tab);
} }
}; };
const std = @import("std"); const std = @import("std");
const Tab = @import("tab/tab.zig").Tab; const Tab = @import("tab/tab.zig").Tab;
const Observable = @import("observable.zig").Observable; const Observable = @import("observable.zig").Observable;
const Observer = @import("observable.zig").Observer;

View File

@@ -1,15 +1,20 @@
pub fn Observable(T: type) type { pub fn Observable(T: type) type {
return struct { return struct {
allocator: std.mem.Allocator,
observers: std.ArrayList(*const Observer(T)), observers: std.ArrayList(*const Observer(T)),
const Self = @This(); const Self = @This();
pub fn init(allocator: std.mem.Allocator) Self { pub fn init(allocator: std.mem.Allocator) Self {
return .{ return .{
.allocator = allocator,
.observers = std.ArrayList(*const Observer(T)).init(allocator), .observers = std.ArrayList(*const Observer(T)).init(allocator),
}; };
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
for(self.observers)|o| {
self.allocator.destroy(o);
}
self.observers.deinit(); self.observers.deinit();
} }

View File

@@ -3,6 +3,8 @@ pub const Tab = struct {
currentLocation: ?*Container, currentLocation: ?*Container,
currentItems: locked(?std.ArrayList(Arc(*Item))), currentItems: locked(?std.ArrayList(Arc(*Item))),
currentParentItems: locked(?std.ArrayList(Arc(*Item))), currentParentItems: locked(?std.ArrayList(Arc(*Item))),
currentChildren: locked(?std.ArrayList(Arc(*Item))),
childrenLoaded: Observable(*Tab),
currentLocationChanged: Observable(?*Container), currentLocationChanged: Observable(?*Container),
currentItemFullName: ?models.FullName, currentItemFullName: ?models.FullName,
currentItem: ?Arc(*models.Item), currentItem: ?Arc(*models.Item),
@@ -21,7 +23,9 @@ pub const Tab = struct {
.allocator = allocator, .allocator = allocator,
.currentItems = .{ .data = null }, .currentItems = .{ .data = null },
.currentParentItems = .{ .data = null }, .currentParentItems = .{ .data = null },
.currentChildren = .{ .data = null },
.currentLocationChanged = Observable(?*Container).init(allocator), .currentLocationChanged = Observable(?*Container).init(allocator),
.childrenLoaded = Observable(*Tab).init(allocator),
.currentLocation = null, .currentLocation = null,
.currentRequested = null, .currentRequested = null,
.currentItem = null, .currentItem = null,
@@ -169,6 +173,13 @@ pub const Tab = struct {
self.currentItemFullName = models.FullName{ self.currentItemFullName = models.FullName{
.path = try self.allocator.dupe(u8, arc.value.*.fullName.path), .path = try self.allocator.dupe(u8, arc.value.*.fullName.path),
}; };
switch (arc.value.*.item) {
.container => {
std.Thread.Pool.spawn(self.threadPool, loadChildrenWrapper, .{ self, arc.retain() }) catch unreachable;
},
else => {},
}
} }
fn loadItemsWrapper(self: *Tab, location: *Container) void { fn loadItemsWrapper(self: *Tab, location: *Container) void {
@@ -180,6 +191,7 @@ pub const Tab = struct {
fn loadParentItemsWrapper(self: *Tab, location: *Container) void { fn loadParentItemsWrapper(self: *Tab, location: *Container) void {
const parentAbsolutePath = location.item.parent orelse return; const parentAbsolutePath = location.item.parent orelse return;
var arena = std.heap.ArenaAllocator.init(self.allocator); var arena = std.heap.ArenaAllocator.init(self.allocator);
defer arena.deinit(); defer arena.deinit();
const allocator = arena.allocator(); const allocator = arena.allocator();
@@ -188,7 +200,7 @@ pub const Tab = struct {
.path = allocator.dupe(u8, parentAbsolutePath.path.path) catch return, .path = allocator.dupe(u8, parentAbsolutePath.path.path) catch return,
}; };
const resolvedParent = location.item.provider.getItemByFullName(parentFullName, &.{ .skipChildInit = true }, allocator) catch |e| { const resolvedParent = location.item.provider.getItemByFullName(parentFullName, &.{}, allocator) catch |e| {
std.debug.print("error {} {s}\r\n", .{ e, parentFullName.path }); std.debug.print("error {} {s}\r\n", .{ e, parentFullName.path });
return; return;
}; };
@@ -196,9 +208,47 @@ pub const Tab = struct {
.container => |c| c, .container => |c| c,
else => return, else => return,
}; };
loadItems(*Tab, self, parentContainer, &self.currentParentItems, (struct { loadItems(
fn a(_: *Tab) void {} void,
}).a, self.allocator) catch {}; {},
parentContainer,
&self.currentParentItems,
(struct {
fn a(_: void) void {}
}).a,
self.allocator,
) catch {};
}
fn loadChildrenWrapper(self: *Tab, newSelectedItemArc: Arc(*models.Item)) void {
defer if (newSelectedItemArc.releaseUnwrap()) |item| item.deinit();
var arena = std.heap.ArenaAllocator.init(self.allocator);
defer arena.deinit();
const allocator = arena.allocator();
const resolvedChild = newSelectedItemArc.value.*.provider.getItemByFullName(newSelectedItemArc.value.*.fullName, &.{}, allocator) catch |e| {
std.debug.print("error {} {s}\r\n", .{ e, newSelectedItemArc.value.*.fullName.path });
return;
};
const childContainer = switch (resolvedChild.item) {
.container => |c| c,
else => return,
};
loadItems(
void,
{},
childContainer,
&self.currentChildren,
(struct {
fn a(_: void) void {}
}).a,
self.allocator,
) catch {};
self.childrenLoaded.notify(self);
} }
pub fn deinit(self: *Tab) void { pub fn deinit(self: *Tab) void {
@@ -217,10 +267,35 @@ pub const Tab = struct {
currentItems.deinit(); currentItems.deinit();
} }
const data2 = self.currentParentItems.data;
self.currentParentItems.data = null;
if (data2) |currentItems| {
for (currentItems.items) |arc_item| {
if (arc_item.releaseUnwrap()) |item| {
item.deinit();
}
}
currentItems.deinit();
}
const data3 = self.currentChildren.data;
self.currentChildren.data = null;
if (data3) |currentItems| {
for (currentItems.items) |arc_item| {
if (arc_item.releaseUnwrap()) |item| {
item.deinit();
}
}
currentItems.deinit();
}
self.cleanCurrentItem(); self.cleanCurrentItem();
self.cleanCurrentRequested(); self.cleanCurrentRequested();
self.currentLocationChanged.deinit();
self.childrenLoaded.deinit();
self.allocator.destroy(self); self.allocator.destroy(self);
} }