Initial public release
This commit is contained in:
		
							parent
							
								
									3c3f068914
								
							
						
					
					
						commit
						4ece4babad
					
				
					 63 changed files with 15101 additions and 0 deletions
				
			
		
							
								
								
									
										273
									
								
								src/tui/Widget.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								src/tui/Widget.zig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,273 @@
 | 
			
		|||
const std = @import("std");
 | 
			
		||||
const Allocator = std.mem.Allocator;
 | 
			
		||||
const nc = @import("notcurses");
 | 
			
		||||
const tp = @import("thespian");
 | 
			
		||||
 | 
			
		||||
pub const Box = @import("Box.zig");
 | 
			
		||||
pub const EventHandler = @import("EventHandler.zig");
 | 
			
		||||
pub const Theme = @import("theme");
 | 
			
		||||
pub const themes = @import("themes").themes;
 | 
			
		||||
pub const scopes = @import("themes").scopes;
 | 
			
		||||
 | 
			
		||||
ptr: *anyopaque,
 | 
			
		||||
plane: *nc.Plane,
 | 
			
		||||
vtable: *const VTable,
 | 
			
		||||
 | 
			
		||||
const Self = @This();
 | 
			
		||||
 | 
			
		||||
pub const WalkFn = *const fn (ctx: *anyopaque, w: *Self) bool;
 | 
			
		||||
 | 
			
		||||
pub const Direction = enum { horizontal, vertical };
 | 
			
		||||
pub const Layout = union(enum) {
 | 
			
		||||
    dynamic,
 | 
			
		||||
    static: usize,
 | 
			
		||||
 | 
			
		||||
    pub inline fn eql(self: Layout, other: Layout) bool {
 | 
			
		||||
        return switch (self) {
 | 
			
		||||
            .dynamic => switch (other) {
 | 
			
		||||
                .dynamic => true,
 | 
			
		||||
                .static => false,
 | 
			
		||||
            },
 | 
			
		||||
            .static => |s| switch (other) {
 | 
			
		||||
                .dynamic => false,
 | 
			
		||||
                .static => |o| s == o,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub const VTable = struct {
 | 
			
		||||
    deinit: *const fn (ctx: *anyopaque, a: Allocator) void,
 | 
			
		||||
    send: *const fn (ctx: *anyopaque, from: tp.pid_ref, m: tp.message) error{Exit}!bool,
 | 
			
		||||
    update: *const fn (ctx: *anyopaque) void,
 | 
			
		||||
    render: *const fn (ctx: *anyopaque, theme: *const Theme) bool,
 | 
			
		||||
    resize: *const fn (ctx: *anyopaque, pos: Box) void,
 | 
			
		||||
    layout: *const fn (ctx: *anyopaque) Layout,
 | 
			
		||||
    subscribe: *const fn (ctx: *anyopaque, h: EventHandler) error{NotSupported}!void,
 | 
			
		||||
    unsubscribe: *const fn (ctx: *anyopaque, h: EventHandler) error{NotSupported}!void,
 | 
			
		||||
    get: *const fn (ctx: *anyopaque, name_: []const u8) ?*Self,
 | 
			
		||||
    walk: *const fn (ctx: *anyopaque, walk_ctx: *anyopaque, f: WalkFn) bool,
 | 
			
		||||
    type_name: []const u8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn to(pimpl: anytype) Self {
 | 
			
		||||
    const impl = @typeInfo(@TypeOf(pimpl));
 | 
			
		||||
    const child: type = impl.Pointer.child;
 | 
			
		||||
    return .{
 | 
			
		||||
        .ptr = pimpl,
 | 
			
		||||
        .plane = &pimpl.plane,
 | 
			
		||||
        .vtable = comptime &.{
 | 
			
		||||
            .type_name = @typeName(child),
 | 
			
		||||
            .deinit = struct {
 | 
			
		||||
                pub fn deinit(ctx: *anyopaque, a: Allocator) void {
 | 
			
		||||
                    return child.deinit(@as(*child, @ptrCast(@alignCast(ctx))), a);
 | 
			
		||||
                }
 | 
			
		||||
            }.deinit,
 | 
			
		||||
            .send = if (@hasDecl(child, "receive")) struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
 | 
			
		||||
                    return child.receive(@as(*child, @ptrCast(@alignCast(ctx))), from_, m);
 | 
			
		||||
                }
 | 
			
		||||
            }.f else struct {
 | 
			
		||||
                pub fn f(_: *anyopaque, _: tp.pid_ref, _: tp.message) error{Exit}!bool {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }.f,
 | 
			
		||||
            .update = if (@hasDecl(child, "update")) struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque) void {
 | 
			
		||||
                    return child.update(@as(*child, @ptrCast(@alignCast(ctx))));
 | 
			
		||||
                }
 | 
			
		||||
            }.f else struct {
 | 
			
		||||
                pub fn f(_: *anyopaque) void {}
 | 
			
		||||
            }.f,
 | 
			
		||||
            .render = if (@hasDecl(child, "render")) struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque, theme: *const Theme) bool {
 | 
			
		||||
                    return child.render(@as(*child, @ptrCast(@alignCast(ctx))), theme);
 | 
			
		||||
                }
 | 
			
		||||
            }.f else struct {
 | 
			
		||||
                pub fn f(_: *anyopaque, _: *const Theme) bool {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }.f,
 | 
			
		||||
            .resize = if (@hasDecl(child, "handle_resize")) struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque, pos: Box) void {
 | 
			
		||||
                    return child.handle_resize(@as(*child, @ptrCast(@alignCast(ctx))), pos);
 | 
			
		||||
                }
 | 
			
		||||
            }.f else struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque, pos: Box) void {
 | 
			
		||||
                    const self: *child = @ptrCast(@alignCast(ctx));
 | 
			
		||||
                    self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return;
 | 
			
		||||
                    self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return;
 | 
			
		||||
                }
 | 
			
		||||
            }.f,
 | 
			
		||||
            .layout = if (@hasDecl(child, "layout")) struct {
 | 
			
		||||
                pub fn f(ctx: *anyopaque) Layout {
 | 
			
		||||
                    return child.layout(@as(*child, @ptrCast(@alignCast(ctx))));
 | 
			
		||||
                }
 | 
			
		||||
            }.f else struct {
 | 
			
		||||
                pub fn f(_: *anyopaque) Layout {
 | 
			
		||||
                    return .dynamic;
 | 
			
		||||
                }
 | 
			
		||||
            }.f,
 | 
			
		||||
            .subscribe = struct {
 | 
			
		||||
                pub fn subscribe(ctx: *anyopaque, h: EventHandler) error{NotSupported}!void {
 | 
			
		||||
                    return if (comptime @hasDecl(child, "subscribe"))
 | 
			
		||||
                        child.subscribe(@as(*child, @ptrCast(@alignCast(ctx))), h)
 | 
			
		||||
                    else
 | 
			
		||||
                        error.NotSupported;
 | 
			
		||||
                }
 | 
			
		||||
            }.subscribe,
 | 
			
		||||
            .unsubscribe = struct {
 | 
			
		||||
                pub fn unsubscribe(ctx: *anyopaque, h: EventHandler) error{NotSupported}!void {
 | 
			
		||||
                    return if (comptime @hasDecl(child, "unsubscribe"))
 | 
			
		||||
                        child.unsubscribe(@as(*child, @ptrCast(@alignCast(ctx))), h)
 | 
			
		||||
                    else
 | 
			
		||||
                        error.NotSupported;
 | 
			
		||||
                }
 | 
			
		||||
            }.unsubscribe,
 | 
			
		||||
            .get = struct {
 | 
			
		||||
                pub fn get(ctx: *anyopaque, name_: []const u8) ?*Self {
 | 
			
		||||
                    return if (comptime @hasDecl(child, "get")) child.get(@as(*child, @ptrCast(@alignCast(ctx))), name_) else null;
 | 
			
		||||
                }
 | 
			
		||||
            }.get,
 | 
			
		||||
            .walk = struct {
 | 
			
		||||
                pub fn walk(ctx: *anyopaque, walk_ctx: *anyopaque, f: WalkFn) bool {
 | 
			
		||||
                    return if (comptime @hasDecl(child, "walk")) child.walk(@as(*child, @ptrCast(@alignCast(ctx))), walk_ctx, f) else false;
 | 
			
		||||
                }
 | 
			
		||||
            }.walk,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn dynamic_cast(self: Self, comptime T: type) ?*T {
 | 
			
		||||
    return if (std.mem.eql(u8, self.vtable.type_name, @typeName(T)))
 | 
			
		||||
        @as(*T, @ptrCast(@alignCast(self.ptr)))
 | 
			
		||||
    else
 | 
			
		||||
        null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn need_render() void {
 | 
			
		||||
    tp.self_pid().send(.{"render"}) catch {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn need_reflow() void {
 | 
			
		||||
    tp.self_pid().send(.{"reflow"}) catch {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn name(self: Self, buf: []u8) []u8 {
 | 
			
		||||
    return self.plane.name(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn box(self: Self) Box {
 | 
			
		||||
    return Box.from(self.plane.*);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: Self, a: Allocator) void {
 | 
			
		||||
    return self.vtable.deinit(self.ptr, a);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn msg(self: *const Self, m: anytype) error{Exit}!bool {
 | 
			
		||||
    return self.vtable.send(self.ptr, tp.self_pid(), tp.message.fmt(m));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn send(self: *const Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
 | 
			
		||||
    return self.vtable.send(self.ptr, from_, m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn update(self: Self) void {
 | 
			
		||||
    return self.vtable.update(self.ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn render(self: Self, theme: *const Theme) bool {
 | 
			
		||||
    return self.vtable.render(self.ptr, theme);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn resize(self: Self, pos: Box) void {
 | 
			
		||||
    return self.vtable.resize(self.ptr, pos);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn layout(self: Self) Layout {
 | 
			
		||||
    return self.vtable.layout(self.ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn subscribe(self: Self, h: EventHandler) !void {
 | 
			
		||||
    return self.vtable.subscribe(self.ptr, h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn unsubscribe(self: Self, h: EventHandler) !void {
 | 
			
		||||
    return self.vtable.unsubscribe(self.ptr, h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get(self: *Self, name_: []const u8) ?*Self {
 | 
			
		||||
    var buf: [256]u8 = undefined;
 | 
			
		||||
    return if (std.mem.eql(u8, self.plane.name(&buf), name_))
 | 
			
		||||
        self
 | 
			
		||||
    else
 | 
			
		||||
        self.vtable.get(self.ptr, name_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn walk(self: *Self, walk_ctx: *anyopaque, f: WalkFn) bool {
 | 
			
		||||
    return if (self.vtable.walk(self.ptr, walk_ctx, f)) true else f(walk_ctx, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn empty(a: Allocator, parent: nc.Plane, layout_: Layout) !Self {
 | 
			
		||||
    const child: type = struct { plane: nc.Plane, layout: Layout };
 | 
			
		||||
    const widget = try a.create(child);
 | 
			
		||||
    const n = try nc.Plane.init(&(Box{}).opts("empty"), parent);
 | 
			
		||||
    widget.* = .{ .plane = n, .layout = layout_ };
 | 
			
		||||
    return .{
 | 
			
		||||
        .ptr = widget,
 | 
			
		||||
        .plane = &widget.plane,
 | 
			
		||||
        .vtable = comptime &.{
 | 
			
		||||
            .type_name = @typeName(child),
 | 
			
		||||
            .deinit = struct {
 | 
			
		||||
                pub fn deinit(ctx: *anyopaque, a_: Allocator) void {
 | 
			
		||||
                    const self: *child = @ptrCast(@alignCast(ctx));
 | 
			
		||||
                    self.plane.deinit();
 | 
			
		||||
                    a_.destroy(self);
 | 
			
		||||
                }
 | 
			
		||||
            }.deinit,
 | 
			
		||||
            .send = struct {
 | 
			
		||||
                pub fn receive(_: *anyopaque, _: tp.pid_ref, _: tp.message) error{Exit}!bool {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }.receive,
 | 
			
		||||
            .update = struct {
 | 
			
		||||
                pub fn update(_: *anyopaque) void {}
 | 
			
		||||
            }.update,
 | 
			
		||||
            .render = struct {
 | 
			
		||||
                pub fn render(_: *anyopaque, _: *const Theme) bool {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }.render,
 | 
			
		||||
            .resize = struct {
 | 
			
		||||
                pub fn resize(_: *anyopaque, _: Box) void {}
 | 
			
		||||
            }.resize,
 | 
			
		||||
            .layout = struct {
 | 
			
		||||
                pub fn layout(ctx: *anyopaque) Layout {
 | 
			
		||||
                    const self: *child = @ptrCast(@alignCast(ctx));
 | 
			
		||||
                    return self.layout;
 | 
			
		||||
                }
 | 
			
		||||
            }.layout,
 | 
			
		||||
            .subscribe = struct {
 | 
			
		||||
                pub fn subscribe(_: *anyopaque, _: EventHandler) error{NotSupported}!void {
 | 
			
		||||
                    return error.NotSupported;
 | 
			
		||||
                }
 | 
			
		||||
            }.subscribe,
 | 
			
		||||
            .unsubscribe = struct {
 | 
			
		||||
                pub fn unsubscribe(_: *anyopaque, _: EventHandler) error{NotSupported}!void {
 | 
			
		||||
                    return error.NotSupported;
 | 
			
		||||
                }
 | 
			
		||||
            }.unsubscribe,
 | 
			
		||||
            .get = struct {
 | 
			
		||||
                pub fn get(_: *anyopaque, _: []const u8) ?*Self {
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }.get,
 | 
			
		||||
            .walk = struct {
 | 
			
		||||
                pub fn walk(_: *anyopaque, _: *anyopaque, _: WalkFn) bool {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }.walk,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue