//! 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, /// Cancel event to signify that the user provided an EOF /// /// Usually this event is only triggered by the system in *non raw mode* /// renderings otherwise the corresponding `.key` event would be fired instead. cancel, /// Resize event to signify that the application should re-draw to resize /// /// Usually no `Container` nor `Element` should act on that event, as it /// only serves for event based loops to force a re-draw with a new `Event`. resize, /// Ring the terminal bell to notify the user. This `Event` is handled by /// every `Container` and will not be passed through the container tree. bell, /// Error event to notify other containers about a recoverable error err: struct { /// actual error err: anyerror, /// associated error message msg: []const u8, }, /// Input line event received in *non raw mode* (instead of individual `key` events) /// /// This event contains the entire line until the ending newline character /// (which is included in the payload of this event). line: []const u8, /// Input key event received from the user key: Key, /// Mouse input event mouse: Mouse, /// Focus event indicating that the application has gained the focus of the user focus: bool, }; /// Merge the two provided `union(enum)` `A` and `B` to a tagged union containing all fields of both tagged unions in `comptime`. /// Declarations are not supported for `comptime` created types, see https://github.com/ziglang/zig/issues/6709 for details. pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type { // TODO maybe it makes sense to have a nested tagged union type (i.e. system: union(enum) and event: union(enum)) // - allows re-definition of system / built-in events // - clearly shows which events are system / built-in ones and which are user defined events // - the memory footprint for the nesting is not really harmful 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 field_names: [a_fields.len + b_fields.len][:0]const u8 = undefined; var field_types: [a_fields.len + b_fields.len]type = undefined; var enum_names: [a_fields.len + b_fields.len][:0]const u8 = undefined; // FIX can I not use `comptime_int`? `u4` would be actually incorrect! // can I get the correct size through the length of the fields? var enum_values: [a_fields.len + b_fields.len]u4 = undefined; var i: usize = 0; for (a_fields, a_enum_fields) |field, enum_field| { field_names[i] = field.name; field_types[i] = field.type; var enum_f = enum_field; enum_f.value = i; enum_names[i] = enum_f.name; enum_values[i] = i; i += 1; } for (b_fields, b_enum_fields) |field, enum_field| { field_names[i] = field.name; field_types[i] = field.type; var enum_f = enum_field; enum_f.value = i; enum_names[i] = enum_f.name; enum_values[i] = i; i += 1; } // NOTE declarations are not supported for `comptime` types: https://github.com/ziglang/zig/issues/6709 // -> will lead to a compilation error when constructing the tagged union // at the end of this function in case at least one of the provided tagged // unions to merge contains declarations (which in this case can only be the // user provided one) const a_enum_decls = @typeInfo(A).@"union".decls; const b_enum_decls = @typeInfo(B).@"union".decls; var decls: [a_enum_decls.len + b_enum_decls.len]std.builtin.Type.Declaration = undefined; var j: usize = 0; for (a_enum_decls) |decl| { decls[j] = decl; j += 1; } for (b_enum_decls) |decl| { decls[j] = decl; j += 1; } const EventType = @Int(.unsigned, @bitSizeOf(@TypeOf(i)) - @clz(i)); const Event = @Enum( EventType, .exhaustive, &enum_names, &enum_values, ); return @Union( .auto, Event, &field_names, &field_types, &@splat(.{}), ); } /// Determine whether the provided type `T` is a tagged union: `union(enum)`. pub fn isTaggedUnion(comptime T: type) bool { switch (@typeInfo(T)) { .@"union" => |u| if (u.tag_type) |_| {} else { return false; }, else => return false, } return true; } /// Determine whether the provided type `T` is a `struct`. pub fn isStruct(comptime T: type) bool { return switch (@typeInfo(T)) { .@"struct" => true, else => false, }; } 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;