add(layout/Marging): relative margins for Elements
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 32s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 32s
This commit is contained in:
@@ -88,13 +88,15 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
|
||||
pub const HStack = @import("layout/HStack.zig").Layout(Event, Element, Renderer);
|
||||
pub const VStack = @import("layout/VStack.zig").Layout(Event, Element, Renderer);
|
||||
pub const Padding = @import("layout/Padding.zig").Layout(Event, Element, Renderer);
|
||||
pub const Marging = @import("layout/Marging.zig").Layout(Event, Element, Renderer);
|
||||
pub const Framing = @import("layout/Framing.zig").Layout(Event, Element, Renderer);
|
||||
};
|
||||
// test widget implementation satisfies the interface
|
||||
// test layout implementation satisfies the interface
|
||||
comptime Type.Interface.satisfiedBy(Type);
|
||||
comptime Type.Interface.satisfiedBy(Type.HStack);
|
||||
comptime Type.Interface.satisfiedBy(Type.VStack);
|
||||
comptime Type.Interface.satisfiedBy(Type.Padding);
|
||||
comptime Type.Interface.satisfiedBy(Type.Marging);
|
||||
comptime Type.Interface.satisfiedBy(Type.Framing);
|
||||
return Type;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
||||
if (!isTaggedUnion(Element)) {
|
||||
@compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) {
|
||||
@compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name);
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) {
|
||||
@compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name);
|
||||
}
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
size: terminal.Size = undefined,
|
||||
@@ -159,7 +165,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
||||
// render bottom: +---+
|
||||
try terminal.setCursorPosition(.{
|
||||
.col = this.size.anchor.col,
|
||||
.row = this.size.anchor.row + this.size.rows,
|
||||
.row = this.size.anchor.row + this.size.rows - 1,
|
||||
});
|
||||
try this.config.style.value(writer, frame[4]);
|
||||
for (0..this.size.cols -| 2) |_| {
|
||||
|
||||
@@ -18,14 +18,14 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
||||
if (!isTaggedUnion(Element)) {
|
||||
@compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Elements = std.ArrayList(Element);
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) {
|
||||
@compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name);
|
||||
}
|
||||
const LayoutType = @typeInfo(Element).Union.fields[0].type;
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) {
|
||||
@compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name);
|
||||
}
|
||||
const Elements = std.ArrayList(Element);
|
||||
const LayoutType = @typeInfo(Element).Union.fields[0].type;
|
||||
const WidgetType = @typeInfo(Element).Union.fields[1].type;
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
|
||||
161
src/layout/Marging.zig
Normal file
161
src/layout/Marging.zig
Normal file
@@ -0,0 +1,161 @@
|
||||
//! Marging layout for a nested `Layout`s or `Widget`s.
|
||||
//!
|
||||
//! # Example
|
||||
//! ...
|
||||
const std = @import("std");
|
||||
const terminal = @import("../terminal.zig");
|
||||
|
||||
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
||||
const Error = @import("../event.zig").Error;
|
||||
const Key = terminal.Key;
|
||||
|
||||
const log = std.log.scoped(.layout_marging);
|
||||
|
||||
pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
if (!isTaggedUnion(Element)) {
|
||||
@compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) {
|
||||
@compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name);
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) {
|
||||
@compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name);
|
||||
}
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
size: terminal.Size = undefined,
|
||||
require_render: bool = false,
|
||||
element: Element = undefined,
|
||||
events: Events = undefined,
|
||||
config: Config = undefined,
|
||||
|
||||
// TODO: marging (for all 4 directions - relative!)
|
||||
const Config = struct {
|
||||
margin: ?u8 = undefined,
|
||||
left: u8 = 0,
|
||||
right: u8 = 0,
|
||||
top: u8 = 0,
|
||||
bottom: u8 = 0,
|
||||
};
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, config: Config, element: Element) @This() {
|
||||
if (config.margin) |margin| {
|
||||
std.debug.assert(margin <= 50);
|
||||
} else {
|
||||
std.debug.assert(config.left + config.right < 100);
|
||||
std.debug.assert(config.top + config.bottom < 100);
|
||||
}
|
||||
return .{
|
||||
.config = config,
|
||||
.element = element,
|
||||
.events = Events.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.events.deinit();
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
layout.deinit();
|
||||
},
|
||||
.widget => |*widget| {
|
||||
widget.deinit();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(this: *@This(), event: Event) !*Events {
|
||||
this.events.clearRetainingCapacity();
|
||||
// order is important
|
||||
switch (event) {
|
||||
.resize => |size| {
|
||||
this.size = size;
|
||||
this.require_render = true;
|
||||
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
|
||||
size.anchor.col,
|
||||
size.anchor.row,
|
||||
size.cols,
|
||||
size.rows,
|
||||
});
|
||||
var sub_event: Event = undefined;
|
||||
if (this.config.margin) |margin| {
|
||||
// used overall margin
|
||||
const h_margin: u16 = @divTrunc(margin * size.cols, 100);
|
||||
const v_margin: u16 = @divFloor(margin * size.rows, 100);
|
||||
sub_event = .{
|
||||
.resize = .{
|
||||
.anchor = .{
|
||||
.col = size.anchor.col + h_margin,
|
||||
.row = size.anchor.row + v_margin,
|
||||
},
|
||||
.cols = size.cols -| (h_margin * 2),
|
||||
.rows = size.rows -| (v_margin * 2),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// use all for directions individually
|
||||
const left_margin: u16 = @divFloor(this.config.left * size.cols, 100);
|
||||
const right_margin: u16 = @divFloor(this.config.right * size.cols, 100);
|
||||
const top_margin: u16 = @divFloor(this.config.top * size.rows, 100);
|
||||
const bottom_margin: u16 = @divFloor(this.config.bottom * size.rows, 100);
|
||||
sub_event = .{
|
||||
.resize = .{
|
||||
.anchor = .{
|
||||
.col = size.anchor.col + left_margin,
|
||||
.row = size.anchor.row + top_margin,
|
||||
},
|
||||
.cols = size.cols -| left_margin -| right_margin,
|
||||
.rows = size.rows -| top_margin -| bottom_margin,
|
||||
},
|
||||
};
|
||||
}
|
||||
// adjust size according to the containing elements
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
const events = try layout.handle(sub_event);
|
||||
try this.events.appendSlice(events.items);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
if (widget.handle(sub_event)) |e| {
|
||||
try this.events.append(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
const events = try layout.handle(event);
|
||||
try this.events.appendSlice(events.items);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
if (widget.handle(event)) |e| {
|
||||
try this.events.append(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
return &this.events;
|
||||
}
|
||||
|
||||
pub fn render(this: *@This(), renderer: *Renderer) !void {
|
||||
if (this.require_render) {
|
||||
try renderer.clear(this.size);
|
||||
this.require_render = false;
|
||||
}
|
||||
|
||||
switch ((&this.element).*) {
|
||||
.layout => |*layout| {
|
||||
try layout.render(renderer);
|
||||
},
|
||||
.widget => |*widget| {
|
||||
try widget.render(renderer);
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,12 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
||||
if (!isTaggedUnion(Element)) {
|
||||
@compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) {
|
||||
@compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name);
|
||||
}
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) {
|
||||
@compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name);
|
||||
}
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
size: terminal.Size = undefined,
|
||||
|
||||
@@ -18,14 +18,14 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
||||
if (!isTaggedUnion(Element)) {
|
||||
@compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Elements = std.ArrayList(Element);
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) {
|
||||
@compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name);
|
||||
}
|
||||
const LayoutType = @typeInfo(Element).Union.fields[0].type;
|
||||
if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) {
|
||||
@compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name);
|
||||
}
|
||||
const Elements = std.ArrayList(Element);
|
||||
const LayoutType = @typeInfo(Element).Union.fields[0].type;
|
||||
const WidgetType = @typeInfo(Element).Union.fields[1].type;
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
|
||||
182
src/main.zig
182
src/main.zig
@@ -50,77 +50,117 @@ pub fn main() !void {
|
||||
// });
|
||||
// break :layout &stack;
|
||||
// });
|
||||
var layout = Layout.createFrom(layout: {
|
||||
var hstack = Layout.HStack.init(allocator, .{
|
||||
Widget.createFrom(blk: {
|
||||
var spacer = Widget.Spacer.init();
|
||||
break :blk &spacer;
|
||||
}),
|
||||
Layout.createFrom(framing: {
|
||||
var framing = Layout.Framing.init(
|
||||
allocator,
|
||||
.{
|
||||
.style = .{
|
||||
.fg = .{
|
||||
.index = 6,
|
||||
},
|
||||
},
|
||||
.frame = .round,
|
||||
.title = .{
|
||||
.str = "VStack",
|
||||
.style = .{
|
||||
.ul_style = .single,
|
||||
.ul = .{ .index = 6 },
|
||||
.bold = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
.{
|
||||
.layout = Layout.createFrom(
|
||||
padding: {
|
||||
var padding = Layout.Padding.init(
|
||||
allocator,
|
||||
.{
|
||||
.padding = 3,
|
||||
},
|
||||
.{
|
||||
.layout = Layout.createFrom(vstack: {
|
||||
var vstack = Layout.VStack.init(allocator, .{
|
||||
Widget.createFrom(blk: {
|
||||
const file = try std.fs.cwd().openFile("./src/app.zig", .{});
|
||||
defer file.close();
|
||||
var widget = Widget.RawText.init(allocator, file);
|
||||
break :blk &widget;
|
||||
}),
|
||||
Widget.createFrom(blk: {
|
||||
var spacer = Widget.Spacer.init();
|
||||
break :blk &spacer;
|
||||
}),
|
||||
Widget.createFrom(blk: {
|
||||
const file = try std.fs.cwd().openFile("./src/main.zig", .{});
|
||||
defer file.close();
|
||||
var widget = Widget.RawText.init(allocator, file);
|
||||
break :blk &widget;
|
||||
}),
|
||||
});
|
||||
break :vstack &vstack;
|
||||
}),
|
||||
},
|
||||
);
|
||||
break :padding &padding;
|
||||
},
|
||||
),
|
||||
},
|
||||
);
|
||||
break :framing &framing;
|
||||
}),
|
||||
Widget.createFrom(blk: {
|
||||
var spacer = Widget.Spacer.init();
|
||||
break :blk &spacer;
|
||||
}),
|
||||
});
|
||||
break :layout &hstack;
|
||||
});
|
||||
var layout = Layout.createFrom(
|
||||
padding: {
|
||||
var padding = Layout.Marging.init(
|
||||
allocator,
|
||||
.{
|
||||
.left = 15,
|
||||
.right = 15,
|
||||
},
|
||||
.{
|
||||
.layout = Layout.createFrom(framing: {
|
||||
var framing = Layout.Framing.init(allocator, .{ .style = .{ .fg = .{ .index = 6 } } }, .{
|
||||
.layout = Layout.createFrom(vstack: {
|
||||
var vstack = Layout.VStack.init(allocator, .{
|
||||
Widget.createFrom(blk: {
|
||||
const file = try std.fs.cwd().openFile("./src/app.zig", .{});
|
||||
defer file.close();
|
||||
var widget = Widget.RawText.init(allocator, file);
|
||||
break :blk &widget;
|
||||
}),
|
||||
Widget.createFrom(blk: {
|
||||
var spacer = Widget.Spacer.init();
|
||||
break :blk &spacer;
|
||||
}),
|
||||
Widget.createFrom(blk: {
|
||||
const file = try std.fs.cwd().openFile("./src/main.zig", .{});
|
||||
defer file.close();
|
||||
var widget = Widget.RawText.init(allocator, file);
|
||||
break :blk &widget;
|
||||
}),
|
||||
});
|
||||
break :vstack &vstack;
|
||||
}),
|
||||
});
|
||||
break :framing &framing;
|
||||
}),
|
||||
},
|
||||
);
|
||||
break :padding &padding;
|
||||
},
|
||||
);
|
||||
// var layout = Layout.createFrom(layout: {
|
||||
// var hstack = Layout.HStack.init(allocator, .{
|
||||
// Widget.createFrom(blk: {
|
||||
// var spacer = Widget.Spacer.init();
|
||||
// break :blk &spacer;
|
||||
// }),
|
||||
// Layout.createFrom(framing: {
|
||||
// var framing = Layout.Framing.init(
|
||||
// allocator,
|
||||
// .{
|
||||
// .style = .{
|
||||
// .fg = .{
|
||||
// .index = 6,
|
||||
// },
|
||||
// },
|
||||
// .frame = .round,
|
||||
// .title = .{
|
||||
// .str = "VStack",
|
||||
// .style = .{
|
||||
// .ul_style = .single,
|
||||
// .ul = .{ .index = 6 },
|
||||
// .bold = true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// .{
|
||||
// .layout = Layout.createFrom(
|
||||
// padding: {
|
||||
// var padding = Layout.Marging.init(
|
||||
// allocator,
|
||||
// .{
|
||||
// .margin = 10,
|
||||
// },
|
||||
// .{
|
||||
// .layout = Layout.createFrom(vstack: {
|
||||
// var vstack = Layout.VStack.init(allocator, .{
|
||||
// Widget.createFrom(blk: {
|
||||
// const file = try std.fs.cwd().openFile("./src/app.zig", .{});
|
||||
// defer file.close();
|
||||
// var widget = Widget.RawText.init(allocator, file);
|
||||
// break :blk &widget;
|
||||
// }),
|
||||
// Widget.createFrom(blk: {
|
||||
// var spacer = Widget.Spacer.init();
|
||||
// break :blk &spacer;
|
||||
// }),
|
||||
// Widget.createFrom(blk: {
|
||||
// const file = try std.fs.cwd().openFile("./src/main.zig", .{});
|
||||
// defer file.close();
|
||||
// var widget = Widget.RawText.init(allocator, file);
|
||||
// break :blk &widget;
|
||||
// }),
|
||||
// });
|
||||
// break :vstack &vstack;
|
||||
// }),
|
||||
// },
|
||||
// );
|
||||
// break :padding &padding;
|
||||
// },
|
||||
// ),
|
||||
// },
|
||||
// );
|
||||
// break :framing &framing;
|
||||
// }),
|
||||
// Widget.createFrom(blk: {
|
||||
// var spacer = Widget.Spacer.init();
|
||||
// break :blk &spacer;
|
||||
// }),
|
||||
// });
|
||||
// break :layout &hstack;
|
||||
// });
|
||||
defer layout.deinit();
|
||||
|
||||
try app.start();
|
||||
|
||||
@@ -131,6 +131,7 @@ pub fn Direct(comptime _: bool) type {
|
||||
|
||||
pub fn clear(this: *@This(), size: Size) !void {
|
||||
_ = this;
|
||||
// TODO: this should instead by dynamic and correct of size (terminal could be too large currently)
|
||||
std.debug.assert(1028 > size.cols);
|
||||
var buf: [1028]u8 = undefined;
|
||||
@memset(buf[0..], ' ');
|
||||
|
||||
Reference in New Issue
Block a user