feat: action map refactor, bugfix

This commit is contained in:
2025-06-22 06:31:52 +02:00
parent 4474933e54
commit 41e93e1f88
6 changed files with 125 additions and 26 deletions

96
src/console/ActionMap.zig Normal file
View File

@@ -0,0 +1,96 @@
key_action_map: []KeyActionMap,
previous_keys: std.ArrayList(vaxis.Key),
_private: Private,
const Self = @This();
const Private = struct {
key_action_map: std.ArrayList(KeyActionMap),
max_action_key_length: u8,
};
pub fn init(allocator: std.mem.Allocator) !Self {
var key_action_map = std.ArrayList(KeyActionMap).init(allocator);
try init_key_action_map(&key_action_map);
var max_action_key_length: u8 = 0;
for (key_action_map.items) |item| {
if (item.keys.len > max_action_key_length) {
max_action_key_length = @intCast(item.keys.len);
}
}
return .{
.key_action_map = key_action_map.items,
.previous_keys = std.ArrayList(vaxis.Key).init(allocator),
._private = .{
.key_action_map = key_action_map,
.max_action_key_length = max_action_key_length,
},
};
}
pub fn deinit(self: *Self) void {
self._private.key_action_map.deinit();
}
fn init_key_action_map(map_list: *std.ArrayList(KeyActionMap)) !void {
try map_list.appendSlice(&.{
.{
.keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.left, .modifiers = .{} }},
.action = Action.GoUp,
},
.{
.keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.right, .modifiers = .{} }},
.action = Action.Enter,
},
.{
.keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.up, .modifiers = .{} }},
.action = Action.SelectPrevious,
},
.{
.keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.down, .modifiers = .{} }},
.action = Action.SelectNext,
},
});
}
pub fn handle_key(self: *Self, key: vaxis.Key, appState: *AppState, arena: std.mem.Allocator) !ActionResult {
try self.previous_keys.append(key);
const action_result = action_result: for (self.key_action_map) |action_map| {
if (action_map.keys.len != self.previous_keys.items.len) continue;
const matches = blk: for (action_map.keys, self.previous_keys.items) |action_key, previous_key| {
if (!previous_key.matches(action_key.code_point, action_key.modifiers)) break :blk false;
} else true;
if (!matches) continue;
self.previous_keys.clearRetainingCapacity();
break :action_result try handle_action(action_map.action, appState, arena);
} else ActionResult.None;
if (self.previous_keys.items.len >= self._private.max_action_key_length) {
self.previous_keys.clearRetainingCapacity();
}
return action_result;
}
const KeyMatch = struct {
code_point: u21,
modifiers: vaxis.Key.Modifiers,
};
const KeyActionMap = struct {
keys: []const KeyMatch,
action: Action,
};
const std = @import("std");
const vaxis = @import("vaxis");
const ActionResult = @import("../core/action/action.zig").ActionResult;
const handle_action = @import("../core/action/action_handler.zig").handle;
const Action = @import("../core/action/action.zig").Action;
const AppState = @import("../core/app_state.zig").AppState;
const RootProvider = @import("../core/provider/root.zig").RootProvider;

View File

@@ -1,21 +0,0 @@
pub fn handle_key(key: vaxis.Key, appState: *AppState, arena: std.mem.Allocator) !ActionResult {
if (key.matches(vaxis.Key.left, .{})) {
return try handle_action(Action.GoUp, appState, arena);
} else if (key.matches(vaxis.Key.right, .{})) {
return try handle_action(Action.Enter, appState, arena);
} else if (key.matches(vaxis.Key.up, .{})) {
return try handle_action(Action.SelectPrevious, appState, arena);
} else if (key.matches(vaxis.Key.down, .{})) {
return try handle_action(Action.SelectNext, appState, arena);
}
return ActionResult.None;
}
const std = @import("std");
const vaxis = @import("vaxis");
const ActionResult = @import("../core/action/action.zig").ActionResult;
const handle_action = @import("../core/action/action_handler.zig").handle;
const Action = @import("../core/action/action.zig").Action;
const AppState = @import("../core/app_state.zig").AppState;
const RootProvider = @import("../core/provider/root.zig").RootProvider;

View File

@@ -7,6 +7,7 @@ const Model = struct {
current_children_view: *vxfw.ListView,
app_common_model: *AppCommonModel,
root_provider: *RootProvider,
action_map: ActionMap,
/// Helper function to return a vxfw.Widget struct
pub fn widget(self: *Model) vxfw.Widget {
@@ -54,7 +55,7 @@ const Model = struct {
var arena = std.heap.ArenaAllocator.init(vm.allocator);
defer arena.deinit();
const result = handle_key(key, &vm.app_common_model.appState, arena.allocator()) catch null;
const result = vm.action_map.handle_key(key, &vm.app_common_model.appState, arena.allocator()) catch null;
if (result) |r| {
if (r == ActionResult.Handled) {
ctx.consumeAndRedraw();
@@ -542,6 +543,7 @@ pub fn main() !void {
.parent_items_view = &parent_items_list,
.current_children_view = &current_children_view,
.root_provider = &rootProvider,
.action_map = try ActionMap.init(allocator),
};
model.app_common_model.usage_number.data += 1;
@@ -572,7 +574,7 @@ const Tab = @import("../core/tab/tab.zig").Tab;
const locked = @import("../core/sync.zig").locked;
const AppCommonModel = @import("../app_common/Model.zig");
const AppState = @import("../core/app_state.zig").AppState;
const handle_key = @import("./action_map.zig").handle_key;
const ActionMap = @import("./ActionMap.zig");
const Arc = @import("zigrc").Arc;
const Observer = @import("../core/observable.zig").Observer;
const draw_context = @import("draw_context.zig");

View File

@@ -65,6 +65,22 @@ pub fn getFullNameByNativePath(allocator: std.mem.Allocator, nativePath: NativeP
}
pub fn getNativePathByFullName(allocator: std.mem.Allocator, fullName: FullName) ![]u8 {
std.debug.assert(!std.mem.endsWith(u8, fullName.path, "/"));
if (fullName.path.len < LocalProviderId.len or
!std.mem.eql(u8, fullName.path[0..LocalProviderId.len], LocalProviderId))
{
return ProviderError.WrongProvider;
}
//NOTE: their might be an other edge case where the fullname is local/ that is not handled by this
if (fullName.path.len == LocalProviderId.len) {
if (native_os == .linux)
return try std.fmt.allocPrint(allocator, "/", .{})
else
return ProviderError.WrongProvider;
}
const fullNameWithoutId = fullName.path[LocalProviderId.len + 1 ..];
var native_path = try std.mem.replaceOwned(u8, allocator, fullNameWithoutId, "/", std.fs.path.sep_str);
@@ -217,6 +233,7 @@ const models = @import("../models.zig");
const Provider = @import("provider.zig").Provider;
const ProviderVTable = @import("provider.zig").VTable;
const GetItemsError = @import("provider.zig").GetItemsError;
const ProviderError = @import("provider.zig").ProviderError;
const InitContext = @import("provider.zig").InitContext;
const FullName = models.FullName;

View File

@@ -18,6 +18,10 @@ pub const GetItemsError = error{
OutOfMemory,
NotExists,
NotSupported,
} || ProviderError;
pub const ProviderError = error{
WrongProvider,
};
pub const Provider = struct {

View File

@@ -39,6 +39,7 @@ pub const Tab = struct {
pub fn setCurrentLocation(self: *Tab, newLocationFullName: models.FullName) !void {
if (self.currentLocation) |c| {
c.item.deinit();
self.currentLocation = null;
}
self.cleanCurrentItem();
@@ -201,7 +202,7 @@ pub const Tab = struct {
};
const resolvedParent = location.item.provider.getItemByFullName(parentFullName, &.{}, allocator) catch |e| {
std.debug.print("error {} {s}\r\n", .{ e, parentFullName.path });
std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, parentFullName.path });
return;
};
const parentContainer = switch (resolvedParent.item) {
@@ -229,7 +230,7 @@ pub const Tab = struct {
const allocator = arena.allocator();
const resolvedChild = newSelectedItemArc.value.*.provider.getItemByFullName(newSelectedItemArc.value.*.fullName, &.{}, allocator) catch |e| {
std.debug.print("error {} {s}\r\n", .{ e, newSelectedItemArc.value.*.fullName.path });
std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, newSelectedItemArc.value.*.fullName.path });
return;
};
const childContainer = switch (resolvedChild.item) {
@@ -362,7 +363,7 @@ fn loadItems(
for (location.children.data.items[processed_number..]) |item_fullname| {
const resolvedItem = location.item.provider.getItemByFullName(item_fullname, &.{}, allocator) catch |e| {
//TODO: save error to container errors
std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path });
std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, item_fullname.path });
continue;
};