Something went wrong. Try again.
A charm-like tui library
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192//! Keybinding helpers: declare named bindings with help text, then match//! incoming `Msg.Key` events against them.//!//! const k = leaves.key;//! const up = k.Binding{//! .keys = &.{ k.special(.up), k.rune('k') },//! .help = .{ .key = "↑/k", .desc = "move up" },//! };//! if (up.matches(msg_key)) { ... }const std = @import("std");const matcha = @import("matcha");const Key = matcha.Msg.Key;
pub const Help = struct { key: []const u8 = "", desc: []const u8 = "",};
pub const Binding = struct { keys: []const Key = &.{}, help: Help = .{}, disabled: bool = false,
pub fn enabled(self: Binding) bool { return !self.disabled and self.keys.len > 0; }
pub fn matches(self: Binding, k: Key) bool { if (!self.enabled()) return false; for (self.keys) |bound| if (keyEql(bound, k)) return true; return false; }};
/// Returns true if `k` matches any of the supplied bindings.pub fn matchesAny(k: Key, bindings: []const Binding) bool { for (bindings) |b| if (b.matches(k)) return true; return false;}
fn keyEql(a: Key, b: Key) bool { if (a.kind != b.kind) return false; if (a.kind == .rune and a.rune != b.rune) return false; return @as(u8, @bitCast(a.mods)) == @as(u8, @bitCast(b.mods));}
pub fn rune(r: u21) Key { return .{ .kind = .rune, .rune = r };}
pub fn ctrl(r: u21) Key { return .{ .kind = .rune, .rune = r, .mods = .{ .ctrl = true } };}
pub fn alt(r: u21) Key { return .{ .kind = .rune, .rune = r, .mods = .{ .alt = true } };}
pub fn shift(r: u21) Key { return .{ .kind = .rune, .rune = r, .mods = .{ .shift = true } };}
pub fn special(kind: Key.Kind) Key { return .{ .kind = kind };}
test "binding matches rune" { const b: Binding = .{ .keys = &.{ rune('k'), special(.up) } }; try std.testing.expect(b.matches(.{ .kind = .rune, .rune = 'k' })); try std.testing.expect(b.matches(.{ .kind = .up })); try std.testing.expect(!b.matches(.{ .kind = .rune, .rune = 'j' }));}
test "disabled binding never matches" { const b: Binding = .{ .keys = &.{rune('k')}, .disabled = true }; try std.testing.expect(!b.matches(.{ .kind = .rune, .rune = 'k' })); try std.testing.expect(!b.enabled());}
test "mods are required to match" { const b: Binding = .{ .keys = &.{ctrl('c')} }; try std.testing.expect(b.matches(.{ .kind = .rune, .rune = 'c', .mods = .{ .ctrl = true } })); try std.testing.expect(!b.matches(.{ .kind = .rune, .rune = 'c' }));}
test "matchesAny" { const up: Binding = .{ .keys = &.{special(.up)} }; const down: Binding = .{ .keys = &.{special(.down)} }; try std.testing.expect(matchesAny(.{ .kind = .up }, &.{ up, down })); try std.testing.expect(!matchesAny(.{ .kind = .left }, &.{ up, down }));}