add(sizing): grow configuration
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m14s
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m14s
Currently the 'grid' and 'mixed' examples are not working yet.
This commit is contained in:
@@ -352,12 +352,15 @@ pub const Layout = packed struct {
|
||||
};
|
||||
const gap: u16 = (this.gap + 1) / 2;
|
||||
|
||||
log.debug("origin: {any}, size: {any}", .{ origin, size });
|
||||
for (0..children.len - 1) |idx| {
|
||||
const child = children[idx];
|
||||
const anchor = switch (this.direction) {
|
||||
.horizontal => ((@as(usize, child.origin.y) -| @as(usize, origin.y)) * @as(usize, size.x)) + @as(usize, child.origin.x) + @as(usize, child.size.x) + gap -| @as(usize, origin.x),
|
||||
.vertical => ((@as(usize, child.origin.y) + @as(usize, child.size.y) + gap -| @as(usize, origin.y)) * @as(usize, size.x)) + @as(usize, child.origin.x) -| @as(usize, origin.x),
|
||||
.horizontal => (@as(usize, child.origin.y) * @as(usize, size.x)) + @as(usize, child.origin.x) + @as(usize, child.size.x) + gap,
|
||||
.vertical => ((@as(usize, child.origin.y) + @as(usize, child.size.y) + gap) * @as(usize, size.x)) + @as(usize, child.origin.x),
|
||||
};
|
||||
log.debug("child.origin: {any}, child.size: {any}", .{ child.origin, child.size });
|
||||
log.debug("anchor: {d}", .{anchor});
|
||||
|
||||
switch (this.direction) {
|
||||
.horizontal => for (0..child.size.y) |row| {
|
||||
@@ -553,6 +556,17 @@ pub const Layout = packed struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Sizing options which should be used by the `Container`
|
||||
pub const Size = packed struct {
|
||||
dim: Point = .{},
|
||||
grow: enum(u2) {
|
||||
both,
|
||||
fixed,
|
||||
vertical,
|
||||
horizontal,
|
||||
} = .both,
|
||||
};
|
||||
|
||||
pub fn Container(comptime Event: type) type {
|
||||
if (!isTaggedUnion(Event)) @compileError("Provided user event `Event` for `Container(comptime Event: type)`");
|
||||
|
||||
@@ -572,10 +586,7 @@ pub fn Container(comptime Event: type) type {
|
||||
border: Border = .{},
|
||||
rectangle: Rectangle = .{},
|
||||
layout: Layout = .{},
|
||||
/// size which should be used by the `Container`
|
||||
size: Point = .{},
|
||||
/// should size grow to the available space?
|
||||
grow: bool = true,
|
||||
size: Size = .{},
|
||||
};
|
||||
|
||||
pub fn init(
|
||||
@@ -609,7 +620,7 @@ pub fn Container(comptime Event: type) type {
|
||||
this.origin = origin;
|
||||
this.element.reposition(origin);
|
||||
|
||||
var offset: Point = Point.add(origin, .{
|
||||
var offset = origin.add(.{
|
||||
.x = layout.padding.left,
|
||||
.y = layout.padding.top,
|
||||
});
|
||||
@@ -633,7 +644,8 @@ pub fn Container(comptime Event: type) type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fit_resize(this: *@This()) Point {
|
||||
/// Resize all fit sized `Containers` to the necessary size required by its child elements.
|
||||
fn fit_resize(this: *@This()) Point {
|
||||
// NOTE this is supposed to be a simple and easy to understand algorithm, there are currently no optimizations done
|
||||
const layout = this.properties.layout;
|
||||
var size: Point = switch (layout.direction) {
|
||||
@@ -647,8 +659,10 @@ pub fn Container(comptime Event: type) type {
|
||||
};
|
||||
|
||||
const sides = this.properties.border.sides;
|
||||
if (sides.right) size.x -|= 1;
|
||||
if (sides.bottom) size.y -|= 1;
|
||||
if (sides.left) size.x += 1;
|
||||
if (sides.right) size.x += 1;
|
||||
if (sides.top) size.y += 1;
|
||||
if (sides.bottom) size.y += 1;
|
||||
|
||||
if (layout.separator.enabled) switch (layout.direction) {
|
||||
.horizontal => size.x += @as(u16, @truncate(this.elements.items.len -| 1)),
|
||||
@@ -670,44 +684,55 @@ pub fn Container(comptime Event: type) type {
|
||||
}
|
||||
|
||||
// assign currently calculated size
|
||||
this.size = Point.max(size, this.properties.size);
|
||||
this.size = switch (this.properties.size.grow) {
|
||||
.both => Point.max(size, this.properties.size.dim),
|
||||
.fixed => this.properties.size.dim,
|
||||
.horizontal => .{
|
||||
.x = @max(size.x, this.properties.size.dim.x),
|
||||
.y = size.y,
|
||||
},
|
||||
.vertical => .{
|
||||
.x = size.x,
|
||||
.y = @max(size.y, this.properties.size.dim.y),
|
||||
},
|
||||
};
|
||||
return this.size;
|
||||
}
|
||||
|
||||
// growable implicitly requires the root `Container` to have a set a size property to the size of the available terminal screen
|
||||
fn grow_resize(this: *@This()) void {
|
||||
fn grow_resize(this: *@This(), max_size: Point) void {
|
||||
const layout = this.properties.layout;
|
||||
var remainder = switch (layout.direction) {
|
||||
.horizontal => this.size.x -| (layout.padding.left + layout.padding.right),
|
||||
.vertical => this.size.y -| (layout.padding.top + layout.padding.bottom),
|
||||
.horizontal => max_size.x -| (layout.padding.left + layout.padding.right),
|
||||
.vertical => max_size.y -| (layout.padding.top + layout.padding.bottom),
|
||||
};
|
||||
remainder -|= layout.gap * @as(u16, @truncate(this.elements.items.len -| 1));
|
||||
|
||||
if (layout.separator.enabled) remainder -|= @as(u16, @truncate(this.elements.items.len -| 1));
|
||||
|
||||
var available = switch (layout.direction) {
|
||||
.horizontal => this.size.y -| (layout.padding.top + layout.padding.bottom),
|
||||
.vertical => this.size.x -| (layout.padding.left + layout.padding.right),
|
||||
.horizontal => max_size.y -| (layout.padding.top + layout.padding.bottom),
|
||||
.vertical => max_size.x -| (layout.padding.left + layout.padding.right),
|
||||
};
|
||||
|
||||
const sides = this.properties.border.sides;
|
||||
switch (layout.direction) {
|
||||
.horizontal => {
|
||||
if (sides.bottom) {
|
||||
if (sides.top) {
|
||||
available -|= 1;
|
||||
remainder -|= 1;
|
||||
}
|
||||
if (sides.top) {
|
||||
if (sides.bottom) {
|
||||
available -|= 1;
|
||||
remainder -|= 1;
|
||||
}
|
||||
},
|
||||
.vertical => {
|
||||
if (sides.right) {
|
||||
if (sides.left) {
|
||||
available -|= 1;
|
||||
remainder -|= 1;
|
||||
}
|
||||
if (sides.left) {
|
||||
if (sides.right) {
|
||||
available -|= 1;
|
||||
remainder -|= 1;
|
||||
}
|
||||
@@ -724,18 +749,23 @@ pub fn Container(comptime Event: type) type {
|
||||
var growable_children: usize = 0;
|
||||
var first_growable_child: *@This() = undefined;
|
||||
for (this.elements.items) |*child| {
|
||||
if (child.properties.grow) {
|
||||
if (child.properties.size.grow != .fixed) {
|
||||
if (growable_children == 0) first_growable_child = child;
|
||||
growable_children += 1;
|
||||
|
||||
switch (layout.direction) {
|
||||
.horizontal => child.size.y = available,
|
||||
.vertical => child.size.x = available,
|
||||
.horizontal => if (child.properties.size.grow != .vertical) {
|
||||
child.size.y = available;
|
||||
},
|
||||
.vertical => if (child.properties.size.grow != .horizontal) {
|
||||
child.size.x = available;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (growable_children > 0 and remainder > 0) {
|
||||
var count: usize = this.elements.items.len;
|
||||
while (growable_children > 0 and remainder > 0 and count > 0) : (count -|= 1) {
|
||||
var smallest_size = switch (layout.direction) {
|
||||
.horizontal => first_growable_child.size.x,
|
||||
.vertical => first_growable_child.size.y,
|
||||
@@ -744,7 +774,7 @@ pub fn Container(comptime Event: type) type {
|
||||
var size_to_correct = remainder;
|
||||
|
||||
for (this.elements.items) |child| {
|
||||
if (!child.properties.grow) continue;
|
||||
if (child.properties.size.grow == .fixed) continue;
|
||||
|
||||
const size = switch (layout.direction) {
|
||||
.horizontal => child.size.x,
|
||||
@@ -771,18 +801,28 @@ pub fn Container(comptime Event: type) type {
|
||||
.horizontal => child.size.x,
|
||||
.vertical => child.size.y,
|
||||
};
|
||||
if (child.properties.grow and child_size == smallest_size) {
|
||||
if (child.properties.size.grow != .fixed and child_size == smallest_size) {
|
||||
switch (layout.direction) {
|
||||
.horizontal => child.size.x += size_to_correct,
|
||||
.vertical => child.size.y += size_to_correct,
|
||||
.horizontal => if (child.properties.size.grow != .vertical) {
|
||||
child.size.x += size_to_correct;
|
||||
},
|
||||
.vertical => if (child.properties.size.grow != .horizontal) {
|
||||
child.size.y += size_to_correct;
|
||||
},
|
||||
}
|
||||
if (overflow > 0) {
|
||||
switch (layout.direction) {
|
||||
.horizontal => child.size.x += 1,
|
||||
.vertical => child.size.y += 1,
|
||||
.horizontal => if (child.properties.size.grow != .vertical) {
|
||||
child.size.x += 1;
|
||||
overflow -|= 1;
|
||||
remainder -|= 1;
|
||||
},
|
||||
.vertical => if (child.properties.size.grow != .horizontal) {
|
||||
child.size.y += 1;
|
||||
overflow -|= 1;
|
||||
remainder -|= 1;
|
||||
},
|
||||
}
|
||||
overflow -|= 1;
|
||||
remainder -|= 1;
|
||||
}
|
||||
remainder -|= size_to_correct;
|
||||
}
|
||||
@@ -790,16 +830,28 @@ pub fn Container(comptime Event: type) type {
|
||||
}
|
||||
|
||||
this.element.resize(this.size);
|
||||
for (this.elements.items) |*child| child.grow_resize();
|
||||
for (this.elements.items) |*child| child.grow_resize(this.size);
|
||||
}
|
||||
|
||||
pub fn resize(this: *@This(), origin: Point, size: Point) void {
|
||||
// NOTE assume that this function is only called for the root `Container`
|
||||
this.size = size; // total screen size;
|
||||
const fit_size = this.fit_resize();
|
||||
this.size = size; // total screen size
|
||||
_ = fit_size;
|
||||
this.grow_resize();
|
||||
log.debug("fit_size: {any}; size: {any}", .{ fit_size, size });
|
||||
// if (fit_size.y > size.y or fit_size.x > size.x) @panic("error: cannot render in available space");
|
||||
switch (this.properties.size.grow) {
|
||||
.both => this.size = Point.max(size, fit_size),
|
||||
.fixed => {},
|
||||
.horizontal => this.size = .{
|
||||
.x = @max(size.x, fit_size.x),
|
||||
.y = size.y,
|
||||
},
|
||||
.vertical => this.size = .{
|
||||
.x = size.x,
|
||||
.y = @max(size.y, fit_size.y),
|
||||
},
|
||||
}
|
||||
log.debug("this.size: {any}", .{this.size});
|
||||
this.grow_resize(this.size);
|
||||
this.reposition(origin);
|
||||
}
|
||||
|
||||
@@ -822,6 +874,7 @@ pub fn Container(comptime Event: type) type {
|
||||
@memset(cells, .{});
|
||||
errdefer this.allocator.free(cells);
|
||||
|
||||
log.debug("contents: this.size: {any}", .{this.size});
|
||||
this.properties.layout.contents(@This(), cells, this.origin, this.size, this.elements.items);
|
||||
this.properties.border.contents(cells, this.size);
|
||||
this.properties.rectangle.contents(cells, this.size);
|
||||
|
||||
Reference in New Issue
Block a user