const Model = struct { crash: bool = false, allocator: std.mem.Allocator, current_items_view: *vxfw.ListView, current_items_view_widget: vxfw.Widget, app_common_model: *AppCommonModel, root_provider: *RootProvider, /// Helper function to return a vxfw.Widget struct pub fn widget(self: *Model) vxfw.Widget { return .{ .userdata = self, .eventHandler = Model.typeErasedEventHandler, .captureHandler = Model.typeErasedCaptureHandler, .drawFn = Model.typeErasedDrawFn, }; } 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), .key_press => |key| { if (key.matches('c', .{ .ctrl = true })) { ctx.quit = true; return; } if (key.matches('r', .{})) { // vm.crash = true; ctx.redraw = true; return ctx.requestFocus(vm.current_items_view_widget); } }, .focus_in => return ctx.requestFocus(vm.current_items_view_widget), else => {}, } } fn typeErasedCaptureHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { const vm: *Model = @ptrCast(@alignCast(ptr)); switch (event) { .key_press => |key| { var arena = std.heap.ArenaAllocator.init(vm.allocator); defer arena.deinit(); const result = handle_key(key, &vm.app_common_model.appState, arena.allocator()) catch null; if (result) |r| { if (r == ActionResult.Handled) { ctx.consumeAndRedraw(); } } }, else => {}, } } fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { const vm: *Model = @ptrCast(@alignCast(ptr)); const rootWidgets = try ctx.arena.create(std.ArrayList(vxfw.SubSurface)); rootWidgets.* = std.ArrayList(vxfw.SubSurface).init(ctx.arena); const max_size = ctx.max.size(); try createCurrentItems(ctx, vm); { if (vm.current_items_view.children.slice.len == 0) { const empty_text_element = try ctx.arena.create(vxfw.Text); empty_text_element.* = vxfw.Text{ .text = "Empty", .overflow = .clip, .softwrap = false, // .style = .{ // .bg = bg, // .fg = fg, // }, }; const items = try ctx.arena.alloc(vxfw.Widget, 1); items[0] = empty_text_element.widget(); vm.current_items_view.children.slice = items; } } 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 .draw(ctx.withConstraints(ctx.min, .{ .width = currentItemsWidth, .height = ctx.max.height.? - currentItemsTop, })), // .surface = try vm.current_items.widget().draw(ctx), }; try rootWidgets.append(list_surface); const header = try ctx.arena.create(std.ArrayList(vxfw.Widget)); header.* = std.ArrayList(vxfw.Widget).init(ctx.arena); { const current_location_text = if (vm.app_common_model.appState.currentTab.currentLocation) |loc| try std.fmt.allocPrint(ctx.arena, "{s} ", .{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, .overflow = .clip, .softwrap = false, .style = .{ .fg = .{ .index = 4 }, }, }; try header.append(current_location_text_element.widget()); } { const currentTab = vm.app_common_model.appState.currentTab; if (currentTab.currentItem) |currentItem| { currentTab.currentItems.mutex.lock(); defer currentTab.currentItems.mutex.unlock(); if (currentTab.currentItems.data) |currentItems| { const current_item_text = for (currentItems.items) |item| { const arc_item = item.retain(); defer if (arc_item.releaseUnwrap()) |i| i.deinit(); if (models.FullName.eql(&arc_item.value.*.fullName, ¤tItem)) { break try std.fmt.allocPrint(ctx.arena, "{s} ", .{arc_item.value.*.displayName}); } } else ""; const current_item_text_element = try ctx.arena.create(vxfw.Text); current_item_text_element.* = vxfw.Text{ .text = current_item_text, .overflow = .clip, .softwrap = false, .style = .{ .fg = .{ .index = 4 }, }, }; try header.append(current_item_text_element.widget()); } } } { const head_items = try ctx.arena.alloc(vxfw.FlexItem, header.items.len); for (0.., header.items) |i, item| { head_items[i] = vxfw.FlexItem{ .widget = item, .flex = 0, }; } const header_element = try ctx.arena.create(vxfw.FlexRow); header_element.* = .{ .children = head_items }; const flex_column_surface: vxfw.SubSurface = .{ .origin = .{ .row = 0, .col = 0 }, .surface = try header_element.widget().draw(ctx), }; try rootWidgets.append(flex_column_surface); } return .{ .size = max_size, .widget = vm.widget(), .buffer = &.{}, .children = rootWidgets.items, }; } 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| models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.path) } else null; currentTab.currentItems.mutex.lock(); defer currentTab.currentItems.mutex.unlock(); break :blk2 if (currentTab.currentItems.data) |items| blk: { const children = try ctx.arena.create(std.ArrayList(*vxfw.Text)); children.* = std.ArrayList(*vxfw.Text).init(ctx.arena); for (items.items) |*original_arc_child| { const arc_child = original_arc_child.retain(); defer if (arc_child.releaseUnwrap()) |item| item.deinit(); const child = arc_child.value.*; const is_active = if (current_full_name) |c_full_name| models.FullName.eql(&c_full_name, &child.fullName) else false; const fg, const bg = colors: { var fg: vaxis.Color = .default; var bg: vaxis.Color = .default; if (is_active) { fg = switch (child.item) { .container => .{ .index = 0 }, .element => .{ .index = 0 }, }; bg = switch (child.item) { .container => .{ .index = 4 }, .element => .{ .index = 7 }, }; } else { fg = switch (child.item) { .container => .{ .index = 4 }, .element => .default, }; bg = .default; } break :colors .{ fg, bg }; }; const text = try ctx.arena.dupe(u8, child.displayName); const text_element = try ctx.arena.create(vxfw.Text); text_element.* = vxfw.Text{ .text = text, .overflow = .clip, .softwrap = false, .style = .{ .bg = bg, .fg = fg, }, }; // children[i] = text_element; try children.append(text_element); } break :blk children; } else { vm.current_items_view.children.slice = &.{}; return; }; }; const widgets = try ctx.arena.alloc(vxfw.Widget, text_items.items.len); for (text_items.items, 0..) |t, i| { widgets[i] = t.widget(); } vm.current_items_view.children.slice = widgets; } }; pub fn main() !void { const DebugAllocator = std.heap.DebugAllocator(.{}); var gpa = DebugAllocator{}; const gp_allocator = gpa.allocator(); defer { _ = gpa.detectLeaks(); _ = gpa.deinit(); } var tsa = std.heap.ThreadSafeAllocator{ .child_allocator = gp_allocator }; const allocator = tsa.allocator(); var pool: std.Thread.Pool = undefined; try pool.init(.{ .allocator = allocator, }); defer pool.deinit(); var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool }; var localContentProv = localContentProvider.provider(); 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); tab1.init(&pool, &rootProvider, allocator); try tab1.setCurrentLocation(start_full_name); // TODO: remove std.Thread.sleep(1 * std.time.ns_per_s); const empty_text_element = try allocator.create(vxfw.Text); defer allocator.destroy(empty_text_element); empty_text_element.* = vxfw.Text{ .text = "Empty", }; const items = try allocator.alloc(vxfw.Widget, 1); defer allocator.free(items); items[0] = empty_text_element.widget(); var list: 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); try appState.setCurrentTab(tab1); const model = try allocator.create(Model); defer allocator.destroy(model); var app_common_model: AppCommonModel = undefined; 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, .root_provider = &rootProvider, }; model.app_common_model.usage_number.data += 1; var app = try vxfw.App.init(allocator); defer app.deinit(); try app.run(model.widget(), .{}); model.app_common_model.usage_number.data -= 1; model.app_common_model.running = false; while (model.app_common_model.usage_number.data > 0) { std.Thread.sleep(10 * std.time.ns_per_ms); } } const std = @import("std"); const vaxis = @import("vaxis"); const vxfw = vaxis.vxfw; const models = @import("../core/models.zig"); const ActionResult = @import("../core/action/action.zig").ActionResult; 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 AppCommonModel = @import("../app_common/Model.zig"); const AppState = @import("../core/app_state.zig").AppState; const handle_key = @import("./action_map.zig").handle_key;