diff --git a/build.zig.zon b/build.zig.zon index e09c76f..5146b5a 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -37,8 +37,8 @@ // internet connectivity. .dependencies = .{ .vaxis = .{ - .url = "git+https://github.com/rockorager/libvaxis.git#ae71b6545c099e73d45df7e5dd7c7a3081839468", - .hash = "vaxis-0.1.0-BWNV_JEMCQBskZQsnlzh6GoyHSDgOi41bCoZIB2pW-E7", + .url = "git+https://github.com/ADIX7/libvaxis.git#cffbf11410f81ed1a906f08afd3748bcd4e73506", + .hash = "vaxis-0.1.0-BWNV_PURCQAg2xZD0vlf6vOBOhDQ3hY53MP9tn0EybNA", }, .dvui = .{ .url = "git+https://github.com/david-vanderson/dvui.git#5703f8f02fb8e1bf2c866517f208745e4d7af869", diff --git a/src/console/draw_context.zig b/src/console/draw_context.zig new file mode 100644 index 0000000..b74023c --- /dev/null +++ b/src/console/draw_context.zig @@ -0,0 +1,50 @@ +pub const ContextModifier = struct { + child: vxfw.Widget, + modifiers: union(enum) { + single: *const fn (ctx: vxfw.DrawContext) vxfw.DrawContext, + multi: []*const fn (ctx: vxfw.DrawContext) vxfw.DrawContext, + }, + fn drawFn(obj: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { + const self: *ContextModifier = @ptrCast(@alignCast(obj)); + const modified_ctx = switch (self.modifiers) { + .single => |modifier| modifier(ctx), + .multi => |modifiers| blk: { + var wip_ctx = ctx; + for (modifiers) |modifier| { + wip_ctx = modifier(wip_ctx); + } + break :blk wip_ctx; + }, + }; + return self.child.draw(modified_ctx); + } + pub fn widget(self: *ContextModifier) vxfw.Widget { + return vxfw.Widget{ + .userdata = @ptrCast(@alignCast(self)), + .drawFn = drawFn, + }; + } +}; + +pub fn fromWidget(widget: vxfw.Widget, modifier: *const fn (ctx: vxfw.DrawContext) vxfw.DrawContext, arena: std.mem.Allocator) !vxfw.Widget { + var context_modifier = try arena.create(ContextModifier); + context_modifier.* = ContextModifier{ + .child = widget, + .modifiers = .{ .single = modifier }, + }; + return context_modifier.widget(); +} + +pub fn maxHeight1(ctx: vxfw.DrawContext) vxfw.DrawContext { + return ctx.withConstraints( + ctx.min, + .{ + .width = ctx.max.width, + .height = 1, + }, + ); +} + +const std = @import("std"); +const vaxis = @import("vaxis"); +const vxfw = vaxis.vxfw; diff --git a/src/console/main.zig b/src/console/main.zig index 3c2f19d..89e1e29 100644 --- a/src/console/main.zig +++ b/src/console/main.zig @@ -66,6 +66,14 @@ const Model = struct { } fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { + // These are for debugging instant crashes + // std.debug.print(vaxis.ctlseqs.rmcup, .{}); + // std.debug.print(vaxis.ctlseqs.show_cursor, .{}); + // std.debug.print(vaxis.ctlseqs.sgr_reset, .{}); + // std.debug.print(vaxis.ctlseqs.erase_below_cursor, .{}); + // std.debug.print(vaxis.ctlseqs.in_band_resize_reset, .{}); + // std.debug.print(vaxis.ctlseqs.mouse_reset, .{}); + const vm: *Model = @ptrCast(@alignCast(ptr)); vm.redraw = false; @@ -234,100 +242,176 @@ const Model = struct { break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) }; } else null; - const widgets = try createItems(ctx, ¤tTab.currentItems, current_full_name); - if (widgets) |w| { - vm.current_items_view.children.slice = w; - } else { - vm.current_items_view.children.slice = &.{}; - } + updateList( + vm.current_items_view, + try createItems(ctx, ¤tTab.currentItems, current_full_name), + ); } fn createParentItems(ctx: vxfw.DrawContext, vm: *Model) !void { const currentTab = vm.app_common_model.appState.currentTab; //TODO: current fullname - const widgets = try createItems(ctx, ¤tTab.currentParentItems, null); - if (widgets) |w| { - vm.parent_items_view.children.slice = w; - } + updateList( + vm.parent_items_view, + try createItems(ctx, ¤tTab.currentParentItems, null), + ); } fn createChildrenItems(ctx: vxfw.DrawContext, vm: *Model) !void { const currentTab = vm.app_common_model.appState.currentTab; //TODO: current fullname - const widgets = try createItems(ctx, ¤tTab.currentChildren, null); - if (widgets) |w| { - vm.current_children_view.children.slice = w; + updateList( + vm.current_children_view, + try createItems(ctx, ¤tTab.currentChildren, null), + ); + } + + fn updateList( + listView: *vxfw.ListView, + result: ?struct { []vxfw.Widget, u32 }, + ) void { + if (result) |r| { + const widgets, const index = r; + listView.children.slice = widgets; + listView.cursor = index; + listView.ensureScroll(); + } else { + listView.children.slice = &.{}; } } + fn createItems( ctx: vxfw.DrawContext, locked_items: *locked(?std.ArrayList(Arc(*models.Item))), current_full_name: ?models.FullName, - ) !?[]vxfw.Widget { - const text_items = blk: { - locked_items.mutex.lock(); - defer locked_items.mutex.unlock(); + ) !?struct { []vxfw.Widget, u32 } { + locked_items.mutex.lock(); + defer locked_items.mutex.unlock(); - const items = locked_items.data orelse return null; - const children = try ctx.arena.create(std.ArrayList(*vxfw.Text)); - children.* = std.ArrayList(*vxfw.Text).init(ctx.arena); + const items = locked_items.data orelse return null; + const children = try ctx.arena.create(std.ArrayList(vxfw.Widget)); + children.* = .init(ctx.arena); - for (items.items) |*original_arc_child| { - const arc_child = original_arc_child.retain(); - defer if (arc_child.releaseUnwrap()) |item| item.deinit(); + var active_item_index: u32 = 0; + for (items.items, 0..) |*original_arc_child, item_index| { + 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 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 }; - }; + if (is_active) { + active_item_index = @intCast(item_index); + } - //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, - .overflow = .clip, + 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 }; + }; + + var item_children_list = std.ArrayList(vxfw.FlexItem).init(ctx.arena); + { + const display_name_text = try ctx.arena.dupe(u8, child.displayName); + const display_name_text_element = try ctx.arena.create(vxfw.Text); + display_name_text_element.* = vxfw.Text{ + .text = display_name_text, + // .padding = .{ .left = 1, .right = 1 }, .softwrap = false, + .overflow = .ellipsis, .style = .{ .bg = bg, .fg = fg, }, }; - try children.append(text_element); + const padding = try ctx.arena.create(vxfw.Padding); + padding.* = .{ + .child = display_name_text_element.widget(), + .padding = vxfw.Padding.horizontal(1), + .fill = .{ + .style = .{ + .bg = bg, + .fg = fg, + }, + }, + }; + try item_children_list.append(.{ + .widget = padding.widget(), + .flex = 1, + }); } - break :blk children; - }; - const widgets = try ctx.arena.alloc(vxfw.Widget, text_items.items.len); - for (text_items.items, 0..) |t, i| { - widgets[i] = t.widget(); + if (child.item == .container) { + const container = child.item.container; + + container.children.mutex.lock(); + defer container.children.mutex.unlock(); + + const container_child_number = container.children.data.items.len; + + const container_child_number_text = try std.fmt.allocPrint(ctx.arena, "{}", .{container_child_number}); + const container_child_number_element = try ctx.arena.create(vxfw.Text); + container_child_number_element.* = vxfw.Text{ + .text = container_child_number_text, + .style = .{ + .bg = bg, + .fg = fg, + }, + }; + + const padding = try ctx.arena.create(vxfw.Padding); + padding.* = .{ + .child = container_child_number_element.widget(), + .padding = vxfw.Padding.horizontal(1), + .fill = .{ + .style = .{ + .bg = bg, + .fg = fg, + }, + }, + }; + try item_children_list.append(.{ + .widget = try draw_context.fromWidget(padding.widget(), draw_context.maxHeight1, ctx.arena), + .flex = 0, + }); + } + + //FlexItems + const item_children = try ctx.arena.alloc(vxfw.FlexItem, item_children_list.items.len); + for (item_children_list.items, 0..) |item_child, i| { + item_children[i] = item_child; + } + + // Item + const item_element = try ctx.arena.create(vxfw.FlexRow); + item_element.* = .{ + .children = item_children, + }; + + try children.append(try draw_context.fromWidget(item_element.widget(), draw_context.maxHeight1, ctx.arena)); } - return widgets; + return .{ children.items, active_item_index }; } }; @@ -463,3 +547,4 @@ 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; +const draw_context = @import("draw_context.zig"); diff --git a/src/core/tab/tab.zig b/src/core/tab/tab.zig index f01b17b..8626fb0 100644 --- a/src/core/tab/tab.zig +++ b/src/core/tab/tab.zig @@ -360,7 +360,7 @@ fn loadItems( defer processed_number = location.children.data.items.len; for (location.children.data.items[processed_number..]) |item_fullname| { - const resolvedItem = location.item.provider.getItemByFullName(item_fullname, &.{ .skipChildInit = true }, allocator) catch |e| { + const resolvedItem = location.item.provider.getItemByFullName(item_fullname, &.{}, allocator) catch |e| { //TODO: save error to container errors std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path }); continue;