feat(event): add focus in/out event to SystemEvents
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 3m54s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 3m54s
feat(app): add minimal size argument for App.start Read more corresponding inputs from stdin and convert them correctly (i.e. in band window resizing), further keys (arrow keys, F-keys, etc.). Respect the provided minimal size for the application which posts an error message in case the size is smaller than the requested minimal size.
This commit is contained in:
@@ -82,7 +82,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start(null);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -104,6 +104,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
for (events.items) |e| {
|
for (events.items) |e| {
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
const min_size: zterm.Size = .{
|
||||||
|
.cols = 25,
|
||||||
|
.rows = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
try app.start(min_size);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -88,7 +93,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
if (Key.matches(key, .{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
if (Key.matches(key, .{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
||||||
try app.interrupt();
|
try app.interrupt();
|
||||||
defer app.start() catch @panic("could not start app event loop");
|
defer app.start(min_size) catch @panic("could not start app event loop");
|
||||||
// TODO: parse environment variables to extract the value of $EDITOR and use it here instead
|
// TODO: parse environment variables to extract the value of $EDITOR and use it here instead
|
||||||
var child = std.process.Child.init(&.{"hx"}, allocator);
|
var child = std.process.Child.init(&.{"hx"}, allocator);
|
||||||
_ = child.spawnAndWait() catch |err| {
|
_ = child.spawnAndWait() catch |err| {
|
||||||
@@ -104,6 +109,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
for (events.items) |e| {
|
for (events.items) |e| {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start(null);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -102,6 +102,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
for (events.items) |e| {
|
for (events.items) |e| {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start(null);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -145,6 +145,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
for (events.items) |e| {
|
for (events.items) |e| {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start(null);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -111,6 +111,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
for (events.items) |e| {
|
for (events.items) |e| {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ pub fn main() !void {
|
|||||||
});
|
});
|
||||||
defer layout.deinit();
|
defer layout.deinit();
|
||||||
|
|
||||||
try app.start();
|
try app.start(null);
|
||||||
defer app.stop() catch unreachable;
|
defer app.stop() catch unreachable;
|
||||||
|
|
||||||
// App.Event loop
|
// App.Event loop
|
||||||
@@ -115,6 +115,7 @@ pub fn main() !void {
|
|||||||
.err => |err| {
|
.err => |err| {
|
||||||
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
|
||||||
},
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = try layout.handle(event);
|
const events = try layout.handle(event);
|
||||||
|
|||||||
189
src/app.zig
189
src/app.zig
@@ -54,13 +54,18 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
|
|||||||
quit_event: std.Thread.ResetEvent = .{},
|
quit_event: std.Thread.ResetEvent = .{},
|
||||||
termios: ?std.posix.termios = null,
|
termios: ?std.posix.termios = null,
|
||||||
attached_handler: bool = false,
|
attached_handler: bool = false,
|
||||||
|
min_size: ?terminal.Size = null,
|
||||||
|
prev_size: terminal.Size = .{ .cols = 0, .rows = 0 },
|
||||||
|
|
||||||
pub const SignalHandler = struct {
|
pub const SignalHandler = struct {
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
callback: *const fn (context: *anyopaque) void,
|
callback: *const fn (context: *anyopaque) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn start(this: *@This()) !void {
|
pub fn start(this: *@This(), min_size: ?terminal.Size) !void {
|
||||||
|
if (fullscreen) { // a minimal size only really makes sense if the application is rendered fullscreen
|
||||||
|
this.min_size = min_size;
|
||||||
|
}
|
||||||
if (this.thread) |_| return;
|
if (this.thread) |_| return;
|
||||||
|
|
||||||
if (!this.attached_handler) {
|
if (!this.attached_handler) {
|
||||||
@@ -137,7 +142,20 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
|
|||||||
|
|
||||||
fn winsizeCallback(ptr: *anyopaque) void {
|
fn winsizeCallback(ptr: *anyopaque) void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(ptr));
|
const this: *@This() = @ptrCast(@alignCast(ptr));
|
||||||
this.postEvent(.{ .resize = terminal.getTerminalSize() });
|
const size = terminal.getTerminalSize();
|
||||||
|
// check for minimal size (if any was provided)
|
||||||
|
if (this.min_size) |min_size| {
|
||||||
|
if (size.cols < min_size.cols or size.rows < min_size.rows) {
|
||||||
|
this.postEvent(.{
|
||||||
|
.err = .{ .err = error.InsufficientSize, .msg = "Terminal size is too small for the requested minimal size" },
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (size.cols != this.prev_size.cols or size.rows != this.prev_size.rows) {
|
||||||
|
this.postEvent(.{ .resize = size });
|
||||||
|
this.prev_size = size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var winch_handler: ?SignalHandler = null;
|
var winch_handler: ?SignalHandler = null;
|
||||||
@@ -159,7 +177,19 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
|
|||||||
// send initial terminal size
|
// send initial terminal size
|
||||||
// changes are handled by the winch signal handler
|
// changes are handled by the winch signal handler
|
||||||
// see `App.start` and `App.registerWinch` for details
|
// see `App.start` and `App.registerWinch` for details
|
||||||
this.postEvent(.{ .resize = terminal.getTerminalSize() });
|
{
|
||||||
|
// TODO: what should happen if the initial window size is too small?
|
||||||
|
// -> currently the first render call will then crash the application (which happens anyway)
|
||||||
|
const size = terminal.getTerminalSize();
|
||||||
|
if (this.min_size) |min_size| {
|
||||||
|
if (size.cols < min_size.cols or size.rows < min_size.rows) {
|
||||||
|
this.postEvent(.{
|
||||||
|
.err = .{ .err = error.InsufficientSize, .msg = "Terminal size is too small for the requested minimal size" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.postEvent(.{ .resize = size });
|
||||||
|
}
|
||||||
|
|
||||||
// thread to read user inputs
|
// thread to read user inputs
|
||||||
var buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
@@ -170,6 +200,159 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
|
|||||||
// escape key presses
|
// escape key presses
|
||||||
if (buf[0] == 0x1b and read_bytes > 1) {
|
if (buf[0] == 0x1b and read_bytes > 1) {
|
||||||
switch (buf[1]) {
|
switch (buf[1]) {
|
||||||
|
0x4F => { // ss3
|
||||||
|
if (read_bytes < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const key: ?Key = switch (buf[2]) {
|
||||||
|
0x1B => null,
|
||||||
|
'A' => .{ .cp = Key.up },
|
||||||
|
'B' => .{ .cp = Key.down },
|
||||||
|
'C' => .{ .cp = Key.right },
|
||||||
|
'D' => .{ .cp = Key.left },
|
||||||
|
'E' => .{ .cp = Key.kp_begin },
|
||||||
|
'F' => .{ .cp = Key.end },
|
||||||
|
'H' => .{ .cp = Key.home },
|
||||||
|
'P' => .{ .cp = Key.f1 },
|
||||||
|
'Q' => .{ .cp = Key.f2 },
|
||||||
|
'R' => .{ .cp = Key.f3 },
|
||||||
|
'S' => .{ .cp = Key.f4 },
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
if (key) |k| {
|
||||||
|
this.postEvent(.{ .key = k });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0x5B => { // csi
|
||||||
|
if (read_bytes < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// We start iterating at index 2 to get past the '['
|
||||||
|
const sequence = for (buf[2..], 2..) |b, i| {
|
||||||
|
switch (b) {
|
||||||
|
0x40...0xFF => break buf[0 .. i + 1],
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
} else continue;
|
||||||
|
|
||||||
|
const final = sequence[sequence.len - 1];
|
||||||
|
switch (final) {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'H', 'P', 'Q', 'R', 'S' => {
|
||||||
|
// Legacy keys
|
||||||
|
// CSI {ABCDEFHPQS}
|
||||||
|
// CSI 1 ; modifier:event_type {ABCDEFHPQS}
|
||||||
|
const key: Key = .{
|
||||||
|
.cp = switch (final) {
|
||||||
|
'A' => Key.up,
|
||||||
|
'B' => Key.down,
|
||||||
|
'C' => Key.right,
|
||||||
|
'D' => Key.left,
|
||||||
|
'E' => Key.kp_begin,
|
||||||
|
'F' => Key.end,
|
||||||
|
'H' => Key.home,
|
||||||
|
'P' => Key.f1,
|
||||||
|
'Q' => Key.f2,
|
||||||
|
'R' => Key.f3,
|
||||||
|
'S' => Key.f4,
|
||||||
|
else => unreachable, // switch case prevents in this case form ever happening
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.postEvent(.{ .key = key });
|
||||||
|
},
|
||||||
|
'~' => {
|
||||||
|
// Legacy keys
|
||||||
|
// CSI number ~
|
||||||
|
// CSI number ; modifier ~
|
||||||
|
// CSI number ; modifier:event_type ; text_as_codepoint ~
|
||||||
|
var field_iter = std.mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
||||||
|
const number_buf = field_iter.next() orelse unreachable; // always will have one field
|
||||||
|
const number = std.fmt.parseUnsigned(u16, number_buf, 10) catch break;
|
||||||
|
|
||||||
|
const key: Key = .{
|
||||||
|
.cp = switch (number) {
|
||||||
|
2 => Key.insert,
|
||||||
|
3 => Key.delete,
|
||||||
|
5 => Key.page_up,
|
||||||
|
6 => Key.page_down,
|
||||||
|
7 => Key.home,
|
||||||
|
8 => Key.end,
|
||||||
|
11 => Key.f1,
|
||||||
|
12 => Key.f2,
|
||||||
|
13 => Key.f3,
|
||||||
|
14 => Key.f4,
|
||||||
|
15 => Key.f5,
|
||||||
|
17 => Key.f6,
|
||||||
|
18 => Key.f7,
|
||||||
|
19 => Key.f8,
|
||||||
|
20 => Key.f9,
|
||||||
|
21 => Key.f10,
|
||||||
|
23 => Key.f11,
|
||||||
|
24 => Key.f12,
|
||||||
|
// 200 => return .{ .event = .paste_start, .n = sequence.len },
|
||||||
|
// 201 => return .{ .event = .paste_end, .n = sequence.len },
|
||||||
|
57427 => Key.kp_begin,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.postEvent(.{ .key = key });
|
||||||
|
},
|
||||||
|
|
||||||
|
'I' => this.postEvent(.{ .focus = true }),
|
||||||
|
'O' => this.postEvent(.{ .focus = false }),
|
||||||
|
// 'M', 'm' => return parseMouse(sequence), // TODO: parse mouse inputs
|
||||||
|
'c' => {
|
||||||
|
// Primary DA (CSI ? Pm c)
|
||||||
|
},
|
||||||
|
'n' => {
|
||||||
|
// Device Status Report
|
||||||
|
// CSI Ps n
|
||||||
|
// CSI ? Ps n
|
||||||
|
std.debug.assert(sequence.len >= 3);
|
||||||
|
},
|
||||||
|
't' => {
|
||||||
|
// XTWINOPS
|
||||||
|
// Split first into fields delimited by ';'
|
||||||
|
var iter = std.mem.splitScalar(u8, sequence[2 .. sequence.len - 1], ';');
|
||||||
|
const ps = iter.first();
|
||||||
|
if (std.mem.eql(u8, "48", ps)) {
|
||||||
|
// in band window resize
|
||||||
|
// CSI 48 ; height ; width ; height_pix ; width_pix t
|
||||||
|
const height_char = iter.next() orelse break;
|
||||||
|
const width_char = iter.next() orelse break;
|
||||||
|
|
||||||
|
// TODO: only post the event if the size has changed?
|
||||||
|
// because there might be too many resize events (which force a re-draw of the entire screen)
|
||||||
|
const size: terminal.Size = .{
|
||||||
|
.rows = std.fmt.parseUnsigned(u16, height_char, 10) catch break,
|
||||||
|
.cols = std.fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||||
|
};
|
||||||
|
// check for minimal size (if any was provided)
|
||||||
|
if (this.min_size) |min_size| {
|
||||||
|
if (size.cols < min_size.cols or size.rows < min_size.rows) {
|
||||||
|
this.postEvent(.{
|
||||||
|
.err = .{ .err = error.InsufficientSize, .msg = "Terminal size is too small for the requested minimal size" },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (size.cols != this.prev_size.cols or size.rows != this.prev_size.rows) {
|
||||||
|
this.postEvent(.{ .resize = size });
|
||||||
|
this.prev_size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'u' => {
|
||||||
|
// Kitty keyboard
|
||||||
|
// CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u
|
||||||
|
// Not all fields will be present. Only unicode-key-code is
|
||||||
|
// mandatory
|
||||||
|
},
|
||||||
|
'y' => {
|
||||||
|
// DECRPM (CSI ? Ps ; Pm $ y)
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
// TODO: parse corresponding codes
|
// TODO: parse corresponding codes
|
||||||
// 0x5B => parseCsi(input, &self.buf), // CSI see https://github.com/rockorager/libvaxis/blob/main/src/Parser.zig
|
// 0x5B => parseCsi(input, &self.buf), // CSI see https://github.com/rockorager/libvaxis/blob/main/src/Parser.zig
|
||||||
else => {},
|
else => {},
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ pub const SystemEvent = union(enum) {
|
|||||||
err: Error,
|
err: Error,
|
||||||
resize: Size,
|
resize: Size,
|
||||||
key: Key,
|
key: Key,
|
||||||
|
focus: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type {
|
pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement a minimal size requirement for Widgets to render correctly?
|
||||||
// import and export of `Widget` implementations
|
// import and export of `Widget` implementations
|
||||||
pub const Text = @import("widget/Text.zig").Widget(Event, Renderer);
|
pub const Text = @import("widget/Text.zig").Widget(Event, Renderer);
|
||||||
pub const RawText = @import("widget/RawText.zig").Widget(Event, Renderer);
|
pub const RawText = @import("widget/RawText.zig").Widget(Event, Renderer);
|
||||||
|
|||||||
@@ -62,28 +62,28 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
},
|
},
|
||||||
.key => |key| {
|
.key => |key| {
|
||||||
var require_render = true;
|
var require_render = true;
|
||||||
if (key.matches(.{ .cp = 'g' })) {
|
if (key.matches(.{ .cp = 'g' }) or key.matches(.{ .cp = terminal.Key.home })) {
|
||||||
// top
|
// top
|
||||||
if (this.idx != 0) {
|
if (this.idx != 0) {
|
||||||
this.idx = 0;
|
this.idx = 0;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'G' })) {
|
} else if (key.matches(.{ .cp = 'G' }) or key.matches(.{ .cp = terminal.Key.end })) {
|
||||||
// bottom
|
// bottom
|
||||||
if (this.idx < this.contents.items.len -| 1) {
|
if (this.idx < this.contents.items.len -| 1) {
|
||||||
this.idx = this.contents.items.len -| 1;
|
this.idx = this.contents.items.len -| 1;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'j' })) {
|
} else if (key.matches(.{ .cp = 'j' }) or key.matches(.{ .cp = terminal.Key.down })) {
|
||||||
// down
|
// down
|
||||||
if (this.idx < this.contents.items.len -| 1) {
|
if (this.idx < this.contents.items.len -| 1) {
|
||||||
this.idx += 1;
|
this.idx += 1;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'k' })) {
|
} else if (key.matches(.{ .cp = 'k' }) or key.matches(.{ .cp = terminal.Key.up })) {
|
||||||
// up
|
// up
|
||||||
if (this.idx > 0) {
|
if (this.idx > 0) {
|
||||||
this.idx -= 1;
|
this.idx -= 1;
|
||||||
|
|||||||
@@ -53,28 +53,28 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.key => |key| {
|
.key => |key| {
|
||||||
if (key.matches(.{ .cp = 'g' })) {
|
if (key.matches(.{ .cp = 'g' }) or key.matches(.{ .cp = Key.home })) {
|
||||||
// top
|
// top
|
||||||
if (this.line != 0) {
|
if (this.line != 0) {
|
||||||
this.line = 0;
|
this.line = 0;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'G' })) {
|
} else if (key.matches(.{ .cp = 'G' }) or key.matches(.{ .cp = Key.end })) {
|
||||||
// bottom
|
// bottom
|
||||||
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
||||||
this.line = this.line_index.items.len -| 1 -| this.size.rows;
|
this.line = this.line_index.items.len -| 1 -| this.size.rows;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'j' })) {
|
} else if (key.matches(.{ .cp = 'j' }) or key.matches(.{ .cp = Key.down })) {
|
||||||
// down
|
// down
|
||||||
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
||||||
this.line += 1;
|
this.line += 1;
|
||||||
} else {
|
} else {
|
||||||
require_render = false;
|
require_render = false;
|
||||||
}
|
}
|
||||||
} else if (key.matches(.{ .cp = 'k' })) {
|
} else if (key.matches(.{ .cp = 'k' }) or key.matches(.{ .cp = Key.up })) {
|
||||||
// up
|
// up
|
||||||
if (this.line > 0) {
|
if (this.line > 0) {
|
||||||
this.line -= 1;
|
this.line -= 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user