mod(element/scrollable): introduce minSize function for Element
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 3m24s

The minimal required size can the queried from the containing `Element`
of the `Container` that is provided to the `Scrollable` to dynamically
adjust its size.

Currently abritrary nesting is not supported / tested.
This commit is contained in:
2025-11-01 13:55:16 +01:00
parent c645c2efee
commit f70ea05244
2 changed files with 34 additions and 12 deletions

View File

@@ -28,7 +28,22 @@ const TextStyles = struct {
const text = "Example"; const text = "Example";
pub fn element(this: *@This()) App.Element { pub fn element(this: *@This()) App.Element {
return .{ .ptr = this, .vtable = &.{ .content = content } }; return .{
.ptr = this,
.vtable = &.{
.minSize = minSize,
.content = content,
},
};
}
fn minSize(ctx: *anyopaque, size: zterm.Point) zterm.Point {
_ = ctx;
_ = size;
return .{
.x = std.meta.fields(zterm.Style.Emphasis).len * TextStyles.text.len,
.y = (std.meta.fields(zterm.Color).len - 1) * (std.meta.fields(zterm.Color).len - 2),
};
} }
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void { fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
@@ -102,12 +117,6 @@ pub fn main() !void {
var box = try App.Container.init(allocator, .{ var box = try App.Container.init(allocator, .{
.layout = .{ .direction = .vertical }, .layout = .{ .direction = .vertical },
.size = .{
.dim = .{
.x = std.meta.fields(zterm.Style.Emphasis).len * TextStyles.text.len,
.y = (std.meta.fields(zterm.Color).len - 1) * (std.meta.fields(zterm.Color).len - 2),
},
},
}, text_styles.element()); }, text_styles.element());
defer box.deinit(); defer box.deinit();

View File

@@ -15,12 +15,25 @@ pub fn Element(Model: type, Event: type) type {
vtable: *const VTable = &.{}, vtable: *const VTable = &.{},
pub const VTable = struct { pub const VTable = struct {
minSize: ?*const fn (ctx: *anyopaque, size: Point) Point = null,
resize: ?*const fn (ctx: *anyopaque, size: Point) void = null, resize: ?*const fn (ctx: *anyopaque, size: Point) void = null,
reposition: ?*const fn (ctx: *anyopaque, origin: Point) void = null, reposition: ?*const fn (ctx: *anyopaque, origin: Point) void = null,
handle: ?*const fn (ctx: *anyopaque, model: *Model, event: Event) anyerror!void = null, handle: ?*const fn (ctx: *anyopaque, model: *Model, event: Event) anyerror!void = null,
content: ?*const fn (ctx: *anyopaque, model: *const Model, cells: []Cell, size: Point) anyerror!void = null, content: ?*const fn (ctx: *anyopaque, model: *const Model, cells: []Cell, size: Point) anyerror!void = null,
}; };
/// Request the minimal size required for this `Element` the provided
/// available *size* can be used as a fallback. This is currently only
/// used for `Scrollable` elements, such that the nested element in
/// the `Scrollable`'s `Container` can request a minimal size for its
/// contents.
pub inline fn minSize(this: @This(), size: Point) Point {
return if (this.vtable.minSize) |minSize_fn|
minSize_fn(this.ptr, size)
else
size;
}
/// Resize the corresponding `Element` with the given *size*. /// Resize the corresponding `Element` with the given *size*.
pub inline fn resize(this: @This(), size: Point) void { pub inline fn resize(this: @This(), size: Point) void {
if (this.vtable.resize) |resize_fn| if (this.vtable.resize) |resize_fn|
@@ -242,16 +255,16 @@ pub fn Scrollable(Model: type, Event: type) type {
const last_max_anchor_y = this.container_size.y -| this.size.y; const last_max_anchor_y = this.container_size.y -| this.size.y;
this.size = size; this.size = size;
this.container.resize(size);
if (this.configuration.scrollbar) { if (this.configuration.scrollbar) {
if (this.container.properties.size.dim.x > this.size.x or this.container.size.x > this.size.x) this.configuration.y_axis = true; if (this.container.properties.size.dim.x > this.size.x or this.container.size.x > this.size.x) this.configuration.y_axis = true;
if (this.container.properties.size.dim.y > this.size.y or this.container.size.y > this.size.y) this.configuration.x_axis = true; if (this.container.properties.size.dim.y > this.size.y or this.container.size.y > this.size.y) this.configuration.x_axis = true;
if (this.configuration.x_axis or this.configuration.y_axis) this.container.resize(.{ const min_size = this.container.element.minSize(size);
.x = this.size.x - if (this.configuration.x_axis) @as(u16, 1) else @as(u16, 0), this.container.resize(.{
.y = this.size.y - if (this.configuration.y_axis) @as(u16, 1) else @as(u16, 0), .x = min_size.x - if (this.configuration.x_axis) @as(u16, 1) else @as(u16, 0),
.y = min_size.y - if (this.configuration.y_axis) @as(u16, 1) else @as(u16, 0),
}); });
} } else this.container.resize(this.container.element.minSize(size)); // notify the container about the minimal size it should have (can be larger)
const new_max_anchor_x = this.container.size.x -| size.x; const new_max_anchor_x = this.container.size.x -| size.x;
const new_max_anchor_y = this.container.size.y -| size.y; const new_max_anchor_y = this.container.size.y -| size.y;