add: layout and widget dynamic dispatch with interface definitions

This commit is contained in:
2024-11-02 17:52:44 +01:00
parent c62fc6fb43
commit 0330b3a2f5
11 changed files with 502 additions and 480 deletions

86
src/event.zig Normal file
View File

@@ -0,0 +1,86 @@
//! Events which are defined by the library. They might be extended by user
//! events.
const std = @import("std");
const terminal = @import("terminal.zig");
// Application events which contain information about default application
// parameter. Either `none` or `err`, where `none` represents no event with no
// message, while `err` represents an error which is propagated. `Widget`s or
// `Layout`s may react to the event but should continue throwing the message up
// to the application event loop.
pub const ApplicationEvent = union(enum) {
none,
err: []const u8,
};
// System events which contain information about events triggered from outside
// of the application which impact the application. E.g. the terminal window
// size has changed, etc.
pub const SystemEvent = union(enum) {
resize: terminal.Size,
};
pub const BuiltinEvent = MergeTaggedUnions(SystemEvent, ApplicationEvent);
pub fn MergeTaggedUnions(comptime A: type, comptime B: type) type {
if (!isTaggedUnion(A) or !isTaggedUnion(B)) {
@compileError("Both types for merging tagged unions need to be of type `union(enum)`.");
}
const a_fields = @typeInfo(A).Union.fields;
const a_fields_tag = @typeInfo(A).Union.tag_type.?;
const a_enum_fields = @typeInfo(a_fields_tag).Enum.fields;
const b_fields = @typeInfo(B).Union.fields;
const b_fields_tag = @typeInfo(B).Union.tag_type.?;
const b_enum_fields = @typeInfo(b_fields_tag).Enum.fields;
var fields: [a_fields.len + b_fields.len]std.builtin.Type.UnionField = undefined;
var enum_fields: [a_fields.len + b_fields.len]std.builtin.Type.EnumField = undefined;
var i: usize = 0;
for (a_fields, a_enum_fields) |field, enum_field| {
fields[i] = field;
var enum_f = enum_field;
enum_f.value = i;
enum_fields[i] = enum_f;
i += 1;
}
for (b_fields, b_enum_fields) |field, enum_field| {
fields[i] = field;
var enum_f = enum_field;
enum_f.value = i;
enum_fields[i] = enum_f;
i += 1;
}
const log2_i = @bitSizeOf(@TypeOf(i)) - @clz(i);
const EventType = @Type(.{ .Int = .{
.signedness = .unsigned,
.bits = log2_i,
} });
const Event = @Type(.{ .Enum = .{
.tag_type = EventType,
.fields = enum_fields[0..],
.decls = &.{},
.is_exhaustive = true,
} });
return @Type(.{ .Union = .{
.layout = .auto,
.tag_type = Event,
.fields = fields[0..],
.decls = &.{},
} });
}
// Determine at `comptime` wether the provided type `E` is an `union(enum)`.
pub fn isTaggedUnion(comptime E: type) bool {
switch (@typeInfo(E)) {
.Union => |u| {
if (u.tag_type) |_| {} else {
return false;
}
},
else => return false,
}
return true;
}