feat: ui improvements
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
50
src/console/draw_context.zig
Normal file
50
src/console/draw_context.zig
Normal 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;
|
||||||
@@ -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, ¤tTab.currentItems, current_full_name);
|
updateList(
|
||||||
if (widgets) |w| {
|
vm.current_items_view,
|
||||||
vm.current_items_view.children.slice = w;
|
try createItems(ctx, ¤tTab.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, ¤tTab.currentParentItems, null);
|
updateList(
|
||||||
if (widgets) |w| {
|
vm.parent_items_view,
|
||||||
vm.parent_items_view.children.slice = w;
|
try createItems(ctx, ¤tTab.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, ¤tTab.currentChildren, null);
|
updateList(
|
||||||
if (widgets) |w| {
|
vm.current_children_view,
|
||||||
vm.current_children_view.children.slice = w;
|
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(
|
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");
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user