mod(example/input): reorder input handling cases; add alt-b/f binding implementations
This commit is contained in:
@@ -87,10 +87,6 @@ const InputField = struct {
|
|||||||
.key => |key| {
|
.key => |key| {
|
||||||
if (key.isAscii()) try this.input.append(key.cp);
|
if (key.isAscii()) try this.input.append(key.cp);
|
||||||
|
|
||||||
// TODO support arrow keys for navigation?
|
|
||||||
// TODO support readline keybindings (i.e. ctrl-k, ctrl-u, ctrl-b, ctrl-f, etc. and the equivalent alt bindings)
|
|
||||||
// create an own `Element` implementation from this
|
|
||||||
|
|
||||||
if (key.eql(.{ .cp = zterm.input.Enter }) or key.eql(.{ .cp = zterm.input.KpEnter }))
|
if (key.eql(.{ .cp = zterm.input.Enter }) or key.eql(.{ .cp = zterm.input.KpEnter }))
|
||||||
this.queue.push(.{ .accept = try this.input.toOwnedSlice() });
|
this.queue.push(.{ .accept = try this.input.toOwnedSlice() });
|
||||||
|
|
||||||
|
|||||||
@@ -28,16 +28,24 @@ const QuitText = struct {
|
|||||||
const InputField = struct {
|
const InputField = struct {
|
||||||
/// Offset from the end describing the current position of the cursor.
|
/// Offset from the end describing the current position of the cursor.
|
||||||
cursor_offset: usize = 0,
|
cursor_offset: usize = 0,
|
||||||
|
/// Configuration for the InputField.
|
||||||
|
configuration: Configuration,
|
||||||
/// Array holding the value of the input.
|
/// Array holding the value of the input.
|
||||||
input: std.ArrayList(u21),
|
input: std.ArrayList(u21),
|
||||||
/// Reference to the app's queue to issue the associated event to trigger when completing the input.
|
/// Reference to the app's queue to issue the associated event to trigger when completing the input.
|
||||||
queue: *App.Queue,
|
queue: *App.Queue,
|
||||||
|
|
||||||
|
/// Configuration for InputField's.
|
||||||
|
pub const Configuration = struct {
|
||||||
|
color: Color = .default,
|
||||||
|
};
|
||||||
|
|
||||||
// TODO make the event to trigger user defined (needs to be `comptime`)
|
// TODO make the event to trigger user defined (needs to be `comptime`)
|
||||||
// - can this even be agnostic to `u8` / `u21`?
|
// - can this even be agnostic to `u8` / `u21`?
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, queue: *App.Queue) @This() {
|
pub fn init(allocator: std.mem.Allocator, queue: *App.Queue, configuration: Configuration) @This() {
|
||||||
return .{
|
return .{
|
||||||
|
.configuration = configuration,
|
||||||
.input = .init(allocator),
|
.input = .init(allocator),
|
||||||
.queue = queue,
|
.queue = queue,
|
||||||
};
|
};
|
||||||
@@ -63,7 +71,7 @@ const InputField = struct {
|
|||||||
.key => |key| {
|
.key => |key| {
|
||||||
assert(this.cursor_offset >= 0 and this.cursor_offset <= this.input.items.len);
|
assert(this.cursor_offset >= 0 and this.cursor_offset <= this.input.items.len);
|
||||||
|
|
||||||
// see *form* implementation in `zk`, which might become part of the library
|
// readline commands
|
||||||
if (key.eql(.{ .cp = zterm.input.Left }) or key.eql(.{ .cp = zterm.input.KpLeft }) or key.eql(.{ .cp = 'b', .mod = .{ .ctrl = true } })) {
|
if (key.eql(.{ .cp = zterm.input.Left }) or key.eql(.{ .cp = zterm.input.KpLeft }) or key.eql(.{ .cp = 'b', .mod = .{ .ctrl = true } })) {
|
||||||
if (this.cursor_offset < this.input.items.len) this.cursor_offset += 1;
|
if (this.cursor_offset < this.input.items.len) this.cursor_offset += 1;
|
||||||
}
|
}
|
||||||
@@ -75,22 +83,6 @@ const InputField = struct {
|
|||||||
|
|
||||||
if (key.eql(.{ .cp = 'a', .mod = .{ .ctrl = true } })) this.cursor_offset = this.input.items.len;
|
if (key.eql(.{ .cp = 'a', .mod = .{ .ctrl = true } })) this.cursor_offset = this.input.items.len;
|
||||||
|
|
||||||
if (key.eql(.{ .cp = zterm.input.Backspace })) {
|
|
||||||
if (this.cursor_offset < this.input.items.len) {
|
|
||||||
_ = if (this.cursor_offset == 0) this.input.pop() else this.input.orderedRemove(this.input.items.len - this.cursor_offset - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.eql(.{ .cp = zterm.input.Delete }) or key.eql(.{ .cp = zterm.input.KpDelete })) {
|
|
||||||
if (this.cursor_offset > 0) {
|
|
||||||
_ = this.input.orderedRemove(this.input.items.len - this.cursor_offset);
|
|
||||||
this.cursor_offset -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO support readline keybindings (i.e. alt bindings alt-b, alt-f
|
|
||||||
// TODO maybe even ctrl-left, ctrl-right?)
|
|
||||||
// readline commands
|
|
||||||
if (key.eql(.{ .cp = 'k', .mod = .{ .ctrl = true } })) {
|
if (key.eql(.{ .cp = 'k', .mod = .{ .ctrl = true } })) {
|
||||||
while (this.cursor_offset > 0) {
|
while (this.cursor_offset > 0) {
|
||||||
_ = this.input.pop();
|
_ = this.input.pop();
|
||||||
@@ -115,10 +107,52 @@ const InputField = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key.eql(.{ .cp = 'b', .mod = .{ .alt = true } }) or key.eql(.{ .cp = zterm.input.Left, .mod = .{ .ctrl = true } }) or key.eql(.{ .cp = zterm.input.KpLeft, .mod = .{ .ctrl = true } })) {
|
||||||
|
var non_whitespace = false;
|
||||||
|
while (this.cursor_offset < this.input.items.len) {
|
||||||
|
if (non_whitespace and this.input.items[this.input.items.len - this.cursor_offset - 1] == ' ') break;
|
||||||
|
|
||||||
|
// see backspace
|
||||||
|
this.cursor_offset += 1;
|
||||||
|
if (this.cursor_offset == this.input.items.len) break;
|
||||||
|
const next = this.input.items[this.input.items.len - this.cursor_offset - 1];
|
||||||
|
if (next != ' ') non_whitespace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.eql(.{ .cp = 'f', .mod = .{ .alt = true } }) or key.eql(.{ .cp = zterm.input.Right, .mod = .{ .ctrl = true } }) or key.eql(.{ .cp = zterm.input.KpRight, .mod = .{ .ctrl = true } })) {
|
||||||
|
var non_whitespace = false;
|
||||||
|
while (this.cursor_offset > 0) {
|
||||||
|
if (non_whitespace and this.input.items[this.input.items.len - this.cursor_offset - 1] == ' ') {
|
||||||
|
this.cursor_offset += 1; // correct cursor position back again to make sure the cursor is not on the whitespace, but at the end of the jumped word
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see backspace
|
||||||
|
this.cursor_offset -= 1;
|
||||||
|
const next = this.input.items[this.input.items.len - this.cursor_offset - 1];
|
||||||
|
if (next != ' ') non_whitespace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// usual input keys
|
// usual input keys
|
||||||
if (key.isAscii()) try this.input.insert(this.input.items.len - this.cursor_offset, key.cp);
|
if (key.isAscii()) try this.input.insert(this.input.items.len - this.cursor_offset, key.cp);
|
||||||
|
|
||||||
|
if (key.eql(.{ .cp = zterm.input.Backspace })) {
|
||||||
|
if (this.cursor_offset < this.input.items.len) {
|
||||||
|
_ = if (this.cursor_offset == 0) this.input.pop() else this.input.orderedRemove(this.input.items.len - this.cursor_offset - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.eql(.{ .cp = zterm.input.Delete }) or key.eql(.{ .cp = zterm.input.KpDelete })) {
|
||||||
|
if (this.cursor_offset > 0) {
|
||||||
|
_ = this.input.orderedRemove(this.input.items.len - this.cursor_offset);
|
||||||
|
this.cursor_offset -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO enter to accept?
|
// TODO enter to accept?
|
||||||
|
// - shift+enter is not recognized by the input reader of `zterm`, so currently it is not possible to add newlines into the text box?
|
||||||
if (key.eql(.{ .cp = zterm.input.Enter }) or key.eql(.{ .cp = zterm.input.KpEnter })) {
|
if (key.eql(.{ .cp = zterm.input.Enter }) or key.eql(.{ .cp = zterm.input.KpEnter })) {
|
||||||
this.queue.push(.{ .accept = try this.input.toOwnedSlice() });
|
this.queue.push(.{ .accept = try this.input.toOwnedSlice() });
|
||||||
this.cursor_offset = 0;
|
this.cursor_offset = 0;
|
||||||
@@ -191,7 +225,9 @@ pub fn main() !void {
|
|||||||
var renderer = zterm.Renderer.Buffered.init(allocator);
|
var renderer = zterm.Renderer.Buffered.init(allocator);
|
||||||
defer renderer.deinit();
|
defer renderer.deinit();
|
||||||
|
|
||||||
var input_field: InputField = .init(allocator, &app.queue);
|
var input_field: InputField = .init(allocator, &app.queue, .{
|
||||||
|
.color = .black,
|
||||||
|
});
|
||||||
defer input_field.deinit();
|
defer input_field.deinit();
|
||||||
|
|
||||||
var mouse_draw: MouseDraw = .{};
|
var mouse_draw: MouseDraw = .{};
|
||||||
@@ -284,6 +320,8 @@ const log = std.log.scoped(.default);
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const zterm = @import("zterm");
|
const zterm = @import("zterm");
|
||||||
|
const Color = zterm.Color;
|
||||||
|
|
||||||
const App = zterm.App(union(enum) {
|
const App = zterm.App(union(enum) {
|
||||||
accept: []u21,
|
accept: []u21,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user