add function for tab local key dispatch and ctrl+f/ctrl+b keybinds
This commit is contained in:
parent
58b23c9fd1
commit
d1478c501b
2 changed files with 54 additions and 4 deletions
50
src/tui.zig
50
src/tui.zig
|
|
@ -325,6 +325,15 @@ comptime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(scoped-keybinds): comptime validator that tabs don't bind
|
||||||
|
// keys already bound in the global keymap. Skipped in commit 1
|
||||||
|
// because tabs (portfolio/options/history) currently declare Enter
|
||||||
|
// as a tab-local binding for `expand_collapse` while Enter remains
|
||||||
|
// globally bound. The validator gets added at the END of the
|
||||||
|
// migration (after commit 8 removes the global Enter binding) so
|
||||||
|
// it doesn't block intermediate commits. Runtime check on user
|
||||||
|
// keys.srf still rejects conflicts at TUI startup.
|
||||||
|
|
||||||
/// Per-symbol fetched data. Owned by `App` and accessed as
|
/// Per-symbol fetched data. Owned by `App` and accessed as
|
||||||
/// `app.symbol_data.*`. Populated by whichever tab fetches first
|
/// `app.symbol_data.*`. Populated by whichever tab fetches first
|
||||||
/// (typically the perf or quote tab); consumed by every tab that
|
/// (typically the perf or quote tab); consumed by every tab that
|
||||||
|
|
@ -799,6 +808,37 @@ pub const App = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tab-local keybind dispatch. Walks the active tab's
|
||||||
|
/// `default_bindings` looking for a key match. On match,
|
||||||
|
/// invokes `tab.handleAction(state, app, action)` and returns
|
||||||
|
/// `true`. Returns `false` if no binding matched.
|
||||||
|
///
|
||||||
|
/// Called as a fallback AFTER the global keymap; under the
|
||||||
|
/// "globals always win" rule, tabs are forbidden (by validator
|
||||||
|
/// at comptime, by user-config check at runtime) from binding
|
||||||
|
/// keys that are already global, so a key reaching here is by
|
||||||
|
/// definition not a global keybind.
|
||||||
|
///
|
||||||
|
/// Adding a tab-local action: declare it in the tab's `Action`
|
||||||
|
/// enum, bind it in `default_bindings`, and `handleAction` runs
|
||||||
|
/// it. No edit to `tui.zig` required.
|
||||||
|
fn dispatchTabLocalKey(self: *App, key: vaxis.Key) bool {
|
||||||
|
inline for (std.meta.fields(@TypeOf(tab_modules))) |field| {
|
||||||
|
if (std.mem.eql(u8, field.name, @tagName(self.active_tab))) {
|
||||||
|
const Module = @field(tab_modules, field.name);
|
||||||
|
for (Module.tab.default_bindings) |binding| {
|
||||||
|
if (key.matches(binding.key.codepoint, binding.key.mods)) {
|
||||||
|
const state_ptr = &@field(self.states, field.name);
|
||||||
|
Module.tab.handleAction(state_ptr, self, binding.action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Outcome of a single keypress in an input-mode buffer (symbol
|
/// Outcome of a single keypress in an input-mode buffer (symbol
|
||||||
/// input, date input, etc.). Returned by `handleInputBuffer` so
|
/// input, date input, etc.). Returned by `handleInputBuffer` so
|
||||||
/// the per-mode caller only needs to wire up the `committed`
|
/// the per-mode caller only needs to wire up the `committed`
|
||||||
|
|
@ -1207,7 +1247,15 @@ pub const App = struct {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = self.keymap.matchAction(key) orelse return;
|
const action = self.keymap.matchAction(key) orelse {
|
||||||
|
// No global binding matched. Fall back to tab-local
|
||||||
|
// dispatch — the active tab may bind this key in its
|
||||||
|
// `default_bindings`. Globals win (no overlap allowed,
|
||||||
|
// enforced by validator + user-config check), so
|
||||||
|
// reaching here means the key is purely tab-local.
|
||||||
|
if (self.dispatchTabLocalKey(key)) return ctx.consumeAndRedraw();
|
||||||
|
return;
|
||||||
|
};
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.quit => {
|
.quit => {
|
||||||
ctx.quit = true;
|
ctx.quit = true;
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ pub const KeyMap = struct {
|
||||||
|
|
||||||
// ── Defaults ─────────────────────────────────────────────────
|
// ── Defaults ─────────────────────────────────────────────────
|
||||||
|
|
||||||
const default_bindings = [_]Binding{
|
pub const global_default_bindings = [_]Binding{
|
||||||
.{ .action = .quit, .key = .{ .codepoint = 'q' } },
|
.{ .action = .quit, .key = .{ .codepoint = 'q' } },
|
||||||
.{ .action = .quit, .key = .{ .codepoint = 'c', .mods = .{ .ctrl = true } } },
|
.{ .action = .quit, .key = .{ .codepoint = 'c', .mods = .{ .ctrl = true } } },
|
||||||
.{ .action = .refresh, .key = .{ .codepoint = 'r' } },
|
.{ .action = .refresh, .key = .{ .codepoint = 'r' } },
|
||||||
|
|
@ -125,7 +125,9 @@ const default_bindings = [_]Binding{
|
||||||
.{ .action = .scroll_top, .key = .{ .codepoint = 'g' } },
|
.{ .action = .scroll_top, .key = .{ .codepoint = 'g' } },
|
||||||
.{ .action = .scroll_bottom, .key = .{ .codepoint = 'G' } },
|
.{ .action = .scroll_bottom, .key = .{ .codepoint = 'G' } },
|
||||||
.{ .action = .page_down, .key = .{ .codepoint = vaxis.Key.page_down } },
|
.{ .action = .page_down, .key = .{ .codepoint = vaxis.Key.page_down } },
|
||||||
|
.{ .action = .page_down, .key = .{ .codepoint = 'f', .mods = .{ .ctrl = true } } },
|
||||||
.{ .action = .page_up, .key = .{ .codepoint = vaxis.Key.page_up } },
|
.{ .action = .page_up, .key = .{ .codepoint = vaxis.Key.page_up } },
|
||||||
|
.{ .action = .page_up, .key = .{ .codepoint = 'b', .mods = .{ .ctrl = true } } },
|
||||||
.{ .action = .select_next, .key = .{ .codepoint = 'j' } },
|
.{ .action = .select_next, .key = .{ .codepoint = 'j' } },
|
||||||
.{ .action = .select_next, .key = .{ .codepoint = vaxis.Key.down } },
|
.{ .action = .select_next, .key = .{ .codepoint = vaxis.Key.down } },
|
||||||
.{ .action = .select_prev, .key = .{ .codepoint = 'k' } },
|
.{ .action = .select_prev, .key = .{ .codepoint = 'k' } },
|
||||||
|
|
@ -173,7 +175,7 @@ const default_bindings = [_]Binding{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn defaults() KeyMap {
|
pub fn defaults() KeyMap {
|
||||||
return .{ .bindings = &default_bindings };
|
return .{ .bindings = &global_default_bindings };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── SRF serialization ────────────────────────────────────────
|
// ── SRF serialization ────────────────────────────────────────
|
||||||
|
|
@ -323,7 +325,7 @@ pub fn printDefaults(io: std.Io) !void {
|
||||||
try out.writeAll("# F1-F12, insert, delete\n");
|
try out.writeAll("# F1-F12, insert, delete\n");
|
||||||
try out.writeAll("# Multiple lines with the same action = multiple bindings.\n");
|
try out.writeAll("# Multiple lines with the same action = multiple bindings.\n");
|
||||||
|
|
||||||
for (default_bindings) |b| {
|
for (global_default_bindings) |b| {
|
||||||
var key_buf: [32]u8 = undefined;
|
var key_buf: [32]u8 = undefined;
|
||||||
const key_str = formatKeyCombo(b.key, &key_buf) orelse continue;
|
const key_str = formatKeyCombo(b.key, &key_buf) orelse continue;
|
||||||
try out.print("action::{s},key::{s}\n", .{ @tagName(b.action), key_str });
|
try out.print("action::{s},key::{s}\n", .{ @tagName(b.action), key_str });
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue