Menu
This commit is contained in:
parent
5e52041e67
commit
402ca205ea
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
.zig-cache/
|
.zig-cache/
|
||||||
|
zig-out/
|
||||||
|
14
src/main.zig
14
src/main.zig
@ -4,6 +4,7 @@ const borders = @import("borders.zig");
|
|||||||
const pane = @import("pane.zig");
|
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");
|
||||||
|
|
||||||
var term_io: term.TermIO = undefined;
|
var term_io: term.TermIO = undefined;
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
var child = pane.Pane{
|
var child = pane.Pane{
|
||||||
.parent = &top,
|
.parent = &top,
|
||||||
.children = null,
|
.children = std.ArrayList(*pane.Pane).init(allocator),
|
||||||
.dimensions = .{
|
.dimensions = .{
|
||||||
.anchor = .Center,
|
.anchor = .Center,
|
||||||
.size_type = .Relative,
|
.size_type = .Relative,
|
||||||
@ -47,13 +48,17 @@ pub fn main() !void {
|
|||||||
.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 m = menu.Menu.init(&child, dim.Fill, "Test", &items);
|
||||||
|
|
||||||
pane.top_pane = ⊤
|
pane.top_pane = ⊤
|
||||||
try top.children.?.append(&child);
|
try top.children.?.append(&child);
|
||||||
|
try child.children.?.append(&m.pane);
|
||||||
|
|
||||||
const childWriter = child.writer(&term_io);
|
const childWriter = child.writer(&term_io);
|
||||||
var key = term_io.getKey(false);
|
var key = term_io.getKey(false);
|
||||||
while (key.value != 113 and !pane.should_exit) {
|
while (key.value != 113 and !pane.should_exit) {
|
||||||
try pane.tick(&term_io);
|
try pane.tick(&term_io, key);
|
||||||
if (key.type == .ASCII and key.value == 111) {
|
if (key.type == .ASCII and key.value == 111) {
|
||||||
try std.fmt.format(childWriter, "\x1b[2J", .{});
|
try std.fmt.format(childWriter, "\x1b[2J", .{});
|
||||||
try term_io.flush();
|
try term_io.flush();
|
||||||
@ -66,8 +71,13 @@ pub fn main() !void {
|
|||||||
try std.fmt.format(childWriter, "Hello\n", .{});
|
try std.fmt.format(childWriter, "Hello\n", .{});
|
||||||
try term_io.flush();
|
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);
|
key = term_io.getKey(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
top.children.?.deinit();
|
top.children.?.deinit();
|
||||||
|
child.children.?.deinit();
|
||||||
}
|
}
|
||||||
|
173
src/menu.zig
Normal file
173
src/menu.zig
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
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 MenuItem = struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: usize,
|
||||||
|
disabled: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Menu = struct {
|
||||||
|
pane: Pane,
|
||||||
|
title: []const u8,
|
||||||
|
items: []MenuItem,
|
||||||
|
selected_item: usize = 0,
|
||||||
|
selected_value: usize = 0,
|
||||||
|
|
||||||
|
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, title: []const u8, items: []MenuItem) Menu {
|
||||||
|
for (items) |item| {
|
||||||
|
std.debug.assert(item.value != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var menu = Menu{
|
||||||
|
.pane = .{
|
||||||
|
.parent = parent,
|
||||||
|
.children = null,
|
||||||
|
.dimensions = dimensions,
|
||||||
|
.vtable = .{
|
||||||
|
.draw = draw,
|
||||||
|
.focus = focus,
|
||||||
|
.update = update,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.title = title,
|
||||||
|
.items = items,
|
||||||
|
};
|
||||||
|
|
||||||
|
menu.reset();
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(self: *Menu) void {
|
||||||
|
self.selected_item = self.firstEnabledItem() catch self.items.len;
|
||||||
|
self.selected_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
|
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 writer.print("{s}", .{self.title});
|
||||||
|
for (0..self.items.len) |i| {
|
||||||
|
try self.printItem(term_io, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
try term_io.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !void {
|
||||||
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
if (self.selected_item == self.items.len or key.type == .NO_KEY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.type == term.KeyType.SEQUENCE) {
|
||||||
|
const key_enum: term.SequenceKey = @enumFromInt(key.value);
|
||||||
|
switch (key_enum) {
|
||||||
|
term.SequenceKey.DOWN => try self.nextItem(term_io),
|
||||||
|
term.SequenceKey.UP => try self.prevItem(term_io),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (key.value) {
|
||||||
|
13 => self.pane.should_draw = false, // 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,
|
||||||
|
'j' => try self.nextItem(term_io),
|
||||||
|
'k' => try self.prevItem(term_io),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try term_io.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus(pane_ptr: *Pane, term_io: *TermIO, focused: bool) !void {
|
||||||
|
if (true) return;
|
||||||
|
_ = focused;
|
||||||
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
try self.printItem(term_io, self.selected_item);
|
||||||
|
try term_io.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printItem(self: *Menu, term_io: *TermIO, item: usize) !void {
|
||||||
|
var writer = self.pane.writer(term_io);
|
||||||
|
|
||||||
|
term_io.enableFormats(.{ .highlight = item == self.selected_item, .dim = self.items[item].disabled or pane.focused_pane != &self.pane });
|
||||||
|
|
||||||
|
if (item < 10) {
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
try writer.print("{d}: {s}", .{ item + 1 % 10, self.items[item].name });
|
||||||
|
} else {
|
||||||
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
|
self.pane.moveCursor(term_io);
|
||||||
|
try writer.print(" : {s}", .{self.items[item].name});
|
||||||
|
}
|
||||||
|
|
||||||
|
term_io.disableFormats(.{ .highlight = true, .dim = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn firstEnabledItem(self: *Menu) !usize {
|
||||||
|
for (0..self.items.len) |i| {
|
||||||
|
if (!self.items[i].disabled) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.AllMenuItemsDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nextItem(self: *Menu, term_io: *TermIO) !void {
|
||||||
|
var i: usize = self.selected_item;
|
||||||
|
while (i < self.items.len - 1 and (self.items[i].disabled or i == self.selected_item)) {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if (!self.items[i].disabled) {
|
||||||
|
try self.selectItem(i, term_io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prevItem(self: *Menu, term_io: *TermIO) !void {
|
||||||
|
var i: usize = self.selected_item;
|
||||||
|
while (i > 0 and (self.items[i].disabled or i == self.selected_item)) {
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
if (!self.items[i].disabled) {
|
||||||
|
try self.selectItem(i, term_io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selectItem(self: *Menu, item: usize, term_io: *TermIO) !void {
|
||||||
|
if (self.items[item].disabled) {
|
||||||
|
return error.MenuItemDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousSelection = self.selected_item;
|
||||||
|
self.selected_item = item;
|
||||||
|
self.selected_value = self.items[self.selected_item].value;
|
||||||
|
|
||||||
|
if (previousSelection != self.selected_item) {
|
||||||
|
try self.printItem(term_io, previousSelection);
|
||||||
|
}
|
||||||
|
try self.printItem(term_io, self.selected_item);
|
||||||
|
}
|
||||||
|
};
|
87
src/pane.zig
87
src/pane.zig
@ -38,20 +38,20 @@ pub fn init(term_io: *TermIO) !void {
|
|||||||
try std.posix.sigaction(std.posix.SIG.INT, &exit_handler, null);
|
try std.posix.sigaction(std.posix.SIG.INT, &exit_handler, null);
|
||||||
|
|
||||||
term_io.enterRawMode();
|
term_io.enterRawMode();
|
||||||
term_io.saveScreen() catch {};
|
term_io.saveScreen();
|
||||||
term_io.hideCursor();
|
term_io.hideCursor();
|
||||||
try term_io.flush();
|
try 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() catch {};
|
term_io.restoreScreen();
|
||||||
term_io.exitRawMode();
|
term_io.exitRawMode();
|
||||||
term_io.flush() catch {};
|
term_io.flush() catch {};
|
||||||
term_io.close();
|
term_io.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(term_io: *TermIO) !void {
|
pub fn tick(term_io: *TermIO, key: term.Key) !void {
|
||||||
if (needs_redraw) {
|
if (needs_redraw) {
|
||||||
needs_redraw = false;
|
needs_redraw = false;
|
||||||
const size = term.getTermSize(term_io);
|
const size = term.getTermSize(term_io);
|
||||||
@ -60,9 +60,15 @@ pub fn tick(term_io: *TermIO) !void {
|
|||||||
try top_pane.ReDraw(term_io, size);
|
try top_pane.ReDraw(term_io, size);
|
||||||
|
|
||||||
redraw_count += 1;
|
redraw_count += 1;
|
||||||
try std.fmt.format(top_pane.writer(term_io), "Resized {} times", .{redraw_count});
|
// try std.fmt.format(top_pane.writer(term_io), "Resized {} times", .{redraw_count});
|
||||||
try term_io.flush();
|
try term_io.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (focused_pane) |pane| {
|
||||||
|
try pane.update(term_io, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
try term_io.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Overflow = struct {
|
pub const Overflow = struct {
|
||||||
@ -85,19 +91,35 @@ pub const Pane = struct {
|
|||||||
calcDims: dim.CalculatedDimensions = undefined,
|
calcDims: dim.CalculatedDimensions = undefined,
|
||||||
background: color.Color = color.Default,
|
background: color.Color = color.Default,
|
||||||
foreground: color.Color = color.Default,
|
foreground: color.Color = color.Default,
|
||||||
|
should_draw: bool = true,
|
||||||
|
|
||||||
pub const WriterContext = struct { pane: *Pane, term_io: *const TermIO };
|
vtable: PaneVtable = .{},
|
||||||
|
|
||||||
|
fn defaultDraw(_: *Pane, _: *TermIO) !void {}
|
||||||
|
fn defaultFocus(_: *Pane, _: *TermIO, _: bool) !void {}
|
||||||
|
fn defaultUpdate(_: *Pane, _: *TermIO, _: term.Key) !void {}
|
||||||
|
|
||||||
|
const PaneVtable = struct {
|
||||||
|
draw: *const fn (*Pane, *TermIO) anyerror!void = defaultDraw,
|
||||||
|
focus: *const fn (*Pane, *TermIO, bool) anyerror!void = defaultFocus,
|
||||||
|
update: *const fn (*Pane, *TermIO, term.Key) anyerror!void = defaultUpdate,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const WriterContext = struct { pane: *Pane, term_io: *TermIO };
|
||||||
pub const Writer = std.io.Writer(WriterContext, std.fs.File.WriteError, write);
|
pub const Writer = std.io.Writer(WriterContext, std.fs.File.WriteError, write);
|
||||||
|
|
||||||
pub fn ReDraw(self: *Pane, term_io: *TermIO, parentDims: dim.CalculatedDimensions) !void {
|
pub fn ReDraw(self: *Pane, term_io: *TermIO, parentDims: dim.CalculatedDimensions) !void {
|
||||||
self.calcDims = .{ .pos = parentDims.pos, .size = self.dimensions.size };
|
if (!self.should_draw) {
|
||||||
if (self.dimensions.size_type == .Relative) {
|
return;
|
||||||
std.debug.assert(self.dimensions.size.width <= 100 and self.dimensions.size.height <= 100);
|
|
||||||
self.calcDims.size.width = (parentDims.size.width * self.dimensions.size.width) / 100;
|
|
||||||
self.calcDims.size.height = (parentDims.size.height * self.dimensions.size.height) / 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try term_io.setColor(self.background, self.foreground);
|
const parentBorderWidth: u2 = if (self.parent) |parent| (if (parent.border != null) 1 else 0) else 0;
|
||||||
|
self.calcDims = .{ .pos = .{ .x = parentDims.pos.x + parentBorderWidth, .y = parentDims.pos.y + parentBorderWidth }, .size = self.dimensions.size };
|
||||||
|
if (self.dimensions.size_type == .Relative) {
|
||||||
|
std.debug.assert(self.dimensions.size.width <= 100 and self.dimensions.size.height <= 100);
|
||||||
|
self.calcDims.size.width = ((parentDims.size.width - parentBorderWidth * 2) * self.dimensions.size.width) / 100;
|
||||||
|
self.calcDims.size.height = ((parentDims.size.height - parentBorderWidth * 2) * self.dimensions.size.height) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
switch (self.dimensions.anchor) {
|
switch (self.dimensions.anchor) {
|
||||||
.TopLeft => {},
|
.TopLeft => {},
|
||||||
@ -105,7 +127,7 @@ pub const Pane = struct {
|
|||||||
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
||||||
},
|
},
|
||||||
.TopRight => {
|
.TopRight => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width;
|
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
||||||
},
|
},
|
||||||
.CenterLeft => {
|
.CenterLeft => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
||||||
@ -115,49 +137,62 @@ pub const Pane = struct {
|
|||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
||||||
},
|
},
|
||||||
.CenterRight => {
|
.CenterRight => {
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width;
|
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
||||||
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
self.calcDims.pos.y = parentDims.pos.y + (parentDims.size.height - self.calcDims.size.height) / 2;
|
||||||
},
|
},
|
||||||
.BottomLeft => {
|
.BottomLeft => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height;
|
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
||||||
},
|
},
|
||||||
.BottomCenter => {
|
.BottomCenter => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height;
|
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
||||||
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
self.calcDims.pos.x = parentDims.pos.x + (parentDims.size.width - self.calcDims.size.width) / 2;
|
||||||
},
|
},
|
||||||
.BottomRight => {
|
.BottomRight => {
|
||||||
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height;
|
self.calcDims.pos.y = parentDims.pos.y + parentDims.size.height - self.calcDims.size.height - parentBorderWidth;
|
||||||
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width;
|
self.calcDims.pos.x = parentDims.pos.x + parentDims.size.width - self.calcDims.size.width - parentBorderWidth;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.border) |border| {
|
|
||||||
var fill = false;
|
var fill = false;
|
||||||
if (self.parent) |parent| {
|
if (self.parent) |parent| {
|
||||||
|
term_io.setColor(self.background, self.foreground);
|
||||||
fill = !self.background.equal(parent.background);
|
fill = !self.background.equal(parent.background);
|
||||||
} else {
|
} else {
|
||||||
|
term_io.setColor(self.background, self.foreground);
|
||||||
fill = !self.background.equal(color.Default);
|
fill = !self.background.equal(color.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.border) |border| {
|
||||||
try term_io.drawBox(self.calcDims, border, fill);
|
try term_io.drawBox(self.calcDims, border, fill);
|
||||||
|
} else if (fill) {
|
||||||
|
term_io.fillBox(self.calcDims);
|
||||||
}
|
}
|
||||||
|
|
||||||
try term_io.flush();
|
try term_io.flush();
|
||||||
|
|
||||||
|
try self.vtable.draw(self, term_io);
|
||||||
//_ = term_io.getKey(true);
|
//_ = 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);
|
||||||
try child.focus(term_io);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(self: *Pane, term_io: *const TermIO) !void {
|
pub fn update(self: *Pane, term_io: *TermIO, key: term.Key) !void {
|
||||||
|
try self.vtable.update(self, term_io, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus(self: *Pane, term_io: *TermIO) void {
|
||||||
focused_pane = self;
|
focused_pane = self;
|
||||||
self.cursor = .{ .x = 0, .y = 0 };
|
self.cursor = .{ .x = 0, .y = 0 };
|
||||||
|
|
||||||
self.moveCursor(term_io);
|
self.moveCursor(term_io);
|
||||||
try term_io.setColor(self.background, self.foreground);
|
term_io.setColor(self.background, self.foreground);
|
||||||
|
|
||||||
|
self.vtable.focus(self, term_io, true) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nextLine(self: *Pane, term_io: *const TermIO) void {
|
fn nextLine(self: *Pane, term_io: *const TermIO) void {
|
||||||
@ -169,7 +204,7 @@ pub const Pane = struct {
|
|||||||
|
|
||||||
pub fn moveCursor(self: *Pane, term_io: *const TermIO) void {
|
pub fn moveCursor(self: *Pane, term_io: *const TermIO) void {
|
||||||
const borderWidth: u1 = if (self.border != null) 1 else 0;
|
const borderWidth: u1 = if (self.border != null) 1 else 0;
|
||||||
term_io.moveCursor(self.calcDims.pos.x + self.cursor.x + borderWidth, self.calcDims.pos.y + self.cursor.y + borderWidth) catch {};
|
term_io.moveCursor(self.calcDims.pos.x + self.cursor.x + borderWidth, self.calcDims.pos.y + self.cursor.y + borderWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(self: *Pane, term_io: *const TermIO, string: []const u8) !void {
|
pub fn print(self: *Pane, term_io: *const TermIO, string: []const u8) !void {
|
||||||
@ -178,19 +213,19 @@ pub const Pane = struct {
|
|||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|
||||||
while (string.len - i >= space) {
|
while (string.len - i >= space) {
|
||||||
try term_io.print("{s}", .{string[i .. i + space]}, .{});
|
term_io.print("{s}", .{string[i .. i + space]}, .{});
|
||||||
self.nextLine(term_io);
|
self.nextLine(term_io);
|
||||||
i += space;
|
i += space;
|
||||||
space = self.calcDims.size.width - (2 * borderWidth);
|
space = self.calcDims.size.width - (2 * borderWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < string.len) {
|
if (i < string.len) {
|
||||||
try term_io.print("{s}", .{string[i..]}, .{});
|
term_io.print("{s}", .{string[i..]}, .{});
|
||||||
self.cursor.x += string.len - i;
|
self.cursor.x += string.len - i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writer(self: *Pane, term_io: *const TermIO) Writer {
|
pub fn writer(self: *Pane, term_io: *TermIO) Writer {
|
||||||
return .{
|
return .{
|
||||||
.context = .{ .pane = self, .term_io = term_io },
|
.context = .{ .pane = self, .term_io = term_io },
|
||||||
};
|
};
|
||||||
@ -198,7 +233,7 @@ pub const Pane = struct {
|
|||||||
|
|
||||||
pub fn write(self: WriterContext, bytes: []const u8) std.fs.File.WriteError!usize {
|
pub fn write(self: WriterContext, bytes: []const u8) std.fs.File.WriteError!usize {
|
||||||
if (self.pane != focused_pane) {
|
if (self.pane != focused_pane) {
|
||||||
try self.pane.focus(self.term_io);
|
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;
|
||||||
|
323
src/term.zig
323
src/term.zig
@ -10,6 +10,7 @@ var orig_termios: posix.termios = undefined;
|
|||||||
pub const MenuError = error{ForgottenMenuItem};
|
pub const MenuError = error{ForgottenMenuItem};
|
||||||
|
|
||||||
pub const KeyType = enum {
|
pub const KeyType = enum {
|
||||||
|
NO_KEY,
|
||||||
ASCII,
|
ASCII,
|
||||||
SEQUENCE,
|
SEQUENCE,
|
||||||
};
|
};
|
||||||
@ -42,12 +43,6 @@ pub const SelectMenuItem = struct {
|
|||||||
selected: bool = false,
|
selected: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MenuItem = struct {
|
|
||||||
name: []const u8,
|
|
||||||
value: usize,
|
|
||||||
disabled: bool = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SelectMenu = struct {
|
pub const SelectMenu = struct {
|
||||||
title: []const u8,
|
title: []const u8,
|
||||||
items: []SelectMenuItem,
|
items: []SelectMenuItem,
|
||||||
@ -66,7 +61,7 @@ pub const SelectMenu = struct {
|
|||||||
pub fn refresh(self: *SelectMenu, term_io: *TermIO) !void {
|
pub fn refresh(self: *SelectMenu, term_io: *TermIO) !void {
|
||||||
term_io.clear();
|
term_io.clear();
|
||||||
|
|
||||||
try term_io.print("{s}", .{self.title}, .{ .underline = true });
|
try term_io.print("{s}", .{self.title});
|
||||||
for (self.items, 0..) |_, i| {
|
for (self.items, 0..) |_, i| {
|
||||||
try self.printItem(i, term_io);
|
try self.printItem(i, term_io);
|
||||||
}
|
}
|
||||||
@ -138,122 +133,6 @@ pub const SelectMenu = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Menu = struct {
|
|
||||||
title: []const u8,
|
|
||||||
items: []MenuItem,
|
|
||||||
selected_item: usize = 0,
|
|
||||||
selected_value: usize = 0,
|
|
||||||
|
|
||||||
pub fn init(title: []const u8, items: []MenuItem) Menu {
|
|
||||||
for (items) |item| {
|
|
||||||
std.debug.assert(item.value != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ .title = title, .items = items };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(self: *Menu) void {
|
|
||||||
self.selected_item = 0;
|
|
||||||
self.selected_value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn refresh(self: *Menu, term_io: *TermIO) !void {
|
|
||||||
term_io.clear();
|
|
||||||
|
|
||||||
try term_io.print("{s}", .{self.title}, .{ .underline = true });
|
|
||||||
for (self.items, 0..) |_, i| {
|
|
||||||
try self.printItem(i, term_io);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(self: *Menu, term_io: *TermIO) !void {
|
|
||||||
if (self.items[self.selected_item].disabled) {
|
|
||||||
try self.nextItem(term_io);
|
|
||||||
}
|
|
||||||
if (self.items[self.selected_item].disabled) {
|
|
||||||
self.selected_item = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.refresh(term_io);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const key: Key = term_io.getKey(true);
|
|
||||||
if (key.type == KeyType.SEQUENCE) {
|
|
||||||
const key_enum: SequenceKey = @enumFromInt(key.value);
|
|
||||||
switch (key_enum) {
|
|
||||||
SequenceKey.DOWN => try self.nextItem(term_io),
|
|
||||||
SequenceKey.UP => try self.prevItem(term_io),
|
|
||||||
else => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (key.value) {
|
|
||||||
13 => break, // Enter
|
|
||||||
'0' => {
|
|
||||||
if (!self.items[9].disabled) {
|
|
||||||
try self.selectItem(9, term_io);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'1'...'9' => {
|
|
||||||
if (!self.items[key.value - '1'].disabled) {
|
|
||||||
try self.selectItem(key.value - '1', term_io);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
0x1b, 'q' => {
|
|
||||||
self.selected_value = 0;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
'j' => try self.nextItem(term_io),
|
|
||||||
'k' => try self.prevItem(term_io),
|
|
||||||
else => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selected_value = self.items[self.selected_item].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn printItem(self: *Menu, item: usize, term_io: *TermIO) !void {
|
|
||||||
if (item < 10) {
|
|
||||||
try term_io.output(1, item + 2, "{d}: {s}", .{ item + 1 % 10, self.items[item].name }, .{ .dim = self.items[item].disabled, .highlight = item == self.selected_item });
|
|
||||||
} else {
|
|
||||||
try term_io.output(1, item + 2, " : {s}", .{self.items[item].name}, .{ .dim = self.items[item].disabled, .highlight = item == self.selected_item });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nextItem(self: *Menu, term_io: *TermIO) !void {
|
|
||||||
var i: usize = self.selected_item;
|
|
||||||
while (i < self.items.len and (self.items[i].disabled or i == self.selected_item)) {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
if (!self.items[i].disabled) {
|
|
||||||
try self.selectItem(i, term_io);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prevItem(self: *Menu, term_io: *TermIO) !void {
|
|
||||||
var i: usize = self.selected_item;
|
|
||||||
while (i > 0 and (self.items[i].disabled or i == self.selected_item)) {
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
if (!self.items[i].disabled) {
|
|
||||||
try self.selectItem(i, term_io);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selectItem(self: *Menu, item: usize, term_io: *TermIO) !void {
|
|
||||||
const previousSelection = self.selected_item;
|
|
||||||
self.selected_item = item;
|
|
||||||
|
|
||||||
if (previousSelection != self.selected_item) {
|
|
||||||
try self.printItem(previousSelection, term_io);
|
|
||||||
}
|
|
||||||
try self.printItem(self.selected_item, term_io);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Input = struct {
|
pub const Input = struct {
|
||||||
pos_x: usize,
|
pos_x: usize,
|
||||||
pos_y: usize,
|
pos_y: usize,
|
||||||
@ -336,10 +215,21 @@ pub const Input = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const TermFormat = packed struct {
|
||||||
|
bold: bool = false,
|
||||||
|
dim: bool = false,
|
||||||
|
italic: bool = false,
|
||||||
|
underline: bool = false,
|
||||||
|
highlight: bool = false,
|
||||||
|
invisible: bool = false,
|
||||||
|
strikethrough: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const TermIO = struct {
|
pub const TermIO = struct {
|
||||||
stdin: fs.File.Reader,
|
stdin: fs.File.Reader,
|
||||||
stdout: std.io.BufferedWriter(4096, fs.File.Writer).Writer,
|
stdout: std.io.BufferedWriter(4096, fs.File.Writer).Writer,
|
||||||
tty_file: fs.File,
|
tty_file: fs.File,
|
||||||
|
current_format: TermFormat = .{},
|
||||||
current_background: color.Color = color.Default,
|
current_background: color.Color = color.Default,
|
||||||
current_foreground: color.Color = color.Default,
|
current_foreground: color.Color = color.Default,
|
||||||
escape_sequence_queued: bool = false,
|
escape_sequence_queued: bool = false,
|
||||||
@ -381,107 +271,197 @@ pub const TermIO = struct {
|
|||||||
posix.tcsetattr(term_io.tty_file.handle, posix.TCSA.FLUSH, orig_termios) catch unreachable;
|
posix.tcsetattr(term_io.tty_file.handle, posix.TCSA.FLUSH, orig_termios) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn saveScreen(term_io: *const TermIO) !void {
|
pub fn enableFormats(term_io: *TermIO, formats: TermFormat) void {
|
||||||
try term_io.stdout.print("\x1b[s", .{});
|
if (formats.bold and !term_io.current_format.bold) {
|
||||||
try term_io.stdout.print("\x1b[?47h", .{});
|
term_io.current_format.bold = true;
|
||||||
try term_io.stdout.print("\x1b[?1049h", .{});
|
term_io.print("\x1b[1m", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restoreScreen(term_io: *const TermIO) !void {
|
if (formats.dim and !term_io.current_format.dim) {
|
||||||
try term_io.stdout.print("\x1b[?1049l", .{});
|
term_io.current_format.dim = true;
|
||||||
try term_io.stdout.print("\x1b[?47l", .{});
|
term_io.print("\x1b[2m", .{});
|
||||||
try term_io.stdout.print("\x1b[u", .{});
|
}
|
||||||
|
|
||||||
|
if (formats.italic and !term_io.current_format.italic) {
|
||||||
|
term_io.current_format.italic = true;
|
||||||
|
term_io.print("\x1b[3m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.underline and !term_io.current_format.underline) {
|
||||||
|
term_io.current_format.underline = true;
|
||||||
|
term_io.print("\x1b[4m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.highlight and !term_io.current_format.highlight) {
|
||||||
|
term_io.current_format.highlight = true;
|
||||||
|
term_io.print("\x1b[7m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.invisible and !term_io.current_format.invisible) {
|
||||||
|
term_io.current_format.invisible = true;
|
||||||
|
term_io.print("\x1b[8m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.strikethrough and !term_io.current_format.strikethrough) {
|
||||||
|
term_io.current_format.strikethrough = true;
|
||||||
|
term_io.print("\x1b[9m", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disableFormats(term_io: *TermIO, formats: TermFormat) void {
|
||||||
|
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.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 = .{};
|
||||||
|
term_io.print("\x1b[0m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.dim and !term_io.current_format.dim) {
|
||||||
|
term_io.current_format.dim = false;
|
||||||
|
term_io.print("\x1b[22m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.italic and !term_io.current_format.italic) {
|
||||||
|
term_io.current_format.italic = false;
|
||||||
|
term_io.print("\x1b[23m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.underline and !term_io.current_format.underline) {
|
||||||
|
term_io.current_format.underline = false;
|
||||||
|
term_io.print("\x1b[24m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.highlight and !term_io.current_format.highlight) {
|
||||||
|
term_io.current_format.highlight = false;
|
||||||
|
term_io.print("\x1b[27m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.invisible and !term_io.current_format.invisible) {
|
||||||
|
term_io.current_format.invisible = false;
|
||||||
|
term_io.print("\x1b[28m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.strikethrough and !term_io.current_format.strikethrough) {
|
||||||
|
term_io.current_format.strikethrough = false;
|
||||||
|
term_io.print("\x1b[29m", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats.bold and !term_io.current_format.bold) {
|
||||||
|
term_io.current_format.bold = false;
|
||||||
|
term_io.print("\x1b[21m", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn saveScreen(term_io: *const TermIO) void {
|
||||||
|
term_io.print("\x1b[s", .{});
|
||||||
|
term_io.print("\x1b[?47h", .{});
|
||||||
|
term_io.print("\x1b[?1049h", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restoreScreen(term_io: *const TermIO) void {
|
||||||
|
term_io.print("\x1b[?1049l", .{});
|
||||||
|
term_io.print("\x1b[?47l", .{});
|
||||||
|
term_io.print("\x1b[u", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(term_io: *const TermIO) void {
|
pub fn clear(term_io: *const TermIO) void {
|
||||||
term_io.stdout.print("\x1b[2J\x1b[H", .{}) catch unreachable;
|
term_io.print("\x1b[2J\x1b[H", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setColor(term_io: *const TermIO, background: color.Color, foreground: color.Color) !void {
|
pub fn setColor(term_io: *TermIO, background: color.Color, foreground: color.Color) void {
|
||||||
try term_io.setBackgroundColor(background);
|
term_io.setBackgroundColor(background);
|
||||||
try term_io.setForegroundColor(foreground);
|
term_io.setForegroundColor(foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setBackgroundColor(term_io: *const TermIO, background: color.Color) !void {
|
pub fn setBackgroundColor(term_io: *TermIO, background: color.Color) void {
|
||||||
if (!background.equal(term_io.current_background)) {
|
if (!background.equal(term_io.current_background)) {
|
||||||
switch (background.type) {
|
switch (background.type) {
|
||||||
.Default => try term_io.print("\x1b[49m", .{}, .{}),
|
.Default => term_io.print("\x1b[49m", .{}),
|
||||||
.RGB => try 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 }),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setForegroundColor(term_io: *const TermIO, foreground: color.Color) !void {
|
term_io.current_background = background;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setForegroundColor(term_io: *TermIO, foreground: color.Color) void {
|
||||||
if (!foreground.equal(term_io.current_foreground)) {
|
if (!foreground.equal(term_io.current_foreground)) {
|
||||||
switch (foreground.type) {
|
switch (foreground.type) {
|
||||||
.Default => try term_io.print("\x1b[39m", .{}, .{}),
|
.Default => term_io.print("\x1b[39m", .{}),
|
||||||
.RGB => try 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 }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
term_io.current_foreground = foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hideCursor(term_io: *const TermIO) void {
|
pub fn hideCursor(term_io: *const TermIO) void {
|
||||||
term_io.stdout.print("\x1b[?25l", .{}) catch unreachable;
|
term_io.print("\x1b[?25l", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn showCursor(term_io: *const TermIO) void {
|
pub fn showCursor(term_io: *const TermIO) void {
|
||||||
term_io.stdout.print("\x1b[?25h", .{}) catch unreachable;
|
term_io.print("\x1b[?25h", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn moveCursor(term_io: *const TermIO, x: usize, y: usize) !void {
|
pub fn moveCursor(term_io: *const TermIO, x: usize, y: usize) void {
|
||||||
try term_io.stdout.print("\x1b[{d};{d}H", .{ y, x });
|
term_io.print("\x1b[{d};{d}H", .{ y, x });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn saveTitle(term_io: *const TermIO) !void {
|
pub fn saveTitle(term_io: *const TermIO) void {
|
||||||
try term_io.stdout.print("\x1b[22;2t", .{});
|
term_io.print("\x1b[22;2t", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restoreTitle(term_io: *const TermIO) !void {
|
pub fn restoreTitle(term_io: *const TermIO) void {
|
||||||
try term_io.stdout.print("\x1b[23;2t", .{});
|
term_io.print("\x1b[23;2t", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setTitle(term_io: *const TermIO, title: []const u8) !void {
|
pub fn setTitle(term_io: *const TermIO, title: []const u8) void {
|
||||||
try term_io.stdout.print("\x1b]0;{s}\x1b\\", .{title});
|
term_io.print("\x1b]0;{s}\x1b\\", .{title});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print(term_io: *const TermIO, comptime format: []const u8, args: anytype, flags: PrintFlags) !void {
|
pub fn print(term_io: *const TermIO, comptime format: []const u8, args: anytype) void {
|
||||||
// try term_io.stdout.print("{s}{s}{s}{s}{s}" ++ format ++ "\x1b[0m", .{ if (flags.bold) "\x1b[1m" else "", if (flags.dim) "\x1b[2m" else "", if (flags.italic) "\x1b[3m" else "", if (flags.underline) "\x1b[4m" else "", if (flags.highlight) "\x1b[7m" else "" } ++ args);
|
term_io.stdout.print(format, args) catch @panic("Failed to print!!");
|
||||||
_ = flags;
|
|
||||||
try term_io.stdout.print(format, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output(term_io: *const TermIO, x: usize, y: usize, comptime format: []const u8, args: anytype, flags: PrintFlags) !void {
|
pub fn output(term_io: *const TermIO, x: usize, y: usize, comptime format: []const u8, args: anytype) void {
|
||||||
// try term_io.stdout.print("\x1b[{d};{d}H{s}{s}{s}{s}{s}" ++ format ++ "\x1b[0m", .{ y, x, if (flags.bold) "\x1b[1m" else "", if (flags.dim) "\x1b[2m" else "", if (flags.italic) "\x1b[3m" else "", if (flags.underline) "\x1b[4m" else "", if (flags.highlight) "\x1b[7m" else "" } ++ args);
|
term_io.stdout.print("\x1b[{d};{d}H" ++ format, .{ y, x } ++ args) catch @panic("Failed to print!!");
|
||||||
_ = flags;
|
}
|
||||||
try term_io.stdout.print("\x1b[{d};{d}H" ++ format, .{ y, x } ++ args);
|
|
||||||
|
pub fn fillBox(term_io: *const TermIO, dims: dim.CalculatedDimensions) void {
|
||||||
|
for (0..dims.size.height) |i| {
|
||||||
|
term_io.moveCursor(dims.pos.x, dims.pos.y + i);
|
||||||
|
term_io.print("\x1b[{d}X", .{dims.size.width});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawBox(term_io: *TermIO, dims: dim.CalculatedDimensions, border: borders.Border, fill: bool) !void {
|
pub fn drawBox(term_io: *TermIO, dims: dim.CalculatedDimensions, border: borders.Border, fill: bool) !void {
|
||||||
if (dims.size.width < 2 or dims.size.height < 2) {
|
if (dims.size.width < 2 or dims.size.height < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try term_io.output(dims.pos.x, dims.pos.y, "{u}", .{border.top_left}, .{});
|
term_io.output(dims.pos.x, dims.pos.y, "{u}", .{border.top_left});
|
||||||
for (0..dims.size.width - 2) |_| {
|
for (0..dims.size.width - 2) |_| {
|
||||||
try term_io.print("{u}", .{border.top}, .{});
|
term_io.print("{u}", .{border.top});
|
||||||
}
|
}
|
||||||
try term_io.print("{u}", .{border.top_right}, .{});
|
term_io.print("{u}", .{border.top_right});
|
||||||
for (1..dims.size.height - 1) |h| {
|
for (1..dims.size.height - 1) |h| {
|
||||||
try term_io.output(dims.pos.x, dims.pos.y + h, "{u}", .{border.left}, .{});
|
term_io.output(dims.pos.x, dims.pos.y + h, "{u}", .{border.left});
|
||||||
if (fill) {
|
if (fill) {
|
||||||
try term_io.print("\x1b[{d}X", .{dims.size.width - 2}, .{});
|
term_io.print("\x1b[{d}X", .{dims.size.width - 2});
|
||||||
}
|
}
|
||||||
try 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();
|
try term_io.flush();
|
||||||
_ = term_io.getKey(true);
|
_ = term_io.getKey(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try 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});
|
||||||
for (0..dims.size.width - 2) |_| {
|
for (0..dims.size.width - 2) |_| {
|
||||||
try term_io.print("{u}", .{border.bottom}, .{});
|
term_io.print("{u}", .{border.bottom});
|
||||||
}
|
}
|
||||||
try term_io.print("{u}", .{border.bottom_right}, .{});
|
term_io.print("{u}", .{border.bottom_right});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getKey(term_io: *TermIO, block: bool) Key {
|
pub fn getKey(term_io: *TermIO, block: bool) Key {
|
||||||
@ -493,10 +473,9 @@ pub const TermIO = struct {
|
|||||||
if (!term_io.escape_sequence_queued) {
|
if (!term_io.escape_sequence_queued) {
|
||||||
const c = term_io.stdin.readByte() catch |err| {
|
const c = term_io.stdin.readByte() catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
error.WouldBlock => if (!block) return .{ .type = KeyType.ASCII, .value = 0 },
|
error.WouldBlock, error.EndOfStream => if (!block) return .{ .type = KeyType.NO_KEY, .value = 0 },
|
||||||
error.EndOfStream => if (!block) return .{ .type = KeyType.ASCII, .value = 0 },
|
|
||||||
else => {
|
else => {
|
||||||
std.debug.print("Error: {}\n", .{err});
|
std.debug.print("Error: {any}\n", .{err});
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user