fix(element/scrollable): support deriving Container size of scrollable
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 13s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 13s
This commit is contained in:
@@ -59,7 +59,9 @@ pub fn main() !void {
|
|||||||
.layout = .{
|
.layout = .{
|
||||||
.gap = 1,
|
.gap = 1,
|
||||||
.padding = .vertical(2),
|
.padding = .vertical(2),
|
||||||
|
.direction = .vertical,
|
||||||
},
|
},
|
||||||
|
.size = .{ .y = 90 },
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try App.Container.init(allocator, .{
|
try box.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .light_green },
|
.rectangle = .{ .fill = .light_green },
|
||||||
@@ -72,10 +74,7 @@ pub fn main() !void {
|
|||||||
}, .{}));
|
}, .{}));
|
||||||
defer box.deinit();
|
defer box.deinit();
|
||||||
|
|
||||||
var scrollable: App.Scrollable = .{
|
var scrollable: App.Scrollable = .{ .container = box };
|
||||||
.container = box,
|
|
||||||
.min_size = .{ .x = 60 },
|
|
||||||
};
|
|
||||||
|
|
||||||
var container = try App.Container.init(allocator, .{
|
var container = try App.Container.init(allocator, .{
|
||||||
.layout = .{
|
.layout = .{
|
||||||
@@ -92,7 +91,7 @@ pub fn main() !void {
|
|||||||
.color = .light_blue,
|
.color = .light_blue,
|
||||||
.sides = .all,
|
.sides = .all,
|
||||||
},
|
},
|
||||||
.size = .{ .x = 200 },
|
.size = .{ .x = 100 },
|
||||||
}, .{}));
|
}, .{}));
|
||||||
try container.append(try App.Container.init(allocator, .{
|
try container.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .blue },
|
.rectangle = .{ .fill = .blue },
|
||||||
|
|||||||
@@ -88,19 +88,25 @@ pub fn main() !void {
|
|||||||
var top_box = try App.Container.init(allocator, .{
|
var top_box = try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .blue },
|
.rectangle = .{ .fill = .blue },
|
||||||
.layout = .{
|
.layout = .{
|
||||||
.gap = 1,
|
.gap = 2,
|
||||||
|
.separator = .{
|
||||||
|
.enabled = true,
|
||||||
|
},
|
||||||
.direction = .vertical,
|
.direction = .vertical,
|
||||||
.padding = .vertical(1),
|
.padding = .vertical(1),
|
||||||
},
|
},
|
||||||
}, .{});
|
}, .{});
|
||||||
try top_box.append(try App.Container.init(allocator, .{
|
try top_box.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .light_green },
|
.rectangle = .{ .fill = .light_green },
|
||||||
|
.size = .{ .y = 30 },
|
||||||
}, .{}));
|
}, .{}));
|
||||||
try top_box.append(try App.Container.init(allocator, .{
|
try top_box.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .light_green },
|
.rectangle = .{ .fill = .light_green },
|
||||||
|
.size = .{ .y = 5 },
|
||||||
}, element));
|
}, element));
|
||||||
try top_box.append(try App.Container.init(allocator, .{
|
try top_box.append(try App.Container.init(allocator, .{
|
||||||
.rectangle = .{ .fill = .light_green },
|
.rectangle = .{ .fill = .light_green },
|
||||||
|
.size = .{ .y = 2 },
|
||||||
}, .{}));
|
}, .{}));
|
||||||
defer top_box.deinit();
|
defer top_box.deinit();
|
||||||
|
|
||||||
@@ -143,10 +149,10 @@ pub fn main() !void {
|
|||||||
defer container.deinit();
|
defer container.deinit();
|
||||||
|
|
||||||
// place empty container containing the element of the scrollable Container.
|
// place empty container containing the element of the scrollable Container.
|
||||||
var scrollable_top: App.Scrollable = .init(top_box, .{ .y = 50 });
|
var scrollable_top: App.Scrollable = .{ .container = top_box };
|
||||||
try container.append(try App.Container.init(allocator, .{}, scrollable_top.element()));
|
try container.append(try App.Container.init(allocator, .{}, scrollable_top.element()));
|
||||||
|
|
||||||
var scrollable_bottom: App.Scrollable = .init(bottom_box, .{ .y = 30 });
|
var scrollable_bottom: App.Scrollable = .{ .container = bottom_box };
|
||||||
try container.append(try App.Container.init(allocator, .{}, scrollable_bottom.element()));
|
try container.append(try App.Container.init(allocator, .{}, scrollable_bottom.element()));
|
||||||
|
|
||||||
try app.start();
|
try app.start();
|
||||||
|
|||||||
@@ -633,14 +633,13 @@ pub fn Container(comptime Event: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fit_resize(this: *@This()) Point {
|
pub fn fit_resize(this: *@This()) Point {
|
||||||
// NOTE this is supposed to be a simple and easy to understand alogrithm, there are currently no optimizations done
|
// NOTE this is supposed to be a simple and easy to understand alogrithm, there are currently no optimizations done
|
||||||
const layout = this.properties.layout;
|
const layout = this.properties.layout;
|
||||||
var size: Point = switch (layout.direction) {
|
var size: Point = switch (layout.direction) {
|
||||||
.horizontal => .{ .x = layout.gap * @as(u16, @truncate(this.elements.items.len -| 1)) },
|
.horizontal => .{ .x = layout.gap * @as(u16, @truncate(this.elements.items.len -| 1)) },
|
||||||
.vertical => .{ .y = layout.gap * @as(u16, @truncate(this.elements.items.len -| 1)) },
|
.vertical => .{ .y = layout.gap * @as(u16, @truncate(this.elements.items.len -| 1)) },
|
||||||
};
|
};
|
||||||
size = Point.add(size, this.properties.size);
|
|
||||||
|
|
||||||
if (this.elements.items.len > 0) switch (layout.direction) {
|
if (this.elements.items.len > 0) switch (layout.direction) {
|
||||||
.horizontal => size.x += layout.padding.left + layout.padding.right,
|
.horizontal => size.x += layout.padding.left + layout.padding.right,
|
||||||
@@ -671,8 +670,8 @@ pub fn Container(comptime Event: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assign currently calculated size
|
// assign currently calculated size
|
||||||
this.size = size;
|
this.size = Point.max(size, this.properties.size);
|
||||||
return size;
|
return this.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// growable implicitly requires the root `Container` to have a set a size property to the size of the available terminal screen
|
// growable implicitly requires the root `Container` to have a set a size property to the size of the available terminal screen
|
||||||
@@ -818,6 +817,7 @@ pub fn Container(comptime Event: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn contents(this: *const @This()) ![]const Cell {
|
pub fn contents(this: *const @This()) ![]const Cell {
|
||||||
|
if (this.size.x == 0 or this.size.y == 0) return Error.TooSmall;
|
||||||
const cells = try this.allocator.alloc(Cell, @as(usize, this.size.x) * @as(usize, this.size.y));
|
const cells = try this.allocator.alloc(Cell, @as(usize, this.size.x) * @as(usize, this.size.y));
|
||||||
@memset(cells, .{});
|
@memset(cells, .{});
|
||||||
errdefer this.allocator.free(cells);
|
errdefer this.allocator.free(cells);
|
||||||
|
|||||||
@@ -70,14 +70,9 @@ 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: Point = .{},
|
size: Point = .{},
|
||||||
/// 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: Point = .{},
|
|
||||||
/// `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: Point = .{},
|
container_size: Point = .{},
|
||||||
container_origin: Point = .{},
|
|
||||||
/// Anchor of the viewport of the scrollable `Container`.
|
/// Anchor of the viewport of the scrollable `Container`.
|
||||||
anchor: Point = .{},
|
anchor: Point = .{},
|
||||||
/// The actual `Container`, that is scrollable.
|
/// The actual `Container`, that is scrollable.
|
||||||
@@ -88,31 +83,19 @@ pub fn Scrollable(Event: type) type {
|
|||||||
.ptr = this,
|
.ptr = this,
|
||||||
.vtable = &.{
|
.vtable = &.{
|
||||||
.resize = resize,
|
.resize = resize,
|
||||||
.reposition = reposition,
|
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
.content = content,
|
.content = content,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(container: Container(Event), min_size: Point) @This() {
|
|
||||||
return .{
|
|
||||||
.container = container,
|
|
||||||
.min_size = min_size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize(ctx: *anyopaque, size: Point) void {
|
fn resize(ctx: *anyopaque, size: Point) void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
this.size = size;
|
this.size = size;
|
||||||
// TODO scrollbar space - depending on configuration and only if necessary?
|
|
||||||
this.container_size = Point.max(size, this.min_size);
|
|
||||||
this.container.resize(.{}, this.container_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reposition(ctx: *anyopaque, origin: Point) void {
|
// TODO scrollbar space - depending on configuration and only if necessary?
|
||||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
this.container_size = Point.max(size, this.container.fit_resize());
|
||||||
this.container_origin = origin; // TODO the size should be a provided origin
|
this.container.resize(.{}, this.container_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(ctx: *anyopaque, event: Event) !void {
|
fn handle(ctx: *anyopaque, event: Event) !void {
|
||||||
@@ -170,7 +153,7 @@ pub fn Scrollable(Event: type) type {
|
|||||||
|
|
||||||
fn content(ctx: *anyopaque, cells: []Cell, origin: Point, size: Point) !void {
|
fn content(ctx: *anyopaque, cells: []Cell, origin: Point, size: Point) !void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
_ = origin; // this should be used
|
_ = origin;
|
||||||
std.debug.assert(cells.len == @as(usize, this.size.x) * @as(usize, this.size.y));
|
std.debug.assert(cells.len == @as(usize, this.size.x) * @as(usize, this.size.y));
|
||||||
|
|
||||||
const container_size = this.container.size;
|
const container_size = this.container.size;
|
||||||
@@ -183,7 +166,6 @@ pub fn Scrollable(Event: type) type {
|
|||||||
@memcpy(container_cells, container_cells_const);
|
@memcpy(container_cells, container_cells_const);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIX this is not resolving the rendering recursively! This means that the content is only shown for the first children
|
|
||||||
for (this.container.elements.items) |child| try render_container(child, container_cells, container_origin, container_size);
|
for (this.container.elements.items) |child| try render_container(child, container_cells, container_origin, container_size);
|
||||||
|
|
||||||
const anchor = (@as(usize, this.anchor.y) * @as(usize, container_size.x)) + @as(usize, this.anchor.x);
|
const anchor = (@as(usize, this.anchor.y) * @as(usize, container_size.x)) + @as(usize, this.anchor.x);
|
||||||
@@ -224,6 +206,7 @@ test "scrollable vertical" {
|
|||||||
.direction = .vertical,
|
.direction = .vertical,
|
||||||
.padding = .all(1),
|
.padding = .all(1),
|
||||||
},
|
},
|
||||||
|
.size = .{ .y = size.y + 15 },
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try .init(allocator, .{
|
try box.append(try .init(allocator, .{
|
||||||
.rectangle = .{ .fill = .grey },
|
.rectangle = .{ .fill = .grey },
|
||||||
@@ -233,7 +216,7 @@ test "scrollable vertical" {
|
|||||||
}, .{}));
|
}, .{}));
|
||||||
defer box.deinit();
|
defer box.deinit();
|
||||||
|
|
||||||
var scrollable: Scrollable(event.SystemEvent) = .init(box, .{ .y = size.y + 15 });
|
var scrollable: Scrollable(event.SystemEvent) = .{ .container = box };
|
||||||
|
|
||||||
var container: Container(event.SystemEvent) = try .init(allocator, .{
|
var container: Container(event.SystemEvent) = try .init(allocator, .{
|
||||||
.border = .{
|
.border = .{
|
||||||
@@ -298,6 +281,7 @@ test "scrollable horizontal" {
|
|||||||
.direction = .horizontal,
|
.direction = .horizontal,
|
||||||
.padding = .all(1),
|
.padding = .all(1),
|
||||||
},
|
},
|
||||||
|
.size = .{ .x = size.x + 15 },
|
||||||
}, .{});
|
}, .{});
|
||||||
try box.append(try .init(allocator, .{
|
try box.append(try .init(allocator, .{
|
||||||
.rectangle = .{ .fill = .grey },
|
.rectangle = .{ .fill = .grey },
|
||||||
@@ -307,7 +291,7 @@ test "scrollable horizontal" {
|
|||||||
}, .{}));
|
}, .{}));
|
||||||
defer box.deinit();
|
defer box.deinit();
|
||||||
|
|
||||||
var scrollable: Scrollable(event.SystemEvent) = .init(box, .{ .x = size.x + 15 });
|
var scrollable: Scrollable(event.SystemEvent) = .{ .container = box };
|
||||||
|
|
||||||
var container: Container(event.SystemEvent) = try .init(allocator, .{
|
var container: Container(event.SystemEvent) = try .init(allocator, .{
|
||||||
.border = .{
|
.border = .{
|
||||||
|
|||||||
Reference in New Issue
Block a user