mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-25 01:36:05 +00:00
Add support for local keybinds
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
@@ -15,8 +15,8 @@ const Widget = @import("Widget.zig");
|
|||||||
|
|
||||||
const TerminalBuffer = @This();
|
const TerminalBuffer = @This();
|
||||||
|
|
||||||
const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
pub const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
||||||
const KeybindMap = std.AutoHashMap(keyboard.Key, struct {
|
pub const KeybindMap = std.AutoHashMap(keyboard.Key, struct {
|
||||||
callback: KeybindCallbackFn,
|
callback: KeybindCallbackFn,
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
});
|
});
|
||||||
@@ -177,14 +177,14 @@ pub fn runEventLoop(
|
|||||||
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
|
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
) !void {
|
) !void {
|
||||||
try self.registerKeybind("Ctrl+K", &moveCursorUp, self);
|
try self.registerGlobalKeybind("Ctrl+K", &moveCursorUp, self);
|
||||||
try self.registerKeybind("Up", &moveCursorUp, self);
|
try self.registerGlobalKeybind("Up", &moveCursorUp, self);
|
||||||
|
|
||||||
try self.registerKeybind("Ctrl+J", &moveCursorDown, self);
|
try self.registerGlobalKeybind("Ctrl+J", &moveCursorDown, self);
|
||||||
try self.registerKeybind("Down", &moveCursorDown, self);
|
try self.registerGlobalKeybind("Down", &moveCursorDown, self);
|
||||||
|
|
||||||
try self.registerKeybind("Tab", &wrapCursor, self);
|
try self.registerGlobalKeybind("Tab", &wrapCursor, self);
|
||||||
try self.registerKeybind("Shift+Tab", &wrapCursorReverse, self);
|
try self.registerGlobalKeybind("Shift+Tab", &wrapCursorReverse, self);
|
||||||
|
|
||||||
defer self.handlable_widgets.deinit(allocator);
|
defer self.handlable_widgets.deinit(allocator);
|
||||||
|
|
||||||
@@ -402,13 +402,14 @@ pub fn reclaim(self: TerminalBuffer) !void {
|
|||||||
|
|
||||||
pub fn registerKeybind(
|
pub fn registerKeybind(
|
||||||
self: *TerminalBuffer,
|
self: *TerminalBuffer,
|
||||||
|
keybinds: *KeybindMap,
|
||||||
keybind: []const u8,
|
keybind: []const u8,
|
||||||
callback: KeybindCallbackFn,
|
callback: KeybindCallbackFn,
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
) !void {
|
) !void {
|
||||||
const key = try self.parseKeybind(keybind);
|
const key = try self.parseKeybind(keybind);
|
||||||
|
|
||||||
self.keybinds.put(key, .{
|
keybinds.put(key, .{
|
||||||
.callback = callback,
|
.callback = callback,
|
||||||
.context = context,
|
.context = context,
|
||||||
}) catch |err| {
|
}) catch |err| {
|
||||||
@@ -420,6 +421,15 @@ pub fn registerKeybind(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn registerGlobalKeybind(
|
||||||
|
self: *TerminalBuffer,
|
||||||
|
keybind: []const u8,
|
||||||
|
callback: KeybindCallbackFn,
|
||||||
|
context: *anyopaque,
|
||||||
|
) !void {
|
||||||
|
try self.registerKeybind(&self.keybinds, keybind, callback, context);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn simulateKeybind(self: *TerminalBuffer, keybind: []const u8) !bool {
|
pub fn simulateKeybind(self: *TerminalBuffer, keybind: []const u8) !bool {
|
||||||
const key = try self.parseKeybind(keybind);
|
const key = try self.parseKeybind(keybind);
|
||||||
|
|
||||||
@@ -552,6 +562,24 @@ fn handleKeybind(
|
|||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const current_widget = self.getActiveWidget();
|
||||||
|
if (current_widget.keybinds) |keybinds| {
|
||||||
|
if (keybinds.get(key)) |binding| {
|
||||||
|
const passthrough_event = try @call(
|
||||||
|
.auto,
|
||||||
|
binding.callback,
|
||||||
|
.{binding.context},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!passthrough_event) {
|
||||||
|
keys.deinit(allocator);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ const VTable = struct {
|
|||||||
|
|
||||||
id: u64,
|
id: u64,
|
||||||
display_name: []const u8,
|
display_name: []const u8,
|
||||||
|
keybinds: ?TerminalBuffer.KeybindMap,
|
||||||
pointer: *anyopaque,
|
pointer: *anyopaque,
|
||||||
vtable: VTable,
|
vtable: VTable,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
display_name: []const u8,
|
display_name: []const u8,
|
||||||
|
keybinds: ?TerminalBuffer.KeybindMap,
|
||||||
pointer: anytype,
|
pointer: anytype,
|
||||||
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||||
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||||
@@ -102,6 +104,7 @@ pub fn init(
|
|||||||
return .{
|
return .{
|
||||||
.id = @intFromPtr(Impl.vtable.draw_fn),
|
.id = @intFromPtr(Impl.vtable.draw_fn),
|
||||||
.display_name = display_name,
|
.display_name = display_name,
|
||||||
|
.keybinds = keybinds,
|
||||||
.pointer = pointer,
|
.pointer = pointer,
|
||||||
.vtable = Impl.vtable,
|
.vtable = Impl.vtable,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ pub fn deinit(self: *BigLabel) void {
|
|||||||
pub fn widget(self: *BigLabel) Widget {
|
pub fn widget(self: *BigLabel) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"BigLabel",
|
"BigLabel",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *CenteredBox) Widget {
|
pub fn widget(self: *CenteredBox) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"CenteredBox",
|
"CenteredBox",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub fn deinit(self: *Label) void {
|
|||||||
pub fn widget(self: *Label) Widget {
|
pub fn widget(self: *Label) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Label",
|
"Label",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ masked: bool,
|
|||||||
maybe_mask: ?u32,
|
maybe_mask: ?u32,
|
||||||
fg: u32,
|
fg: u32,
|
||||||
bg: u32,
|
bg: u32,
|
||||||
|
keybinds: TerminalBuffer.KeybindMap,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
@@ -32,8 +33,9 @@ pub fn init(
|
|||||||
width: usize,
|
width: usize,
|
||||||
fg: u32,
|
fg: u32,
|
||||||
bg: u32,
|
bg: u32,
|
||||||
) Text {
|
) !*Text {
|
||||||
return .{
|
var self = try allocator.create(Text);
|
||||||
|
self.* = Text{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.text = .empty,
|
.text = .empty,
|
||||||
@@ -47,16 +49,24 @@ pub fn init(
|
|||||||
.maybe_mask = maybe_mask,
|
.maybe_mask = maybe_mask,
|
||||||
.fg = fg,
|
.fg = fg,
|
||||||
.bg = bg,
|
.bg = bg,
|
||||||
|
.keybinds = .init(allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try buffer.registerKeybind(&self.keybinds, "Ctrl+U", &clearTextEntry, self);
|
||||||
|
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Text) void {
|
pub fn deinit(self: *Text) void {
|
||||||
self.text.deinit(self.allocator);
|
self.text.deinit(self.allocator);
|
||||||
|
self.keybinds.deinit();
|
||||||
|
self.allocator.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn widget(self: *Text) Widget {
|
pub fn widget(self: *Text) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Text",
|
"Text",
|
||||||
|
self.keybinds,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
@@ -208,3 +218,11 @@ fn write(self: *Text, char: u8) !void {
|
|||||||
self.end += 1;
|
self.end += 1;
|
||||||
self.goRight();
|
self.goRight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clearTextEntry(ptr: *anyopaque) !bool {
|
||||||
|
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
self.clear();
|
||||||
|
self.buffer.drawNextFrame(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *Cascade) Widget {
|
pub fn widget(self: *Cascade) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Cascade",
|
"Cascade",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *ColorMix) Widget {
|
pub fn widget(self: *ColorMix) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"ColorMix",
|
"ColorMix",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *Doom) Widget {
|
pub fn widget(self: *Doom) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Doom",
|
"Doom",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
realloc,
|
realloc,
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *DurFile) Widget {
|
pub fn widget(self: *DurFile) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"DurFile",
|
"DurFile",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
realloc,
|
realloc,
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *GameOfLife) Widget {
|
pub fn widget(self: *GameOfLife) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"GameOfLife",
|
"GameOfLife",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
realloc,
|
realloc,
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ pub fn init(
|
|||||||
pub fn widget(self: *Matrix) Widget {
|
pub fn widget(self: *Matrix) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Matrix",
|
"Matrix",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
realloc,
|
realloc,
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ pub fn deinit(self: *InfoLine) void {
|
|||||||
pub fn widget(self: *InfoLine) Widget {
|
pub fn widget(self: *InfoLine) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"InfoLine",
|
"InfoLine",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ pub fn deinit(self: *Session) void {
|
|||||||
pub fn widget(self: *Session) Widget {
|
pub fn widget(self: *Session) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"Session",
|
"Session",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ pub fn deinit(self: *UserList) void {
|
|||||||
pub fn widget(self: *UserList) Widget {
|
pub fn widget(self: *UserList) Widget {
|
||||||
return Widget.init(
|
return Widget.init(
|
||||||
"UserList",
|
"UserList",
|
||||||
|
null,
|
||||||
self,
|
self,
|
||||||
deinit,
|
deinit,
|
||||||
null,
|
null,
|
||||||
|
|||||||
44
src/main.zig
44
src/main.zig
@@ -93,7 +93,7 @@ const UiState = struct {
|
|||||||
session: Session,
|
session: Session,
|
||||||
saved_users: SavedUsers,
|
saved_users: SavedUsers,
|
||||||
login: UserList,
|
login: UserList,
|
||||||
password: Text,
|
password: *Text,
|
||||||
password_widget: Widget,
|
password_widget: Widget,
|
||||||
insert_mode: bool,
|
insert_mode: bool,
|
||||||
edge_margin: Position,
|
edge_margin: Position,
|
||||||
@@ -792,7 +792,7 @@ pub fn main() !void {
|
|||||||
);
|
);
|
||||||
defer state.password_label.deinit();
|
defer state.password_label.deinit();
|
||||||
|
|
||||||
state.password = Text.init(
|
state.password = try Text.init(
|
||||||
state.allocator,
|
state.allocator,
|
||||||
&state.buffer,
|
&state.buffer,
|
||||||
true,
|
true,
|
||||||
@@ -1078,27 +1078,23 @@ pub fn main() !void {
|
|||||||
try widgets.append(state.allocator, &layer3);
|
try widgets.append(state.allocator, &layer3);
|
||||||
}
|
}
|
||||||
|
|
||||||
try state.buffer.registerKeybind("Esc", &disableInsertMode, &state);
|
try state.buffer.registerGlobalKeybind("Esc", &disableInsertMode, &state);
|
||||||
try state.buffer.registerKeybind("I", &enableInsertMode, &state);
|
try state.buffer.registerGlobalKeybind("I", &enableInsertMode, &state);
|
||||||
|
|
||||||
try state.buffer.registerKeybind("Ctrl+C", &quit, &state);
|
try state.buffer.registerGlobalKeybind("Ctrl+C", &quit, &state);
|
||||||
|
|
||||||
// TODO: Make this generic for any Text widget present in the UI
|
try state.buffer.registerGlobalKeybind("K", &viMoveCursorUp, &state);
|
||||||
// TODO: Per-widget keybinds, will fix insert_mode hack too
|
try state.buffer.registerGlobalKeybind("J", &viMoveCursorDown, &state);
|
||||||
try state.buffer.registerKeybind("Ctrl+U", &clearPassword, &state);
|
|
||||||
|
|
||||||
try state.buffer.registerKeybind("K", &viMoveCursorUp, &state);
|
try state.buffer.registerGlobalKeybind("Enter", &authenticate, &state);
|
||||||
try state.buffer.registerKeybind("J", &viMoveCursorDown, &state);
|
|
||||||
|
|
||||||
try state.buffer.registerKeybind("Enter", &authenticate, &state);
|
try state.buffer.registerGlobalKeybind(state.config.shutdown_key, &shutdownCmd, &state);
|
||||||
|
try state.buffer.registerGlobalKeybind(state.config.restart_key, &restartCmd, &state);
|
||||||
try state.buffer.registerKeybind(state.config.shutdown_key, &shutdownCmd, &state);
|
try state.buffer.registerGlobalKeybind(state.config.show_password_key, &togglePasswordMask, &state);
|
||||||
try state.buffer.registerKeybind(state.config.restart_key, &restartCmd, &state);
|
if (state.config.sleep_cmd != null) try state.buffer.registerGlobalKeybind(state.config.sleep_key, &sleepCmd, &state);
|
||||||
try state.buffer.registerKeybind(state.config.show_password_key, &togglePasswordMask, &state);
|
if (state.config.hibernate_cmd != null) try state.buffer.registerGlobalKeybind(state.config.hibernate_key, &hibernateCmd, &state);
|
||||||
if (state.config.sleep_cmd != null) try state.buffer.registerKeybind(state.config.sleep_key, &sleepCmd, &state);
|
if (state.config.brightness_down_key) |key| try state.buffer.registerGlobalKeybind(key, &decreaseBrightnessCmd, &state);
|
||||||
if (state.config.hibernate_cmd != null) try state.buffer.registerKeybind(state.config.hibernate_key, &hibernateCmd, &state);
|
if (state.config.brightness_up_key) |key| try state.buffer.registerGlobalKeybind(key, &increaseBrightnessCmd, &state);
|
||||||
if (state.config.brightness_down_key) |key| try state.buffer.registerKeybind(key, &decreaseBrightnessCmd, &state);
|
|
||||||
if (state.config.brightness_up_key) |key| try state.buffer.registerKeybind(key, &increaseBrightnessCmd, &state);
|
|
||||||
|
|
||||||
if (state.config.initial_info_text) |text| {
|
if (state.config.initial_info_text) |text| {
|
||||||
try state.info_line.addMessage(text, state.config.bg, state.config.fg);
|
try state.info_line.addMessage(text, state.config.bg, state.config.fg);
|
||||||
@@ -1212,16 +1208,6 @@ fn viMoveCursorDown(ptr: *anyopaque) !bool {
|
|||||||
return try state.buffer.simulateKeybind("Down");
|
return try state.buffer.simulateKeybind("Down");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clearPassword(ptr: *anyopaque) !bool {
|
|
||||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
|
||||||
|
|
||||||
if (state.buffer.getActiveWidget().id == state.password_widget.id) {
|
|
||||||
state.password.clear();
|
|
||||||
state.buffer.drawNextFrame(true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn togglePasswordMask(ptr: *anyopaque) !bool {
|
fn togglePasswordMask(ptr: *anyopaque) !bool {
|
||||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user