mod: replace Layout.content with Layout.render
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 30s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 30s
The App.Renderer is used for the new `Layout.render` method. Each layout renders itself now with corresponding renderers which might only update parts of the screen, etc.
This commit is contained in:
@@ -11,32 +11,28 @@ const Key = terminal.Key;
|
||||
|
||||
const log = std.log.scoped(.layout_framing);
|
||||
|
||||
pub fn Layout(comptime Event: type) type {
|
||||
pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Element = union(enum) {
|
||||
layout: @import("../layout.zig").Layout(Event),
|
||||
layout: @import("../layout.zig").Layout(Event, Renderer),
|
||||
widget: @import("../widget.zig").Widget(Event),
|
||||
};
|
||||
const Events = std.ArrayList(Event);
|
||||
const Contents = std.ArrayList(u8);
|
||||
return struct {
|
||||
size: terminal.Size = undefined,
|
||||
contents: Contents = undefined,
|
||||
element: Element = undefined,
|
||||
events: Events = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, element: Element) @This() {
|
||||
return .{
|
||||
.contents = Contents.init(allocator),
|
||||
.element = element,
|
||||
.events = Events.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.contents.deinit();
|
||||
this.events.deinit();
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
@@ -85,19 +81,18 @@ pub fn Layout(comptime Event: type) type {
|
||||
return &this.events;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) !*Contents {
|
||||
this.contents.clearRetainingCapacity();
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
// TODO: padding contents accordingly
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
const layout_content = try layout.content();
|
||||
try this.contents.appendSlice(layout_content.items);
|
||||
try layout.render(renderer);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
try this.contents.appendSlice(try widget.content());
|
||||
const content = try widget.content();
|
||||
// TODO: use renderer
|
||||
_ = try terminal.write(content);
|
||||
},
|
||||
}
|
||||
return &this.contents;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,23 +11,21 @@ const Key = terminal.Key;
|
||||
|
||||
const log = std.log.scoped(.layout_hstack);
|
||||
|
||||
pub fn Layout(comptime Event: type) type {
|
||||
pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Widget = @import("../widget.zig").Widget(Event);
|
||||
const Lay = @import("../layout.zig").Layout(Event);
|
||||
const Lay = @import("../layout.zig").Layout(Event, Renderer);
|
||||
const Element = union(enum) {
|
||||
layout: Lay,
|
||||
widget: Widget,
|
||||
};
|
||||
const Elements = std.ArrayList(Element);
|
||||
const Events = std.ArrayList(Event);
|
||||
const Contents = std.ArrayList(u8);
|
||||
return struct {
|
||||
// TODO: current focused `Element`?
|
||||
size: terminal.Size = undefined,
|
||||
contents: Contents = undefined,
|
||||
elements: Elements = undefined,
|
||||
events: Events = undefined,
|
||||
|
||||
@@ -53,7 +51,6 @@ pub fn Layout(comptime Event: type) type {
|
||||
@compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(Lay) ++ " or " ++ @typeName(Widget) ++ " but " ++ @typeName(ChildType));
|
||||
}
|
||||
return .{
|
||||
.contents = Contents.init(allocator),
|
||||
.elements = elements,
|
||||
.events = Events.init(allocator),
|
||||
};
|
||||
@@ -61,7 +58,6 @@ pub fn Layout(comptime Event: type) type {
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.events.deinit();
|
||||
this.contents.deinit();
|
||||
for (this.elements.items) |*element| {
|
||||
switch (element.*) {
|
||||
.layout => |*layout| {
|
||||
@@ -116,21 +112,21 @@ pub fn Layout(comptime Event: type) type {
|
||||
return &this.events;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) !*Contents {
|
||||
this.contents.clearRetainingCapacity();
|
||||
// TODO: concat contents accordingly to create a vertical stack
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
// TODO: concat contents accordingly to create a horizontal stack
|
||||
for (this.elements.items) |*element| {
|
||||
switch (element.*) {
|
||||
.layout => |*layout| {
|
||||
const layout_content = try layout.content();
|
||||
try this.contents.appendSlice(layout_content.items);
|
||||
try layout.render(renderer);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
try this.contents.appendSlice(try widget.content());
|
||||
// TODO: clear per widget if necessary (i.e. can I query that?)
|
||||
// TODO: render using `renderer`
|
||||
const content = try widget.content();
|
||||
_ = try terminal.write(content);
|
||||
},
|
||||
}
|
||||
}
|
||||
return &this.contents;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,32 +11,28 @@ const Key = terminal.Key;
|
||||
|
||||
const log = std.log.scoped(.layout_padding);
|
||||
|
||||
pub fn Layout(comptime Event: type) type {
|
||||
pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Element = union(enum) {
|
||||
layout: @import("../layout.zig").Layout(Event),
|
||||
layout: @import("../layout.zig").Layout(Event, Renderer),
|
||||
widget: @import("../widget.zig").Widget(Event),
|
||||
};
|
||||
const Events = std.ArrayList(Event);
|
||||
const Contents = std.ArrayList(u8);
|
||||
return struct {
|
||||
size: terminal.Size = undefined,
|
||||
contents: Contents = undefined,
|
||||
element: Element = undefined,
|
||||
events: Events = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, element: Element) @This() {
|
||||
return .{
|
||||
.contents = Contents.init(allocator),
|
||||
.element = element,
|
||||
.events = Events.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.contents.deinit();
|
||||
this.events.deinit();
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
@@ -85,19 +81,18 @@ pub fn Layout(comptime Event: type) type {
|
||||
return &this.events;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) !*Contents {
|
||||
this.contents.clearRetainingCapacity();
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
// TODO: padding contents accordingly
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
const layout_content = try layout.content();
|
||||
try this.contents.appendSlice(layout_content.items);
|
||||
try layout.render(renderer);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
try this.contents.appendSlice(try widget.content());
|
||||
const content = try widget.content();
|
||||
// TODO: use renderer
|
||||
_ = try terminal.write(content);
|
||||
},
|
||||
}
|
||||
return &this.contents;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,23 +11,24 @@ const Key = terminal.Key;
|
||||
|
||||
const log = std.log.scoped(.layout_vstack);
|
||||
|
||||
pub fn Layout(comptime Event: type) type {
|
||||
pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Widget = @import("../widget.zig").Widget(Event);
|
||||
const Lay = @import("../layout.zig").Layout(Event);
|
||||
const Lay = @import("../layout.zig").Layout(Event, Renderer);
|
||||
const Element = union(enum) {
|
||||
layout: Lay,
|
||||
widget: Widget,
|
||||
};
|
||||
const Elements = std.ArrayList(Element);
|
||||
const Events = std.ArrayList(Event);
|
||||
const Contents = std.ArrayList(u8);
|
||||
return struct {
|
||||
// TODO: current focused `Element`?
|
||||
// FIX: this should not be 'hardcoded' but dynamically be calculated and updated (i.e. through the event system)
|
||||
anchor: terminal.Position = .{ .col = 1, .row = 1 },
|
||||
size: terminal.Size = undefined,
|
||||
contents: Contents = undefined,
|
||||
element_rows: u16 = undefined,
|
||||
elements: Elements = undefined,
|
||||
events: Events = undefined,
|
||||
|
||||
@@ -53,7 +54,6 @@ pub fn Layout(comptime Event: type) type {
|
||||
@compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(Lay) ++ " or " ++ @typeName(Widget) ++ " but " ++ @typeName(ChildType));
|
||||
}
|
||||
return .{
|
||||
.contents = Contents.init(allocator),
|
||||
.elements = elements,
|
||||
.events = Events.init(allocator),
|
||||
};
|
||||
@@ -61,7 +61,6 @@ pub fn Layout(comptime Event: type) type {
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.events.deinit();
|
||||
this.contents.deinit();
|
||||
for (this.elements.items) |*element| {
|
||||
switch (element.*) {
|
||||
.layout => |*layout| {
|
||||
@@ -83,9 +82,15 @@ pub fn Layout(comptime Event: type) type {
|
||||
this.size = size;
|
||||
log.debug("Using size: {{ .cols = {d}, .rows = {d} }}", .{ size.cols, size.rows });
|
||||
const len: u16 = @truncate(this.elements.items.len);
|
||||
const rows = size.rows / len;
|
||||
this.element_rows = @divTrunc(size.rows, len);
|
||||
var overflow = this.size.rows % len;
|
||||
// adjust size according to the containing elements
|
||||
for (this.elements.items) |*element| {
|
||||
var rows = this.element_rows;
|
||||
if (overflow > 0) {
|
||||
overflow -|= 1;
|
||||
rows += 1;
|
||||
}
|
||||
const sub_event: Event = .{
|
||||
.resize = .{
|
||||
.cols = size.cols,
|
||||
@@ -124,26 +129,32 @@ pub fn Layout(comptime Event: type) type {
|
||||
return &this.events;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) !*Contents {
|
||||
this.contents.clearRetainingCapacity();
|
||||
// TODO: concat contents accordingly to create a horizontal stack
|
||||
for (this.elements.items, 1..) |*element, i| {
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
// FIX: renderer should clear only what is going to change! (i.e. the 'active' widget / layout)
|
||||
try renderer.clear(this.anchor, this.size);
|
||||
var overflow = this.size.rows % this.elements.items.len;
|
||||
for (this.elements.items, 0..) |*element, i| {
|
||||
const row_mul: u16 = @truncate(i);
|
||||
var row = row_mul * this.element_rows + 1;
|
||||
if (i > 0 and overflow > 0) {
|
||||
overflow -|= 1;
|
||||
row += 1;
|
||||
}
|
||||
const pos: terminal.Position = .{ .col = 1, .row = row };
|
||||
log.debug("using position: .{{ .cols = {d}, .rows = {d} }}", .{ pos.col, pos.row });
|
||||
// TODO: do this using the renderer
|
||||
try terminal.setCursorPosition(pos);
|
||||
switch (element.*) {
|
||||
.layout => |*layout| {
|
||||
const layout_content = try layout.content();
|
||||
try this.contents.appendSlice(layout_content.items);
|
||||
try layout.render(renderer);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
const widget_content = try widget.content();
|
||||
try this.contents.appendSlice(widget_content);
|
||||
// TODO: clear per widget if necesary (i.e. can I query that?)
|
||||
const content = try widget.content();
|
||||
_ = try terminal.write(content);
|
||||
},
|
||||
}
|
||||
// TODO: support clear positioning of content on the tui screen
|
||||
if (i != this.elements.items.len) {
|
||||
try this.contents.appendSlice("\n"); // NOTE: this assumes that the previous content fills all the provided size.rows accordingly with content, such that a newline introduces the start of the next content
|
||||
}
|
||||
}
|
||||
return &this.contents;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user