Focus works now! :D
This commit is contained in:
parent
8ae8ce9ac7
commit
e1da270181
@ -48,7 +48,7 @@ pub const Button = struct {
|
|||||||
_ = term_io;
|
_ = term_io;
|
||||||
var self: *Button = @fieldParentPtr("pane", pane_ptr);
|
var self: *Button = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
if (key.type == .ASCII and key.value == '\r') {
|
if (key.key == .Return) {
|
||||||
self.callback();
|
self.callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
115
src/focus.zig
Normal file
115
src/focus.zig
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const pane = @import("pane.zig");
|
||||||
|
const term = @import("term.zig");
|
||||||
|
|
||||||
|
const Pane = pane.Pane;
|
||||||
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
|
var index: usize = 0;
|
||||||
|
var focus_list: std.ArrayList(*Pane) = undefined;
|
||||||
|
pub var current_focus: ?*Pane = null;
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) !void {
|
||||||
|
focus_list = std.ArrayList(*Pane).init(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit() void {
|
||||||
|
focus_list.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_focus_list(top_pane: *Pane) !void {
|
||||||
|
focus_list.clearAndFree();
|
||||||
|
try add_children(top_pane);
|
||||||
|
|
||||||
|
if (current_focus) |current| {
|
||||||
|
for (focus_list.items, 0..) |item, i| {
|
||||||
|
if (item == current) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (focus_list.items.len != 0) {
|
||||||
|
index = 0;
|
||||||
|
current_focus = focus_list.items[index];
|
||||||
|
current_focus.?.focused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_children(p: *Pane) !void {
|
||||||
|
var iter = p.vtable.child_iterator(p);
|
||||||
|
while (iter.next()) |child| {
|
||||||
|
if (child.focusable) {
|
||||||
|
try focus_list.append(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
try add_children(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_first(term_io: *TermIO, p: *Pane) !void {
|
||||||
|
if (p.focusable) {
|
||||||
|
try focus_pane(term_io, p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try focus_first_child(term_io, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_first_child(term_io: *TermIO, p: *Pane) !void {
|
||||||
|
var iter = p.vtable.child_iterator(p);
|
||||||
|
|
||||||
|
while (iter.next()) |child| {
|
||||||
|
if (child.focusable) {
|
||||||
|
try focus_pane(term_io, child);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try focus_first_child(term_io, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_next(term_io: *TermIO) void {
|
||||||
|
if (focus_list.items.len < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_index = (index + 1) % focus_list.items.len;
|
||||||
|
|
||||||
|
focus_index(term_io, new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_prev(term_io: *TermIO) void {
|
||||||
|
if (focus_list.items.len < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_index = focus_list.items.len - 1;
|
||||||
|
if (index != 0) {
|
||||||
|
new_index = index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus_index(term_io, new_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_pane(term_io: *TermIO, p: *Pane) !void {
|
||||||
|
for (focus_list.items, 0..) |item, i| {
|
||||||
|
if (item == p) {
|
||||||
|
focus_index(term_io, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_index(term_io: *TermIO, idx: usize) void {
|
||||||
|
if (idx > focus_list.items.len or idx == index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_focus) |current| {
|
||||||
|
current.focus(term_io, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = idx;
|
||||||
|
current_focus = focus_list.items[index];
|
||||||
|
current_focus.?.focus(term_io, true);
|
||||||
|
}
|
15
src/main.zig
15
src/main.zig
@ -11,6 +11,7 @@ const line = @import("line.zig");
|
|||||||
const tabs = @import("tabs.zig");
|
const tabs = @import("tabs.zig");
|
||||||
const stack = @import("stack.zig");
|
const stack = @import("stack.zig");
|
||||||
const text = @import("text.zig");
|
const text = @import("text.zig");
|
||||||
|
const focus = @import("focus.zig");
|
||||||
|
|
||||||
var term_io: term.TermIO = undefined;
|
var term_io: term.TermIO = undefined;
|
||||||
var side_menu: menu.Menu = undefined;
|
var side_menu: menu.Menu = undefined;
|
||||||
@ -28,18 +29,26 @@ 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 == 0) {
|
if (item == 0) {
|
||||||
pane.focus(&m.pane, &term_io);
|
focus.focus_pane(&term_io, &m.pane) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 == 0) {
|
if (item == 0) {
|
||||||
pane.focus(&side_menu.pane, &term_io);
|
focus.focus_pane(&term_io, &side_menu.pane) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
defer {
|
||||||
|
if (gpa.deinit() == .leak) {
|
||||||
|
std.debug.print("Memory was leaked!! D:\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (@import("builtin").mode == .Debug) {
|
if (@import("builtin").mode == .Debug) {
|
||||||
try log.init("panes.log", .Debug);
|
try log.init("panes.log", .Debug);
|
||||||
}
|
}
|
||||||
@ -116,7 +125,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
pane.set_layout(&top.pane, &tabbar.pane);
|
pane.set_layout(&top.pane, &tabbar.pane);
|
||||||
try pane.init(&term_io);
|
try pane.init(allocator, &term_io);
|
||||||
defer pane.cleanup(&term_io);
|
defer pane.cleanup(&term_io);
|
||||||
|
|
||||||
while (!pane.should_exit.load(.acquire)) {
|
while (!pane.should_exit.load(.acquire)) {
|
||||||
|
21
src/menu.zig
21
src/menu.zig
@ -71,26 +71,17 @@ pub const Menu = struct {
|
|||||||
|
|
||||||
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.index == self.items.len or key.type == .NO_KEY) {
|
if (self.index == self.items.len or key.key == .None) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.type == term.KeyType.SEQUENCE) {
|
switch (key.key) {
|
||||||
const key_enum: term.SequenceKey = @enumFromInt(key.value);
|
.Return => self.on_select(self.index),
|
||||||
switch (key_enum) {
|
.Escape, .Q => {}, // self.index = 0,
|
||||||
term.SequenceKey.DOWN => try self.nextItem(term_io),
|
.J, .Down => try self.nextItem(term_io),
|
||||||
term.SequenceKey.UP => try self.prevItem(term_io),
|
.K, .Up => try self.prevItem(term_io),
|
||||||
else => return false,
|
else => return false,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch (key.value) {
|
|
||||||
13 => self.on_select(self.index), // Enter
|
|
||||||
0x1b, 'q' => {}, // self.index = 0,
|
|
||||||
'j' => try self.nextItem(term_io),
|
|
||||||
'k' => try self.prevItem(term_io),
|
|
||||||
else => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
80
src/pane.zig
80
src/pane.zig
@ -4,13 +4,12 @@ const color = @import("colors.zig");
|
|||||||
const dim = @import("dimensions.zig");
|
const dim = @import("dimensions.zig");
|
||||||
const stack = @import("stack.zig");
|
const stack = @import("stack.zig");
|
||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
|
const focus = @import("focus.zig");
|
||||||
const Border = @import("borders.zig").Border;
|
const Border = @import("borders.zig").Border;
|
||||||
|
|
||||||
const TermIO = term.TermIO;
|
const TermIO = term.TermIO;
|
||||||
|
|
||||||
var window = Window{ .pane = Pane{}, .child = null };
|
var window = Window{ .pane = Pane{}, .child = null };
|
||||||
pub var focused_pane: *Pane = undefined;
|
|
||||||
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(false);
|
var needs_redraw: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
||||||
var redraw_count: usize = 0;
|
var redraw_count: usize = 0;
|
||||||
@ -33,14 +32,12 @@ fn panic_signal(_: i32) callconv(.C) void {
|
|||||||
@panic("Segmentation fault!!");
|
@panic("Segmentation fault!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_layout(layout: *Pane, initial_focus: *Pane) void {
|
pub fn set_layout(layout: *Pane, _: *Pane) void {
|
||||||
layout.parent = &window.pane;
|
layout.parent = &window.pane;
|
||||||
window.child = layout;
|
window.child = layout;
|
||||||
focused_pane = initial_focus;
|
|
||||||
focused_pane.focused = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(term_io: *TermIO) !void {
|
pub fn init(allocator: std.mem.Allocator, term_io: *TermIO) !void {
|
||||||
term_io.enterRawMode();
|
term_io.enterRawMode();
|
||||||
term_io.saveScreen();
|
term_io.saveScreen();
|
||||||
term_io.hideCursor();
|
term_io.hideCursor();
|
||||||
@ -67,6 +64,8 @@ pub fn init(term_io: *TermIO) !void {
|
|||||||
};
|
};
|
||||||
try std.posix.sigaction(std.posix.SIG.SEGV, &panic_handler, null);
|
try std.posix.sigaction(std.posix.SIG.SEGV, &panic_handler, null);
|
||||||
|
|
||||||
|
try focus.init(allocator);
|
||||||
|
|
||||||
window.resize(term_io);
|
window.resize(term_io);
|
||||||
try window.draw(term_io);
|
try window.draw(term_io);
|
||||||
}
|
}
|
||||||
@ -78,17 +77,23 @@ pub fn cleanup(term_io: *TermIO) void {
|
|||||||
// term_io.stdout.context.flush() catch @panic("Failed to flush buffered writer\n");
|
// term_io.stdout.context.flush() catch @panic("Failed to flush buffered writer\n");
|
||||||
term_io.flush();
|
term_io.flush();
|
||||||
term_io.close();
|
term_io.close();
|
||||||
|
|
||||||
|
focus.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(term_io: *TermIO) !void {
|
pub fn tick(term_io: *TermIO) !void {
|
||||||
const key = term_io.getKey(false);
|
const key = term_io.getKey(false);
|
||||||
if (key.value == 113) {
|
if (key.key == .Q or key.key == .Escape) {
|
||||||
should_exit.store(true, .release);
|
should_exit.store(true, .release);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.type == .ASCII and key.value == '\t') {
|
if (key.key == .Tab) {
|
||||||
cycle_focus(term_io);
|
if (key.mod.shift) {
|
||||||
|
focus.focus_prev(term_io);
|
||||||
|
} else {
|
||||||
|
focus.focus_next(term_io);
|
||||||
|
}
|
||||||
term_io.flush();
|
term_io.flush();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -104,36 +109,11 @@ pub fn tick(term_io: *TermIO) !void {
|
|||||||
log.debug("Resized {} times", .{redraw_count});
|
log.debug("Resized {} times", .{redraw_count});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try focused_pane.update(term_io, key)) {
|
if (focus.current_focus) |focused| {
|
||||||
|
if (try focused.update(term_io, key)) {
|
||||||
term_io.flush();
|
term_io.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle_focus(term_io: *TermIO) void {
|
|
||||||
// TODO: There must be a better way of doing this
|
|
||||||
|
|
||||||
const parent_stack: *stack.Stack = @fieldParentPtr("pane", focused_pane.parent);
|
|
||||||
var index = (focused_index + 1) % parent_stack.children.len;
|
|
||||||
var to_focus = parent_stack.children[index].pane;
|
|
||||||
|
|
||||||
while (!to_focus.focusable and index != focused_index) {
|
|
||||||
index = (index + 1) % parent_stack.children.len;
|
|
||||||
to_focus = parent_stack.children[index].pane;
|
|
||||||
}
|
|
||||||
|
|
||||||
focus(to_focus, term_io);
|
|
||||||
focused_index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn focus(to_focus: *Pane, term_io: *TermIO) void {
|
|
||||||
if (to_focus == focused_pane) return;
|
|
||||||
|
|
||||||
focused_pane.focus(term_io, false);
|
|
||||||
|
|
||||||
focused_pane = to_focus;
|
|
||||||
focused_index = 0; // TODO: Fix this
|
|
||||||
|
|
||||||
to_focus.focus(term_io, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Overflow = struct {
|
pub const Overflow = struct {
|
||||||
@ -166,6 +146,7 @@ pub const Pane = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PaneVtable = struct {
|
const PaneVtable = struct {
|
||||||
|
child_iterator: *const fn (*Pane) ChildIterator = ChildIterator.empty,
|
||||||
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,
|
resize: *const fn (*Pane) void = defaultResize,
|
||||||
@ -292,7 +273,6 @@ pub const Pane = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus(self: *Pane, term_io: *TermIO, focused: bool) void {
|
pub fn focus(self: *Pane, term_io: *TermIO, focused: bool) void {
|
||||||
focused_pane = self;
|
|
||||||
self.focused = focused;
|
self.focused = focused;
|
||||||
self.cursor = .{ .x = 0, .y = 0 };
|
self.cursor = .{ .x = 0, .y = 0 };
|
||||||
|
|
||||||
@ -361,6 +341,31 @@ pub const Pane = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ChildIterator = struct {
|
||||||
|
parent: *Pane,
|
||||||
|
index: usize = 0,
|
||||||
|
vtable: VTable,
|
||||||
|
|
||||||
|
const VTable = struct {
|
||||||
|
next: *const fn (self: *ChildIterator) ?*Pane,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn next(self: *ChildIterator) ?*Pane {
|
||||||
|
return self.vtable.next(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty(_: *Pane) ChildIterator {
|
||||||
|
return ChildIterator{
|
||||||
|
.parent = undefined,
|
||||||
|
.vtable = .{ .next = empty_next },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_next(_: *ChildIterator) ?*Pane {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Window = struct {
|
const Window = struct {
|
||||||
pane: Pane,
|
pane: Pane,
|
||||||
child: ?*Pane,
|
child: ?*Pane,
|
||||||
@ -383,6 +388,7 @@ const Window = struct {
|
|||||||
|
|
||||||
fn draw(self: *Window, term_io: *TermIO) !void {
|
fn draw(self: *Window, term_io: *TermIO) !void {
|
||||||
if (self.child) |child| {
|
if (self.child) |child| {
|
||||||
|
try focus.build_focus_list(child);
|
||||||
try child.draw(term_io);
|
try child.draw(term_io);
|
||||||
term_io.flush();
|
term_io.flush();
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ pub const Stack = struct {
|
|||||||
.focusable = false,
|
.focusable = false,
|
||||||
.style = config.style,
|
.style = config.style,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
|
.child_iterator = child_iterator,
|
||||||
.resize = resize,
|
.resize = resize,
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
},
|
},
|
||||||
@ -252,4 +253,22 @@ pub const Stack = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn child_iterator(self: *Pane) pane.ChildIterator {
|
||||||
|
return pane.ChildIterator{
|
||||||
|
.parent = self,
|
||||||
|
.vtable = .{ .next = iter_next },
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn iter_next(iter: *pane.ChildIterator) ?*Pane {
|
||||||
|
const stack: *Stack = @fieldParentPtr("pane", iter.parent);
|
||||||
|
|
||||||
|
if (iter.index >= stack.children.len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
defer iter.index += 1;
|
||||||
|
return stack.children[iter.index].pane;
|
||||||
|
}
|
||||||
|
55
src/tabs.zig
55
src/tabs.zig
@ -5,6 +5,7 @@ const menu = @import("menu.zig");
|
|||||||
const pane = @import("pane.zig");
|
const pane = @import("pane.zig");
|
||||||
const term = @import("term.zig");
|
const term = @import("term.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const focus = @import("focus.zig");
|
||||||
|
|
||||||
const Pane = pane.Pane;
|
const Pane = pane.Pane;
|
||||||
const Style = pane.Style;
|
const Style = pane.Style;
|
||||||
@ -40,7 +41,7 @@ pub const TabBar = struct {
|
|||||||
.style = config.style,
|
.style = config.style,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
.focus = focus,
|
.focus = on_focus,
|
||||||
.update = update,
|
.update = update,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -87,10 +88,8 @@ pub const TabBar = struct {
|
|||||||
fn update(pane_ptr: *pane.Pane, term_io: *TermIO, key: term.Key) !bool {
|
fn update(pane_ptr: *pane.Pane, term_io: *TermIO, key: term.Key) !bool {
|
||||||
var self: *TabBar = @fieldParentPtr("pane", pane_ptr);
|
var self: *TabBar = @fieldParentPtr("pane", pane_ptr);
|
||||||
|
|
||||||
if (key.type == .SEQUENCE) {
|
switch (key.key) {
|
||||||
const seq: term.SequenceKey = @enumFromInt(key.value);
|
.H, .Left => {
|
||||||
switch (seq) {
|
|
||||||
.LEFT => {
|
|
||||||
if (self.index > 0) {
|
if (self.index > 0) {
|
||||||
self.index -= 1;
|
self.index -= 1;
|
||||||
try draw(pane_ptr, term_io);
|
try draw(pane_ptr, term_io);
|
||||||
@ -98,33 +97,14 @@ pub const TabBar = struct {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.RIGHT => {
|
.L, .Right => {
|
||||||
self.index = @min(self.index + 1, self.tabs.len - 1);
|
self.index = @min(self.index + 1, self.tabs.len - 1);
|
||||||
try draw(pane_ptr, term_io);
|
try draw(pane_ptr, term_io);
|
||||||
try self.config.target.update_content(self.tabs[self.index].pane, term_io);
|
try self.config.target.update_content(self.tabs[self.index].pane, term_io);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
else => {},
|
.Return => {
|
||||||
}
|
try focus.focus_first(term_io, self.config.target.content);
|
||||||
}
|
|
||||||
|
|
||||||
switch (key.value) {
|
|
||||||
'h' => {
|
|
||||||
if (self.index > 0) {
|
|
||||||
self.index -= 1;
|
|
||||||
try draw(pane_ptr, term_io);
|
|
||||||
try self.config.target.update_content(self.tabs[self.index].pane, term_io);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'l' => {
|
|
||||||
self.index = @min(self.index + 1, self.tabs.len - 1);
|
|
||||||
try draw(pane_ptr, term_io);
|
|
||||||
try self.config.target.update_content(self.tabs[self.index].pane, term_io);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
'\r' => {
|
|
||||||
pane.focus(self.config.target.content, term_io);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
@ -133,7 +113,7 @@ pub const TabBar = struct {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus(pane_ptr: *Pane, term_io: *TermIO) !void {
|
fn on_focus(pane_ptr: *Pane, term_io: *TermIO) !void {
|
||||||
try draw(pane_ptr, term_io);
|
try draw(pane_ptr, term_io);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -146,6 +126,7 @@ pub const TabbedTarget = struct {
|
|||||||
mem.pane = Pane{
|
mem.pane = Pane{
|
||||||
.style = style,
|
.style = style,
|
||||||
.vtable = .{
|
.vtable = .{
|
||||||
|
.child_iterator = child_iterator,
|
||||||
.resize = resize,
|
.resize = resize,
|
||||||
.draw = draw,
|
.draw = draw,
|
||||||
},
|
},
|
||||||
@ -175,4 +156,22 @@ pub const TabbedTarget = struct {
|
|||||||
|
|
||||||
try self.content.draw(term_io);
|
try self.content.draw(term_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn child_iterator(self: *Pane) pane.ChildIterator {
|
||||||
|
return pane.ChildIterator{
|
||||||
|
.parent = self,
|
||||||
|
.vtable = .{ .next = iter_next },
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn iter_next(iter: *pane.ChildIterator) ?*Pane {
|
||||||
|
const target: *TabbedTarget = @fieldParentPtr("pane", iter.parent);
|
||||||
|
|
||||||
|
if (iter.index > 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter.index += 1;
|
||||||
|
return target.content;
|
||||||
|
}
|
||||||
|
92
src/term.zig
92
src/term.zig
@ -27,9 +27,65 @@ pub const SequenceKey = enum(u8) {
|
|||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const KeyCode = enum {
|
||||||
|
None,
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
G,
|
||||||
|
H,
|
||||||
|
I,
|
||||||
|
J,
|
||||||
|
K,
|
||||||
|
L,
|
||||||
|
M,
|
||||||
|
N,
|
||||||
|
O,
|
||||||
|
P,
|
||||||
|
Q,
|
||||||
|
R,
|
||||||
|
S,
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
V,
|
||||||
|
W,
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
Z,
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
Four,
|
||||||
|
Five,
|
||||||
|
Six,
|
||||||
|
Seven,
|
||||||
|
Eight,
|
||||||
|
Nine,
|
||||||
|
Space,
|
||||||
|
Escape,
|
||||||
|
Tab,
|
||||||
|
Return,
|
||||||
|
Backspace,
|
||||||
|
Delete,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Modifiers = packed struct {
|
||||||
|
ctrl: bool = false,
|
||||||
|
shift: bool = false,
|
||||||
|
alt: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Key = struct {
|
pub const Key = struct {
|
||||||
type: KeyType,
|
key: KeyCode,
|
||||||
value: u8,
|
mod: Modifiers = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PrintFlags = struct {
|
pub const PrintFlags = struct {
|
||||||
@ -480,7 +536,7 @@ 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, error.EndOfStream => if (!block) return .{ .type = KeyType.NO_KEY, .value = 0 },
|
error.WouldBlock, error.EndOfStream => if (!block) return .{ .key = .None },
|
||||||
else => {
|
else => {
|
||||||
log.err("{any}", .{err});
|
log.err("{any}", .{err});
|
||||||
@panic("Unexpected error when reading from stdin");
|
@panic("Unexpected error when reading from stdin");
|
||||||
@ -488,15 +544,27 @@ pub const TermIO = struct {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (c != 0x1b and sequence_len == 0) {
|
if (c != 0x1b and sequence_len == 0) {
|
||||||
return .{ .type = KeyType.ASCII, .value = c };
|
switch (c) {
|
||||||
|
1...8, 10...12, 14...26 => return Key{ .key = @enumFromInt(@intFromEnum(KeyCode.A) + (c - 'a')), .mod = .{ .ctrl = true } },
|
||||||
|
'a'...'z' => return Key{ .key = @enumFromInt(@intFromEnum(KeyCode.A) + (c - 'a')) },
|
||||||
|
'A'...'Z' => return Key{ .key = @enumFromInt(@intFromEnum(KeyCode.A) + (c - 'A')), .mod = .{ .shift = true } },
|
||||||
|
'0'...'9' => return Key{ .key = @enumFromInt(@intFromEnum(KeyCode.Zero) + (c - '0')) },
|
||||||
|
' ' => return Key{ .key = .Space },
|
||||||
|
27 => return Key{ .key = .Escape },
|
||||||
|
'\t' => return Key{ .key = .Tab },
|
||||||
|
'\r' => return Key{ .key = .Return },
|
||||||
|
127 => return Key{ .key = .Backspace },
|
||||||
|
else => return Key{ .key = .None },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term_io.escape_sequence_queued = false;
|
term_io.escape_sequence_queued = false;
|
||||||
|
|
||||||
// Escape sequence
|
// Escape sequence
|
||||||
sequence_len = 0;
|
sequence_len = 0;
|
||||||
escape_sequence[sequence_len] = term_io.stdin.readByte() catch return .{ .type = KeyType.ASCII, .value = 0x1b };
|
escape_sequence[sequence_len] = term_io.stdin.readByte() catch return .{ .key = .Escape };
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
sequence_len += 1;
|
sequence_len += 1;
|
||||||
@ -535,16 +603,18 @@ pub fn getTermIO(stdin: std.io.AnyReader, stdout: std.io.BufferedWriter(4096, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decipherEscapeSequence(sequence: []u8) Key {
|
fn decipherEscapeSequence(sequence: []u8) Key {
|
||||||
|
// I should probably make this better
|
||||||
if (sequence.len == 2) {
|
if (sequence.len == 2) {
|
||||||
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'A' })) return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.UP) };
|
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'A' })) return .{ .key = .Up };
|
||||||
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'B' })) return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.DOWN) };
|
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'B' })) return .{ .key = .Down };
|
||||||
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'C' })) return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.RIGHT) };
|
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'C' })) return .{ .key = .Right };
|
||||||
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'D' })) return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.LEFT) };
|
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'D' })) return .{ .key = .Left };
|
||||||
|
if (std.mem.eql(u8, sequence, &[2]u8{ '[', 'Z' })) return .{ .key = .Tab, .mod = .{ .shift = true } };
|
||||||
} else if (sequence.len == 3) {
|
} else if (sequence.len == 3) {
|
||||||
if (std.mem.eql(u8, sequence, &[3]u8{ '[', '3', '~' })) return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.DELETE) };
|
if (std.mem.eql(u8, sequence, &[3]u8{ '[', '3', '~' })) return .{ .key = .Delete };
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .type = KeyType.SEQUENCE, .value = @intFromEnum(SequenceKey.UNKNOWN) };
|
return .{ .key = .None };
|
||||||
}
|
}
|
||||||
|
|
||||||
test "stdout" {
|
test "stdout" {
|
||||||
|
Loading…
Reference in New Issue
Block a user