Refactored the way layouts are created
This commit is contained in:
parent
11dd99dcfa
commit
a4ee6e5961
@ -1,32 +1,33 @@
|
|||||||
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 ButtonConfig = struct {
|
||||||
|
text: []const u8,
|
||||||
|
callback: *const fn () void,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Button = struct {
|
pub const Button = struct {
|
||||||
pane: Pane,
|
pane: Pane,
|
||||||
text: []const u8,
|
text: []const u8,
|
||||||
callback: *const fn () void,
|
callback: *const fn () void,
|
||||||
|
|
||||||
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, text: []const u8, callback: *const fn () void) Button {
|
pub fn create(mem: *Button, config: ButtonConfig) *Pane {
|
||||||
return .{
|
mem.text = config.text;
|
||||||
.pane = .{
|
mem.callback = config.callback;
|
||||||
.parent = parent,
|
mem.pane = Pane{
|
||||||
.children = null,
|
|
||||||
.dimensions = dimensions,
|
|
||||||
.focusable = true,
|
.focusable = true,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
.focus = focus,
|
|
||||||
.update = update,
|
.update = update,
|
||||||
|
.focus = focus,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
.text = text,
|
|
||||||
.callback = callback,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return &mem.pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
@ -37,7 +38,6 @@ pub const Button = struct {
|
|||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
|
|
||||||
try writer.writeAll(self.text);
|
try writer.writeAll(self.text);
|
||||||
term_io.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
|
25
src/line.zig
25
src/line.zig
@ -2,37 +2,36 @@ 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 borders = @import("borders.zig");
|
const borders = @import("borders.zig");
|
||||||
|
|
||||||
|
const Pane = pane.Pane;
|
||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
pub const HorizontalLine = struct {
|
pub const HorizontalLine = struct {
|
||||||
pane: pane.Pane,
|
pane: Pane,
|
||||||
border: borders.Border,
|
border: borders.Border,
|
||||||
|
|
||||||
pub fn create(parent: *pane.Pane, dimensions: dim.Dimensions, border: borders.Border) HorizontalLine {
|
pub fn create(mem: *HorizontalLine, border: borders.Border) *Pane {
|
||||||
return .{
|
mem.border = border;
|
||||||
.pane = pane.Pane{
|
mem.pane = Pane{
|
||||||
.parent = parent,
|
|
||||||
.children = null,
|
|
||||||
.dimensions = dimensions,
|
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
.border = border,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return &mem.pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(pane_ptr: *pane.Pane, term_io: *TermIO) !void {
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
const self: *HorizontalLine = @fieldParentPtr("pane", pane_ptr);
|
const self: *HorizontalLine = @fieldParentPtr("pane", pane_ptr);
|
||||||
const y = pane_ptr.calcDims.size.height / 2;
|
const y = pane_ptr.dimensions.size.height / 2;
|
||||||
pane_ptr.cursor = .{ .x = 0, .y = y };
|
pane_ptr.cursor = .{ .x = 0, .y = y };
|
||||||
pane_ptr.moveCursor(term_io);
|
pane_ptr.moveCursor(term_io);
|
||||||
for (0..pane_ptr.calcDims.size.width) |_| {
|
for (0..pane_ptr.dimensions.size.width) |_| {
|
||||||
term_io.print("{u}", .{self.border.top});
|
term_io.print("{u}", .{self.border.top});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const VerticalLine = struct {
|
pub const VerticalLine = struct {
|
||||||
pane: pane.Pane,
|
pane: Pane,
|
||||||
};
|
};
|
||||||
|
107
src/main.zig
107
src/main.zig
@ -26,28 +26,19 @@ fn on_click() void {
|
|||||||
|
|
||||||
fn on_side_select(item: usize) void {
|
fn on_side_select(item: usize) void {
|
||||||
log.debug("Side menu: Item {d} was selected", .{item});
|
log.debug("Side menu: Item {d} was selected", .{item});
|
||||||
if (item == 1) {
|
if (item == 0) {
|
||||||
pane.focus(&m.pane, &term_io);
|
pane.focus(&m.pane, &term_io);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_select(item: usize) void {
|
fn on_select(item: usize) void {
|
||||||
log.debug("Main menu: Item {d} was selected", .{item});
|
log.debug("Main menu: Item {d} was selected", .{item});
|
||||||
if (item == 1) {
|
if (item == 0) {
|
||||||
pane.focus(&side_menu.pane, &term_io);
|
pane.focus(&side_menu.pane, &term_io);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
defer {
|
|
||||||
if (gpa.deinit() == .leak) {
|
|
||||||
log.warn("Memory was leaked D:", .{});
|
|
||||||
std.debug.print("Memory was leaked D:\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try log.init("panes.log", .Debug);
|
try log.init("panes.log", .Debug);
|
||||||
defer log.deinit();
|
defer log.deinit();
|
||||||
|
|
||||||
@ -55,60 +46,58 @@ pub fn main() !void {
|
|||||||
var stdout = std.io.bufferedWriter(std.io.getStdOut().writer().any());
|
var stdout = std.io.bufferedWriter(std.io.getStdOut().writer().any());
|
||||||
term_io = try term.getTermIO(stdin.any(), stdout.writer());
|
term_io = try term.getTermIO(stdin.any(), stdout.writer());
|
||||||
|
|
||||||
var top = pane.Pane{
|
var top: stack.Stack = undefined;
|
||||||
.dimensions = .{
|
var child: stack.Stack = undefined;
|
||||||
.x = dim.Fill,
|
var button: btn.Button = undefined;
|
||||||
.y = dim.Fill,
|
var tabbar: tabs.TabBar = undefined;
|
||||||
},
|
|
||||||
.border = borders.BoldBorder,
|
var items = [_]menu.MenuItem{ .{ .name = "Item 1" }, .{ .name = "Item ab" } };
|
||||||
.children = std.ArrayList(*pane.Pane).init(allocator),
|
var items2 = [_]menu.MenuItem{ .{ .name = "Item 1" }, .{ .name = "Item ab" } };
|
||||||
.background = color.RGB(30, 30, 30),
|
var tabs_ = [_]tabs.Tab{ .{ .name = "Tab 1", .pane = &side_menu.pane }, .{ .name = "Tab 2", .pane = &child.pane }, .{ .name = "Tab 3", .pane = &child.pane }, .{ .name = "Tab 4", .pane = &child.pane }, .{ .name = "Tab 5", .pane = &child.pane } };
|
||||||
.foreground = color.RGB(0, 255, 0),
|
|
||||||
};
|
_ = stack.Stack.create(&top, .Horizontal, &[_]stack.StackedPane{
|
||||||
|
.{
|
||||||
|
.pane = menu.Menu.create(&side_menu, .{ .title = "Menu", .align_text = .Center, .expand_highlight = true, .on_select = on_side_select }, &items2),
|
||||||
|
.dimensions = .{ .width = .{ .type = .Fill, .value = 1 }, .height = .{ .type = .Fill, .value = 100 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.pane = stack.Stack.create(&child, .Vertical, &[_]stack.StackedPane{
|
||||||
|
.{
|
||||||
|
.pane = tabs.TabBar.create(&tabbar, .{ .highlight_color = color.RGB(0, 0, 255) }, &tabs_),
|
||||||
|
.dimensions = .{ .width = .{ .type = .Fill, .value = 100 }, .height = .{ .type = .Absolute, .value = 1 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.pane = menu.Menu.create(&m, .{ .title = "Test", .align_text = .Left, .expand_highlight = true, .on_select = on_select }, &items),
|
||||||
|
.dimensions = .{ .width = .{ .type = .Fill, .value = 100 }, .height = .{ .type = .Fill, .value = 1 } },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.pane = btn.Button.create(&button, .{ .text = "<Button>", .callback = on_click }),
|
||||||
|
.dimensions = .{ .width = .{ .type = .Absolute, .value = 8 }, .height = .{ .type = .Absolute, .value = 1 } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
.dimensions = .{ .width = .{ .type = .Fill, .value = 9 }, .height = .{ .type = .Fill, .value = 100 } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
top.pane.border = borders.BoldBorder;
|
||||||
|
top.pane.background = color.RGB(30, 30, 30);
|
||||||
|
top.pane.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.background = color.RGB(80, 80, 80);
|
||||||
side_menu.pane.foreground = top.foreground;
|
side_menu.pane.foreground = top.pane.foreground;
|
||||||
|
|
||||||
var child = pane.Pane{
|
child.pane.border = borders.BoldBorder;
|
||||||
.parent = &top,
|
child.pane.background = color.RGB(125, 0, 125);
|
||||||
.children = std.ArrayList(*pane.Pane).init(allocator),
|
child.pane.foreground = color.RGB(255, 125, 10);
|
||||||
.dimensions = .{
|
|
||||||
.anchor = .CenterRight,
|
|
||||||
.x = .{ .value = 80, .type = .Relative },
|
|
||||||
.y = .{ .value = 100, .type = .Relative },
|
|
||||||
},
|
|
||||||
.border = borders.BoldBorder,
|
|
||||||
.background = color.RGB(125, 0, 125),
|
|
||||||
.foreground = color.RGB(255, 125, 10),
|
|
||||||
};
|
|
||||||
|
|
||||||
var button = btn.Button.init(&child, .{ .anchor = .BottomLeft, .x = .{ .value = 8 }, .y = .{ .value = 1 } }, "<Button>", on_click);
|
button.pane.background = child.pane.background;
|
||||||
button.pane.background = child.background;
|
button.pane.foreground = child.pane.foreground;
|
||||||
button.pane.foreground = child.foreground;
|
|
||||||
|
|
||||||
var items = [_]menu.MenuItem{ .{ .name = "Item 1", .value = 1 }, .{ .name = "Item ab", .value = 2 } };
|
m.pane.background = child.pane.background;
|
||||||
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.foreground = child.pane.foreground;
|
||||||
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);
|
pane.set_layout(&top.pane, &m.pane);
|
||||||
var _tabs = [_]tabs.Tab{ .{ .name = "Tab 1", .pane = &side_menu.pane }, .{ .name = "Tab 2", .pane = &child }, .{ .name = "Tab 3", .pane = &child }, .{ .name = "Tab 4", .pane = &child }, .{ .name = "Tab 5", .pane = &child } };
|
try pane.init(&term_io);
|
||||||
var tabbar = tabs.TabBar.create(&child, .{ .highlight_color = color.RGB(0, 0, 255) }, &_tabs, .{ .type = .Relative, .value = 100 });
|
|
||||||
tabbar.pane.dimensions.anchor = .Center;
|
|
||||||
// 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 child.children.?.append(&m.pane);
|
|
||||||
try child.children.?.append(&tabbar.pane);
|
|
||||||
try child.children.?.append(&button.pane);
|
|
||||||
|
|
||||||
defer top.children.?.deinit();
|
|
||||||
defer child.children.?.deinit();
|
|
||||||
|
|
||||||
try pane.init(&term_io, &top, &m.pane);
|
|
||||||
defer pane.cleanup(&term_io);
|
defer pane.cleanup(&term_io);
|
||||||
|
|
||||||
while (!pane.should_exit.load(.acquire)) {
|
while (!pane.should_exit.load(.acquire)) {
|
||||||
|
76
src/menu.zig
76
src/menu.zig
@ -14,7 +14,6 @@ pub const TextAlignment = enum {
|
|||||||
|
|
||||||
pub const MenuItem = struct {
|
pub const MenuItem = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
value: usize,
|
|
||||||
disabled: bool = false,
|
disabled: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,6 +21,7 @@ pub const MenuConfig = struct {
|
|||||||
title: []const u8 = "",
|
title: []const u8 = "",
|
||||||
align_text: TextAlignment = .Left,
|
align_text: TextAlignment = .Left,
|
||||||
expand_highlight: bool = false,
|
expand_highlight: bool = false,
|
||||||
|
on_select: *const fn (usize) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Menu = struct {
|
pub const Menu = struct {
|
||||||
@ -29,39 +29,28 @@ pub const Menu = struct {
|
|||||||
config: MenuConfig,
|
config: MenuConfig,
|
||||||
items: []MenuItem,
|
items: []MenuItem,
|
||||||
on_select: *const fn (usize) void,
|
on_select: *const fn (usize) void,
|
||||||
selected_item: usize = 0,
|
index: usize = 0,
|
||||||
selected_value: usize = 0,
|
|
||||||
|
|
||||||
pub fn init(parent: ?*Pane, dimensions: dim.Dimensions, config: MenuConfig, items: []MenuItem, on_select: *const fn (usize) void) Menu {
|
pub fn create(mem: *Menu, config: MenuConfig, items: []MenuItem) *Pane {
|
||||||
for (items) |item| {
|
mem.config = config;
|
||||||
std.debug.assert(item.value != 0);
|
mem.on_select = config.on_select;
|
||||||
}
|
mem.items = items;
|
||||||
|
mem.pane = .{
|
||||||
var menu = Menu{
|
|
||||||
.pane = .{
|
|
||||||
.parent = parent,
|
|
||||||
.children = null,
|
|
||||||
.dimensions = dimensions,
|
|
||||||
.focusable = true,
|
.focusable = true,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
.focus = focus,
|
.focus = focus,
|
||||||
.update = update,
|
.update = update,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
.config = config,
|
|
||||||
.items = items,
|
|
||||||
.on_select = on_select,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
menu.reset();
|
mem.reset();
|
||||||
|
|
||||||
return menu;
|
return &mem.pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(self: *Menu) void {
|
pub fn reset(self: *Menu) void {
|
||||||
self.selected_item = self.firstEnabledItem() catch self.items.len;
|
self.index = self.firstEnabledItem() catch self.items.len;
|
||||||
self.selected_value = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
@ -69,18 +58,16 @@ pub const Menu = struct {
|
|||||||
|
|
||||||
try self.printTitle(term_io);
|
try self.printTitle(term_io);
|
||||||
for (0..self.items.len) |i| {
|
for (0..self.items.len) |i| {
|
||||||
if (i == self.selected_item) {
|
if (i == self.index) {
|
||||||
term_io.enableFormats(.{ .dim = !self.pane.focused });
|
term_io.enableFormats(.{ .dim = !self.pane.focused });
|
||||||
}
|
}
|
||||||
try self.printItem(term_io, i);
|
try self.printItem(term_io, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
term_io.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(pane_ptr: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
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.index == self.items.len or key.type == .NO_KEY) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +80,8 @@ pub const Menu = struct {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (key.value) {
|
switch (key.value) {
|
||||||
13 => self.on_select(self.selected_value), // Enter
|
13 => self.on_select(self.index), // Enter
|
||||||
0x1b, 'q' => self.selected_value = 0,
|
0x1b, 'q' => {}, // self.index = 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 => return false,
|
else => return false,
|
||||||
@ -108,15 +95,15 @@ pub const Menu = struct {
|
|||||||
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
var self: *Menu = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
term_io.enableFormats(.{ .dim = !pane_ptr.focused });
|
term_io.enableFormats(.{ .dim = !pane_ptr.focused });
|
||||||
try self.printItem(term_io, self.selected_item);
|
try self.printItem(term_io, self.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printTitle(self: *Menu, term_io: *TermIO) !void {
|
fn printTitle(self: *Menu, term_io: *TermIO) !void {
|
||||||
var writer = self.pane.writer(term_io);
|
var writer = self.pane.writer(term_io);
|
||||||
switch (self.config.align_text) {
|
switch (self.config.align_text) {
|
||||||
.Left => self.pane.cursor = .{ .x = 0, .y = 0 },
|
.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 },
|
.Center => self.pane.cursor = .{ .x = (self.pane.dimensions.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 },
|
.Right => self.pane.cursor = .{ .x = self.pane.dimensions.size.width - self.config.title.len, .y = 0 },
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
@ -127,7 +114,7 @@ pub const Menu = struct {
|
|||||||
var writer = self.pane.writer(term_io);
|
var writer = self.pane.writer(term_io);
|
||||||
const label = self.items[item].name;
|
const label = self.items[item].name;
|
||||||
|
|
||||||
term_io.enableFormats(.{ .highlight = item == self.selected_item, .dim = self.items[item].disabled });
|
term_io.enableFormats(.{ .highlight = item == self.index, .dim = self.items[item].disabled });
|
||||||
|
|
||||||
switch (self.config.align_text) {
|
switch (self.config.align_text) {
|
||||||
.Left => {
|
.Left => {
|
||||||
@ -137,11 +124,11 @@ pub const Menu = struct {
|
|||||||
try writer.writeAll(label);
|
try writer.writeAll(label);
|
||||||
|
|
||||||
if (self.config.expand_highlight) {
|
if (self.config.expand_highlight) {
|
||||||
try writer.writeByteNTimes(' ', self.pane.calcDims.size.width - label.len);
|
try writer.writeByteNTimes(' ', self.pane.dimensions.size.width - label.len);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Center => {
|
.Center => {
|
||||||
const left = (self.pane.calcDims.size.width - label.len) / 2;
|
const left = (self.pane.dimensions.size.width - label.len) / 2;
|
||||||
if (self.config.expand_highlight) {
|
if (self.config.expand_highlight) {
|
||||||
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
@ -154,16 +141,16 @@ pub const Menu = struct {
|
|||||||
try writer.writeAll(label);
|
try writer.writeAll(label);
|
||||||
|
|
||||||
if (self.config.expand_highlight) {
|
if (self.config.expand_highlight) {
|
||||||
try writer.writeByteNTimes(' ', (self.pane.calcDims.size.width - label.len) - left);
|
try writer.writeByteNTimes(' ', (self.pane.dimensions.size.width - label.len) - left);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Right => {
|
.Right => {
|
||||||
if (self.config.expand_highlight) {
|
if (self.config.expand_highlight) {
|
||||||
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
self.pane.cursor = .{ .x = 0, .y = item + 1 };
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
try writer.writeByteNTimes(' ', self.pane.calcDims.size.width - label.len);
|
try writer.writeByteNTimes(' ', self.pane.dimensions.size.width - label.len);
|
||||||
} else {
|
} else {
|
||||||
self.pane.cursor = .{ .x = self.pane.calcDims.size.width - label.len, .y = item + 1 };
|
self.pane.cursor = .{ .x = self.pane.dimensions.size.width - label.len, .y = item + 1 };
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +172,8 @@ pub const Menu = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn nextItem(self: *Menu, term_io: *TermIO) !void {
|
fn nextItem(self: *Menu, term_io: *TermIO) !void {
|
||||||
var i: usize = self.selected_item;
|
var i: usize = self.index;
|
||||||
while (i < self.items.len - 1 and (self.items[i].disabled or i == self.selected_item)) {
|
while (i < self.items.len - 1 and (self.items[i].disabled or i == self.index)) {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
if (!self.items[i].disabled) {
|
if (!self.items[i].disabled) {
|
||||||
@ -195,8 +182,8 @@ pub const Menu = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn prevItem(self: *Menu, term_io: *TermIO) !void {
|
fn prevItem(self: *Menu, term_io: *TermIO) !void {
|
||||||
var i: usize = self.selected_item;
|
var i: usize = self.index;
|
||||||
while (i > 0 and (self.items[i].disabled or i == self.selected_item)) {
|
while (i > 0 and (self.items[i].disabled or i == self.index)) {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
}
|
}
|
||||||
if (!self.items[i].disabled) {
|
if (!self.items[i].disabled) {
|
||||||
@ -209,13 +196,12 @@ pub const Menu = struct {
|
|||||||
return error.MenuItemDisabled;
|
return error.MenuItemDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousSelection = self.selected_item;
|
const previousSelection = self.index;
|
||||||
self.selected_item = item;
|
self.index = item;
|
||||||
self.selected_value = self.items[self.selected_item].value;
|
|
||||||
|
|
||||||
if (previousSelection != self.selected_item) {
|
if (previousSelection != self.index) {
|
||||||
try self.printItem(term_io, previousSelection);
|
try self.printItem(term_io, previousSelection);
|
||||||
}
|
}
|
||||||
try self.printItem(term_io, self.selected_item);
|
try self.printItem(term_io, self.index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
264
src/pane.zig
264
src/pane.zig
@ -2,16 +2,45 @@ const std = @import("std");
|
|||||||
const log = @import("log.zig");
|
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 stack = @import("stack.zig");
|
||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
const Border = @import("borders.zig").Border;
|
const Border = @import("borders.zig").Border;
|
||||||
|
|
||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
pub var top_pane: *Pane = undefined;
|
const Window = struct {
|
||||||
|
pane: Pane,
|
||||||
|
child: ?*Pane,
|
||||||
|
|
||||||
|
fn resize(self: *Window, term_io: *TermIO) void {
|
||||||
|
const size = term.getTermSize(term_io);
|
||||||
|
window.pane.dimensions = dim.CalculatedDimensions{
|
||||||
|
.pos = .{ .x = 1, .y = 1 },
|
||||||
|
.internal_pos = .{ .x = 1, .y = 1 },
|
||||||
|
.size = size,
|
||||||
|
.internal_size = size,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self.child) |child| {
|
||||||
|
child.dimensions.size = self.pane.dimensions.size;
|
||||||
|
child.dimensions.pos = self.pane.dimensions.pos;
|
||||||
|
child.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Window, term_io: *TermIO) !void {
|
||||||
|
if (self.child) |child| {
|
||||||
|
try child.draw(term_io);
|
||||||
|
term_io.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var window = Window{ .pane = Pane{}, .child = null };
|
||||||
pub var focused_pane: *Pane = undefined;
|
pub var focused_pane: *Pane = undefined;
|
||||||
pub var focused_index: usize = 0;
|
pub var focused_index: usize = 1;
|
||||||
pub var should_exit: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
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 needs_redraw: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
||||||
var redraw_count: usize = 0;
|
var redraw_count: usize = 0;
|
||||||
|
|
||||||
fn resize_signal(_: i32) callconv(.C) void {
|
fn resize_signal(_: i32) callconv(.C) void {
|
||||||
@ -22,11 +51,14 @@ fn exit_signal(_: i32) callconv(.C) void {
|
|||||||
should_exit.store(true, .release);
|
should_exit.store(true, .release);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(term_io: *TermIO, top: *Pane, focused: *Pane) !void {
|
pub fn set_layout(layout: *Pane, initial_focus: *Pane) void {
|
||||||
top_pane = top;
|
layout.parent = &window.pane;
|
||||||
focused.focused = true;
|
window.child = layout;
|
||||||
focused_pane = focused;
|
focused_pane = initial_focus;
|
||||||
|
focused_pane.focused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(term_io: *TermIO) !void {
|
||||||
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,
|
||||||
@ -45,6 +77,9 @@ pub fn init(term_io: *TermIO, top: *Pane, focused: *Pane) !void {
|
|||||||
term_io.saveScreen();
|
term_io.saveScreen();
|
||||||
term_io.hideCursor();
|
term_io.hideCursor();
|
||||||
term_io.flush();
|
term_io.flush();
|
||||||
|
|
||||||
|
window.resize(term_io);
|
||||||
|
try window.draw(term_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cleanup(term_io: *TermIO) void {
|
pub fn cleanup(term_io: *TermIO) void {
|
||||||
@ -71,20 +106,13 @@ pub fn tick(term_io: *TermIO) !void {
|
|||||||
|
|
||||||
if (needs_redraw.load(.acquire)) {
|
if (needs_redraw.load(.acquire)) {
|
||||||
needs_redraw.store(false, .release);
|
needs_redraw.store(false, .release);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
window.resize(term_io);
|
||||||
term_io.clear();
|
term_io.clear();
|
||||||
try top_pane.ReDraw(term_io, dims);
|
try window.draw(term_io);
|
||||||
|
|
||||||
redraw_count += 1;
|
redraw_count += 1;
|
||||||
log.debug("Resized {} times", .{redraw_count});
|
log.debug("Resized {} times", .{redraw_count});
|
||||||
term_io.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try focused_pane.update(term_io, key)) {
|
if (try focused_pane.update(term_io, key)) {
|
||||||
@ -93,21 +121,18 @@ pub fn tick(term_io: *TermIO) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle_focus(term_io: *TermIO) void {
|
pub fn cycle_focus(term_io: *TermIO) void {
|
||||||
if (focused_pane.parent) |parent| {
|
const parent_stack: *stack.Stack = @fieldParentPtr("pane", focused_pane.parent);
|
||||||
if (parent.children) |children| {
|
var index = (focused_index + 1) % parent_stack.children.len;
|
||||||
var index = (focused_index + 1) % children.items.len;
|
var to_focus = parent_stack.children[index].pane;
|
||||||
var to_focus = children.items[index];
|
|
||||||
|
|
||||||
while (!to_focus.focusable and index != focused_index) {
|
while (!to_focus.focusable and index != focused_index) {
|
||||||
index = (index + 1) % children.items.len;
|
index = (index + 1) % parent_stack.children.len;
|
||||||
to_focus = children.items[index];
|
to_focus = parent_stack.children[index].pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
focus(to_focus, term_io);
|
focus(to_focus, term_io);
|
||||||
focused_index = index;
|
focused_index = index;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn focus(to_focus: *Pane, term_io: *TermIO) void {
|
pub fn focus(to_focus: *Pane, term_io: *TermIO) void {
|
||||||
if (to_focus == focused_pane) return;
|
if (to_focus == focused_pane) return;
|
||||||
@ -131,13 +156,11 @@ pub const Cursor = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Pane = struct {
|
pub const Pane = struct {
|
||||||
parent: ?*Pane = null,
|
parent: *Pane = undefined,
|
||||||
children: ?std.ArrayList(*Pane),
|
|
||||||
border: ?Border = null,
|
border: ?Border = null,
|
||||||
cursor: Cursor = .{ .x = 0, .y = 0 },
|
cursor: Cursor = .{ .x = 0, .y = 0 },
|
||||||
overflow: Overflow = .{ .x = .Hidden, .y = .Hidden },
|
overflow: Overflow = .{ .x = .Hidden, .y = .Hidden },
|
||||||
dimensions: dim.Dimensions,
|
dimensions: 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,
|
should_draw: bool = true,
|
||||||
@ -148,6 +171,7 @@ pub const Pane = struct {
|
|||||||
|
|
||||||
fn defaultDraw(_: *Pane, _: *TermIO) !void {}
|
fn defaultDraw(_: *Pane, _: *TermIO) !void {}
|
||||||
fn defaultFocus(_: *Pane, _: *TermIO) !void {}
|
fn defaultFocus(_: *Pane, _: *TermIO) !void {}
|
||||||
|
fn defaultResize(_: *Pane) void {}
|
||||||
fn defaultUpdate(_: *Pane, _: *TermIO, _: term.Key) !bool {
|
fn defaultUpdate(_: *Pane, _: *TermIO, _: term.Key) !bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -155,99 +179,119 @@ pub const Pane = struct {
|
|||||||
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) anyerror!void = defaultFocus,
|
focus: *const fn (*Pane, *TermIO) anyerror!void = defaultFocus,
|
||||||
|
resize: *const fn (*Pane) void = defaultResize,
|
||||||
update: *const fn (*Pane, *TermIO, term.Key) anyerror!bool = 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 };
|
||||||
pub const Writer = std.io.Writer(WriterContext, anyerror, write);
|
pub const Writer = std.io.Writer(WriterContext, anyerror, 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 {
|
||||||
if (!self.should_draw) {
|
// if (!self.should_draw) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// if (self.dimensions.x.type == .Relative) {
|
||||||
|
// std.debug.assert(self.dimensions.x.value <= 100);
|
||||||
|
// self.dimensions.size.width = (parentDims.internal_size.width * self.dimensions.x.value) / 100;
|
||||||
|
// } else {
|
||||||
|
// self.dimensions.size.width = self.dimensions.x.value;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (self.dimensions.y.type == .Relative) {
|
||||||
|
// std.debug.assert(self.dimensions.y.value <= 100);
|
||||||
|
// self.dimensions.size.height = (parentDims.internal_size.height * self.dimensions.y.value) / 100;
|
||||||
|
// } else {
|
||||||
|
// self.dimensions.size.height = self.dimensions.y.value;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// self.dimensions.pos = parentDims.internal_pos;
|
||||||
|
// switch (self.dimensions.anchor) {
|
||||||
|
// .TopLeft => {},
|
||||||
|
// .TopCenter => {
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.dimensions.size.width) / 2;
|
||||||
|
// },
|
||||||
|
// .TopRight => {
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.dimensions.size.width;
|
||||||
|
// },
|
||||||
|
// .CenterLeft => {
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.dimensions.size.height) / 2;
|
||||||
|
// },
|
||||||
|
// .Center => {
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.dimensions.size.width) / 2;
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.dimensions.size.height) / 2;
|
||||||
|
// },
|
||||||
|
// .CenterRight => {
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.dimensions.size.width;
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.dimensions.size.height) / 2;
|
||||||
|
// },
|
||||||
|
// .BottomLeft => {
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.dimensions.size.height;
|
||||||
|
// },
|
||||||
|
// .BottomCenter => {
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.dimensions.size.height;
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.dimensions.size.width) / 2;
|
||||||
|
// },
|
||||||
|
// .BottomRight => {
|
||||||
|
// self.dimensions.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.dimensions.size.height;
|
||||||
|
// self.dimensions.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.dimensions.size.width;
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var fill = false;
|
||||||
|
// term_io.setColor(self.background, self.foreground);
|
||||||
|
// fill = !self.background.equal(self.parent.background);
|
||||||
|
//
|
||||||
|
// self.dimensions.internal_size = self.dimensions.size;
|
||||||
|
// self.dimensions.internal_pos = self.dimensions.pos;
|
||||||
|
// if (self.border) |border| {
|
||||||
|
// try term_io.drawBox(self.dimensions, border, fill);
|
||||||
|
//
|
||||||
|
// self.dimensions.internal_size = .{
|
||||||
|
// .width = self.dimensions.size.width - 2,
|
||||||
|
// .height = self.dimensions.size.height - 2,
|
||||||
|
// };
|
||||||
|
// self.dimensions.internal_pos = .{
|
||||||
|
// .x = self.dimensions.pos.x + 1,
|
||||||
|
// .y = self.dimensions.pos.y + 1,
|
||||||
|
// };
|
||||||
|
// } else if (fill) {
|
||||||
|
// term_io.fillBox(self.dimensions);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// term_io.flush();
|
||||||
|
//
|
||||||
|
// try self.vtable.draw(self, term_io);
|
||||||
|
//
|
||||||
|
// // TODO: Lots to do here
|
||||||
|
// // if (self.children) |children| {
|
||||||
|
// // for (children.items) |child| {
|
||||||
|
// // try child.ReDraw(term_io, self.calcDims);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
if (self.dimensions.x.type == .Relative) {
|
pub fn resize(self: *Pane) void {
|
||||||
std.debug.assert(self.dimensions.x.value <= 100);
|
if (self.border) |_| {
|
||||||
self.calcDims.size.width = (parentDims.internal_size.width * self.dimensions.x.value) / 100;
|
self.dimensions.internal_pos = .{ .x = self.dimensions.pos.x + 1, .y = self.dimensions.pos.y + 1 };
|
||||||
|
self.dimensions.internal_size = .{ .height = self.dimensions.size.height - 2, .width = self.dimensions.size.width - 2 };
|
||||||
} else {
|
} else {
|
||||||
self.calcDims.size.width = self.dimensions.x.value;
|
self.dimensions.internal_pos = self.dimensions.pos;
|
||||||
|
self.dimensions.internal_size = self.dimensions.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.dimensions.y.type == .Relative) {
|
self.vtable.resize(self);
|
||||||
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;
|
pub fn draw(self: *Pane, term_io: *TermIO) !void {
|
||||||
switch (self.dimensions.anchor) {
|
|
||||||
.TopLeft => {},
|
|
||||||
.TopCenter => {
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
|
||||||
},
|
|
||||||
.TopRight => {
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
|
||||||
},
|
|
||||||
.CenterLeft => {
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
|
||||||
},
|
|
||||||
.Center => {
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
|
||||||
},
|
|
||||||
.CenterRight => {
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + (parentDims.internal_size.height - self.calcDims.size.height) / 2;
|
|
||||||
},
|
|
||||||
.BottomLeft => {
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
|
||||||
},
|
|
||||||
.BottomCenter => {
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + (parentDims.internal_size.width - self.calcDims.size.width) / 2;
|
|
||||||
},
|
|
||||||
.BottomRight => {
|
|
||||||
self.calcDims.pos.y = parentDims.internal_pos.y + parentDims.internal_size.height - self.calcDims.size.height;
|
|
||||||
self.calcDims.pos.x = parentDims.internal_pos.x + parentDims.internal_size.width - self.calcDims.size.width;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var fill = false;
|
|
||||||
if (self.parent) |parent| {
|
|
||||||
term_io.setColor(self.background, self.foreground);
|
|
||||||
fill = !self.background.equal(parent.background);
|
|
||||||
} else {
|
|
||||||
term_io.setColor(self.background, self.foreground);
|
|
||||||
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);
|
term_io.setColor(self.background, self.foreground);
|
||||||
|
try term_io.drawBox(self.dimensions, border, !self.background.equal(self.parent.background));
|
||||||
self.calcDims.internal_size = .{
|
} else if (!self.background.equal(self.parent.background)) {
|
||||||
.width = self.calcDims.size.width - 2,
|
term_io.setColor(self.background, self.foreground);
|
||||||
.height = self.calcDims.size.height - 2,
|
term_io.fillBox(self.dimensions);
|
||||||
};
|
|
||||||
self.calcDims.internal_pos = .{
|
|
||||||
.x = self.calcDims.pos.x + 1,
|
|
||||||
.y = self.calcDims.pos.y + 1,
|
|
||||||
};
|
|
||||||
} else if (fill) {
|
|
||||||
term_io.fillBox(self.calcDims);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term_io.flush();
|
|
||||||
|
|
||||||
try self.vtable.draw(self, term_io);
|
try self.vtable.draw(self, term_io);
|
||||||
|
|
||||||
if (self.children) |children| {
|
|
||||||
for (children.items) |child| {
|
|
||||||
try child.ReDraw(term_io, self.calcDims);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
pub fn update(self: *Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
@ -274,19 +318,19 @@ 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);
|
term_io.moveCursor(self.dimensions.pos.x + self.cursor.x + borderWidth, self.dimensions.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 {
|
||||||
const borderWidth: u2 = if (self.border != null) 1 else 0;
|
const borderWidth: u2 = if (self.border != null) 1 else 0;
|
||||||
var space = self.calcDims.size.width - self.cursor.x - (2 * borderWidth);
|
var space = self.dimensions.size.width - self.cursor.x - (2 * borderWidth);
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|
||||||
while (string.len - i >= space) {
|
while (string.len - i >= space) {
|
||||||
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.dimensions.size.width - (2 * borderWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < string.len) {
|
if (i < string.len) {
|
||||||
@ -310,7 +354,7 @@ pub const Pane = struct {
|
|||||||
'\n' => self.pane.nextLine(self.term_io),
|
'\n' => self.pane.nextLine(self.term_io),
|
||||||
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.dimensions.size.width - (2 * borderWidth)) {
|
||||||
switch (self.pane.overflow.x) {
|
switch (self.pane.overflow.x) {
|
||||||
.Wrap => self.pane.nextLine(self.term_io),
|
.Wrap => self.pane.nextLine(self.term_io),
|
||||||
.Hidden => return bytes.len,
|
.Hidden => return bytes.len,
|
||||||
|
263
src/stack.zig
Normal file
263
src/stack.zig
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
const dim = @import("dimensions.zig");
|
||||||
|
const term = @import("term.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Pane = @import("pane.zig").Pane;
|
||||||
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
|
pub const StackDirection = enum {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StackedDimension = struct {
|
||||||
|
type: enum { Fill, Absolute },
|
||||||
|
value: usize = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StackedDimensions = struct {
|
||||||
|
width: StackedDimension = .{ .type = .Fill },
|
||||||
|
height: StackedDimension = .{ .type = .Fill },
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StackedPane = struct {
|
||||||
|
pane: *Pane,
|
||||||
|
dimensions: StackedDimensions = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn breakpoint() void {}
|
||||||
|
|
||||||
|
pub const Stack = struct {
|
||||||
|
pane: Pane,
|
||||||
|
direction: StackDirection,
|
||||||
|
children: []const StackedPane,
|
||||||
|
|
||||||
|
pub fn create(mem: *Stack, direction: StackDirection, children: []const StackedPane) *Pane {
|
||||||
|
// if (@typeInfo(@TypeOf(children)) != .Struct) {
|
||||||
|
// @compileError("Invalid type" ++ @typeName(@TypeOf(children)));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const fields = std.meta.fields(@TypeOf(children));
|
||||||
|
// var children_array: [fields.len]StackedPane = undefined;
|
||||||
|
// inline for (fields, 0..) |field, i| {
|
||||||
|
// if (field.type != StackedPane) {
|
||||||
|
// @compileError("Invalid child type");
|
||||||
|
// }
|
||||||
|
// children_array[i] = @field(children, field.name);
|
||||||
|
// }
|
||||||
|
|
||||||
|
mem.direction = direction;
|
||||||
|
mem.children = children;
|
||||||
|
mem.pane = Pane{
|
||||||
|
.focusable = false,
|
||||||
|
.vtable = .{
|
||||||
|
.resize = resize,
|
||||||
|
.draw = draw,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (mem.children) |child| {
|
||||||
|
child.pane.parent = &mem.pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoint();
|
||||||
|
|
||||||
|
return &mem.pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(pane_ptr: *Pane) void {
|
||||||
|
var self: *Stack = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
if (self.direction == .Vertical) {
|
||||||
|
self.resizeV();
|
||||||
|
} else {
|
||||||
|
self.resizeH();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.children) |child| {
|
||||||
|
if (child.pane.should_draw and child.pane.dimensions.size.width > 0 and child.pane.dimensions.size.height > 0) {
|
||||||
|
child.pane.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resizeV(self: *Stack) void {
|
||||||
|
const height = self.pane.dimensions.internal_size.height;
|
||||||
|
const width = self.pane.dimensions.internal_size.width;
|
||||||
|
|
||||||
|
var total_fill: usize = 0;
|
||||||
|
var total_absolute: usize = 0;
|
||||||
|
for (self.children) |child| {
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.height.type) {
|
||||||
|
.Fill => total_fill += child.dimensions.height.value,
|
||||||
|
.Absolute => total_absolute += child.dimensions.height.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_absolute > height) {
|
||||||
|
var used: usize = 0;
|
||||||
|
for (self.children) |child| {
|
||||||
|
child.pane.dimensions.pos = .{
|
||||||
|
.x = self.pane.dimensions.internal_pos.x,
|
||||||
|
.y = self.pane.dimensions.internal_pos.y + used,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
child.pane.dimensions.size = .{ .width = 0, .height = 0 };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.height.type) {
|
||||||
|
.Fill => {
|
||||||
|
child.pane.dimensions.size.height = 0;
|
||||||
|
},
|
||||||
|
.Absolute => {
|
||||||
|
if (used + child.dimensions.height.value > height) {
|
||||||
|
child.pane.dimensions.size.height = height - used;
|
||||||
|
} else {
|
||||||
|
child.pane.dimensions.size.height = child.dimensions.height.value;
|
||||||
|
}
|
||||||
|
used += child.pane.dimensions.size.height;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var used: usize = 0;
|
||||||
|
var available = height - total_absolute;
|
||||||
|
const ratio = available / total_fill;
|
||||||
|
|
||||||
|
for (self.children) |child| {
|
||||||
|
child.pane.dimensions.pos = .{
|
||||||
|
.x = self.pane.dimensions.internal_pos.x,
|
||||||
|
.y = self.pane.dimensions.internal_pos.y + used,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
child.pane.dimensions.size = .{ .width = 0, .height = 0 };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.height.type) {
|
||||||
|
.Fill => {
|
||||||
|
child.pane.dimensions.size.height = child.dimensions.height.value * ratio;
|
||||||
|
available -= child.pane.dimensions.size.height;
|
||||||
|
},
|
||||||
|
.Absolute => child.pane.dimensions.size.height = child.dimensions.height.value,
|
||||||
|
}
|
||||||
|
switch (child.dimensions.width.type) {
|
||||||
|
.Fill => {
|
||||||
|
child.pane.dimensions.size.width = (child.dimensions.width.value / 100) * width;
|
||||||
|
},
|
||||||
|
.Absolute => {
|
||||||
|
child.pane.dimensions.size.width = @min(child.dimensions.width.value, width);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
used += child.pane.dimensions.size.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resizeH(self: *Stack) void {
|
||||||
|
const width = self.pane.dimensions.internal_size.width;
|
||||||
|
const height = self.pane.dimensions.internal_size.height;
|
||||||
|
|
||||||
|
var total_fill: usize = 0;
|
||||||
|
var total_absolute: usize = 0;
|
||||||
|
for (self.children) |child| {
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.width.type) {
|
||||||
|
.Fill => total_fill += child.dimensions.width.value,
|
||||||
|
.Absolute => total_absolute += child.dimensions.width.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_absolute > height) {
|
||||||
|
var used: usize = 0;
|
||||||
|
for (self.children) |child| {
|
||||||
|
child.pane.dimensions.pos = .{
|
||||||
|
.x = self.pane.dimensions.internal_pos.x + used,
|
||||||
|
.y = self.pane.dimensions.internal_pos.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
child.pane.dimensions.size = .{ .width = 0, .height = 0 };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.height.type) {
|
||||||
|
.Fill => child.pane.dimensions.size.width = 0,
|
||||||
|
.Absolute => {
|
||||||
|
if (used + child.dimensions.width.value > width) {
|
||||||
|
child.pane.dimensions.size.width = width - used;
|
||||||
|
} else {
|
||||||
|
child.pane.dimensions.size.width = child.dimensions.width.value;
|
||||||
|
}
|
||||||
|
used += child.pane.dimensions.size.width;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var used: usize = 0;
|
||||||
|
var available = width - total_absolute;
|
||||||
|
const ratio = available / total_fill;
|
||||||
|
|
||||||
|
for (self.children) |child| {
|
||||||
|
child.pane.dimensions.pos = .{
|
||||||
|
.x = self.pane.dimensions.internal_pos.x + used,
|
||||||
|
.y = self.pane.dimensions.internal_pos.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!child.pane.should_draw) {
|
||||||
|
child.pane.dimensions.size = .{ .width = 0, .height = 0 };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (child.dimensions.width.type) {
|
||||||
|
.Fill => {
|
||||||
|
child.pane.dimensions.size.width = child.dimensions.width.value * ratio;
|
||||||
|
available -= child.pane.dimensions.size.width;
|
||||||
|
},
|
||||||
|
.Absolute => child.pane.dimensions.size.width = child.dimensions.width.value,
|
||||||
|
}
|
||||||
|
switch (child.dimensions.height.type) {
|
||||||
|
.Fill => {
|
||||||
|
child.pane.dimensions.size.height = (child.dimensions.height.value / 100) * height;
|
||||||
|
},
|
||||||
|
.Absolute => {
|
||||||
|
child.pane.dimensions.size.height = @min(child.dimensions.height.value, height);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
used += child.pane.dimensions.size.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
|
const self: *Stack = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
|
if (self.pane.border) |border| {
|
||||||
|
term_io.setColor(self.pane.background, self.pane.foreground);
|
||||||
|
try term_io.drawBox(self.pane.dimensions, border, !self.pane.background.equal(self.pane.parent.background));
|
||||||
|
} else if (!self.pane.background.equal(self.pane.parent.background)) {
|
||||||
|
term_io.setColor(self.pane.background, self.pane.foreground);
|
||||||
|
term_io.fillBox(self.pane.dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.children) |child| {
|
||||||
|
if (child.pane.should_draw and self.pane.dimensions.size.width > 0 and self.pane.dimensions.size.height > 0) {
|
||||||
|
try child.pane.draw(term_io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
30
src/tabs.zig
30
src/tabs.zig
@ -6,18 +6,19 @@ const pane = @import("pane.zig");
|
|||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Pane = pane.Pane;
|
||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
pub const Tab = struct {
|
pub const Tab = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
pane: *pane.Pane,
|
pane: *Pane,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const TabBar = struct {
|
pub const TabBar = struct {
|
||||||
pane: pane.Pane,
|
pane: Pane,
|
||||||
config: Config,
|
config: Config,
|
||||||
tabs: []Tab,
|
tabs: []Tab,
|
||||||
index: usize = 0,
|
index: usize,
|
||||||
|
|
||||||
const Config = struct {
|
const Config = struct {
|
||||||
align_text: menu.TextAlignment = .Center,
|
align_text: menu.TextAlignment = .Center,
|
||||||
@ -26,31 +27,26 @@ pub const TabBar = struct {
|
|||||||
underline: bool = true,
|
underline: bool = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn create(parent: *pane.Pane, config: Config, tabs: []Tab, width: dims.Dimension) TabBar {
|
pub fn create(mem: *TabBar, config: Config, tabs: []Tab) *Pane {
|
||||||
return TabBar{
|
mem.config = config;
|
||||||
.pane = pane.Pane{
|
mem.tabs = tabs;
|
||||||
.parent = parent,
|
mem.index = 0;
|
||||||
.children = null,
|
mem.pane = Pane{
|
||||||
.focusable = true,
|
.focusable = true,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
.update = update,
|
.update = update,
|
||||||
},
|
},
|
||||||
.dimensions = .{
|
|
||||||
.x = width,
|
|
||||||
.y = .{ .value = 1, .type = .Absolute },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.config = config,
|
|
||||||
.tabs = tabs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return &mem.pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(pane_ptr: *pane.Pane, term_io: *TermIO) !void {
|
fn draw(pane_ptr: *pane.Pane, term_io: *TermIO) !void {
|
||||||
var self: *TabBar = @fieldParentPtr("pane", pane_ptr);
|
var self: *TabBar = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
const tab_width = self.pane.calcDims.internal_size.width / self.tabs.len;
|
const tab_width = self.pane.dimensions.internal_size.width / self.tabs.len;
|
||||||
const extra = self.pane.calcDims.internal_size.width - (tab_width * self.tabs.len);
|
const extra = self.pane.dimensions.internal_size.width - (tab_width * self.tabs.len);
|
||||||
var writer = pane_ptr.writer(term_io);
|
var writer = pane_ptr.writer(term_io);
|
||||||
self.pane.cursor = .{ .x = 0, .y = 0 };
|
self.pane.cursor = .{ .x = 0, .y = 0 };
|
||||||
self.pane.moveCursor(term_io);
|
self.pane.moveCursor(term_io);
|
||||||
|
@ -463,12 +463,6 @@ pub const TermIO = struct {
|
|||||||
term_io.print("\x1b[{d}X", .{dims.size.width - 2});
|
term_io.print("\x1b[{d}X", .{dims.size.width - 2});
|
||||||
}
|
}
|
||||||
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) {
|
|
||||||
log.info("Had to flush buffer while drawing box", .{});
|
|
||||||
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});
|
||||||
for (0..dims.size.width - 2) |_| {
|
for (0..dims.size.width - 2) |_| {
|
||||||
|
Loading…
Reference in New Issue
Block a user