feat: ui improvements
This commit is contained in:
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 {
|
||||
// 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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user