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

81
src/layout.zig Normal file
View File

@@ -0,0 +1,81 @@
//! Dynamic dispatch for layout implementations.
//! Each layout should at last implement these functions:
//! - handle(this: *@This(), event: Event) Event {}
//! - content(this: *@This()) *std.ArrayList(u8) {}
//! - deinit(this: *@This()) void {}
//!
//! Create a `Layout` using `createFrom(object: anytype)` and use them through
//! the defined interface. The layout will take care of calling the correct
//! implementation of the corresponding underlying type.
//!
//! Each `Layout` is responsible for clearing the allocated memory of the used
//! widgets when deallocated. This means that `deinit()` will also deallocate
//! every used widget too.
const std = @import("std");
const lib_event = @import("event.zig");
pub fn Layout(comptime E: type) type {
if (!lib_event.isTaggedUnion(E)) {
@compileError("Provided user event `E` for `Layout(comptime E: type)` is not of type `union(enum)`.");
}
return struct {
pub const Event = lib_event.MergeTaggedUnions(lib_event.BuiltinEvent, E);
const LayoutType = @This();
const Ptr = usize;
const VTable = struct {
handle: *const fn (this: *LayoutType, event: Event) Event,
content: *const fn (this: *LayoutType) *std.ArrayList(u8),
deinit: *const fn (this: *LayoutType) void,
};
object: Ptr = undefined,
vtable: *const VTable = undefined,
// Handle the provided `Event` for this `Widget`.
pub fn handle(this: *LayoutType, event: Event) Event {
return this.vtable.handle(this, event);
}
// Return the entire content of this `Widget`.
pub fn content(this: *LayoutType) *std.ArrayList(u8) {
return this.vtable.content(this);
}
pub fn deinit(this: *LayoutType) void {
this.vtable.deinit(this);
this.* = undefined;
}
pub fn createFrom(object: anytype) LayoutType {
return LayoutType{
.object = @intFromPtr(object),
.vtable = &.{
.handle = struct {
// Handle the provided `Event` for this `Widget`.
fn handle(this: *LayoutType, event: Event) Event {
const layout: @TypeOf(object) = @ptrFromInt(this.object);
return layout.handle(event);
}
}.handle,
.content = struct {
// Return the entire content of this `Widget`.
fn content(this: *LayoutType) *std.ArrayList(u8) {
const layout: @TypeOf(object) = @ptrFromInt(this.object);
return layout.content();
}
}.content,
.deinit = struct {
fn deinit(this: *LayoutType) void {
const layout: @TypeOf(object) = @ptrFromInt(this.object);
layout.deinit();
}
}.deinit,
},
};
}
// import and export of `Layout` implementations
pub const Pane = @import("layout/Pane.zig").Layout(E);
};
}