intermediate #1
11
README.md
11
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
|
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
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user