mod: change widget interface Widget.content replaced with Widget.render
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 29s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 29s
The .resize `Event` has been adapted to include an _anchor_, which provide the full necessary information for each widget where to render on the screen with what requested size. Each Widget can then dynamically decide how and what to render (i.e. provide placeholder text in case the size is too small, etc.).
This commit is contained in:
@@ -47,7 +47,7 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
|
||||
pub const Event = mergeTaggedUnions(event.SystemEvent, E);
|
||||
pub const Renderer = R(fullscreen);
|
||||
pub const Layout = @import("layout.zig").Layout(Event, Renderer);
|
||||
pub const Widget = @import("widget.zig").Widget(Event);
|
||||
pub const Widget = @import("widget.zig").Widget(Event, Renderer);
|
||||
|
||||
queue: Queue(Event, 256) = .{},
|
||||
thread: ?std.Thread = null,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
}
|
||||
const Element = union(enum) {
|
||||
layout: @import("../layout.zig").Layout(Event, Renderer),
|
||||
widget: @import("../widget.zig").Widget(Event),
|
||||
widget: @import("../widget.zig").Widget(Event, Renderer),
|
||||
};
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ 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 Widget = @import("../widget.zig").Widget(Event, Renderer);
|
||||
const Lay = @import("../layout.zig").Layout(Event, Renderer);
|
||||
const Element = union(enum) {
|
||||
layout: Lay,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
}
|
||||
const Element = union(enum) {
|
||||
layout: @import("../layout.zig").Layout(Event, Renderer),
|
||||
widget: @import("../widget.zig").Widget(Event),
|
||||
widget: @import("../widget.zig").Widget(Event, Renderer),
|
||||
};
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
|
||||
@@ -15,7 +15,7 @@ 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 Widget = @import("../widget.zig").Widget(Event, Renderer);
|
||||
const Lay = @import("../layout.zig").Layout(Event, Renderer);
|
||||
const Element = union(enum) {
|
||||
layout: Lay,
|
||||
@@ -80,10 +80,16 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
switch (event) {
|
||||
.resize => |size| {
|
||||
this.size = size;
|
||||
log.debug("Using size: {{ .cols = {d}, .rows = {d} }}", .{ size.cols, size.rows });
|
||||
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
|
||||
size.anchor.col,
|
||||
size.anchor.row,
|
||||
size.cols,
|
||||
size.rows,
|
||||
});
|
||||
const len: u16 = @truncate(this.elements.items.len);
|
||||
this.element_rows = @divTrunc(size.rows, len);
|
||||
var overflow = this.size.rows % len;
|
||||
var offset: u16 = 0;
|
||||
// adjust size according to the containing elements
|
||||
for (this.elements.items) |*element| {
|
||||
var rows = this.element_rows;
|
||||
@@ -93,10 +99,15 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
}
|
||||
const sub_event: Event = .{
|
||||
.resize = .{
|
||||
.anchor = .{
|
||||
.col = size.anchor.col,
|
||||
.row = size.anchor.row + offset,
|
||||
},
|
||||
.cols = size.cols,
|
||||
.rows = rows,
|
||||
},
|
||||
};
|
||||
offset += rows;
|
||||
switch (element.*) {
|
||||
.layout => |*layout| {
|
||||
const events = try layout.handle(sub_event);
|
||||
@@ -140,8 +151,8 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
overflow -|= 1;
|
||||
row += 1;
|
||||
}
|
||||
// TODO: that's the anchor of each component (only necessary for each widget rendering)
|
||||
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.*) {
|
||||
@@ -150,8 +161,7 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
},
|
||||
.widget => |*widget| {
|
||||
// TODO: clear per widget if necesary (i.e. can I query that?)
|
||||
const content = try widget.content();
|
||||
_ = try terminal.write(content);
|
||||
try widget.render(renderer);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub const code_point = @import("code_point");
|
||||
const log = std.log.scoped(.terminal);
|
||||
|
||||
pub const Size = struct {
|
||||
anchor: Position = .{ .col = 1, .row = 1 }, // top left corner by default
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Dynamic dispatch for widget implementations.
|
||||
//! Each widget should at last implement these functions:
|
||||
//! - handle(this: *@This(), event: Event) ?Event {}
|
||||
//! - content(this: *@This()) ![]u8 {}
|
||||
//! - render(this: *@This(), renderer: Renderer) !void {}
|
||||
//! - deinit(this: *@This()) void {}
|
||||
//!
|
||||
//! Create a `Widget` using `createFrom(object: anytype)` and use them through
|
||||
@@ -10,12 +10,14 @@
|
||||
//!
|
||||
//! Each `Widget` may cache its content and should if the contents will not
|
||||
//! change for a long time.
|
||||
const std = @import("std");
|
||||
//!
|
||||
//! When `Widget.render` is called the provided `Renderer` type is expected
|
||||
//! which handles how contents are rendered for a given widget.
|
||||
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
||||
|
||||
const log = std.log.scoped(.widget);
|
||||
const log = @import("std").log.scoped(.widget);
|
||||
|
||||
pub fn Widget(comptime Event: type) type {
|
||||
pub fn Widget(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)`.");
|
||||
}
|
||||
@@ -25,7 +27,7 @@ pub fn Widget(comptime Event: type) type {
|
||||
|
||||
const VTable = struct {
|
||||
handle: *const fn (this: *WidgetType, event: Event) ?Event,
|
||||
content: *const fn (this: *WidgetType) anyerror![]u8,
|
||||
render: *const fn (this: *WidgetType, renderer: Renderer) anyerror!void,
|
||||
deinit: *const fn (this: *WidgetType) void,
|
||||
};
|
||||
|
||||
@@ -36,16 +38,21 @@ pub fn Widget(comptime Event: type) type {
|
||||
pub fn handle(this: *WidgetType, event: Event) ?Event {
|
||||
switch (event) {
|
||||
.resize => |size| {
|
||||
log.debug("received size: .{{ .cols = {d}, .rows = {d} }}", .{ size.cols, size.rows });
|
||||
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
|
||||
size.anchor.col,
|
||||
size.anchor.row,
|
||||
size.cols,
|
||||
size.rows,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return this.vtable.handle(this, event);
|
||||
}
|
||||
|
||||
// Return the entire content of this `Widget`.
|
||||
pub fn content(this: *WidgetType) ![]u8 {
|
||||
return try this.vtable.content(this);
|
||||
// Render the content of this `Widget` given the `Size` of the widget (.resize System`Event`).
|
||||
pub fn render(this: *WidgetType, renderer: Renderer) !void {
|
||||
try this.vtable.render(this, renderer);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *WidgetType) void {
|
||||
@@ -64,13 +71,13 @@ pub fn Widget(comptime Event: type) type {
|
||||
return widget.handle(event);
|
||||
}
|
||||
}.handle,
|
||||
.content = struct {
|
||||
.render = struct {
|
||||
// Return the entire content of this `Widget`.
|
||||
fn content(this: *WidgetType) ![]u8 {
|
||||
fn render(this: *WidgetType, renderer: Renderer) !void {
|
||||
const widget: @TypeOf(object) = @ptrFromInt(this.object);
|
||||
return try widget.content();
|
||||
try widget.render(renderer);
|
||||
}
|
||||
}.content,
|
||||
}.render,
|
||||
.deinit = struct {
|
||||
fn deinit(this: *WidgetType) void {
|
||||
const widget: @TypeOf(object) = @ptrFromInt(this.object);
|
||||
@@ -82,7 +89,7 @@ pub fn Widget(comptime Event: type) type {
|
||||
}
|
||||
|
||||
// TODO: import and export of `Widget` implementations (with corresponding initialization using `Event`)
|
||||
pub const RawText = @import("widget/RawText.zig").Widget(Event);
|
||||
pub const Spacer = @import("widget/Spacer.zig").Widget(Event);
|
||||
pub const RawText = @import("widget/RawText.zig").Widget(Event, Renderer);
|
||||
pub const Spacer = @import("widget/Spacer.zig").Widget(Event, Renderer);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const Style = terminal.Style;
|
||||
|
||||
const log = std.log.scoped(.widget_rawtext);
|
||||
|
||||
pub fn Widget(comptime Event: type) type {
|
||||
pub fn Widget(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)`.");
|
||||
}
|
||||
@@ -75,20 +75,23 @@ pub fn Widget(comptime Event: type) type {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) ![]u8 {
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
try terminal.setCursorPosition(this.size.anchor);
|
||||
// TODO: render `this.contents`
|
||||
_ = renderer;
|
||||
if (this.size.rows >= this.line_index.items.len) {
|
||||
return this.contents.items;
|
||||
_ = try terminal.write(this.contents.items);
|
||||
} else {
|
||||
// more rows than we can display
|
||||
const i = this.line_index.items[this.line];
|
||||
const e = this.size.rows + this.line + 1;
|
||||
if (e >= this.line_index.items.len) {
|
||||
return this.contents.items[i..];
|
||||
_ = try terminal.write(this.contents.items[i..]);
|
||||
}
|
||||
// last line should not end with the last character (likely a newline character)
|
||||
// FIX: what about files which do not end with a newline?
|
||||
const x = this.line_index.items[e] - 1;
|
||||
return this.contents.items[i..x];
|
||||
_ = try terminal.write(this.contents.items[i..x]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ const Error = @import("../event.zig").Error;
|
||||
|
||||
const log = std.log.scoped(.widget_spacer);
|
||||
|
||||
pub fn Widget(comptime Event: type) type {
|
||||
pub fn Widget(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)`.");
|
||||
}
|
||||
@@ -25,9 +25,10 @@ pub fn Widget(comptime Event: type) type {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) ![]u8 {
|
||||
pub fn render(this: *@This(), renderer: Renderer) !void {
|
||||
// FIX: this should rather clear the `terminal.Size` provided through the event system using the _renderer_.
|
||||
_ = this;
|
||||
return &[0]u8{};
|
||||
_ = renderer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user