diff --git a/examples/styles/text.zig b/examples/styles/text.zig index 2dd178c..969a5da 100644 --- a/examples/styles/text.zig +++ b/examples/styles/text.zig @@ -28,7 +28,22 @@ const TextStyles = struct { const text = "Example"; 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 { @@ -102,12 +117,6 @@ pub fn main() !void { var box = try App.Container.init(allocator, .{ .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()); defer box.deinit(); diff --git a/src/element.zig b/src/element.zig index 90b178e..bdb5679 100644 --- a/src/element.zig +++ b/src/element.zig @@ -15,12 +15,25 @@ pub fn Element(Model: type, Event: type) type { vtable: *const VTable = &.{}, pub const VTable = struct { + minSize: ?*const fn (ctx: *anyopaque, size: Point) Point = null, resize: ?*const fn (ctx: *anyopaque, size: Point) void = null, reposition: ?*const fn (ctx: *anyopaque, origin: Point) 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, }; + /// 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*. pub inline fn resize(this: @This(), size: Point) void { 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; this.size = size; - this.container.resize(size); 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.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(.{ - .x = this.size.x - if (this.configuration.x_axis) @as(u16, 1) else @as(u16, 0), - .y = this.size.y - if (this.configuration.y_axis) @as(u16, 1) else @as(u16, 0), + const min_size = this.container.element.minSize(size); + this.container.resize(.{ + .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_y = this.container.size.y -| size.y;