A ui toolkit for building gpui apps
rust gpui
at main 129 lines 3.2 kB view raw
1//! Extension traits and helpers for GPUI keymap integration 2 3use gpui::{Action, KeyBinding}; 4 5/// Helper to create a list of key bindings from action-keystroke pairs 6pub fn create_bindings<A: Action + Clone>( 7 mappings: &[(&str, A)], 8 context: Option<&str>, 9) -> Vec<KeyBinding> { 10 mappings 11 .iter() 12 .map(|(keystrokes, action)| KeyBinding::new(keystrokes, action.clone(), context)) 13 .collect() 14} 15 16/// Helper to create a single binding 17pub fn bind<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> KeyBinding { 18 KeyBinding::new(keystrokes, action, context) 19} 20 21/// Helper to create multiple bindings with the same context 22pub struct BindingBuilder { 23 context: Option<String>, 24 bindings: Vec<KeyBinding>, 25} 26 27impl BindingBuilder { 28 /// Create a new binding builder 29 pub fn new() -> Self { 30 Self { 31 context: None, 32 bindings: Vec::new(), 33 } 34 } 35 36 /// Create a new binding builder with a context 37 pub fn with_context(context: impl Into<String>) -> Self { 38 Self { 39 context: Some(context.into()), 40 bindings: Vec::new(), 41 } 42 } 43 44 /// Add a binding to the builder 45 pub fn bind<A: Action>(mut self, keystrokes: &str, action: A) -> Self { 46 let binding = KeyBinding::new(keystrokes, action, self.context.as_deref()); 47 self.bindings.push(binding); 48 self 49 } 50 51 /// Build the list of bindings 52 pub fn build(self) -> Vec<KeyBinding> { 53 self.bindings 54 } 55} 56 57impl Default for BindingBuilder { 58 fn default() -> Self { 59 Self::new() 60 } 61} 62 63/// Macro to simplify creating multiple bindings 64#[macro_export] 65macro_rules! bindings { 66 // With context 67 (context: $context:expr, $($key:expr => $action:expr),* $(,)?) => { 68 { 69 vec![ 70 $( 71 $crate::keymap_ext::bind($key, $action, Some($context)) 72 ),* 73 ] 74 } 75 }; 76 77 // Without context 78 ($($key:expr => $action:expr),* $(,)?) => { 79 { 80 vec![ 81 $( 82 $crate::keymap_ext::bind($key, $action, None) 83 ),* 84 ] 85 } 86 }; 87} 88 89#[cfg(test)] 90mod tests { 91 use super::*; 92 use gpui::actions; 93 94 actions!(test, [TestAction, AnotherAction]); 95 96 #[test] 97 fn test_binding_builder() { 98 let bindings = BindingBuilder::new() 99 .bind("cmd-s", TestAction) 100 .bind("cmd-z", AnotherAction) 101 .build(); 102 103 assert_eq!(bindings.len(), 2); 104 } 105 106 #[test] 107 fn test_binding_builder_with_context() { 108 let builder = BindingBuilder::with_context("Editor"); 109 assert_eq!(builder.context, Some("Editor".to_string())); 110 } 111 112 #[test] 113 fn test_create_bindings() { 114 let mappings = [ 115 ("cmd-s", TestAction), 116 ("cmd-z", TestAction), 117 ("cmd-y", TestAction), 118 ]; 119 120 let bindings = create_bindings(&mappings, Some("Editor")); 121 assert_eq!(bindings.len(), 3); 122 } 123 124 #[test] 125 fn test_bind() { 126 let _binding = bind("cmd-s", TestAction, Some("Editor")); 127 // Just checking it compiles 128 } 129}