add(layout/vstack): intial implementation of vstack layout
This commit is contained in:
@@ -21,6 +21,9 @@ delay for each frame in each line of the output.
|
|||||||
- [ ] Have clickable/navigatable links inside of the tui application
|
- [ ] Have clickable/navigatable links inside of the tui application
|
||||||
- [ ] Launch simple http server alongside tui application
|
- [ ] Launch simple http server alongside tui application
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] Split into own repository
|
||||||
- [ ] Create other layouts
|
- [ ] Create other layouts
|
||||||
- [ ] horizontal stack
|
- [ ] horizontal stack
|
||||||
- [ ] vertical stack
|
- [ ] vertical stack
|
||||||
@@ -28,13 +31,15 @@ delay for each frame in each line of the output.
|
|||||||
- [ ] Building Block `Layout`s
|
- [ ] Building Block `Layout`s
|
||||||
- [ ] Framing `Layout`
|
- [ ] Framing `Layout`
|
||||||
- [ ] Padding `Layout`
|
- [ ] Padding `Layout`
|
||||||
|
- [ ] Create demo gifs using [vhs](https://github.com/charmbracelet/vhs)
|
||||||
|
- [ ] Move documentation into new repository
|
||||||
|
- [ ] add dependency to new repository into this project
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Branch: `own-tty-visuals`
|
## Branch: `own-tty-visuals`
|
||||||
|
|
||||||
- [ ] How can I support to run a sub-process inside of a given pane / layout?
|
- [ ] How can I support to run a sub-process inside of a given pane / layout?
|
||||||
- [ ] Create demo gifs using [vhs](https://github.com/charmbracelet/vhs)
|
|
||||||
|
|
||||||
- [x] Could I simulate a corresponding event loop?
|
- [x] Could I simulate a corresponding event loop?
|
||||||
- emmit as many as possible through another thread (until the event queue is full?) [1023]
|
- emmit as many as possible through another thread (until the event queue is full?) [1023]
|
||||||
@@ -43,3 +48,5 @@ delay for each frame in each line of the output.
|
|||||||
-> Or buffered writer to the `std.posix.STDOUT_FILENO`?
|
-> Or buffered writer to the `std.posix.STDOUT_FILENO`?
|
||||||
-> I could use this to see if it makes sense to implement a buffered version using a screen buffer (to only render the differences?)
|
-> I could use this to see if it makes sense to implement a buffered version using a screen buffer (to only render the differences?)
|
||||||
- seems pretty good (with some exceptions)
|
- seems pretty good (with some exceptions)
|
||||||
|
|
||||||
|
- [ ] styling could be tricky with a given layout (which introduces corresponding line breaks ...)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
|||||||
const Error = @import("../event.zig").Error;
|
const Error = @import("../event.zig").Error;
|
||||||
const Key = @import("../key.zig");
|
const Key = @import("../key.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.layout_vstack);
|
||||||
|
|
||||||
pub fn Layout(comptime Event: type) type {
|
pub fn Layout(comptime Event: type) type {
|
||||||
if (!isTaggedUnion(Event)) {
|
if (!isTaggedUnion(Event)) {
|
||||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||||
@@ -78,9 +80,17 @@ pub fn Layout(comptime Event: type) type {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
.resize => |size| {
|
.resize => |size| {
|
||||||
this.size = size;
|
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;
|
||||||
// adjust size according to the containing elements
|
// adjust size according to the containing elements
|
||||||
for (this.elements.items) |*element| {
|
for (this.elements.items) |*element| {
|
||||||
const sub_event = event;
|
const sub_event: Event = .{
|
||||||
|
.resize = .{
|
||||||
|
.cols = size.cols,
|
||||||
|
.rows = rows,
|
||||||
|
},
|
||||||
|
};
|
||||||
switch (element.*) {
|
switch (element.*) {
|
||||||
.layout => |*layout| {
|
.layout => |*layout| {
|
||||||
const events = try layout.handle(sub_event);
|
const events = try layout.handle(sub_event);
|
||||||
@@ -115,17 +125,22 @@ pub fn Layout(comptime Event: type) type {
|
|||||||
|
|
||||||
pub fn content(this: *@This()) !*Contents {
|
pub fn content(this: *@This()) !*Contents {
|
||||||
this.contents.clearRetainingCapacity();
|
this.contents.clearRetainingCapacity();
|
||||||
// TODO: concat contents accordingly to create a vertical stack
|
// TODO: concat contents accordingly to create a horizontal stack
|
||||||
for (this.elements.items) |*element| {
|
for (this.elements.items, 1..) |*element, i| {
|
||||||
switch (element.*) {
|
switch (element.*) {
|
||||||
.layout => |*layout| {
|
.layout => |*layout| {
|
||||||
const layout_content = try layout.content();
|
const layout_content = try layout.content();
|
||||||
try this.contents.appendSlice(layout_content.items);
|
try this.contents.appendSlice(layout_content.items);
|
||||||
},
|
},
|
||||||
.widget => |*widget| {
|
.widget => |*widget| {
|
||||||
try this.contents.appendSlice(try widget.content());
|
const widget_content = try widget.content();
|
||||||
|
try this.contents.appendSlice(widget_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;
|
return &this.contents;
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.zig
14
src/main.zig
@@ -33,17 +33,21 @@ pub fn main() !void {
|
|||||||
var rawText = App.Widget.RawText.init(allocator, file);
|
var rawText = App.Widget.RawText.init(allocator, file);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
const doc = try std.fs.cwd().openFile("./doc/test.md", .{});
|
||||||
|
var docText = App.Widget.RawText.init(allocator, doc);
|
||||||
|
doc.close();
|
||||||
|
|
||||||
var framing = App.Layout.Framing.init(allocator, .{
|
var framing = App.Layout.Framing.init(allocator, .{
|
||||||
.widget = App.Widget.createFrom(&rawText),
|
.widget = App.Widget.createFrom(&rawText),
|
||||||
});
|
});
|
||||||
var vstack = App.Layout.VStack.init(allocator, .{
|
var hstack = App.Layout.HStack.init(allocator, .{
|
||||||
App.Layout.createFrom(&framing),
|
App.Layout.createFrom(&framing),
|
||||||
});
|
});
|
||||||
var hstack = App.Layout.HStack.init(allocator, .{
|
var vstack = App.Layout.VStack.init(allocator, .{
|
||||||
App.Layout.createFrom(&vstack),
|
App.Widget.createFrom(&docText),
|
||||||
|
App.Layout.createFrom(&hstack),
|
||||||
});
|
});
|
||||||
|
var layout = App.Layout.createFrom(&vstack);
|
||||||
var layout = App.Layout.createFrom(&hstack);
|
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start();
|
||||||
|
|||||||
@@ -6,30 +6,37 @@ const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
|||||||
const Error = @import("../event.zig").Error;
|
const Error = @import("../event.zig").Error;
|
||||||
const Key = @import("../key.zig");
|
const Key = @import("../key.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.widget_rawtext);
|
||||||
|
|
||||||
pub fn Widget(comptime Event: type) type {
|
pub fn Widget(comptime Event: type) type {
|
||||||
if (!isTaggedUnion(Event)) {
|
if (!isTaggedUnion(Event)) {
|
||||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||||
}
|
}
|
||||||
|
const Contents = std.ArrayList(u8);
|
||||||
return struct {
|
return struct {
|
||||||
c: std.ArrayList(u8) = undefined,
|
contents: Contents = undefined,
|
||||||
line_index: std.ArrayList(usize) = undefined,
|
line_index: std.ArrayList(usize) = undefined,
|
||||||
line: usize = 0,
|
line: usize = 0,
|
||||||
size: terminal.Size = undefined,
|
size: terminal.Size = undefined,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, file: std.fs.File) @This() {
|
pub fn init(allocator: std.mem.Allocator, file: std.fs.File) @This() {
|
||||||
var c = std.ArrayList(u8).init(allocator);
|
var contents = Contents.init(allocator);
|
||||||
var line_index = std.ArrayList(usize).init(allocator);
|
var line_index = std.ArrayList(usize).init(allocator);
|
||||||
file.reader().readAllArrayList(&c, 4192) catch {};
|
file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {};
|
||||||
for (c.items, 0..) |item, i| {
|
line_index.append(0) catch {};
|
||||||
|
for (contents.items, 0..) |item, i| {
|
||||||
if (item == '\n') {
|
if (item == '\n') {
|
||||||
line_index.append(i) catch {};
|
line_index.append(i + 1) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .{ .c = c, .line_index = line_index };
|
return .{
|
||||||
|
.contents = contents,
|
||||||
|
.line_index = line_index,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(this: *@This()) void {
|
pub fn deinit(this: *@This()) void {
|
||||||
this.c.deinit();
|
this.contents.deinit();
|
||||||
this.line_index.deinit();
|
this.line_index.deinit();
|
||||||
this.* = undefined;
|
this.* = undefined;
|
||||||
}
|
}
|
||||||
@@ -39,8 +46,9 @@ pub fn Widget(comptime Event: type) type {
|
|||||||
// store the received size
|
// store the received size
|
||||||
.resize => |size| {
|
.resize => |size| {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
if (this.line > this.line_index.items.len - 1 - size.rows) {
|
log.debug("Using size: {{ .cols = {d}, .rows = {d} }}", .{ size.cols, size.rows });
|
||||||
this.line = this.line_index.items.len - 1 - size.rows;
|
if (this.line > this.line_index.items.len -| 1 -| size.rows) {
|
||||||
|
this.line = this.line_index.items.len -| 1 -| size.rows;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.key => |key| {
|
.key => |key| {
|
||||||
@@ -50,11 +58,11 @@ pub fn Widget(comptime Event: type) type {
|
|||||||
}
|
}
|
||||||
if (key.matches(.{ .cp = 'G' })) {
|
if (key.matches(.{ .cp = 'G' })) {
|
||||||
// bottom
|
// bottom
|
||||||
this.line = this.line_index.items.len - 1 - this.size.rows;
|
this.line = this.line_index.items.len -| 1 -| this.size.rows;
|
||||||
}
|
}
|
||||||
if (key.matches(.{ .cp = 'j' })) {
|
if (key.matches(.{ .cp = 'j' })) {
|
||||||
// down
|
// down
|
||||||
if (this.line < this.line_index.items.len - 1 - this.size.rows) {
|
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
||||||
this.line +|= 1;
|
this.line +|= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,16 +78,17 @@ pub fn Widget(comptime Event: type) type {
|
|||||||
|
|
||||||
pub fn content(this: *@This()) ![]u8 {
|
pub fn content(this: *@This()) ![]u8 {
|
||||||
if (this.size.rows >= this.line_index.items.len) {
|
if (this.size.rows >= this.line_index.items.len) {
|
||||||
return this.c.items;
|
return this.contents.items;
|
||||||
} else {
|
} else {
|
||||||
// more rows than we can display
|
// more rows than we can display
|
||||||
const i = this.line_index.items[this.line];
|
const i = this.line_index.items[this.line];
|
||||||
|
log.debug("i := {d} this.line := {d}", .{ i, this.line });
|
||||||
const e = this.size.rows + this.line;
|
const e = this.size.rows + this.line;
|
||||||
if (e >= this.line_index.items.len) {
|
if (e >= this.line_index.items.len) {
|
||||||
return this.c.items[i..];
|
return this.contents.items[i..];
|
||||||
}
|
}
|
||||||
const x = this.line_index.items[e];
|
const x = this.line_index.items[e] - 1;
|
||||||
return this.c.items[i..x];
|
return this.contents.items[i..x];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user