diff --git a/README.md b/README.md index 0b22e58..e49c279 100644 --- a/README.md +++ b/README.md @@ -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 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 How is the user input handled in the containers? Should there be active diff --git a/examples/container.zig b/examples/container.zig index 62c6dee..d732fc1 100644 --- a/examples/container.zig +++ b/examples/container.zig @@ -31,6 +31,7 @@ pub fn main() !void { .separator = .{ .enabled = false }, }, .layout = .{ + .gap = 2, .padding = .all(5), .direction = .vertical, }, @@ -39,7 +40,7 @@ pub fn main() !void { .rectangle = .{ .fill = .blue }, .layout = .{ .gap = 1, - .direction = .horizontal, + .direction = .vertical, .padding = .vertical(1), }, }); @@ -54,7 +55,10 @@ pub fn main() !void { })); try container.append(box); 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, .{ .rectangle = .{ .fill = .blue }, diff --git a/src/container.zig b/src/container.zig index 596a1db..4d89e84 100644 --- a/src/container.zig +++ b/src/container.zig @@ -235,12 +235,7 @@ pub fn Container(comptime Event: type) type { } return struct { allocator: std.mem.Allocator, - /// Size of actual columns and rows used to render this `Container` - /// 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 = .{}, + size: Size, properties: Properties, 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() { return .{ .allocator = allocator, - .viewport = .{}, + .size = .{}, .properties = properties, .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 { switch (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} }}", .{ - s.anchor.col, - s.anchor.row, - s.cols, - s.rows, + size.anchor.col, + size.anchor.row, + size.cols, + size.rows, }); - this.viewport = s; - - const size: Size = .{ - .cols = s.cols, - .rows = s.rows, - }; this.size = size; if (this.elements.items.len == 0) break :resize; @@ -349,8 +338,8 @@ pub fn Container(comptime Event: type) type { } element_size = .{ .anchor = .{ - .col = this.viewport.anchor.col + offset, - .row = this.viewport.anchor.row, + .col = this.size.anchor.col + offset, + .row = this.size.anchor.row, }, .cols = cols, .rows = size.rows, @@ -373,8 +362,8 @@ pub fn Container(comptime Event: type) type { } element_size = .{ .anchor = .{ - .col = this.viewport.anchor.col, - .row = this.viewport.anchor.row + offset, + .col = this.size.anchor.col, + .row = this.size.anchor.row + offset, }, .cols = size.cols, .rows = rows, @@ -409,58 +398,13 @@ pub fn Container(comptime Event: type) type { } 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)); - defer this.allocator.free(content_cells); - @memset(content_cells, .{}); + const cells = try this.allocator.alloc(Cell, @as(usize, this.size.cols) * @as(usize, this.size.rows)); + @memset(cells, .{}); - const viewport_cells = try this.allocator.alloc(Cell, @as(usize, this.viewport.cols) * @as(usize, this.viewport.rows)); - @memset(viewport_cells, .{}); + this.properties.border.contents(cells, this.size, this.properties.layout, @truncate(this.elements.items.len)); + this.properties.rectangle.contents(cells, this.size); - this.properties.border.contents(content_cells, this.size, this.properties.layout, @truncate(this.elements.items.len)); - 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; + return cells; } }; } diff --git a/src/render.zig b/src/render.zig index bb01d14..f9875d0 100644 --- a/src/render.zig +++ b/src/render.zig @@ -62,17 +62,17 @@ pub const Buffered = struct { /// Render provided cells at size (anchor and dimension) into the *virtual screen*. 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(); if (cells.len == 0) return; var idx: usize = 0; 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| { - for (0..viewport.cols) |col| { + blk: for (0..size.rows) |row| { + for (0..size.cols) |col| { const cell = cells[idx]; idx += 1;