feat(container): introduce fixed_size property for fixed sizing of Containers
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 42s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 42s
Moved min_size property from `Container` to the `Scrollable` element, where it is only used anyway.
This commit is contained in:
@@ -51,11 +51,14 @@ pub fn main() !void {
|
|||||||
// - some sort of chat? -> write messages and have them displayed in a scrollable array at the right hand side?
|
// - some sort of chat? -> write messages and have them displayed in a scrollable array at the right hand side?
|
||||||
// - on the left some buttons?
|
// - on the left some buttons?
|
||||||
var box = try App.Container.init(allocator, .{
|
var box = try App.Container.init(allocator, .{
|
||||||
|
.border = .{
|
||||||
|
.color = .blue,
|
||||||
|
.sides = .all,
|
||||||
|
},
|
||||||
.layout = .{
|
.layout = .{
|
||||||
.gap = 1,
|
.gap = 1,
|
||||||
.padding = .vertical(2),
|
.padding = .vertical(2),
|
||||||
},
|
},
|
||||||
.min_size = .{ .cols = 50 },
|
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try App.Container.init(allocator, .{
|
try box.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .light_green },
|
.rectangle = .{ .fill = .light_green },
|
||||||
@@ -70,6 +73,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
var scrollable: App.Scrollable = .{
|
var scrollable: App.Scrollable = .{
|
||||||
.container = box,
|
.container = box,
|
||||||
|
.min_size = .{ .cols = 60 },
|
||||||
};
|
};
|
||||||
|
|
||||||
var container = try App.Container.init(allocator, .{
|
var container = try App.Container.init(allocator, .{
|
||||||
@@ -87,9 +91,11 @@ pub fn main() !void {
|
|||||||
.color = .light_blue,
|
.color = .light_blue,
|
||||||
.sides = .all,
|
.sides = .all,
|
||||||
},
|
},
|
||||||
|
.fixed_size = .{ .cols = 200 },
|
||||||
}, .{}));
|
}, .{}));
|
||||||
try container.append(try App.Container.init(allocator, .{
|
try container.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .blue },
|
.rectangle = .{ .fill = .blue },
|
||||||
|
.fixed_size = .{ .cols = 30 },
|
||||||
}, .{}));
|
}, .{}));
|
||||||
defer container.deinit(); // also de-initializes the children
|
defer container.deinit(); // also de-initializes the children
|
||||||
|
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ pub fn Container(comptime Event: type) type {
|
|||||||
border: Border = .{},
|
border: Border = .{},
|
||||||
rectangle: Rectangle = .{},
|
rectangle: Rectangle = .{},
|
||||||
layout: Layout = .{},
|
layout: Layout = .{},
|
||||||
min_size: Size = .{},
|
fixed_size: Size = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
@@ -590,24 +590,6 @@ pub fn Container(comptime Event: type) type {
|
|||||||
try this.elements.append(element);
|
try this.elements.append(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn minSize(this: @This()) Size {
|
|
||||||
var size: Size = .{};
|
|
||||||
const len: u16 = @truncate(this.elements.items.len);
|
|
||||||
if (len > 0) {
|
|
||||||
for (this.elements.items) |element| {
|
|
||||||
size = size.add(element.minSize());
|
|
||||||
}
|
|
||||||
var gap = this.properties.layout.gap;
|
|
||||||
if (this.properties.layout.separator.enabled) gap += 1;
|
|
||||||
|
|
||||||
switch (this.properties.layout.direction) {
|
|
||||||
.horizontal => size.cols += gap * (len - 1),
|
|
||||||
.vertical => size.rows += gap * (len - 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size.max(this.properties.min_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(this: *@This(), event: Event) !void {
|
pub fn handle(this: *@This(), event: Event) !void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.resize => |size| resize: {
|
.resize => |size| resize: {
|
||||||
@@ -618,11 +600,32 @@ pub fn Container(comptime Event: type) type {
|
|||||||
size.rows,
|
size.rows,
|
||||||
});
|
});
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
if (this.properties.fixed_size.cols > 0 and size.cols < this.properties.fixed_size.cols) return error.TooSmall;
|
||||||
|
if (this.properties.fixed_size.rows > 0 and size.rows < this.properties.fixed_size.rows) return error.TooSmall;
|
||||||
|
|
||||||
try this.element.handle(event);
|
try this.element.handle(event);
|
||||||
|
|
||||||
if (this.elements.items.len == 0) break :resize;
|
if (this.elements.items.len == 0) break :resize;
|
||||||
|
|
||||||
const layout = this.properties.layout;
|
const layout = this.properties.layout;
|
||||||
|
var fixed_size_elements: u16 = 0;
|
||||||
|
var fixed_size: Size = .{};
|
||||||
|
for (this.elements.items) |element| {
|
||||||
|
switch (layout.direction) {
|
||||||
|
.horizontal => if (element.properties.fixed_size.cols > 0) {
|
||||||
|
fixed_size_elements += 1;
|
||||||
|
},
|
||||||
|
.vertical => if (element.properties.fixed_size.rows > 0) {
|
||||||
|
fixed_size_elements += 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fixed_size = fixed_size.add(element.properties.fixed_size);
|
||||||
|
}
|
||||||
|
// check if the available screen is large enough
|
||||||
|
switch (layout.direction) {
|
||||||
|
.horizontal => if (fixed_size.cols > size.cols) return error.TooSmall,
|
||||||
|
.vertical => if (fixed_size.rows > size.rows) return error.TooSmall,
|
||||||
|
}
|
||||||
const sides = this.properties.border.sides;
|
const sides = this.properties.border.sides;
|
||||||
const padding = layout.padding;
|
const padding = layout.padding;
|
||||||
var gap = layout.gap;
|
var gap = layout.gap;
|
||||||
@@ -630,18 +633,28 @@ pub fn Container(comptime Event: type) type {
|
|||||||
|
|
||||||
const len: u16 = @truncate(this.elements.items.len);
|
const len: u16 = @truncate(this.elements.items.len);
|
||||||
const element_cols = blk: {
|
const element_cols = blk: {
|
||||||
var cols = size.cols - gap * (len - 1);
|
var cols = size.cols - fixed_size.cols - gap * (len - 1);
|
||||||
if (sides.left) cols -= 1;
|
if (sides.left) cols -= 1;
|
||||||
if (sides.right) cols -= 1;
|
if (sides.right) cols -= 1;
|
||||||
cols -= padding.left + padding.right;
|
cols -= padding.left + padding.right;
|
||||||
break :blk @divTrunc(cols, len);
|
if (fixed_size_elements == len) break :blk 0;
|
||||||
|
if (fixed_size_elements == 0) {
|
||||||
|
break :blk @divTrunc(cols, len);
|
||||||
|
} else {
|
||||||
|
break :blk @divTrunc(cols, len - fixed_size_elements);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const element_rows = blk: {
|
const element_rows = blk: {
|
||||||
var rows = size.rows - gap * (len - 1);
|
var rows = size.rows - fixed_size.rows - gap * (len - 1);
|
||||||
if (sides.top) rows -= 1;
|
if (sides.top) rows -= 1;
|
||||||
if (sides.bottom) rows -= 1;
|
if (sides.bottom) rows -= 1;
|
||||||
rows -= padding.top + padding.bottom;
|
rows -= padding.top + padding.bottom;
|
||||||
break :blk @divTrunc(rows, len);
|
if (fixed_size_elements == len) break :blk 0;
|
||||||
|
if (fixed_size_elements == 0) {
|
||||||
|
break :blk @divTrunc(rows, len);
|
||||||
|
} else {
|
||||||
|
break :blk @divTrunc(rows, len - fixed_size_elements);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var offset: u16 = switch (layout.direction) {
|
var offset: u16 = switch (layout.direction) {
|
||||||
.horizontal => padding.left,
|
.horizontal => padding.left,
|
||||||
@@ -649,18 +662,28 @@ pub fn Container(comptime Event: type) type {
|
|||||||
};
|
};
|
||||||
var overflow = switch (layout.direction) {
|
var overflow = switch (layout.direction) {
|
||||||
.horizontal => blk: {
|
.horizontal => blk: {
|
||||||
var cols = size.cols - gap * (len - 1);
|
var cols = size.cols - fixed_size.cols - gap * (len - 1);
|
||||||
if (sides.left) cols -= 1;
|
if (sides.left) cols -= 1;
|
||||||
if (sides.right) cols -= 1;
|
if (sides.right) cols -= 1;
|
||||||
cols -= padding.left + padding.right;
|
cols -= padding.left + padding.right;
|
||||||
break :blk cols - element_cols * len;
|
if (fixed_size_elements == len) break :blk 0;
|
||||||
|
if (fixed_size_elements == 0) {
|
||||||
|
break :blk cols - element_cols * len;
|
||||||
|
} else {
|
||||||
|
break :blk cols - element_cols * (len - fixed_size_elements);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.vertical => blk: {
|
.vertical => blk: {
|
||||||
var rows = size.rows - gap * (len - 1);
|
var rows = size.rows - fixed_size.rows - gap * (len - 1);
|
||||||
if (sides.top) rows -= 1;
|
if (sides.top) rows -= 1;
|
||||||
if (sides.bottom) rows -= 1;
|
if (sides.bottom) rows -= 1;
|
||||||
rows -= padding.top + padding.bottom;
|
rows -= padding.top + padding.bottom;
|
||||||
break :blk rows - element_rows * len;
|
if (fixed_size_elements == len) break :blk 0;
|
||||||
|
if (fixed_size_elements == 0) {
|
||||||
|
break :blk rows - element_rows * len;
|
||||||
|
} else {
|
||||||
|
break :blk rows - element_rows * (len - fixed_size_elements);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -668,7 +691,11 @@ pub fn Container(comptime Event: type) type {
|
|||||||
var element_size: Size = undefined;
|
var element_size: Size = undefined;
|
||||||
switch (layout.direction) {
|
switch (layout.direction) {
|
||||||
.horizontal => {
|
.horizontal => {
|
||||||
var cols = element_cols;
|
// TODO: this should not always be the max size property!
|
||||||
|
var cols = blk: {
|
||||||
|
if (element.properties.fixed_size.cols > 0) break :blk element.properties.fixed_size.cols;
|
||||||
|
break :blk element_cols;
|
||||||
|
};
|
||||||
if (overflow > 0) {
|
if (overflow > 0) {
|
||||||
overflow -|= 1;
|
overflow -|= 1;
|
||||||
cols += 1;
|
cols += 1;
|
||||||
@@ -692,7 +719,10 @@ pub fn Container(comptime Event: type) type {
|
|||||||
offset += cols;
|
offset += cols;
|
||||||
},
|
},
|
||||||
.vertical => {
|
.vertical => {
|
||||||
var rows = element_rows;
|
var rows = blk: {
|
||||||
|
if (element.properties.fixed_size.rows > 0) break :blk element.properties.fixed_size.rows;
|
||||||
|
break :blk element_rows;
|
||||||
|
};
|
||||||
if (overflow > 0) {
|
if (overflow > 0) {
|
||||||
overflow -|= 1;
|
overflow -|= 1;
|
||||||
rows += 1;
|
rows += 1;
|
||||||
|
|||||||
@@ -58,12 +58,16 @@ pub fn Scrollable(Event: type) type {
|
|||||||
/// `Size` of the actual contents where the anchor and the size is
|
/// `Size` of the actual contents where the anchor and the size is
|
||||||
/// representing the size and location on screen.
|
/// representing the size and location on screen.
|
||||||
size: Size = .{},
|
size: Size = .{},
|
||||||
|
/// Minimal `Size` of the scrollable `Container` to be used. If the
|
||||||
|
/// actual scrollable container's size is larger it will be used instead
|
||||||
|
/// (no scrolling will be necessary for that screen size).
|
||||||
|
min_size: Size = .{},
|
||||||
/// `Size` of the `Container` content that is scrollable and mapped to
|
/// `Size` of the `Container` content that is scrollable and mapped to
|
||||||
/// the *size* of the `Scrollable` `Element`.
|
/// the *size* of the `Scrollable` `Element`.
|
||||||
container_size: Size = .{},
|
container_size: Size = .{},
|
||||||
/// Anchor of the viewport of the scrollable `Container`.
|
/// Anchor of the viewport of the scrollable `Container`.
|
||||||
anchor: Position = .{},
|
anchor: Position = .{},
|
||||||
/// The actual container, that is scrollable.
|
/// The actual `Container`, that is scrollable.
|
||||||
container: Container(Event),
|
container: Container(Event),
|
||||||
|
|
||||||
pub fn element(this: *@This()) Element(Event) {
|
pub fn element(this: *@This()) Element(Event) {
|
||||||
@@ -76,8 +80,11 @@ pub fn Scrollable(Event: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(container: Container(Event)) @This() {
|
pub fn init(container: Container(Event), min_size: Size) @This() {
|
||||||
return .{ .container = container };
|
return .{
|
||||||
|
.container = container,
|
||||||
|
.min_size = min_size,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(ctx: *anyopaque, event: Event) !void {
|
fn handle(ctx: *anyopaque, event: Event) !void {
|
||||||
@@ -86,7 +93,7 @@ pub fn Scrollable(Event: type) type {
|
|||||||
.resize => |size| {
|
.resize => |size| {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
// TODO: scrollbar space - depending on configuration and only if necessary?
|
// TODO: scrollbar space - depending on configuration and only if necessary?
|
||||||
this.container_size = size.max(this.container.minSize());
|
this.container_size = size.max(this.min_size);
|
||||||
this.container_size.anchor = size.anchor;
|
this.container_size.anchor = size.anchor;
|
||||||
try this.container.handle(.{ .resize = this.container_size });
|
try this.container.handle(.{ .resize = this.container_size });
|
||||||
},
|
},
|
||||||
@@ -190,7 +197,6 @@ test "scrollable vertical" {
|
|||||||
.direction = .vertical,
|
.direction = .vertical,
|
||||||
.padding = .all(1),
|
.padding = .all(1),
|
||||||
},
|
},
|
||||||
.min_size = .{ .rows = size.rows + 15 },
|
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try .init(std.testing.allocator, .{
|
try box.append(try .init(std.testing.allocator, .{
|
||||||
.rectangle = .{ .fill = .grey },
|
.rectangle = .{ .fill = .grey },
|
||||||
@@ -200,7 +206,7 @@ test "scrollable vertical" {
|
|||||||
}, .{}));
|
}, .{}));
|
||||||
defer box.deinit();
|
defer box.deinit();
|
||||||
|
|
||||||
var scrollable: Scrollable(event.SystemEvent) = .init(box);
|
var scrollable: Scrollable(event.SystemEvent) = .init(box, .{ .rows = size.rows + 15 });
|
||||||
|
|
||||||
var container: Container(event.SystemEvent) = try .init(std.testing.allocator, .{
|
var container: Container(event.SystemEvent) = try .init(std.testing.allocator, .{
|
||||||
.border = .{
|
.border = .{
|
||||||
@@ -265,7 +271,6 @@ test "scrollable horizontal" {
|
|||||||
.direction = .horizontal,
|
.direction = .horizontal,
|
||||||
.padding = .all(1),
|
.padding = .all(1),
|
||||||
},
|
},
|
||||||
.min_size = .{ .cols = size.cols + 15 },
|
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try .init(std.testing.allocator, .{
|
try box.append(try .init(std.testing.allocator, .{
|
||||||
.rectangle = .{ .fill = .grey },
|
.rectangle = .{ .fill = .grey },
|
||||||
@@ -275,7 +280,7 @@ test "scrollable horizontal" {
|
|||||||
}, .{}));
|
}, .{}));
|
||||||
defer box.deinit();
|
defer box.deinit();
|
||||||
|
|
||||||
var scrollable: Scrollable(event.SystemEvent) = .init(box);
|
var scrollable: Scrollable(event.SystemEvent) = .init(box, .{ .cols = size.cols + 15 });
|
||||||
|
|
||||||
var container: Container(event.SystemEvent) = try .init(std.testing.allocator, .{
|
var container: Container(event.SystemEvent) = try .init(std.testing.allocator, .{
|
||||||
.border = .{
|
.border = .{
|
||||||
|
|||||||
Reference in New Issue
Block a user