mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-22 06:53:37 +00:00
Make handling inputs widget-independent
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
157
src/main.zig
157
src/main.zig
@@ -66,6 +66,8 @@ fn ttyControlTransferSignalHandler(_: c_int) callconv(.c) void {
|
||||
|
||||
const UiState = struct {
|
||||
allocator: Allocator,
|
||||
active_widget_index: usize,
|
||||
handlable_widgets: std.ArrayList(*Widget),
|
||||
auth_fails: u64,
|
||||
run: bool,
|
||||
update: bool,
|
||||
@@ -97,7 +99,7 @@ const UiState = struct {
|
||||
saved_users: SavedUsers,
|
||||
login: UserList,
|
||||
password: Text,
|
||||
active_input: enums.Input,
|
||||
password_widget: Widget,
|
||||
insert_mode: bool,
|
||||
edge_margin: Position,
|
||||
config: Config,
|
||||
@@ -780,6 +782,8 @@ pub fn main() !void {
|
||||
);
|
||||
defer state.password.deinit();
|
||||
|
||||
state.password_widget = state.password.widget();
|
||||
|
||||
state.version_label = Label.init(
|
||||
ly_version_str,
|
||||
null,
|
||||
@@ -941,12 +945,12 @@ pub fn main() !void {
|
||||
state.config.auth_fails,
|
||||
);
|
||||
|
||||
state.active_widget_index = 0;
|
||||
state.auth_fails = 0;
|
||||
state.run = true;
|
||||
state.update = true;
|
||||
state.animation_timed_out = false;
|
||||
state.animate = state.config.animation != .none;
|
||||
state.active_input = state.config.default_input;
|
||||
state.insert_mode = !state.config.vi_mode or state.config.vi_default_mode == .insert;
|
||||
state.edge_margin = Position.init(
|
||||
state.config.edge_margin,
|
||||
@@ -955,6 +959,8 @@ pub fn main() !void {
|
||||
|
||||
// Load last saved username and desktop selection, if any
|
||||
// Skip if autologin is active to prevent overriding autologin session
|
||||
var default_input = state.config.default_input;
|
||||
|
||||
if (state.config.save and !state.is_autologin) {
|
||||
if (state.saved_users.last_username_index) |index| load_last_user: {
|
||||
// If the saved index isn't valid, bail out
|
||||
@@ -971,7 +977,7 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
state.active_input = .password;
|
||||
default_input = .password;
|
||||
|
||||
state.session.label.current = @min(user.session_index, state.session.label.list.items.len - 1);
|
||||
}
|
||||
@@ -979,6 +985,10 @@ pub fn main() !void {
|
||||
|
||||
// TODO: Layer system where we can put widgets in specific layers (to
|
||||
// allow certain widgets to be below or above others, like animations)
|
||||
const info_line_widget = state.info_line.widget();
|
||||
const session_widget = state.session.widget();
|
||||
const login_widget = state.login.widget();
|
||||
|
||||
var widgets: std.ArrayList(Widget) = .empty;
|
||||
defer widgets.deinit(state.allocator);
|
||||
|
||||
@@ -1012,13 +1022,13 @@ pub fn main() !void {
|
||||
try widgets.append(state.allocator, state.capslock_label.widget());
|
||||
}
|
||||
try widgets.append(state.allocator, state.box.widget());
|
||||
try widgets.append(state.allocator, state.info_line.widget());
|
||||
try widgets.append(state.allocator, info_line_widget);
|
||||
try widgets.append(state.allocator, state.session_specifier_label.widget());
|
||||
try widgets.append(state.allocator, state.session.widget());
|
||||
try widgets.append(state.allocator, session_widget);
|
||||
try widgets.append(state.allocator, state.login_label.widget());
|
||||
try widgets.append(state.allocator, state.login.widget());
|
||||
try widgets.append(state.allocator, login_widget);
|
||||
try widgets.append(state.allocator, state.password_label.widget());
|
||||
try widgets.append(state.allocator, state.password.widget());
|
||||
try widgets.append(state.allocator, state.password_widget);
|
||||
if (!state.config.hide_version_string) {
|
||||
try widgets.append(state.allocator, state.version_label.widget());
|
||||
}
|
||||
@@ -1026,28 +1036,6 @@ pub fn main() !void {
|
||||
try widgets.append(state.allocator, cascade.widget());
|
||||
}
|
||||
|
||||
// Position components and place cursor accordingly
|
||||
for (widgets.items) |*widget| try widget.update(&state);
|
||||
positionComponents(&state);
|
||||
|
||||
switch (state.active_input) {
|
||||
.info_line => state.info_line.label.handle(null, state.insert_mode),
|
||||
.session => state.session.label.handle(null, state.insert_mode),
|
||||
.login => state.login.label.handle(null, state.insert_mode),
|
||||
.password => state.password.handle(null, state.insert_mode) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"tui",
|
||||
"failed to handle password input: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
try state.buffer.registerKeybind("Esc", &disableInsertMode);
|
||||
try state.buffer.registerKeybind("I", &enableInsertMode);
|
||||
|
||||
@@ -1057,11 +1045,11 @@ pub fn main() !void {
|
||||
|
||||
try state.buffer.registerKeybind("Ctrl+K", &moveCursorUp);
|
||||
try state.buffer.registerKeybind("Up", &moveCursorUp);
|
||||
try state.buffer.registerKeybind("J", &viMoseCursorUp);
|
||||
try state.buffer.registerKeybind("K", &viMoveCursorUp);
|
||||
|
||||
try state.buffer.registerKeybind("Ctrl+J", &moveCursorDown);
|
||||
try state.buffer.registerKeybind("Down", &moveCursorDown);
|
||||
try state.buffer.registerKeybind("K", &viMoveCursorDown);
|
||||
try state.buffer.registerKeybind("J", &viMoseCursorDown);
|
||||
|
||||
try state.buffer.registerKeybind("Tab", &wrapCursor);
|
||||
try state.buffer.registerKeybind("Shift+Tab", &wrapCursorReverse);
|
||||
@@ -1104,21 +1092,51 @@ pub fn main() !void {
|
||||
);
|
||||
}
|
||||
|
||||
// Position components and place cursor accordingly
|
||||
if (state.is_autologin) _ = try authenticate(&state);
|
||||
|
||||
const active_widget = switch (default_input) {
|
||||
.info_line => info_line_widget,
|
||||
.session => session_widget,
|
||||
.login => login_widget,
|
||||
.password => state.password_widget,
|
||||
};
|
||||
|
||||
// Run the event loop
|
||||
state.handlable_widgets = .empty;
|
||||
defer state.handlable_widgets.deinit(state.allocator);
|
||||
|
||||
var i: usize = 0;
|
||||
for (widgets.items) |*widget| {
|
||||
if (widget.vtable.handle_fn != null) {
|
||||
try state.handlable_widgets.append(state.allocator, widget);
|
||||
|
||||
if (widget.id == active_widget.id) state.active_widget_index = i;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (widgets.items) |*widget| try widget.update(&state);
|
||||
positionComponents(&state);
|
||||
|
||||
while (state.run) {
|
||||
if (state.update) {
|
||||
for (widgets.items) |*widget| try widget.update(&state);
|
||||
|
||||
switch (state.active_input) {
|
||||
.info_line => state.info_line.label.handle(null, state.insert_mode),
|
||||
.session => state.session.label.handle(null, state.insert_mode),
|
||||
.login => state.login.label.handle(null, state.insert_mode),
|
||||
.password => state.password.handle(null, state.insert_mode) catch |err| {
|
||||
try state.info_line.addMessage(state.lang.err_alloc, state.config.error_bg, state.config.error_fg);
|
||||
try state.log_file.err("tui", "failed to handle password input: {s}", .{@errorName(err)});
|
||||
},
|
||||
}
|
||||
// Reset cursor
|
||||
const current_widget = getActiveWidget(&state);
|
||||
current_widget.handle(null, state.insert_mode) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"tui",
|
||||
"failed to set cursor in active widget: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
|
||||
try TerminalBuffer.clearScreen(false);
|
||||
|
||||
@@ -1237,19 +1255,20 @@ pub fn main() !void {
|
||||
if (maybe_keys) |*keys| {
|
||||
defer keys.deinit(state.allocator);
|
||||
|
||||
const current_widget = getActiveWidget(&state);
|
||||
for (keys.items) |key| {
|
||||
switch (state.active_input) {
|
||||
.info_line => state.info_line.label.handle(key, state.insert_mode),
|
||||
.session => state.session.label.handle(key, state.insert_mode),
|
||||
.login => state.login.label.handle(key, state.insert_mode),
|
||||
.password => state.password.handle(key, state.insert_mode) catch {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
},
|
||||
}
|
||||
current_widget.handle(key, state.insert_mode) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"tui",
|
||||
"failed to handle active widget: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
state.update = true;
|
||||
@@ -1257,6 +1276,16 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn getActiveWidget(state: *UiState) *Widget {
|
||||
return state.handlable_widgets.items[state.active_widget_index];
|
||||
}
|
||||
|
||||
fn setActiveWidget(state: *UiState, widget: *Widget) void {
|
||||
for (state.handlable_widgets.items, 0..) |widg, i| {
|
||||
if (widg.id == widget.id) state.active_widget_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
fn disableInsertMode(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@@ -1279,7 +1308,7 @@ fn enableInsertMode(ptr: *anyopaque) !bool {
|
||||
fn clearPassword(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (state.active_input == .password) {
|
||||
if (getActiveWidget(state) == &state.password_widget) {
|
||||
state.password.clear();
|
||||
state.update = true;
|
||||
}
|
||||
@@ -1288,34 +1317,38 @@ fn clearPassword(ptr: *anyopaque) !bool {
|
||||
|
||||
fn moveCursorUp(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.active_widget_index == 0) return false;
|
||||
|
||||
state.active_input.move(true, false);
|
||||
state.active_widget_index -= 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn viMoseCursorUp(ptr: *anyopaque) !bool {
|
||||
fn viMoveCursorUp(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.insert_mode) return true;
|
||||
if (state.active_widget_index == 0) return false;
|
||||
|
||||
state.active_input.move(false, false);
|
||||
state.active_widget_index -= 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn moveCursorDown(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.active_widget_index == state.handlable_widgets.items.len - 1) return false;
|
||||
|
||||
state.active_input.move(false, false);
|
||||
state.active_widget_index += 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn viMoveCursorDown(ptr: *anyopaque) !bool {
|
||||
fn viMoseCursorDown(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.insert_mode) return true;
|
||||
if (state.active_widget_index == state.handlable_widgets.items.len - 1) return false;
|
||||
|
||||
state.active_input.move(true, false);
|
||||
state.active_widget_index += 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
@@ -1323,7 +1356,7 @@ fn viMoveCursorDown(ptr: *anyopaque) !bool {
|
||||
fn wrapCursor(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(false, true);
|
||||
state.active_widget_index = (state.active_widget_index + 1) % state.handlable_widgets.items.len;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
@@ -1331,7 +1364,7 @@ fn wrapCursor(ptr: *anyopaque) !bool {
|
||||
fn wrapCursorReverse(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(true, true);
|
||||
state.active_widget_index = (state.active_widget_index - 1) % state.handlable_widgets.items.len;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
@@ -1500,7 +1533,7 @@ fn authenticate(ptr: *anyopaque) !bool {
|
||||
const auth_err = shared_err.readError();
|
||||
if (auth_err) |err| {
|
||||
state.auth_fails += 1;
|
||||
state.active_input = .password;
|
||||
setActiveWidget(state, &state.password_widget);
|
||||
|
||||
try state.info_line.addMessage(
|
||||
getAuthErrorMsg(err, state.lang),
|
||||
|
||||
@@ -4,13 +4,14 @@ const keyboard = @import("keyboard.zig");
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
|
||||
const VTable = struct {
|
||||
deinit_fn: *const fn (ptr: *anyopaque) void,
|
||||
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
|
||||
deinit_fn: ?*const fn (ptr: *anyopaque) void,
|
||||
realloc_fn: ?*const fn (ptr: *anyopaque) anyerror!void,
|
||||
draw_fn: *const fn (ptr: *anyopaque) void,
|
||||
update_fn: *const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
|
||||
handle_fn: *const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key, insert_mode: bool) anyerror!void,
|
||||
update_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
|
||||
handle_fn: ?*const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key, insert_mode: bool) anyerror!void,
|
||||
};
|
||||
|
||||
id: u64,
|
||||
pointer: *anyopaque,
|
||||
vtable: VTable,
|
||||
|
||||
@@ -18,7 +19,7 @@ pub fn init(
|
||||
pointer: anytype,
|
||||
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||
comptime draw_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime update_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!void,
|
||||
comptime handle_fn: ?fn (ptr: @TypeOf(pointer), maybe_key: ?keyboard.Key, insert_mode: bool) anyerror!void,
|
||||
) Widget {
|
||||
@@ -27,73 +28,64 @@ pub fn init(
|
||||
pub fn deinitImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (deinit_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
return @call(
|
||||
.always_inline,
|
||||
deinit_fn.?,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn reallocImpl(ptr: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (realloc_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
return @call(
|
||||
.always_inline,
|
||||
realloc_fn.?,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn drawImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (draw_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
return @call(
|
||||
.always_inline,
|
||||
draw_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn updateImpl(ptr: *anyopaque, ctx: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (update_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
return @call(
|
||||
.always_inline,
|
||||
update_fn.?,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn handleImpl(ptr: *anyopaque, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (handle_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
}
|
||||
return @call(
|
||||
.always_inline,
|
||||
handle_fn.?,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
}
|
||||
|
||||
const vtable = VTable{
|
||||
.deinit_fn = deinitImpl,
|
||||
.realloc_fn = reallocImpl,
|
||||
.deinit_fn = if (deinit_fn != null) deinitImpl else null,
|
||||
.realloc_fn = if (realloc_fn != null) reallocImpl else null,
|
||||
.draw_fn = drawImpl,
|
||||
.update_fn = updateImpl,
|
||||
.handle_fn = handleImpl,
|
||||
.update_fn = if (update_fn != null) updateImpl else null,
|
||||
.handle_fn = if (handle_fn != null) handleImpl else null,
|
||||
};
|
||||
};
|
||||
|
||||
return .{
|
||||
.id = @intFromPtr(Impl.vtable.draw_fn),
|
||||
.pointer = pointer,
|
||||
.vtable = Impl.vtable,
|
||||
};
|
||||
@@ -102,27 +94,31 @@ pub fn init(
|
||||
pub fn deinit(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.deinit_fn,
|
||||
.{impl},
|
||||
);
|
||||
if (self.vtable.deinit_fn) |deinit_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
deinit_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Widget) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.realloc_fn,
|
||||
.{impl},
|
||||
);
|
||||
if (self.vtable.realloc_fn) |realloc_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
realloc_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
@call(
|
||||
.auto,
|
||||
self.vtable.draw_fn,
|
||||
.{impl},
|
||||
@@ -132,19 +128,23 @@ pub fn draw(self: *Widget) void {
|
||||
pub fn update(self: *Widget, ctx: *anyopaque) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.update_fn,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
if (self.vtable.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(self: *Widget, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.handle_fn,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
if (self.vtable.handle_fn) |handle_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
handle_fn,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user