Forgot about git again
This commit is contained in:
parent
d30342c3f7
commit
0b6f6cf3b5
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
.zig-cache/
|
.zig-cache/
|
||||||
zig-out/
|
zig-out/
|
||||||
|
panes.log
|
||||||
|
15
justfile
Normal file
15
justfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
build OPTIMIZE="Debug":
|
||||||
|
zig build -Doptimize={{OPTIMIZE}}
|
||||||
|
|
||||||
|
run OPTIMIZE="Debug": (build OPTIMIZE)
|
||||||
|
./zig-out/bin/panes
|
||||||
|
cat panes.log
|
||||||
|
|
||||||
|
debug: (build "Debug")
|
||||||
|
gdb ./zig-out/bin/panes
|
||||||
|
|
||||||
|
test:
|
||||||
|
zig build test --summary new
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -r .zig-cache zig-out
|
@ -52,3 +52,14 @@ pub const BigBorder = Border{
|
|||||||
.left = '▌',
|
.left = '▌',
|
||||||
.right = '▐',
|
.right = '▐',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const RoundBorder = Border{
|
||||||
|
.top_left = '╭',
|
||||||
|
.top_right = '╮',
|
||||||
|
.bottom_left = '╰',
|
||||||
|
.bottom_right = '╯',
|
||||||
|
.top = '─',
|
||||||
|
.bottom = '─',
|
||||||
|
.left = '│',
|
||||||
|
.right = '│',
|
||||||
|
};
|
||||||
|
65
src/button.zig
Normal file
65
src/button.zig
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const log = @import("log.zig");
|
||||||
|
const dim = @import("dimensions.zig");
|
||||||
|
const pane = @import("pane.zig");
|
||||||
|
const term = @import("term.zig");
|
||||||
|
const Pane = pane.Pane;
|
||||||
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
|
pub const Button = struct {
|
||||||
|
pane: Pane,
|
||||||
|
text: []const u8,
|
||||||
|
callback: *const fn () void,
|
||||||
|
|
||||||
|
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, text: []const u8, callback: *const fn () void) Button {
|
||||||
|
return .{
|
||||||
|
.pane = .{
|
||||||
|
.parent = parent,
|
||||||
|
.children = null,
|
||||||
|
.dimensions = dimensions,
|
||||||
|
.focusable = true,
|
||||||
|
.vtable = .{
|
||||||
|
.draw = draw,
|
||||||
|
.focus = focus,
|
||||||
|
.update = update,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.text = text,
|
||||||
|
.callback = callback,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
|
var self: *Button = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
var writer = self.pane.writer(term_io);
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = 0 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
|
||||||
|
try writer.writeAll(self.text);
|
||||||
|
term_io.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
|
_ = term_io;
|
||||||
|
var self: *Button = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
if (key.type == .ASCII and key.value == '\r') {
|
||||||
|
self.callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
|
var self: *Button = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
var writer = self.pane.writer(term_io);
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = 0 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
|
||||||
|
term_io.enableFormats(.{ .highlight = pane_ptr.focused });
|
||||||
|
try writer.writeAll(self.text);
|
||||||
|
term_io.disableFormats(.{ .highlight = true });
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
pub const Color = struct {
|
pub const Color = struct {
|
||||||
type: enum { Default, RGB },
|
type: enum { Inherit, Default, RGB },
|
||||||
red: u8,
|
red: u8,
|
||||||
green: u8,
|
green: u8,
|
||||||
blue: u8,
|
blue: u8,
|
||||||
@ -17,6 +17,13 @@ pub const Color = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Inherit = Color{
|
||||||
|
.type = .Inherit,
|
||||||
|
.red = undefined,
|
||||||
|
.green = undefined,
|
||||||
|
.blue = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Default = Color{
|
pub const Default = Color{
|
||||||
.type = .Default,
|
.type = .Default,
|
||||||
.red = undefined,
|
.red = undefined,
|
||||||
|
@ -20,22 +20,25 @@ pub const Size = struct {
|
|||||||
height: usize,
|
height: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Dimension = struct {
|
||||||
|
value: usize,
|
||||||
|
type: enum { Relative, Absolute } = .Absolute,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Dimensions = struct {
|
pub const Dimensions = struct {
|
||||||
size_type: enum { Relative, Absolute },
|
x: Dimension,
|
||||||
size: Size,
|
y: Dimension,
|
||||||
anchor: Anchor,
|
anchor: Anchor = .TopLeft,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CalculatedDimensions = struct {
|
pub const CalculatedDimensions = struct {
|
||||||
size: Size,
|
size: Size,
|
||||||
|
internal_size: Size,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
|
internal_pos: Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Fill = Dimensions{
|
pub const Fill = Dimension{
|
||||||
.size_type = .Relative,
|
.type = .Relative,
|
||||||
.anchor = .TopLeft,
|
.value = 100,
|
||||||
.size = .{
|
|
||||||
.width = 100,
|
|
||||||
.height = 100,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
38
src/line.zig
Normal file
38
src/line.zig
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const dim = @import("dimensions.zig");
|
||||||
|
const pane = @import("pane.zig");
|
||||||
|
const term = @import("term.zig");
|
||||||
|
const borders = @import("borders.zig");
|
||||||
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
|
pub const HorizontalLine = struct {
|
||||||
|
pane: pane.Pane,
|
||||||
|
border: borders.Border,
|
||||||
|
|
||||||
|
pub fn create(parent: *pane.Pane, dimensions: dim.Dimensions, border: borders.Border) HorizontalLine {
|
||||||
|
return .{
|
||||||
|
.pane = pane.Pane{
|
||||||
|
.parent = parent,
|
||||||
|
.children = null,
|
||||||
|
.dimensions = dimensions,
|
||||||
|
.vtable = .{
|
||||||
|
.draw = draw,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.border = border,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(pane_ptr: *pane.Pane, term_io: *TermIO) !void {
|
||||||
|
const self: *HorizontalLine = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
const y = pane_ptr.calcDims.size.height / 2;
|
||||||
|
pane_ptr.cursor = .{ .x = 0, .y = y };
|
||||||
|
pane_ptr.moveCursor(term_io);
|
||||||
|
for (0..pane_ptr.calcDims.size.width) |_| {
|
||||||
|
term_io.print("{u}", .{self.border.top});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VerticalLine = struct {
|
||||||
|
pane: pane.Pane,
|
||||||
|
};
|
153
src/log.zig
Normal file
153
src/log.zig
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const fs = std.fs;
|
||||||
|
|
||||||
|
var log_level: Level = .Debug;
|
||||||
|
var writer: ?std.fs.File.Writer = null;
|
||||||
|
|
||||||
|
const Level = enum {
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
Disable,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(log_file: []const u8, level: Level) !void {
|
||||||
|
log_level = level;
|
||||||
|
const file = try fs.cwd().createFile(log_file, .{});
|
||||||
|
writer = file.writer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setLevel(level: Level) void {
|
||||||
|
log_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
if (writer) |w| {
|
||||||
|
w.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug(comptime format: []const u8, args: anytype) void {
|
||||||
|
if (@intFromEnum(log_level) <= @intFromEnum(Level.Debug)) {
|
||||||
|
if (writer) |w| {
|
||||||
|
_ = w.write("[Debug] ") catch {};
|
||||||
|
std.fmt.format(w, format, args) catch {};
|
||||||
|
_ = w.writeByte('\n') catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(comptime format: []const u8, args: anytype) void {
|
||||||
|
if (@intFromEnum(log_level) <= @intFromEnum(Level.Info)) {
|
||||||
|
if (writer) |w| {
|
||||||
|
_ = w.write("\x1b[1m[Info] ") catch {};
|
||||||
|
std.fmt.format(w, format, args) catch {};
|
||||||
|
_ = w.write("\x1b[0m\n") catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn warn(comptime format: []const u8, args: anytype) void {
|
||||||
|
if (@intFromEnum(log_level) <= @intFromEnum(Level.Warn)) {
|
||||||
|
if (writer) |w| {
|
||||||
|
_ = w.write("\x1b[1;33m[Warn] ") catch {};
|
||||||
|
std.fmt.format(w, format, args) catch {};
|
||||||
|
_ = w.write("\x1b[0m\n") catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn err(comptime format: []const u8, args: anytype) void {
|
||||||
|
if (@intFromEnum(log_level) <= @intFromEnum(Level.Error)) {
|
||||||
|
if (writer) |w| {
|
||||||
|
_ = w.write("\x1b[1;31m[Error] ") catch {};
|
||||||
|
std.fmt.format(w, format, args) catch {};
|
||||||
|
_ = w.write("\x1b[0m\n") catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "debug" {
|
||||||
|
try init("test.log", .Debug);
|
||||||
|
setLevel(.Debug);
|
||||||
|
|
||||||
|
debug("Hello", .{});
|
||||||
|
info("Hello", .{});
|
||||||
|
warn("Hello", .{});
|
||||||
|
err("Hello", .{});
|
||||||
|
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
const file = try fs.cwd().openFile("test.log", .{});
|
||||||
|
defer file.close();
|
||||||
|
const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024);
|
||||||
|
defer std.testing.allocator.free(contents);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("[Debug] Hello\n\x1b[1m[Info] Hello\x1b[0m\n\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents);
|
||||||
|
|
||||||
|
try fs.cwd().deleteFile("test.log");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "info" {
|
||||||
|
try init("test.log", .Info);
|
||||||
|
setLevel(.Info);
|
||||||
|
|
||||||
|
debug("Hello", .{});
|
||||||
|
info("Hello", .{});
|
||||||
|
warn("Hello", .{});
|
||||||
|
err("Hello", .{});
|
||||||
|
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
const file = try fs.cwd().openFile("test.log", .{});
|
||||||
|
defer file.close();
|
||||||
|
const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024);
|
||||||
|
defer std.testing.allocator.free(contents);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("\x1b[1m[Info] Hello\x1b[0m\n\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents);
|
||||||
|
|
||||||
|
try fs.cwd().deleteFile("test.log");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "warn" {
|
||||||
|
try init("test.log", .Warn);
|
||||||
|
setLevel(.Warn);
|
||||||
|
|
||||||
|
debug("Hello", .{});
|
||||||
|
info("Hello", .{});
|
||||||
|
warn("Hello", .{});
|
||||||
|
err("Hello", .{});
|
||||||
|
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
const file = try fs.cwd().openFile("test.log", .{});
|
||||||
|
defer file.close();
|
||||||
|
const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024);
|
||||||
|
defer std.testing.allocator.free(contents);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("\x1b[1;33m[Warn] Hello\x1b[0m\n\x1b[1;31m[Error] Hello\x1b[0m\n", contents);
|
||||||
|
|
||||||
|
try fs.cwd().deleteFile("test.log");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "error" {
|
||||||
|
try init("test.log", .Error);
|
||||||
|
setLevel(.Error);
|
||||||
|
|
||||||
|
debug("Hello", .{});
|
||||||
|
info("Hello", .{});
|
||||||
|
warn("Hello", .{});
|
||||||
|
err("Hello", .{});
|
||||||
|
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
const file = try fs.cwd().openFile("test.log", .{});
|
||||||
|
defer file.close();
|
||||||
|
const contents = try file.readToEndAlloc(std.testing.allocator, 1024 * 1024);
|
||||||
|
defer std.testing.allocator.free(contents);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("\x1b[1;31m[Error] Hello\x1b[0m\n", contents);
|
||||||
|
|
||||||
|
try fs.cwd().deleteFile("test.log");
|
||||||
|
}
|
97
src/main.zig
97
src/main.zig
@ -5,79 +5,108 @@ const pane = @import("pane.zig");
|
|||||||
const color = @import("colors.zig");
|
const color = @import("colors.zig");
|
||||||
const dim = @import("dimensions.zig");
|
const dim = @import("dimensions.zig");
|
||||||
const menu = @import("menu.zig");
|
const menu = @import("menu.zig");
|
||||||
|
const btn = @import("button.zig");
|
||||||
|
const log = @import("log.zig");
|
||||||
|
const line = @import("line.zig");
|
||||||
|
|
||||||
var term_io: term.TermIO = undefined;
|
var term_io: term.TermIO = undefined;
|
||||||
|
var side_menu: menu.Menu = undefined;
|
||||||
|
var m: menu.Menu = undefined;
|
||||||
|
|
||||||
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
|
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
|
||||||
pane.cleanup(&term_io);
|
pane.cleanup(&term_io);
|
||||||
std.builtin.default_panic(msg, error_return_trace, ret_addr);
|
std.builtin.default_panic(msg, error_return_trace, ret_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_click() void {
|
||||||
|
log.debug("Button was pressed", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_side_select(item: usize) void {
|
||||||
|
log.debug("Side menu: Item {d} was selected", .{item});
|
||||||
|
if (item == 1) {
|
||||||
|
pane.focus(&m.pane, &term_io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_select(item: usize) void {
|
||||||
|
log.debug("Main menu: Item {d} was selected", .{item});
|
||||||
|
if (item == 1) {
|
||||||
|
pane.focus(&side_menu.pane, &term_io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
defer {
|
defer {
|
||||||
if (gpa.deinit() == .leak) {
|
if (gpa.deinit() == .leak) {
|
||||||
|
log.warn("Memory was leaked D:", .{});
|
||||||
std.debug.print("Memory was leaked D:\n", .{});
|
std.debug.print("Memory was leaked D:\n", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
term_io = try term.getTermIO();
|
try log.init("panes.log", .Debug);
|
||||||
|
defer log.deinit();
|
||||||
|
|
||||||
try pane.init(&term_io);
|
const stdin = std.io.getStdIn().reader();
|
||||||
defer pane.cleanup(&term_io);
|
var stdout = std.io.bufferedWriter(std.io.getStdOut().writer().any());
|
||||||
|
term_io = try term.getTermIO(stdin.any(), stdout.writer());
|
||||||
|
|
||||||
var top = pane.Pane{
|
var top = pane.Pane{
|
||||||
.dimensions = dim.Fill,
|
.dimensions = .{
|
||||||
|
.x = dim.Fill,
|
||||||
|
.y = dim.Fill,
|
||||||
|
},
|
||||||
.border = borders.BoldBorder,
|
.border = borders.BoldBorder,
|
||||||
.children = std.ArrayList(*pane.Pane).init(allocator),
|
.children = std.ArrayList(*pane.Pane).init(allocator),
|
||||||
.background = color.RGB(30, 30, 30),
|
.background = color.RGB(30, 30, 30),
|
||||||
.foreground = color.RGB(0, 255, 0),
|
.foreground = color.RGB(0, 255, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var items2 = [_]menu.MenuItem{ .{ .name = "Item 1", .value = 1 }, .{ .name = "Item ab", .value = 2 } };
|
||||||
|
side_menu = menu.Menu.init(&top, dim.Dimensions{ .anchor = .TopLeft, .x = .{ .value = 20, .type = .Relative }, .y = .{ .value = 100, .type = .Relative } }, .{ .title = "Menu", .align_text = .Right, .expand_highlight = true }, &items2, on_side_select);
|
||||||
|
side_menu.pane.background = color.RGB(80, 80, 80);
|
||||||
|
side_menu.pane.foreground = top.foreground;
|
||||||
|
|
||||||
var child = pane.Pane{
|
var child = pane.Pane{
|
||||||
.parent = &top,
|
.parent = &top,
|
||||||
.children = std.ArrayList(*pane.Pane).init(allocator),
|
.children = std.ArrayList(*pane.Pane).init(allocator),
|
||||||
.dimensions = .{
|
.dimensions = .{
|
||||||
.anchor = .Center,
|
.anchor = .CenterRight,
|
||||||
.size_type = .Relative,
|
.x = .{ .value = 80, .type = .Relative },
|
||||||
.size = .{ .width = 50, .height = 50 },
|
.y = .{ .value = 100, .type = .Relative },
|
||||||
},
|
},
|
||||||
.border = borders.BoldBorder,
|
.border = borders.BoldBorder,
|
||||||
.background = color.RGB(125, 0, 125),
|
.background = color.RGB(125, 0, 125),
|
||||||
.foreground = color.RGB(255, 125, 10),
|
.foreground = color.RGB(255, 125, 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
var items = [_]menu.MenuItem{ .{ .name = "Item 1", .value = 1 }, .{ .name = "Item 2", .value = 2 } };
|
var button = btn.Button.init(&child, .{ .anchor = .BottomLeft, .x = .{ .value = 8 }, .y = .{ .value = 1 } }, "<Button>", on_click);
|
||||||
var m = menu.Menu.init(&child, dim.Fill, "Test", &items);
|
button.pane.background = child.background;
|
||||||
|
button.pane.foreground = child.foreground;
|
||||||
|
|
||||||
pane.top_pane = ⊤
|
var items = [_]menu.MenuItem{ .{ .name = "Item 1", .value = 1 }, .{ .name = "Item ab", .value = 2 } };
|
||||||
|
m = menu.Menu.init(&child, dim.Dimensions{ .anchor = .TopLeft, .x = .{ .value = 100, .type = .Relative }, .y = .{ .value = 50, .type = .Relative } }, .{ .title = "Test", .align_text = .Left, .expand_highlight = true }, &items, on_select);
|
||||||
|
m.pane.background = child.background;
|
||||||
|
m.pane.foreground = child.foreground;
|
||||||
|
|
||||||
|
var l = line.HorizontalLine.create(&child, .{ .anchor = .Center, .x = .{ .value = 100, .type = .Relative }, .y = .{ .value = 1 } }, borders.BasicBorder);
|
||||||
|
// var l = line.HorizontalLine.create(@ptrFromInt(0x7fffabc0), .{ .anchor = .Center, .x = .{ .value = 100, .type = .Relative }, .y = .{ .value = 1 } }, borders.BasicBorder);
|
||||||
|
|
||||||
|
try top.children.?.append(&side_menu.pane);
|
||||||
try top.children.?.append(&child);
|
try top.children.?.append(&child);
|
||||||
try child.children.?.append(&m.pane);
|
try child.children.?.append(&m.pane);
|
||||||
|
try child.children.?.append(&l.pane);
|
||||||
|
try child.children.?.append(&button.pane);
|
||||||
|
|
||||||
const childWriter = child.writer(&term_io);
|
defer top.children.?.deinit();
|
||||||
var key = term_io.getKey(false);
|
defer child.children.?.deinit();
|
||||||
while (key.value != 113 and !pane.should_exit) {
|
|
||||||
try pane.tick(&term_io, key);
|
try pane.init(&term_io, &top, &m.pane);
|
||||||
if (key.type == .ASCII and key.value == 111) {
|
defer pane.cleanup(&term_io);
|
||||||
try std.fmt.format(childWriter, "\x1b[2J", .{});
|
|
||||||
try term_io.flush();
|
while (!pane.should_exit.load(.acquire)) {
|
||||||
}
|
try pane.tick(&term_io);
|
||||||
if (key.type == .ASCII and key.value == 110) {
|
|
||||||
try std.fmt.format(childWriter, "Hello", .{});
|
|
||||||
try term_io.flush();
|
|
||||||
}
|
|
||||||
if (key.type == .ASCII and key.value == 109) {
|
|
||||||
try std.fmt.format(childWriter, "Hello\n", .{});
|
|
||||||
try term_io.flush();
|
|
||||||
}
|
|
||||||
if (key.type == .ASCII and key.value == 108) {
|
|
||||||
m.pane.focus(&term_io);
|
|
||||||
try term_io.flush();
|
|
||||||
}
|
|
||||||
key = term_io.getKey(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
top.children.?.deinit();
|
|
||||||
child.children.?.deinit();
|
|
||||||
}
|
}
|
||||||
|
126
src/menu.zig
126
src/menu.zig
@ -1,24 +1,38 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const log = @import("log.zig");
|
||||||
const dim = @import("dimensions.zig");
|
const dim = @import("dimensions.zig");
|
||||||
const pane = @import("pane.zig");
|
const pane = @import("pane.zig");
|
||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
const Pane = pane.Pane;
|
const Pane = pane.Pane;
|
||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
|
pub const TextAlignment = enum {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Center,
|
||||||
|
};
|
||||||
|
|
||||||
pub const MenuItem = struct {
|
pub const MenuItem = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
value: usize,
|
value: usize,
|
||||||
disabled: bool = false,
|
disabled: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const MenuConfig = struct {
|
||||||
|
title: []const u8 = "",
|
||||||
|
align_text: TextAlignment = .Left,
|
||||||
|
expand_highlight: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Menu = struct {
|
pub const Menu = struct {
|
||||||
pane: Pane,
|
pane: Pane,
|
||||||
title: []const u8,
|
config: MenuConfig,
|
||||||
items: []MenuItem,
|
items: []MenuItem,
|
||||||
|
on_select: *const fn (usize) void,
|
||||||
selected_item: usize = 0,
|
selected_item: usize = 0,
|
||||||
selected_value: usize = 0,
|
selected_value: usize = 0,
|
||||||
|
|
||||||
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, title: []const u8, items: []MenuItem) Menu {
|
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, config: MenuConfig, items: []MenuItem, on_select: *const fn (usize) void) Menu {
|
||||||
for (items) |item| {
|
for (items) |item| {
|
||||||
std.debug.assert(item.value != 0);
|
std.debug.assert(item.value != 0);
|
||||||
}
|
}
|
||||||
@ -28,14 +42,16 @@ pub const Menu = struct {
|
|||||||
.parent = parent,
|
.parent = parent,
|
||||||
.children = null,
|
.children = null,
|
||||||
.dimensions = dimensions,
|
.dimensions = dimensions,
|
||||||
|
.focusable = true,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
.focus = focus,
|
.focus = focus,
|
||||||
.update = update,
|
.update = update,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.title = title,
|
.config = config,
|
||||||
.items = items,
|
.items = items,
|
||||||
|
.on_select = on_select,
|
||||||
};
|
};
|
||||||
|
|
||||||
menu.reset();
|
menu.reset();
|
||||||
@ -50,22 +66,22 @@ pub const Menu = struct {
|
|||||||
|
|
||||||
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
self.pane.cursor = .{ .x = 0, .y = 0 };
|
|
||||||
self.pane.moveCursor(term_io);
|
|
||||||
|
|
||||||
var writer = self.pane.writer(term_io);
|
try self.printTitle(term_io);
|
||||||
try writer.print("{s}", .{self.title});
|
|
||||||
for (0..self.items.len) |i| {
|
for (0..self.items.len) |i| {
|
||||||
|
if (i == self.selected_item) {
|
||||||
|
term_io.enableFormats(.{ .dim = !self.pane.focused });
|
||||||
|
}
|
||||||
try self.printItem(term_io, i);
|
try self.printItem(term_io, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
try term_io.flush();
|
term_io.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !void {
|
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
if (self.selected_item == self.items.len or key.type == .NO_KEY) {
|
if (self.selected_item == self.items.len or key.type == .NO_KEY) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.type == term.KeyType.SEQUENCE) {
|
if (key.type == term.KeyType.SEQUENCE) {
|
||||||
@ -73,54 +89,86 @@ pub const Menu = struct {
|
|||||||
switch (key_enum) {
|
switch (key_enum) {
|
||||||
term.SequenceKey.DOWN => try self.nextItem(term_io),
|
term.SequenceKey.DOWN => try self.nextItem(term_io),
|
||||||
term.SequenceKey.UP => try self.prevItem(term_io),
|
term.SequenceKey.UP => try self.prevItem(term_io),
|
||||||
else => {},
|
else => return false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (key.value) {
|
switch (key.value) {
|
||||||
13 => self.pane.should_draw = false, // Enter
|
13 => self.on_select(self.selected_value), // Enter
|
||||||
'0' => {
|
|
||||||
self.selectItem(9, term_io) catch |err| switch (err) {
|
|
||||||
error.MenuItemDisabled => {},
|
|
||||||
else => return err,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
'1'...'9' => {
|
|
||||||
self.selectItem(key.value - '1', term_io) catch |err| switch (err) {
|
|
||||||
error.MenuItemDisabled => {},
|
|
||||||
else => return err,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
0x1b, 'q' => self.selected_value = 0,
|
0x1b, 'q' => self.selected_value = 0,
|
||||||
'j' => try self.nextItem(term_io),
|
'j' => try self.nextItem(term_io),
|
||||||
'k' => try self.prevItem(term_io),
|
'k' => try self.prevItem(term_io),
|
||||||
else => {},
|
else => return false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try term_io.flush();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus(pane_ptr: *Pane, term_io: *TermIO, focused: bool) !void {
|
fn focus(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
if (true) return;
|
|
||||||
_ = focused;
|
|
||||||
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
term_io.enableFormats(.{ .dim = !pane_ptr.focused });
|
||||||
try self.printItem(term_io, self.selected_item);
|
try self.printItem(term_io, self.selected_item);
|
||||||
try term_io.flush();
|
}
|
||||||
|
|
||||||
|
fn printTitle(self: *Menu, term_io: *TermIO) !void {
|
||||||
|
var writer = self.pane.writer(term_io);
|
||||||
|
switch (self.config.align_text) {
|
||||||
|
.Left => self.pane.cursor = .{ .x = 0, .y = 0 },
|
||||||
|
.Center => self.pane.cursor = .{ .x = (self.pane.calcDims.size.width - self.config.title.len) / 2, .y = 0 },
|
||||||
|
.Right => self.pane.cursor = .{ .x = self.pane.calcDims.size.width - self.config.title.len, .y = 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
try writer.writeAll(self.config.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printItem(self: *Menu, term_io: *TermIO, item: usize) !void {
|
fn printItem(self: *Menu, term_io: *TermIO, item: usize) !void {
|
||||||
var writer = self.pane.writer(term_io);
|
var writer = self.pane.writer(term_io);
|
||||||
|
const label = self.items[item].name;
|
||||||
|
|
||||||
term_io.enableFormats(.{ .highlight = item == self.selected_item, .dim = self.items[item].disabled or pane.focused_pane != &self.pane });
|
term_io.enableFormats(.{ .highlight = item == self.selected_item, .dim = self.items[item].disabled });
|
||||||
|
|
||||||
if (item < 10) {
|
switch (self.config.align_text) {
|
||||||
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
.Left => {
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
try writer.print("{d}: {s}", .{ item + 1 % 10, self.items[item].name });
|
self.pane.moveCursor(term_io);
|
||||||
} else {
|
|
||||||
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
try writer.writeAll(label);
|
||||||
self.pane.moveCursor(term_io);
|
|
||||||
try writer.print(" : {s}", .{self.items[item].name});
|
if (self.config.expand_highlight) {
|
||||||
|
try writer.writeByteNTimes(' ', self.pane.calcDims.size.width - label.len);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Center => {
|
||||||
|
const left = (self.pane.calcDims.size.width - label.len) / 2;
|
||||||
|
if (self.config.expand_highlight) {
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
try writer.writeByteNTimes(' ', left);
|
||||||
|
} else {
|
||||||
|
self.pane.cursor = .{ .x = left, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll(label);
|
||||||
|
|
||||||
|
if (self.config.expand_highlight) {
|
||||||
|
try writer.writeByteNTimes(' ', (self.pane.calcDims.size.width - label.len) - left);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Right => {
|
||||||
|
if (self.config.expand_highlight) {
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
try writer.writeByteNTimes(' ', self.pane.calcDims.size.width - label.len);
|
||||||
|
} else {
|
||||||
|
self.pane.cursor = .{ .x = self.pane.calcDims.size.width - label.len, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll(label);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
term_io.disableFormats(.{ .highlight = true, .dim = true });
|
term_io.disableFormats(.{ .highlight = true, .dim = true });
|
||||||
|
180
src/pane.zig
180
src/pane.zig
@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const log = @import("log.zig");
|
||||||
const color = @import("colors.zig");
|
const color = @import("colors.zig");
|
||||||
const dim = @import("dimensions.zig");
|
const dim = @import("dimensions.zig");
|
||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
@ -7,22 +8,25 @@ const Border = @import("borders.zig").Border;
|
|||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
pub var top_pane: *Pane = undefined;
|
pub var top_pane: *Pane = undefined;
|
||||||
pub var focused_pane: ?*Pane = null;
|
pub var focused_pane: *Pane = undefined;
|
||||||
pub var should_exit: bool = false;
|
pub var focused_index: usize = 0;
|
||||||
var needs_redraw = true;
|
pub var should_exit: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
||||||
|
var needs_redraw: std.atomic.Value(bool) = std.atomic.Value(bool).init(true);
|
||||||
var redraw_count: usize = 0;
|
var redraw_count: usize = 0;
|
||||||
|
|
||||||
fn resize_signal(sig: i32) callconv(.C) void {
|
fn resize_signal(_: i32) callconv(.C) void {
|
||||||
_ = sig;
|
needs_redraw.store(true, .release);
|
||||||
needs_redraw = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_signal(sig: i32) callconv(.C) void {
|
fn exit_signal(_: i32) callconv(.C) void {
|
||||||
_ = sig;
|
should_exit.store(true, .release);
|
||||||
should_exit = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(term_io: *TermIO) !void {
|
pub fn init(term_io: *TermIO, top: *Pane, focused: *Pane) !void {
|
||||||
|
top_pane = top;
|
||||||
|
focused.focused = true;
|
||||||
|
focused_pane = focused;
|
||||||
|
|
||||||
var resize_handler = std.posix.Sigaction{
|
var resize_handler = std.posix.Sigaction{
|
||||||
.handler = .{ .handler = resize_signal },
|
.handler = .{ .handler = resize_signal },
|
||||||
.mask = std.posix.empty_sigset,
|
.mask = std.posix.empty_sigset,
|
||||||
@ -40,35 +44,80 @@ pub fn init(term_io: *TermIO) !void {
|
|||||||
term_io.enterRawMode();
|
term_io.enterRawMode();
|
||||||
term_io.saveScreen();
|
term_io.saveScreen();
|
||||||
term_io.hideCursor();
|
term_io.hideCursor();
|
||||||
try term_io.flush();
|
term_io.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cleanup(term_io: *TermIO) void {
|
pub fn cleanup(term_io: *TermIO) void {
|
||||||
term_io.showCursor();
|
term_io.showCursor();
|
||||||
term_io.restoreScreen();
|
term_io.restoreScreen();
|
||||||
term_io.exitRawMode();
|
term_io.exitRawMode();
|
||||||
term_io.flush() catch {};
|
// term_io.stdout.context.flush() catch @panic("Failed to flush buffered writer\n");
|
||||||
|
term_io.flush();
|
||||||
term_io.close();
|
term_io.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(term_io: *TermIO, key: term.Key) !void {
|
pub fn tick(term_io: *TermIO) !void {
|
||||||
if (needs_redraw) {
|
const key = term_io.getKey(false);
|
||||||
needs_redraw = false;
|
if (key.value == 113) {
|
||||||
|
should_exit.store(true, .release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.type == .ASCII and key.value == '\t') {
|
||||||
|
cycle_focus(term_io);
|
||||||
|
term_io.flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_redraw.load(.acquire)) {
|
||||||
|
needs_redraw.store(false, .release);
|
||||||
const size = term.getTermSize(term_io);
|
const size = term.getTermSize(term_io);
|
||||||
|
const dims = dim.CalculatedDimensions{
|
||||||
|
.pos = .{ .x = 1, .y = 1 },
|
||||||
|
.internal_pos = .{ .x = 1, .y = 1 },
|
||||||
|
.size = size,
|
||||||
|
.internal_size = size,
|
||||||
|
};
|
||||||
|
|
||||||
term_io.clear();
|
term_io.clear();
|
||||||
try top_pane.ReDraw(term_io, size);
|
try top_pane.ReDraw(term_io, dims);
|
||||||
|
|
||||||
redraw_count += 1;
|
redraw_count += 1;
|
||||||
// try std.fmt.format(top_pane.writer(term_io), "Resized {} times", .{redraw_count});
|
log.debug("Resized {} times", .{redraw_count});
|
||||||
try term_io.flush();
|
term_io.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focused_pane) |pane| {
|
if (try focused_pane.update(term_io, key)) {
|
||||||
try pane.update(term_io, key);
|
term_io.flush();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try term_io.flush();
|
pub fn cycle_focus(term_io: *TermIO) void {
|
||||||
|
if (focused_pane.parent) |parent| {
|
||||||
|
if (parent.children) |children| {
|
||||||
|
var index = (focused_index + 1) % children.items.len;
|
||||||
|
var to_focus = children.items[index];
|
||||||
|
|
||||||
|
while (!to_focus.focusable and index != focused_index) {
|
||||||
|
index = (index + 1) % children.items.len;
|
||||||
|
to_focus = children.items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(to_focus, term_io);
|
||||||
|
focused_index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus(to_focus: *Pane, term_io: *TermIO) void {
|
||||||
|
if (to_focus == focused_pane) return;
|
||||||
|
|
||||||
|
focused_pane.focus(term_io, false);
|
||||||
|
|
||||||
|
focused_pane = to_focus;
|
||||||
|
focused_index = 0; // TODO: Fix this
|
||||||
|
|
||||||
|
to_focus.focus(term_io, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Overflow = struct {
|
pub const Overflow = struct {
|
||||||
@ -92,17 +141,21 @@ pub const Pane = struct {
|
|||||||
background: color.Color = color.Default,
|
background: color.Color = color.Default,
|
||||||
foreground: color.Color = color.Default,
|
foreground: color.Color = color.Default,
|
||||||
should_draw: bool = true,
|
should_draw: bool = true,
|
||||||
|
focusable: bool = false,
|
||||||
|
focused: bool = false,
|
||||||
|
|
||||||
vtable: PaneVtable = .{},
|
vtable: PaneVtable = .{},
|
||||||
|
|
||||||
fn defaultDraw(_: *Pane, _: *TermIO) !void {}
|
fn defaultDraw(_: *Pane, _: *TermIO) !void {}
|
||||||
fn defaultFocus(_: *Pane, _: *TermIO, _: bool) !void {}
|
fn defaultFocus(_: *Pane, _: *TermIO) !void {}
|
||||||
fn defaultUpdate(_: *Pane, _: *TermIO, _: term.Key) !void {}
|
fn defaultUpdate(_: *Pane, _: *TermIO, _: term.Key) !bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const PaneVtable = struct {
|
const PaneVtable = struct {
|
||||||
draw: *const fn (*Pane, *TermIO) anyerror!void = defaultDraw,
|
draw: *const fn (*Pane, *TermIO) anyerror!void = defaultDraw,
|
||||||
focus: *const fn (*Pane, *TermIO, bool) anyerror!void = defaultFocus,
|
focus: *const fn (*Pane, *TermIO) anyerror!void = defaultFocus,
|
||||||
update: *const fn (*Pane, *TermIO, term.Key) anyerror!void = defaultUpdate,
|
update: *const fn (*Pane, *TermIO, term.Key) anyerror!bool = defaultUpdate,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const WriterContext = struct { pane: *Pane, term_io: *TermIO };
|
pub const WriterContext = struct { pane: *Pane, term_io: *TermIO };
|
||||||
@ -113,43 +166,50 @@ pub const Pane = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentBorderWidth: u2 = if (self.parent) |parent| (if (parent.border != null) 1 else 0) else 0;
|
if (self.dimensions.x.type == .Relative) {
|
||||||
self.calcDims = .{ .pos = .{ .x = parentDims.pos.x + parentBorderWidth, .y = parentDims.pos.y + parentBorderWidth }, .size = self.dimensions.size };
|
std.debug.assert(self.dimensions.x.value <= 100);
|
||||||
if (self.dimensions.size_type == .Relative) {
|
self.calcDims.size.width = (parentDims.internal_size.width * self.dimensions.x.value) / 100;
|
||||||
std.debug.assert(self.dimensions.size.width <= 100 and self.dimensions.size.height <= 100);
|
} else {
|
||||||
self.calcDims.size.width = ((parentDims.size.width - parentBorderWidth * 2) * self.dimensions.size.width) / 100;
|
self.calcDims.size.width = self.dimensions.x.value;
|
||||||
self.calcDims.size.height = ((parentDims.size.height - parentBorderWidth * 2) * self.dimensions.size.height) / 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.dimensions.y.type == .Relative) {
|
||||||
|
std.debug.assert(self.dimensions.y.value <= 100);
|
||||||
|
self.calcDims.size.height = (parentDims.internal_size.height * self.dimensions.y.value) / 100;
|
||||||
|
} else {
|
||||||
|
self.calcDims.size.height = self.dimensions.y.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.calcDims.pos = parentDims.internal_pos;
|
||||||
switch (self.dimensions.anchor) {
|
switch (self.dimensions.anchor) {
|
||||||
.TopLeft => {},
|
.TopLeft => {},
|
||||||
.TopCenter => {
|
.TopCenter => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
||||||
},
|
},
|
||||||
.TopRight => {
|
.TopRight => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
||||||
},
|
},
|
||||||
.CenterLeft => {
|
.CenterLeft => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
||||||
},
|
},
|
||||||
.Center => {
|
.Center => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
||||||
},
|
},
|
||||||
.CenterRight => {
|
.CenterRight => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
||||||
},
|
},
|
||||||
.BottomLeft => {
|
.BottomLeft => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
||||||
},
|
},
|
||||||
.BottomCenter => {
|
.BottomCenter => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
||||||
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
||||||
},
|
},
|
||||||
.BottomRight => {
|
.BottomRight => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,37 +222,47 @@ pub const Pane = struct {
|
|||||||
fill = !self.background.equal(color.Default);
|
fill = !self.background.equal(color.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.calcDims.internal_size = self.calcDims.size;
|
||||||
|
self.calcDims.internal_pos = self.calcDims.pos;
|
||||||
if (self.border) |border| {
|
if (self.border) |border| {
|
||||||
try term_io.drawBox(self.calcDims, border, fill);
|
try term_io.drawBox(self.calcDims, border, fill);
|
||||||
|
|
||||||
|
self.calcDims.internal_size = .{
|
||||||
|
.width = self.calcDims.size.width - 2,
|
||||||
|
.height = self.calcDims.size.height - 2,
|
||||||
|
};
|
||||||
|
self.calcDims.internal_pos = .{
|
||||||
|
.x = self.calcDims.pos.x + 1,
|
||||||
|
.y = self.calcDims.pos.y + 1,
|
||||||
|
};
|
||||||
} else if (fill) {
|
} else if (fill) {
|
||||||
term_io.fillBox(self.calcDims);
|
term_io.fillBox(self.calcDims);
|
||||||
}
|
}
|
||||||
|
|
||||||
try term_io.flush();
|
term_io.flush();
|
||||||
|
|
||||||
try self.vtable.draw(self, term_io);
|
try self.vtable.draw(self, term_io);
|
||||||
//_ = term_io.getKey(true);
|
|
||||||
|
|
||||||
if (self.children) |children| {
|
if (self.children) |children| {
|
||||||
for (children.items) |child| {
|
for (children.items) |child| {
|
||||||
child.focus(term_io);
|
|
||||||
try child.ReDraw(term_io, self.calcDims);
|
try child.ReDraw(term_io, self.calcDims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *Pane, term_io: *TermIO, key: term.Key) !void {
|
pub fn update(self: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
try self.vtable.update(self, term_io, key);
|
return try self.vtable.update(self, term_io, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(self: *Pane, term_io: *TermIO) void {
|
pub fn focus(self: *Pane, term_io: *TermIO, focused: bool) void {
|
||||||
focused_pane = self;
|
focused_pane = self;
|
||||||
|
self.focused = focused;
|
||||||
self.cursor = .{ .x = 0, .y = 0 };
|
self.cursor = .{ .x = 0, .y = 0 };
|
||||||
|
|
||||||
self.moveCursor(term_io);
|
self.moveCursor(term_io);
|
||||||
term_io.setColor(self.background, self.foreground);
|
term_io.setColor(self.background, self.foreground);
|
||||||
|
|
||||||
self.vtable.focus(self, term_io, true) catch {};
|
self.vtable.focus(self, term_io) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nextLine(self: *Pane, term_io: *const TermIO) void {
|
fn nextLine(self: *Pane, term_io: *const TermIO) void {
|
||||||
@ -232,10 +302,7 @@ pub const Pane = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(self: WriterContext, bytes: []const u8) !usize {
|
pub fn write(self: WriterContext, bytes: []const u8) !usize {
|
||||||
if (self.pane != focused_pane) {
|
self.term_io.setColor(self.pane.background, self.pane.foreground);
|
||||||
self.pane.focus(self.term_io);
|
|
||||||
}
|
|
||||||
|
|
||||||
const borderWidth: u2 = if (self.pane.border != null) 1 else 0;
|
const borderWidth: u2 = if (self.pane.border != null) 1 else 0;
|
||||||
for (bytes) |byte| {
|
for (bytes) |byte| {
|
||||||
switch (byte) {
|
switch (byte) {
|
||||||
@ -244,7 +311,10 @@ pub const Pane = struct {
|
|||||||
else => {
|
else => {
|
||||||
self.pane.cursor.x += try self.term_io.stdout.write(&[_]u8{byte});
|
self.pane.cursor.x += try self.term_io.stdout.write(&[_]u8{byte});
|
||||||
if (self.pane.cursor.x >= self.pane.calcDims.size.width - (2 * borderWidth)) {
|
if (self.pane.cursor.x >= self.pane.calcDims.size.width - (2 * borderWidth)) {
|
||||||
self.pane.nextLine(self.term_io);
|
switch (self.pane.overflow.x) {
|
||||||
|
.Wrap => self.pane.nextLine(self.term_io),
|
||||||
|
.Hidden => return bytes.len,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
16
src/scratch.zig
Normal file
16
src/scratch.zig
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const stdout = std.io.getStdOut().writer().any();
|
||||||
|
//const stdout = getStdout();
|
||||||
|
_ = try stdout.write("Hello 123\n");
|
||||||
|
try printABC(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getStdout() std.io.AnyWriter {
|
||||||
|
return std.io.getStdOut().writer().any();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printABC(stdout: std.io.AnyWriter) !void {
|
||||||
|
_ = try stdout.write("ABC\n");
|
||||||
|
}
|
57
src/term.zig
57
src/term.zig
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
|
const log = @import("log.zig");
|
||||||
const borders = @import("borders.zig");
|
const borders = @import("borders.zig");
|
||||||
const dim = @import("dimensions.zig");
|
const dim = @import("dimensions.zig");
|
||||||
const color = @import("colors.zig");
|
const color = @import("colors.zig");
|
||||||
@ -241,8 +242,12 @@ pub const TermIO = struct {
|
|||||||
self.tty_file.close();
|
self.tty_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *TermIO) !void {
|
pub fn flush(self: *TermIO) void {
|
||||||
try self.stdout.context.flush();
|
self.stdout.context.flush() catch @panic("Failed to flush buffered writer\n");
|
||||||
|
// var key = self.getKey(true);
|
||||||
|
// while (key.type != .ASCII or key.value != 27) {
|
||||||
|
// key = self.getKey(true);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enterRawMode(term_io: *const TermIO) void {
|
pub fn enterRawMode(term_io: *const TermIO) void {
|
||||||
@ -315,43 +320,45 @@ pub const TermIO = struct {
|
|||||||
if ((!term_io.current_format.bold or formats.bold) and (!term_io.current_format.dim or formats.dim) and
|
if ((!term_io.current_format.bold or formats.bold) and (!term_io.current_format.dim or formats.dim) and
|
||||||
(!term_io.current_format.italic or formats.italic) and (!term_io.current_format.underline or formats.underline) and
|
(!term_io.current_format.italic or formats.italic) and (!term_io.current_format.underline or formats.underline) and
|
||||||
(!term_io.current_format.highlight or formats.highlight) and (!term_io.current_format.invisible or formats.invisible) and
|
(!term_io.current_format.highlight or formats.highlight) and (!term_io.current_format.invisible or formats.invisible) and
|
||||||
(!term_io.current_format.strikethrough or formats.strikethrough))
|
(!term_io.current_format.strikethrough or formats.strikethrough) and (term_io.current_background.equal(color.Default) and
|
||||||
|
(term_io.current_foreground.equal(color.Default))))
|
||||||
{
|
{
|
||||||
term_io.current_format = .{};
|
term_io.current_format = .{};
|
||||||
term_io.print("\x1b[0m", .{});
|
term_io.print("\x1b[0m", .{});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.dim and !term_io.current_format.dim) {
|
if (formats.dim and term_io.current_format.dim) {
|
||||||
term_io.current_format.dim = false;
|
term_io.current_format.dim = false;
|
||||||
term_io.print("\x1b[22m", .{});
|
term_io.print("\x1b[22m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.italic and !term_io.current_format.italic) {
|
if (formats.italic and term_io.current_format.italic) {
|
||||||
term_io.current_format.italic = false;
|
term_io.current_format.italic = false;
|
||||||
term_io.print("\x1b[23m", .{});
|
term_io.print("\x1b[23m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.underline and !term_io.current_format.underline) {
|
if (formats.underline and term_io.current_format.underline) {
|
||||||
term_io.current_format.underline = false;
|
term_io.current_format.underline = false;
|
||||||
term_io.print("\x1b[24m", .{});
|
term_io.print("\x1b[24m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.highlight and !term_io.current_format.highlight) {
|
if (formats.highlight and term_io.current_format.highlight) {
|
||||||
term_io.current_format.highlight = false;
|
term_io.current_format.highlight = false;
|
||||||
term_io.print("\x1b[27m", .{});
|
term_io.print("\x1b[27m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.invisible and !term_io.current_format.invisible) {
|
if (formats.invisible and term_io.current_format.invisible) {
|
||||||
term_io.current_format.invisible = false;
|
term_io.current_format.invisible = false;
|
||||||
term_io.print("\x1b[28m", .{});
|
term_io.print("\x1b[28m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.strikethrough and !term_io.current_format.strikethrough) {
|
if (formats.strikethrough and term_io.current_format.strikethrough) {
|
||||||
term_io.current_format.strikethrough = false;
|
term_io.current_format.strikethrough = false;
|
||||||
term_io.print("\x1b[29m", .{});
|
term_io.print("\x1b[29m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formats.bold and !term_io.current_format.bold) {
|
if (formats.bold and term_io.current_format.bold) {
|
||||||
term_io.current_format.bold = false;
|
term_io.current_format.bold = false;
|
||||||
term_io.print("\x1b[21m", .{});
|
term_io.print("\x1b[21m", .{});
|
||||||
}
|
}
|
||||||
@ -382,6 +389,7 @@ pub const TermIO = struct {
|
|||||||
if (!background.equal(term_io.current_background)) {
|
if (!background.equal(term_io.current_background)) {
|
||||||
switch (background.type) {
|
switch (background.type) {
|
||||||
.Default => term_io.print("\x1b[49m", .{}),
|
.Default => term_io.print("\x1b[49m", .{}),
|
||||||
|
.Inherit => {},
|
||||||
.RGB => term_io.print("\x1b[48;2;{};{};{}m", .{ background.red, background.green, background.blue }),
|
.RGB => term_io.print("\x1b[48;2;{};{};{}m", .{ background.red, background.green, background.blue }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,6 +401,7 @@ pub const TermIO = struct {
|
|||||||
if (!foreground.equal(term_io.current_foreground)) {
|
if (!foreground.equal(term_io.current_foreground)) {
|
||||||
switch (foreground.type) {
|
switch (foreground.type) {
|
||||||
.Default => term_io.print("\x1b[39m", .{}),
|
.Default => term_io.print("\x1b[39m", .{}),
|
||||||
|
.Inherit => {},
|
||||||
.RGB => term_io.print("\x1b[38;2;{};{};{}m", .{ foreground.red, foreground.green, foreground.blue }),
|
.RGB => term_io.print("\x1b[38;2;{};{};{}m", .{ foreground.red, foreground.green, foreground.blue }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,8 +465,9 @@ pub const TermIO = struct {
|
|||||||
term_io.output(dims.pos.x + dims.size.width - 1, dims.pos.y + h, "{u}", .{border.right});
|
term_io.output(dims.pos.x + dims.size.width - 1, dims.pos.y + h, "{u}", .{border.right});
|
||||||
|
|
||||||
if (term_io.stdout.context.buf.len - term_io.stdout.context.end < dims.size.width * 3) {
|
if (term_io.stdout.context.buf.len - term_io.stdout.context.end < dims.size.width * 3) {
|
||||||
try term_io.flush();
|
log.info("Had to flush buffer while drawing box", .{});
|
||||||
_ = term_io.getKey(true);
|
term_io.flush();
|
||||||
|
// _ = term_io.getKey(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term_io.output(dims.pos.x, dims.pos.y + dims.size.height - 1, "{u}", .{border.bottom_left});
|
term_io.output(dims.pos.x, dims.pos.y + dims.size.height - 1, "{u}", .{border.bottom_left});
|
||||||
@ -478,8 +488,8 @@ pub const TermIO = struct {
|
|||||||
switch (err) {
|
switch (err) {
|
||||||
error.WouldBlock, error.EndOfStream => if (!block) return .{ .type = KeyType.NO_KEY, .value = 0 },
|
error.WouldBlock, error.EndOfStream => if (!block) return .{ .type = KeyType.NO_KEY, .value = 0 },
|
||||||
else => {
|
else => {
|
||||||
std.debug.print("Error: {any}\n", .{err});
|
log.err("{any}", .{err});
|
||||||
unreachable;
|
@panic("Unexpected error when reading from stdin");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -508,7 +518,7 @@ pub const TermIO = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getTermSize(term_io: *const TermIO) dim.CalculatedDimensions {
|
pub fn getTermSize(term_io: *const TermIO) dim.Size {
|
||||||
var ws: std.posix.winsize = undefined;
|
var ws: std.posix.winsize = undefined;
|
||||||
const ret = std.os.linux.ioctl(term_io.tty_file.handle, std.os.linux.T.IOCGWINSZ, @intFromPtr(&ws));
|
const ret = std.os.linux.ioctl(term_io.tty_file.handle, std.os.linux.T.IOCGWINSZ, @intFromPtr(&ws));
|
||||||
if (ret == -1 or ws.ws_col == 0) {
|
if (ret == -1 or ws.ws_col == 0) {
|
||||||
@ -516,17 +526,16 @@ pub fn getTermSize(term_io: *const TermIO) dim.CalculatedDimensions {
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dim.CalculatedDimensions{
|
return dim.Size{
|
||||||
.pos = .{ .x = 1, .y = 1 },
|
.width = ws.ws_col,
|
||||||
.size = .{ .width = ws.ws_col, .height = ws.ws_row },
|
.height = ws.ws_row,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTermIO() !TermIO {
|
pub fn getTermIO(stdin: std.io.AnyReader, stdout: std.io.BufferedWriter(4096, std.io.AnyWriter).Writer) !TermIO {
|
||||||
var bw = std.io.bufferedWriter(std.io.getStdOut().writer().any());
|
|
||||||
return TermIO{
|
return TermIO{
|
||||||
.stdin = std.io.getStdIn().reader().any(),
|
.stdin = stdin,
|
||||||
.stdout = bw.writer(),
|
.stdout = stdout,
|
||||||
.tty_file = try fs.cwd().openFile("/dev/tty", .{ .mode = .read_write }),
|
.tty_file = try fs.cwd().openFile("/dev/tty", .{ .mode = .read_write }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -544,9 +553,9 @@ fn decipherEscapeSequence(sequence: []u8) Key {
|
|||||||
return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.UNKNOWN) };
|
return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.UNKNOWN) };
|
||||||
}
|
}
|
||||||
|
|
||||||
test "cool test" {
|
test "stdout" {
|
||||||
term_test.term_io.print("Hello {}", .{123});
|
term_test.term_io.print("Hello {}", .{123});
|
||||||
try term_test.term_io.flush();
|
term_test.term_io.flush();
|
||||||
try std.testing.expectEqualStrings("Hello 123", term_test.readAllTestStdout());
|
try std.testing.expectEqualStrings("Hello 123", term_test.readAllTestStdout());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user