intermediate #1

Merged
yves-biener merged 31 commits from intermediate into main 2025-02-16 16:02:59 +01:00
4 changed files with 37 additions and 78 deletions
Showing only changes of commit 26d31a38de - Show all commits

View File

@@ -108,6 +108,17 @@ cells of the content (and may be overwritten by child elements contents).
The border of an element should be around independent of the scrolling of the The border of an element should be around independent of the scrolling of the
contents, just like padding. contents, just like padding.
### Scrollable contents
Contents that is scrollable should be done *virtually* through the contents of
the `Container`. This means each container contents implements scrolling for
itself if required.
This still has one issue: Layout of child elements that are already too large
(i.e. or become too small). The library could provide automatic rendering of a
scrollbar given the right parameters however. The scrolling input action would
then also be implemented by the user.
### Input ### Input
How is the user input handled in the containers? Should there be active How is the user input handled in the containers? Should there be active

View File

@@ -31,6 +31,7 @@ pub fn main() !void {
.separator = .{ .enabled = false }, .separator = .{ .enabled = false },
}, },
.layout = .{ .layout = .{
.gap = 2,
.padding = .all(5), .padding = .all(5),
.direction = .vertical, .direction = .vertical,
}, },
@@ -39,7 +40,7 @@ pub fn main() !void {
.rectangle = .{ .fill = .blue }, .rectangle = .{ .fill = .blue },
.layout = .{ .layout = .{
.gap = 1, .gap = 1,
.direction = .horizontal, .direction = .vertical,
.padding = .vertical(1), .padding = .vertical(1),
}, },
}); });
@@ -54,7 +55,10 @@ pub fn main() !void {
})); }));
try container.append(box); try container.append(box);
try container.append(try App.Container.init(allocator, .{ try container.append(try App.Container.init(allocator, .{
.border = .{ .color = .light_blue, .corners = .squared }, .border = .{
.color = .light_blue,
.sides = .vertical(),
},
})); }));
try container.append(try App.Container.init(allocator, .{ try container.append(try App.Container.init(allocator, .{
.rectangle = .{ .fill = .blue }, .rectangle = .{ .fill = .blue },

View File

@@ -235,12 +235,7 @@ pub fn Container(comptime Event: type) type {
} }
return struct { return struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
/// Size of actual columns and rows used to render this `Container` size: Size,
/// The anchor of this `Size` corresponds to the absolute screen position for the viewport.
viewport: Size,
/// Size of the contents columns and rows used for the contents of this `Container`
/// The anchor of this `Size` corresponds to the contents inside of itself.
size: Size = .{},
properties: Properties, properties: Properties,
elements: std.ArrayList(@This()), elements: std.ArrayList(@This()),
@@ -257,7 +252,7 @@ pub fn Container(comptime Event: type) type {
pub fn init(allocator: std.mem.Allocator, properties: Properties) !@This() { pub fn init(allocator: std.mem.Allocator, properties: Properties) !@This() {
return .{ return .{
.allocator = allocator, .allocator = allocator,
.viewport = .{}, .size = .{},
.properties = properties, .properties = properties,
.elements = std.ArrayList(@This()).init(allocator), .elements = std.ArrayList(@This()).init(allocator),
}; };
@@ -277,19 +272,13 @@ pub fn Container(comptime Event: type) type {
pub fn handle(this: *@This(), event: Event) !void { pub fn handle(this: *@This(), event: Event) !void {
switch (event) { switch (event) {
.init => log.debug(".init event", .{}), .init => log.debug(".init event", .{}),
.resize => |s| resize: { .resize => |size| resize: {
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{ log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
s.anchor.col, size.anchor.col,
s.anchor.row, size.anchor.row,
s.cols, size.cols,
s.rows, size.rows,
}); });
this.viewport = s;
const size: Size = .{
.cols = s.cols,
.rows = s.rows,
};
this.size = size; this.size = size;
if (this.elements.items.len == 0) break :resize; if (this.elements.items.len == 0) break :resize;
@@ -349,8 +338,8 @@ pub fn Container(comptime Event: type) type {
} }
element_size = .{ element_size = .{
.anchor = .{ .anchor = .{
.col = this.viewport.anchor.col + offset, .col = this.size.anchor.col + offset,
.row = this.viewport.anchor.row, .row = this.size.anchor.row,
}, },
.cols = cols, .cols = cols,
.rows = size.rows, .rows = size.rows,
@@ -373,8 +362,8 @@ pub fn Container(comptime Event: type) type {
} }
element_size = .{ element_size = .{
.anchor = .{ .anchor = .{
.col = this.viewport.anchor.col, .col = this.size.anchor.col,
.row = this.viewport.anchor.row + offset, .row = this.size.anchor.row + offset,
}, },
.cols = size.cols, .cols = size.cols,
.rows = rows, .rows = rows,
@@ -409,58 +398,13 @@ pub fn Container(comptime Event: type) type {
} }
pub fn contents(this: *const @This()) ![]const Cell { pub fn contents(this: *const @This()) ![]const Cell {
const content_cells = try this.allocator.alloc(Cell, @as(usize, this.size.cols) * @as(usize, this.size.rows)); const cells = try this.allocator.alloc(Cell, @as(usize, this.size.cols) * @as(usize, this.size.rows));
defer this.allocator.free(content_cells); @memset(cells, .{});
@memset(content_cells, .{});
const viewport_cells = try this.allocator.alloc(Cell, @as(usize, this.viewport.cols) * @as(usize, this.viewport.rows)); this.properties.border.contents(cells, this.size, this.properties.layout, @truncate(this.elements.items.len));
@memset(viewport_cells, .{}); this.properties.rectangle.contents(cells, this.size);
this.properties.border.contents(content_cells, this.size, this.properties.layout, @truncate(this.elements.items.len)); return cells;
this.properties.rectangle.contents(content_cells, this.size);
log.debug("Content::contents .scroll.size: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
this.size.anchor.col,
this.size.anchor.row,
this.size.cols,
this.size.rows,
});
const cols = blk: {
var cols: u16 = this.size.cols - this.size.anchor.col;
if (cols > this.viewport.cols) {
cols = this.viewport.cols;
}
break :blk cols;
};
const rows = blk: {
var rows: u16 = this.size.rows - this.size.anchor.col;
if (rows > this.viewport.rows) {
rows = this.viewport.rows;
}
break :blk rows;
};
var content_row: usize = this.size.anchor.row;
var content_col: usize = this.size.anchor.col;
var viewport_row: usize = 0;
var viewport_col: usize = 0;
for (0..rows) |_| {
for (0..cols) |_| {
// TODO: try to do this with @memcpy instead to improve performance
const cell = content_cells[(content_row * this.size.cols) + content_col];
viewport_cells[(viewport_row * this.viewport.cols) + viewport_col] = cell;
content_col += 1;
viewport_col += 1;
}
content_row += 1;
viewport_row += 1;
content_col = 0;
viewport_col = 0;
}
return viewport_cells;
} }
}; };
} }

View File

@@ -62,17 +62,17 @@ pub const Buffered = struct {
/// Render provided cells at size (anchor and dimension) into the *virtual screen*. /// Render provided cells at size (anchor and dimension) into the *virtual screen*.
pub fn render(this: *@This(), comptime T: type, container: *T) !void { pub fn render(this: *@This(), comptime T: type, container: *T) !void {
const viewport: Size = container.viewport; const size: Size = container.size;
const cells: []const Cell = try container.contents(); const cells: []const Cell = try container.contents();
if (cells.len == 0) return; if (cells.len == 0) return;
var idx: usize = 0; var idx: usize = 0;
var vs = this.virtual_screen; var vs = this.virtual_screen;
const anchor: usize = (@as(usize, viewport.anchor.row) * @as(usize, this.size.cols)) + @as(usize, viewport.anchor.col); const anchor: usize = (@as(usize, size.anchor.row) * @as(usize, this.size.cols)) + @as(usize, size.anchor.col);
blk: for (0..viewport.rows) |row| { blk: for (0..size.rows) |row| {
for (0..viewport.cols) |col| { for (0..size.cols) |col| {
const cell = cells[idx]; const cell = cells[idx];
idx += 1; idx += 1;