Files
zterm/src/layout.zig
Yves Biener c2c3f41ff3
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 4m26s
mod(memory): do not create items on the stack instead using the provided allocator
2025-01-06 21:56:04 +01:00

107 lines
4.9 KiB
Zig

//! Dynamic dispatch for layout implementations. Each `Layout` has to implement
//! the `Layout.Interface`.
//!
//! Create a `Layout` using `createFrom(object: anytype)` and use them through
//! the defined `Layout.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
//! `Element`s (union of `Layout` or `Widget`) when deallocated. This means
//! that `deinit()` will also deallocate every used `Element` too.
//!
//! When `Layout.render` is called the provided `Renderer` type is expected
//! which handles how contents are rendered for a given layout.
const std = @import("std");
const isTaggedUnion = @import("event.zig").isTaggedUnion;
pub fn Layout(comptime Event: type, comptime Renderer: type) type {
if (!isTaggedUnion(Event)) {
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
}
const Events = std.ArrayList(Event);
const Type = struct {
const LayoutType = @This();
const Element = union(enum) {
layout: LayoutType,
widget: @import("widget.zig").Widget(Event, Renderer),
};
pub const Interface = @import("interface").Interface(.{
.handle = fn (anytype, Event) anyerror!*Events,
.render = fn (anytype, *Renderer) anyerror!void,
.deinit = fn (anytype) void,
}, .{});
const VTable = struct {
handle: *const fn (this: *LayoutType, event: Event) anyerror!*Events,
render: *const fn (this: *LayoutType, renderer: *Renderer) anyerror!void,
deinit: *const fn (this: *LayoutType) void,
};
object: *anyopaque = undefined,
vtable: *const VTable = undefined,
// Handle the provided `Event` for this `Layout`.
pub fn handle(this: *LayoutType, event: Event) !*Events {
return try this.vtable.handle(this, event);
}
// Render this `Layout` completely. This will render contained sub-elements too.
pub fn render(this: *LayoutType, renderer: *Renderer) !void {
return try this.vtable.render(this, renderer);
}
pub fn deinit(this: *LayoutType) void {
this.vtable.deinit(this);
}
pub fn createFrom(object: anytype) LayoutType {
return LayoutType{
.object = @ptrCast(@alignCast(object)),
.vtable = &.{
.handle = struct {
// Handle the provided `Event` for this `Layout`.
fn handle(this: *LayoutType, event: Event) !*Events {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object));
return try layout.handle(event);
}
}.handle,
.render = struct {
// Render the contents of this `Layout`.
fn render(this: *LayoutType, renderer: *Renderer) !void {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object));
try layout.render(renderer);
}
}.render,
.deinit = struct {
fn deinit(this: *LayoutType) void {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object));
layout.deinit();
}
}.deinit,
},
};
}
// import and export of `Layout` implementations
pub const HContainer = @import("layout/HContainer.zig").Layout(Event, Element, Renderer);
pub const HStack = @import("layout/HStack.zig").Layout(Event, Element, Renderer);
pub const VContainer = @import("layout/VContainer.zig").Layout(Event, Element, Renderer);
pub const VStack = @import("layout/VStack.zig").Layout(Event, Element, Renderer);
pub const Padding = @import("layout/Padding.zig").Layout(Event, Element, Renderer);
pub const Margin = @import("layout/Margin.zig").Layout(Event, Element, Renderer);
pub const Framing = @import("layout/Framing.zig").Layout(Event, Element, Renderer);
pub const Tab = @import("layout/Tab.zig").Layout(Event, Element, Renderer);
};
// test layout implementation satisfies the interface
comptime Type.Interface.satisfiedBy(Type);
comptime Type.Interface.satisfiedBy(Type.HContainer);
comptime Type.Interface.satisfiedBy(Type.HStack);
comptime Type.Interface.satisfiedBy(Type.VContainer);
comptime Type.Interface.satisfiedBy(Type.VStack);
comptime Type.Interface.satisfiedBy(Type.Padding);
comptime Type.Interface.satisfiedBy(Type.Margin);
comptime Type.Interface.satisfiedBy(Type.Framing);
comptime Type.Interface.satisfiedBy(Type.Tab);
return Type;
}