@@ -5,10 +5,16 @@ use ratatui::prelude::{Line, Modifier, Span, Style, Widget};
5
5
use ratatui:: style:: Color ;
6
6
use ratatui:: widgets:: Clear ;
7
7
8
+ /// Enum to define shortcut structures for the constructor
9
+ pub enum Shortcut < ' a > {
10
+ Pair ( & ' a str , & ' a str ) ,
11
+ Trio ( & ' a str , & ' a str , & ' a str ) ,
12
+ }
13
+
8
14
/// A widget to display keyboard shortcuts in the UI
9
15
#[ derive( Clone , Default ) ]
10
16
pub struct Shortcuts {
11
- shortcuts : Vec < ( String , String ) > ,
17
+ shortcuts : Vec < ( String , String , Option < String > ) > ,
12
18
separator : String ,
13
19
shortcut_label_style : Style ,
14
20
shortcut_key_style : Style ,
@@ -18,12 +24,17 @@ pub struct Shortcuts {
18
24
}
19
25
20
26
impl Shortcuts {
21
- /// Create a new shortcuts widget from a vector of (key, label) pairs
22
- pub fn from ( values : Vec < ( & str , & str ) > ) -> Self {
27
+ /// Create a new shortcuts widget from a vector of ShortcutDef enums
28
+ pub fn new ( values : Vec < Shortcut > ) -> Self {
23
29
Self {
24
30
shortcuts : values
25
31
. into_iter ( )
26
- . map ( |( k, l) | ( k. to_string ( ) , l. to_string ( ) ) )
32
+ . map ( |def| match def {
33
+ Shortcut :: Pair ( k, l) => ( k. to_string ( ) , l. to_string ( ) , None ) ,
34
+ Shortcut :: Trio ( k1, l, k2) => {
35
+ ( k1. to_string ( ) , l. to_string ( ) , Some ( k2. to_string ( ) ) )
36
+ }
37
+ } )
27
38
. collect ( ) ,
28
39
separator : " | " . to_string ( ) ,
29
40
shortcut_label_style : Style :: default ( ) . add_modifier ( Modifier :: BOLD ) ,
@@ -36,65 +47,73 @@ impl Shortcuts {
36
47
}
37
48
}
38
49
39
- /// Create a new shortcuts widget directly (static constructor)
40
- ///
41
- /// # Example
42
- /// ```
43
- /// let shortcuts = Shortcuts::new(vec![
44
- /// ("Esc", "exit"),
45
- /// ("↑", "move up"),
46
- /// ]);
47
- /// frame.render_widget(shortcuts, area);
48
- /// ```
49
- pub fn new ( values : Vec < ( & str , & str ) > ) -> Self {
50
- Self :: from ( values)
51
- }
52
-
53
50
/// Get the line representation of all shortcuts
54
51
pub fn as_line ( & self ) -> Line {
55
52
if self . shortcuts . is_empty ( ) {
56
53
return Line :: default ( ) . alignment ( self . alignment ) ;
57
54
}
58
55
59
- let mut spans = Vec :: with_capacity ( self . shortcuts . len ( ) * 5 + 2 ) ;
56
+ let mut spans = Vec :: with_capacity ( self . shortcuts . len ( ) * 5 + 2 ) ; // Adjusted capacity estimate
60
57
61
58
// Add start padding if configured
62
59
if !self . padding_start . is_empty ( ) {
63
60
spans. push ( Span :: raw ( & self . padding_start ) ) ;
64
61
}
65
62
66
63
// Process each shortcut
67
- for ( i, ( key , label) ) in self . shortcuts . iter ( ) . enumerate ( ) {
64
+ for ( i, ( key1 , label, key2_opt ) ) in self . shortcuts . iter ( ) . enumerate ( ) {
68
65
// Add separator before shortcut (except for the first one)
69
66
if i > 0 {
70
67
spans. push ( Span :: raw ( & self . separator ) ) ;
71
68
}
72
69
73
- // Render the key-label pair
74
- if label. contains ( key) {
75
- // Create mnemonic spans (key is part of the label)
76
- let first_char = key. chars ( ) . next ( ) . unwrap_or ( '?' ) ;
77
-
78
- if let Some ( idx) = label. find ( first_char) {
79
- // Split the label around the key character
80
- let before = & label[ ..idx] ;
81
- let highlight = & label[ idx..idx + 1 ] ;
82
- let after = & label[ idx + 1 ..] ;
83
-
84
- spans. push ( Span :: styled ( before, self . shortcut_label_style ) ) ;
85
- spans. push ( Span :: styled ( highlight, self . shortcut_key_style ) ) ;
86
- spans. push ( Span :: styled ( after, self . shortcut_label_style ) ) ;
87
- } else {
88
- // Fallback to regular key + label
89
- spans. push ( Span :: styled ( key, self . shortcut_key_style ) ) ;
70
+ match key2_opt {
71
+ Some ( key2) => {
72
+ // Three-part shortcut: key1 label key2
73
+ spans. push ( Span :: styled ( key1, self . shortcut_key_style ) ) ;
90
74
spans. push ( Span :: raw ( " " ) ) ;
91
75
spans. push ( Span :: styled ( label, self . shortcut_label_style ) ) ;
76
+ spans. push ( Span :: raw ( " " ) ) ;
77
+ spans. push ( Span :: styled ( key2, self . shortcut_key_style ) ) ;
78
+ }
79
+ None => {
80
+ // Two-part shortcut: Try mnemonic highlighting first
81
+ if label. contains ( key1) {
82
+ // Create mnemonic spans (key is part of the label)
83
+ let first_char = key1. chars ( ) . next ( ) . unwrap_or ( '?' ) ; // Use key1
84
+
85
+ if let Some ( idx) = label. find ( first_char) {
86
+ // Ensure the key is not empty before slicing
87
+ let key_len = key1. chars ( ) . count ( ) ;
88
+ if key_len > 0 && idx + key_len <= label. chars ( ) . count ( ) {
89
+ // Split the label around the key
90
+ let before = label. chars ( ) . take ( idx) . collect :: < String > ( ) ;
91
+ let highlight =
92
+ label. chars ( ) . skip ( idx) . take ( key_len) . collect :: < String > ( ) ;
93
+ let after = label. chars ( ) . skip ( idx + key_len) . collect :: < String > ( ) ;
94
+
95
+ spans. push ( Span :: styled ( before, self . shortcut_label_style ) ) ;
96
+ spans. push ( Span :: styled ( highlight, self . shortcut_key_style ) ) ;
97
+ spans. push ( Span :: styled ( after, self . shortcut_label_style ) ) ;
98
+ } else {
99
+ // Fallback if slicing indices are invalid (should be rare)
100
+ spans. push ( Span :: styled ( key1, self . shortcut_key_style ) ) ;
101
+ spans. push ( Span :: raw ( " " ) ) ;
102
+ spans. push ( Span :: styled ( label, self . shortcut_label_style ) ) ;
103
+ }
104
+ } else {
105
+ // Fallback to regular key + label if char not found
106
+ spans. push ( Span :: styled ( key1, self . shortcut_key_style ) ) ;
107
+ spans. push ( Span :: raw ( " " ) ) ;
108
+ spans. push ( Span :: styled ( label, self . shortcut_label_style ) ) ;
109
+ }
110
+ } else {
111
+ // Regular shortcut (key + label)
112
+ spans. push ( Span :: styled ( key1, self . shortcut_key_style ) ) ;
113
+ spans. push ( Span :: raw ( " " ) ) ;
114
+ spans. push ( Span :: styled ( label, self . shortcut_label_style ) ) ;
115
+ }
92
116
}
93
- } else {
94
- // Regular shortcut (key + label)
95
- spans. push ( Span :: styled ( key, self . shortcut_key_style ) ) ;
96
- spans. push ( Span :: raw ( " " ) ) ;
97
- spans. push ( Span :: styled ( label, self . shortcut_label_style ) ) ;
98
117
}
99
118
}
100
119
0 commit comments