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,
allocator: std.mem.Allocator,
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,
root_provider: *RootProvider,
@@ -19,7 +20,7 @@ const Model = struct {
fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
const vm: *Model = @ptrCast(@alignCast(ptr));
switch (event) {
.init => return ctx.requestFocus(vm.current_items_view_widget),
.init => return ctx.requestFocus(vm.current_items_view.widget()),
.key_press => |key| {
if (key.matches('c', .{ .ctrl = true })) {
ctx.quit = true;
@@ -28,10 +29,10 @@ const Model = struct {
if (key.matches('r', .{})) {
// vm.crash = 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 => {},
}
}
@@ -61,6 +62,8 @@ const Model = struct {
const max_size = ctx.max.size();
try createCurrentItems(ctx, vm);
try createParentItems(ctx, vm);
try createChildrenItems(ctx, vm);
{
if (vm.current_items_view.children.slice.len == 0) {
@@ -85,18 +88,46 @@ const Model = struct {
const parentItemsWidth = max_size.width / 9;
const childrenWidth = max_size.width * 4 / 9;
const currentItemsWidth = max_size.width - parentItemsWidth - childrenWidth;
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 = .{
.origin = .{ .row = currentItemsTop, .col = 0 },
.surface = try vm.parent_items_view
.widget()
.draw(ctx.withConstraints(ctx.min, .{
.width = currentItemsWidth,
.width = parentItemsWidth,
.height = ctx.max.height.? - currentItemsTop,
})),
// .surface = try vm.current_items.widget().draw(ctx),
};
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));
header.* = std.ArrayList(vxfw.Widget).init(ctx.arena);
@@ -182,20 +213,49 @@ const Model = struct {
}
fn createCurrentItems(ctx: vxfw.DrawContext, vm: *Model) !void {
if (vm.crash) @panic("asd123");
const text_items = blk2: {
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;
currentTab.currentItems.mutex.lock();
defer currentTab.currentItems.mutex.unlock();
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 = &.{};
}
}
break :blk2 if (currentTab.currentItems.data) |items| blk: {
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: {
items1.mutex.lock();
defer items1.mutex.unlock();
break :blk2 if (items1.data) |items| blk: {
const children = try ctx.arena.create(std.ArrayList(*vxfw.Text));
children.* = std.ArrayList(*vxfw.Text).init(ctx.arena);
@@ -231,7 +291,8 @@ const Model = struct {
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);
text_element.* = vxfw.Text{
.text = text,
@@ -243,13 +304,11 @@ const Model = struct {
},
};
// children[i] = text_element;
try children.append(text_element);
}
break :blk children;
} else {
vm.current_items_view.children.slice = &.{};
return;
return null;
};
};
@@ -258,7 +317,7 @@ const Model = struct {
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);
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,
.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);
try appState.addTab(tab1);
@@ -333,11 +398,13 @@ pub fn main() !void {
try AppCommonModel.init(&app_common_model, allocator, appState);
defer app_common_model.deinit();
model.* = .{
.allocator = allocator,
.app_common_model = &app_common_model,
.current_items_view = &list,
.current_items_view_widget = list_widget,
.current_items_view = &current_items_list,
.parent_items_view = &parent_items_list,
.current_children_view = &current_children_view,
.root_provider = &rootProvider,
};
@@ -346,6 +413,16 @@ pub fn main() !void {
var app = try vxfw.App.init(allocator);
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(), .{});
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 AppState = @import("../core/app_state.zig").AppState;
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 AppState = struct {
allocator: std.mem.Allocator,
currentTab: *Tab = undefined,
tabs: std.ArrayList(*Tab),
currentTabChanged: Observable(*Tab),
tabPreCurrentItemsUnload: Observable(*Tab),
tabChildrenLoaded: Observable(*Tab),
pub fn init(allocator: std.mem.Allocator) AppState {
return .{
.allocator = allocator,
.tabs = std.ArrayList(*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 {
try self.tabs.append(tab);
// tab.preCurrentItemsUnload.attach(.{
// .ctx = @ptrCast(@alignCast(self)),
// .update = preCurrentItemsUnload,
// });
const tabChildrenLoadedHandlerObserver = try tab.childrenLoaded.allocator.create(Observer(*Tab));
tabChildrenLoadedHandlerObserver.* = Observer(*Tab){
.ctx = @ptrCast(@alignCast(self)),
.update = tabChildrenLoadedHandler,
};
try tab.childrenLoaded.attach(tabChildrenLoadedHandlerObserver);
}
pub fn setCurrentTab(self: *AppState, newTab: *Tab) !void {
@@ -34,8 +39,20 @@ pub const AppState = struct {
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 {
self.tabPreCurrentItemsUnload.deinit();
self.tabChildrenLoaded.deinit();
self.currentTabChanged.deinit();
for (self.tabs.items) |tab| {
tab.deinit();
@@ -43,12 +60,13 @@ pub const AppState = struct {
self.tabs.deinit();
}
fn preCurrentItemsUnload(ctx: *anyopaque, tab: *Tab) void {
const self: AppState = @ptrCast(@alignCast(ctx));
self.tabPreCurrentItemsUnload.notify(tab);
fn tabChildrenLoadedHandler(ctx: *anyopaque, tab: *Tab) void {
const self: *AppState = @ptrCast(@alignCast(ctx));
self.tabChildrenLoaded.notify(tab);
}
};
const std = @import("std");
const Tab = @import("tab/tab.zig").Tab;
const Observable = @import("observable.zig").Observable;
const Observer = @import("observable.zig").Observer;

View File

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

View File

@@ -3,6 +3,8 @@ pub const Tab = struct {
currentLocation: ?*Container,
currentItems: locked(?std.ArrayList(Arc(*Item))),
currentParentItems: locked(?std.ArrayList(Arc(*Item))),
currentChildren: locked(?std.ArrayList(Arc(*Item))),
childrenLoaded: Observable(*Tab),
currentLocationChanged: Observable(?*Container),
currentItemFullName: ?models.FullName,
currentItem: ?Arc(*models.Item),
@@ -21,7 +23,9 @@ pub const Tab = struct {
.allocator = allocator,
.currentItems = .{ .data = null },
.currentParentItems = .{ .data = null },
.currentChildren = .{ .data = null },
.currentLocationChanged = Observable(?*Container).init(allocator),
.childrenLoaded = Observable(*Tab).init(allocator),
.currentLocation = null,
.currentRequested = null,
.currentItem = null,
@@ -169,6 +173,13 @@ pub const Tab = struct {
self.currentItemFullName = models.FullName{
.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 {
@@ -180,6 +191,7 @@ pub const Tab = struct {
fn loadParentItemsWrapper(self: *Tab, location: *Container) void {
const parentAbsolutePath = location.item.parent orelse return;
var arena = std.heap.ArenaAllocator.init(self.allocator);
defer arena.deinit();
const allocator = arena.allocator();
@@ -188,7 +200,7 @@ pub const Tab = struct {
.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 });
return;
};
@@ -196,9 +208,47 @@ pub const Tab = struct {
.container => |c| c,
else => return,
};
loadItems(*Tab, self, parentContainer, &self.currentParentItems, (struct {
fn a(_: *Tab) void {}
}).a, self.allocator) catch {};
loadItems(
void,
{},
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 {
@@ -217,10 +267,35 @@ pub const Tab = struct {
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.cleanCurrentRequested();
self.currentLocationChanged.deinit();
self.childrenLoaded.deinit();
self.allocator.destroy(self);
}