//! Events which are defined by the library. They might be extended by user //! events. See `App` for more details about user defined events. /// System events available to every `zterm.App` pub const SystemEvent = union(enum) { /// Initialize event, which is send once at the beginning of the event loop and before the first render loop /// TODO not sure if this is necessary or if there is an actual usecase for this - for now it will remain init, /// Quit event to signify the end of the event loop (rendering should stop afterwards) quit, /// Resize event to signify that the application should re-draw to resize resize, /// Error event to notify other containers about a recoverable error err: struct { /// actual error err: anyerror, /// associated error message msg: []const u8, }, /// Input key event received from the user key: Key, /// Mouse input event mouse: Mouse, /// Focus event for mouse interaction /// TODO this should instead be a union with a `Size` to derive which container / element the focus meant for focus: bool, }; 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` whether 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; } const std = @import("std"); const input = @import("input.zig"); const terminal = @import("terminal.zig"); const Key = input.Key; const Mouse = input.Mouse; const Point = @import("point.zig").Point;