feat: ui improvements

This commit is contained in:
2025-06-06 16:08:51 +02:00
parent 1fc509f897
commit 3e3f03f11a
4 changed files with 200 additions and 65 deletions

View File

@@ -37,8 +37,8 @@
// internet connectivity. // internet connectivity.
.dependencies = .{ .dependencies = .{
.vaxis = .{ .vaxis = .{
.url = "git+https://github.com/rockorager/libvaxis.git#ae71b6545c099e73d45df7e5dd7c7a3081839468", .url = "git+https://github.com/ADIX7/libvaxis.git#cffbf11410f81ed1a906f08afd3748bcd4e73506",
.hash = "vaxis-0.1.0-BWNV_JEMCQBskZQsnlzh6GoyHSDgOi41bCoZIB2pW-E7", .hash = "vaxis-0.1.0-BWNV_PURCQAg2xZD0vlf6vOBOhDQ3hY53MP9tn0EybNA",
}, },
.dvui = .{ .dvui = .{
.url = "git+https://github.com/david-vanderson/dvui.git#5703f8f02fb8e1bf2c866517f208745e4d7af869", .url = "git+https://github.com/david-vanderson/dvui.git#5703f8f02fb8e1bf2c866517f208745e4d7af869",

View File

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

View File

@@ -66,6 +66,14 @@ const Model = struct {
} }
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { 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)); const vm: *Model = @ptrCast(@alignCast(ptr));
vm.redraw = false; vm.redraw = false;
@@ -234,45 +242,58 @@ const Model = struct {
break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) }; break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) };
} else null; } else null;
const widgets = try createItems(ctx, &currentTab.currentItems, current_full_name); updateList(
if (widgets) |w| { vm.current_items_view,
vm.current_items_view.children.slice = w; try createItems(ctx, &currentTab.currentItems, current_full_name),
} else { );
vm.current_items_view.children.slice = &.{};
}
} }
fn createParentItems(ctx: vxfw.DrawContext, vm: *Model) !void { fn createParentItems(ctx: vxfw.DrawContext, vm: *Model) !void {
const currentTab = vm.app_common_model.appState.currentTab; const currentTab = vm.app_common_model.appState.currentTab;
//TODO: current fullname //TODO: current fullname
const widgets = try createItems(ctx, &currentTab.currentParentItems, null); updateList(
if (widgets) |w| { vm.parent_items_view,
vm.parent_items_view.children.slice = w; try createItems(ctx, &currentTab.currentParentItems, null),
} );
} }
fn createChildrenItems(ctx: vxfw.DrawContext, vm: *Model) !void { fn createChildrenItems(ctx: vxfw.DrawContext, vm: *Model) !void {
const currentTab = vm.app_common_model.appState.currentTab; const currentTab = vm.app_common_model.appState.currentTab;
//TODO: current fullname //TODO: current fullname
const widgets = try createItems(ctx, &currentTab.currentChildren, null); updateList(
if (widgets) |w| { vm.current_children_view,
vm.current_children_view.children.slice = w; try createItems(ctx, &currentTab.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( fn createItems(
ctx: vxfw.DrawContext, ctx: vxfw.DrawContext,
locked_items: *locked(?std.ArrayList(Arc(*models.Item))), locked_items: *locked(?std.ArrayList(Arc(*models.Item))),
current_full_name: ?models.FullName, current_full_name: ?models.FullName,
) !?[]vxfw.Widget { ) !?struct { []vxfw.Widget, u32 } {
const text_items = blk: {
locked_items.mutex.lock(); locked_items.mutex.lock();
defer locked_items.mutex.unlock(); defer locked_items.mutex.unlock();
const items = locked_items.data orelse return null; const items = locked_items.data orelse return null;
const children = try ctx.arena.create(std.ArrayList(*vxfw.Text)); const children = try ctx.arena.create(std.ArrayList(vxfw.Widget));
children.* = std.ArrayList(*vxfw.Text).init(ctx.arena); children.* = .init(ctx.arena);
for (items.items) |*original_arc_child| { var active_item_index: u32 = 0;
for (items.items, 0..) |*original_arc_child, item_index| {
const arc_child = original_arc_child.retain(); const arc_child = original_arc_child.retain();
defer if (arc_child.releaseUnwrap()) |item| item.deinit(); defer if (arc_child.releaseUnwrap()) |item| item.deinit();
@@ -282,6 +303,10 @@ const Model = struct {
else else
false; false;
if (is_active) {
active_item_index = @intCast(item_index);
}
const fg, const bg = colors: { const fg, const bg = colors: {
var fg: vaxis.Color = .default; var fg: vaxis.Color = .default;
var bg: vaxis.Color = .default; var bg: vaxis.Color = .default;
@@ -304,30 +329,89 @@ const Model = struct {
break :colors .{ fg, bg }; break :colors .{ fg, bg };
}; };
//NOTE: the right padding is wrong, if the text is too long, the remainder of the text and also the space will be clipped var item_children_list = std.ArrayList(vxfw.FlexItem).init(ctx.arena);
const text = try std.fmt.allocPrint(ctx.arena, " {s} ", .{child.displayName}); {
const text_element = try ctx.arena.create(vxfw.Text); const display_name_text = try ctx.arena.dupe(u8, child.displayName);
text_element.* = vxfw.Text{ const display_name_text_element = try ctx.arena.create(vxfw.Text);
.text = text, display_name_text_element.* = vxfw.Text{
.overflow = .clip, .text = display_name_text,
// .padding = .{ .left = 1, .right = 1 },
.softwrap = false, .softwrap = false,
.overflow = .ellipsis,
.style = .{ .style = .{
.bg = bg, .bg = bg,
.fg = fg, .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;
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 widgets = try ctx.arena.alloc(vxfw.Widget, text_items.items.len); const padding = try ctx.arena.create(vxfw.Padding);
for (text_items.items, 0..) |t, i| { padding.* = .{
widgets[i] = t.widget(); .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,
});
} }
return widgets; //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 .{ 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 handle_key = @import("./action_map.zig").handle_key;
const Arc = @import("zigrc").Arc; const Arc = @import("zigrc").Arc;
const Observer = @import("../core/observable.zig").Observer; const Observer = @import("../core/observable.zig").Observer;
const draw_context = @import("draw_context.zig");

View File

@@ -360,7 +360,7 @@ fn loadItems(
defer processed_number = location.children.data.items.len; defer processed_number = location.children.data.items.len;
for (location.children.data.items[processed_number..]) |item_fullname| { 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 //TODO: save error to container errors
std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path }); std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path });
continue; continue;