Skip to content

Commit 3eda13b

Browse files
committed
support mulit binds
1 parent ff38bca commit 3eda13b

6 files changed

Lines changed: 139 additions & 29 deletions

File tree

layouts/multi_binds.tvkl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:| A, B, C, D | 1, 2, 3, 4, 5, 6, 7, 8, 9, 0|-

src/layout.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ pub struct Layout {
88

99
#[derive(Debug)]
1010
pub struct Button {
11-
pub name: Arc<str>,
1211
pub width: u16,
13-
pub rdev_key: Option<Key>,
12+
pub binds: Vec<(Arc<str>, Option<Key>)>
1413
}
1514

src/lexer.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ pub enum TokenType {
1313
RBracket, // "]"
1414
LBrace, // "{"
1515
RBrace, // "}"
16+
Comma, // ","
1617
}
1718

18-
const RESERVE_SYMBOL: [char; 9] = [':', '-', '|', '\'', '[', ']', '{', '}', '$'];
19+
const RESERVE_SYMBOL: [char; 10] = [':', '-', '|', '\'', '[', ']', '{', '}', '$', ','];
1920

2021
#[derive(Debug, PartialEq, Clone)]
2122
pub struct Token {
@@ -68,6 +69,10 @@ impl<'a> Lexer<'a> {
6869
self.src.next();
6970
Some(Token { token_type: TokenType::RBrace, value: "}".to_string() })
7071
}
72+
',' => {
73+
self.src.next();
74+
Some(Token { token_type: TokenType::Comma, value: ",".to_string() })
75+
}
7176
'\'' | '\"' => {
7277
Some(self.collect_quoted_name(c))
7378
}
@@ -189,5 +194,30 @@ mod tests{
189194
assert_eq!(tokens, right_result);
190195
}
191196

197+
#[test]
198+
fn multi_binds(){
199+
let input = ":| A | B, C, D |-";
200+
201+
let mut lexer = Lexer::new(input);
202+
let tokens:Vec<Token> = lexer.tokenization();
203+
204+
let right_result = vec![
205+
Token { token_type: TokenType::LineHead, value: String::from(":") },
206+
Token { token_type: TokenType::Split, value: String::from("|") },
207+
Token { token_type: TokenType::Name, value: String::from("A")},
208+
Token { token_type: TokenType::Split, value: String::from("|") },
209+
Token { token_type: TokenType::Name, value: String::from("B") },
210+
Token { token_type: TokenType::Comma, value: String::from(",") },
211+
Token { token_type: TokenType::Name, value: String::from("C") },
212+
Token { token_type: TokenType::Comma, value: String::from(",") },
213+
Token { token_type: TokenType::Name, value: String::from("D") },
214+
Token { token_type: TokenType::Split, value: String::from("|") },
215+
Token { token_type: TokenType::LineTail, value: String::from("-") },
216+
];
217+
218+
assert_eq!(tokens, right_result);
219+
}
220+
221+
192222

193223
}

src/parser.rs

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,24 @@ impl Parser {
6565
while self.current < self.tokens.len() && self.peek()?.token_type != TokenType::LineTail {
6666
let name_token = self.consume(TokenType::Name)?;
6767
let name_str = name_token.value.clone();
68-
68+
let mut binds = vec![];
69+
binds.push((Arc::from(name_str.as_str()), get_rdev_key(&name_str)));
6970
let mut attr = get_default_width(&name_str);
7071

72+
while self.peek()?.token_type == TokenType::Comma {
73+
self.consume(TokenType::Comma)?;
74+
let name_token = self.consume(TokenType::Name)?;
75+
let name_str = name_token.value.clone();
76+
binds.push((Arc::from(name_str.as_str()), get_rdev_key(&name_str)));
77+
}
78+
7179
if self.peek()?.token_type == TokenType::LBracket {
7280
self.parse_attr(&mut attr)?;
7381
}
7482

7583
row.push(Button {
76-
rdev_key: get_rdev_key(&name_str),
7784
width: attr.width,
78-
name: Arc::from(name_str.as_str()),
85+
binds,
7986
});
8087

8188
self.consume(TokenType::Split)?;
@@ -151,7 +158,7 @@ fn get_rdev_key(name: &str) -> Option<Key> {
151158
"x" => Some(Key::KeyX),
152159
"c" => Some(Key::KeyC),
153160
"v" => Some(Key::KeyV),
154-
"b" => Some(Key::KeyN),
161+
"b" => Some(Key::KeyB),
155162
"n" => Some(Key::KeyN),
156163
"m" => Some(Key::KeyM),
157164
"ctrl" | "lctrl" => Some(Key::ControlLeft),
@@ -215,10 +222,10 @@ mod tests {
215222
let result = parser.parse().unwrap();
216223

217224
assert_eq!(result.layer.len(), 1);
218-
assert_eq!(result.layer[0][0].name.as_ref(), "Tab");
219-
assert_eq!(result.layer[0][0].rdev_key, Some(Key::Tab));
220-
assert_eq!(result.layer[0][1].name.as_ref(), "P");
221-
assert_eq!(result.layer[0][1].rdev_key, Some(Key::KeyP));
225+
assert_eq!(result.layer[0][0].binds[0].0.as_ref(), "Tab");
226+
assert_eq!(result.layer[0][0].binds[0].1, Some(Key::Tab));
227+
assert_eq!(result.layer[0][1].binds[0].0.as_ref(), "P");
228+
assert_eq!(result.layer[0][1].binds[0].1, Some(Key::KeyP));
222229
}
223230

224231
#[test]
@@ -320,12 +327,65 @@ mod tests {
320327
let result = parser.parse().unwrap();
321328

322329
assert_eq!(result.layer.len(), 1);
323-
assert_eq!(result.layer[0][0].name.as_ref(), "Tab");
324-
assert_eq!(result.layer[0][0].rdev_key, Some(Key::Tab));
330+
assert_eq!(result.layer[0][0].binds[0].0.as_ref(), "Tab");
331+
assert_eq!(result.layer[0][0].binds[0].1, Some(Key::Tab));
325332
assert_eq!(result.layer[0][0].width, 10);
326-
assert_eq!(result.layer[0][1].name.as_ref(), "P");
327-
assert_eq!(result.layer[0][1].rdev_key, Some(Key::KeyP));
333+
assert_eq!(result.layer[0][1].binds[0].0.as_ref(), "P");
334+
assert_eq!(result.layer[0][1].binds[0].1, Some(Key::KeyP));
335+
assert_eq!(result.layer[0][1].width, 4);
336+
}
337+
338+
#[test]
339+
fn test_multi_binds() {
340+
// Input sequence for: :| A, C, D| B | -
341+
let tokens = vec![
342+
Token {
343+
token_type: TokenType::LineHead,
344+
value: ":".into(),
345+
},
346+
Token {
347+
token_type: TokenType::Split,
348+
value: "|".into(),
349+
},
350+
t_name("A"),
351+
Token {
352+
token_type: TokenType::Comma,
353+
value: ",".into(),
354+
},
355+
t_name("C"),
356+
Token {
357+
token_type: TokenType::Comma,
358+
value: ",".into(),
359+
},
360+
t_name("D"),
361+
Token {
362+
token_type: TokenType::Split,
363+
value: "|".into(),
364+
},
365+
t_name("B"),
366+
Token {
367+
token_type: TokenType::Split,
368+
value: "|".into(),
369+
},
370+
Token {
371+
token_type: TokenType::LineTail,
372+
value: "-".into(),
373+
},
374+
];
375+
376+
let mut parser = Parser::new(tokens);
377+
let result = parser.parse().unwrap();
378+
379+
assert_eq!(result.layer.len(), 1);
380+
assert_eq!(result.layer[0][0].binds, [
381+
(Arc::from("A"), Some(Key::KeyA)),
382+
(Arc::from("C"), Some(Key::KeyC)),
383+
(Arc::from("D"), Some(Key::KeyD)),
384+
]);
385+
assert_eq!(result.layer[0][1].binds[0].0.as_ref(), "B");
386+
assert_eq!(result.layer[0][1].binds[0].1, Some(Key::KeyB));
328387
assert_eq!(result.layer[0][1].width, 4);
329388
}
330389

390+
331391
}

src/render.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ use crate::layout::Layout;
1111
pub fn render_ui(f: &mut Frame, pressed_keys: &HashSet<Key>, kps: usize, kbd_layout: &Layout) {
1212
let area = f.size();
1313

14-
let color = Color::Rgb(176, 176, 176);
14+
let default_border_color = Color::Rgb(176, 176, 176);
1515
let outer_block = Block::default()
1616
.borders(Borders::ALL)
17-
.title(" Terminal Virtual Keyboard")
17+
.title(" Terminal Virtual Keyboard ")
1818
.border_type(BorderType::Thick)
19-
.border_style(Style::default().fg(color));
19+
.border_style(Style::default().fg(default_border_color));
2020

2121
let inner_area = outer_block.inner(area);
2222
f.render_widget(outer_block, area);
@@ -45,25 +45,45 @@ pub fn render_ui(f: &mut Frame, pressed_keys: &HashSet<Key>, kps: usize, kbd_lay
4545
.constraints(key_constraints)
4646
.split(row_areas[r_idx]);
4747

48-
for (k_idx, key_def) in row.iter().enumerate() {
49-
let is_pressed = key_def.rdev_key.map_or(false, |k| pressed_keys.contains(&k));
50-
51-
let style = if is_pressed {
52-
Style::default().bg(color).fg(Color::Black)
53-
} else {
54-
Style::default().fg(Color::Gray)
48+
for (k_idx, button) in row.iter().enumerate() {
49+
let active_bind_idx = button.binds.iter().enumerate().rev()
50+
.find(|(_, (_, key))| key.map_or(false, |k| pressed_keys.contains(&k)))
51+
.map(|(i, _)| i);
52+
53+
let (display_name, style) = match active_bind_idx {
54+
Some(idx) => {
55+
let name = button.binds[idx].0.as_ref();
56+
let layer_color = match idx {
57+
0 => Color::Rgb(176, 176, 176),
58+
1 => Color::Rgb(173, 173, 123),
59+
2 => Color::Rgb(123, 173, 144),
60+
_ => Color::Rgb(123, 159, 173),
61+
};
62+
63+
(name, Style::default().bg(layer_color).fg(Color::Black).add_modifier(Modifier::BOLD))
64+
}
65+
None => {
66+
let name = button.binds.get(0).map(|b| b.0.as_ref()).unwrap_or("");
67+
(name, Style::default().fg(Color::Gray))
68+
}
5569
};
5670

57-
let key_label = Paragraph::new(key_def.name.as_ref())
71+
let key_widget = Paragraph::new(display_name)
5872
.alignment(Alignment::Center)
5973
.block(
6074
Block::default()
6175
.borders(Borders::ALL)
6276
.border_type(BorderType::Plain)
63-
.style(style)
77+
.style(if active_bind_idx.is_some() { style } else { Style::default().fg(default_border_color) })
6478
);
79+
80+
let final_widget = if active_bind_idx.is_some() {
81+
key_widget.style(style)
82+
} else {
83+
key_widget
84+
};
6585

66-
f.render_widget(key_label, key_areas[k_idx]);
86+
f.render_widget(final_widget, key_areas[k_idx]);
6787
}
6888
}
6989
}

tvkl/grammar/tvkl.gram

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# "" Literal string
1313
# =============================================================================
1414

15-
line ::= ":" "|" { item "|" }* "-"
15+
line ::= ":" "|" { item (, item)* "|" }* "-"
1616

1717
item ::= ( quoted_name | name ) [ attr_decl ]
1818

0 commit comments

Comments
 (0)