Skip to content

Commit 30bcd4d

Browse files
committed
WIP mod tap
1 parent 38c1c7b commit 30bcd4d

File tree

4 files changed

+157
-16
lines changed

4 files changed

+157
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ libc = "0.2"
2020
once_cell = "1.4"
2121
pango = "0.14.0"
2222
pangocairo = "0.14.0"
23+
regex = "1"
2324
serde = { version = "1.0", features = ["derive"] }
2425
serde_json = "1.0"
2526
log = "0.4.0"

src/keyboard.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ impl Keyboard {
264264
}
265265
}
266266

267-
fn layout(&self) -> &Layout {
267+
pub fn layout(&self) -> &Layout {
268268
&self.inner().board.layout()
269269
}
270270

src/picker/mod.rs

Lines changed: 154 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ use glib::clone;
44
use gtk::prelude::*;
55
use gtk::subclass::prelude::*;
66
use once_cell::sync::Lazy;
7-
use std::{cell::RefCell, collections::HashMap};
7+
use regex::Regex;
8+
use std::{
9+
cell::{Cell, RefCell},
10+
collections::HashMap,
11+
};
812

913
use crate::Keyboard;
1014
use backend::DerefCell;
@@ -28,10 +32,23 @@ pub static SCANCODE_LABELS: Lazy<HashMap<String, String>> = Lazy::new(|| {
2832
labels
2933
});
3034

35+
fn parse_mod_tap(name: &str) -> Option<(&str, &str)> {
36+
let mt_re = Regex::new("MT\\(([^()]+), ([^()]+)\\)").unwrap();
37+
mt_re.captures(name).map(|captures| {
38+
let mod_name = captures.get(1).unwrap().as_str();
39+
let kc_name = captures.get(2).unwrap().as_str();
40+
(mod_name, kc_name)
41+
})
42+
}
43+
3144
#[derive(Default)]
3245
pub struct PickerInner {
3346
group_box: DerefCell<PickerGroupBox>,
3447
keyboard: RefCell<Option<Keyboard>>,
48+
mod_tap_box: DerefCell<gtk::Box>,
49+
mod_tap_check: DerefCell<gtk::CheckButton>,
50+
mod_tap_mods: DerefCell<gtk::ComboBoxText>,
51+
mod_tap_signal_blocked: Cell<bool>,
3552
}
3653

3754
#[glib::object_subclass]
@@ -52,13 +69,50 @@ impl ObjectImpl for PickerInner {
5269
}));
5370
};
5471

72+
let mod_tap_mods = cascade! {
73+
gtk::ComboBoxText::new();
74+
..append(Some("LEFT_CTRL"), "Left Ctrl");
75+
..append(Some("LEFT_SHIFT"), "Left Shift");
76+
..append(Some("LEFT_ALT"), "Left Alt");
77+
..append(Some("LEFT_SUPER"), "Left Super");
78+
..append(Some("RIGHT_CTRL"), "Right Ctrl");
79+
..append(Some("RIGHT_SHIFT"), "Right Shift");
80+
..append(Some("RIGHT_ALT"), "Right Alt");
81+
..append(Some("RIGHT_SUPER"), "Right Super");
82+
..set_active_id(Some("LEFT_CTRL"));
83+
..connect_active_id_notify(clone!(@weak picker => move |_| {
84+
picker.mod_tap_updated();
85+
}));
86+
};
87+
88+
let mod_tap_check = cascade! {
89+
gtk::CheckButton::with_label("Mod-Tap");
90+
..bind_property("active", &mod_tap_mods, "sensitive").flags(glib::BindingFlags::SYNC_CREATE).build();
91+
..connect_toggled(clone!(@weak picker => move |_| {
92+
picker.update_key_visibility();
93+
picker.mod_tap_updated();
94+
}));
95+
};
96+
97+
let mod_tap_box = cascade! {
98+
gtk::Box::new(gtk::Orientation::Horizontal, 8);
99+
..add(&mod_tap_check);
100+
..add(&mod_tap_mods);
101+
};
102+
55103
cascade! {
56104
picker;
105+
..set_spacing(18);
106+
..set_orientation(gtk::Orientation::Vertical);
57107
..add(&group_box);
108+
..add(&mod_tap_box);
58109
..show_all();
59110
};
60111

61112
self.group_box.set(group_box);
113+
self.mod_tap_box.set(mod_tap_box);
114+
self.mod_tap_check.set(mod_tap_check);
115+
self.mod_tap_mods.set(mod_tap_mods);
62116
}
63117
}
64118

@@ -82,49 +136,134 @@ impl Picker {
82136
PickerInner::from_instance(self)
83137
}
84138

139+
fn update_key_visibility(&self) {
140+
let kb = match self.keyboard() {
141+
Some(kb) => kb,
142+
None => return,
143+
};
144+
let is_mod_tap =
145+
self.inner().mod_tap_box.get_visible() && self.inner().mod_tap_check.is_active();
146+
self.inner().group_box.set_key_visibility(|name| {
147+
// Check that scancode is available for the keyboard
148+
let visible = kb.has_scancode(name);
149+
let sensitive = !is_mod_tap || kb.layout().scancode_from_name(name).unwrap_or(0) < 256;
150+
(visible, sensitive)
151+
});
152+
}
153+
85154
pub(crate) fn set_keyboard(&self, keyboard: Option<Keyboard>) {
86155
if let Some(old_kb) = &*self.inner().keyboard.borrow() {
87156
old_kb.set_picker(None);
88157
}
89158

90159
if let Some(kb) = &keyboard {
91-
// Check that scancode is available for the keyboard
92-
self.inner().group_box.set_key_visibility(|name| {
93-
let visible = kb.has_scancode(name);
94-
let sensitive = true;
95-
(visible, sensitive)
96-
});
97160
kb.set_picker(Some(&self));
161+
162+
self.inner()
163+
.mod_tap_box
164+
.set_visible(kb.layout().meta.has_mod_tap);
98165
}
99166

100167
*self.inner().keyboard.borrow_mut() = keyboard;
168+
self.update_key_visibility();
101169
}
102170

103-
pub(crate) fn set_selected(&self, scancode_names: Vec<String>) {
171+
pub(crate) fn set_selected(&self, mut scancode_names: Vec<String>) {
172+
self.inner().mod_tap_signal_blocked.set(true);
173+
174+
self.inner().mod_tap_box.set_sensitive(false);
175+
self.inner().mod_tap_check.set_active(false);
176+
self.inner().mod_tap_mods.set_active_id(Some("LEFT_CTRL"));
177+
178+
if scancode_names.len() == 1 {
179+
self.inner().mod_tap_box.set_sensitive(true);
180+
if let Some((mod_name, _)) = parse_mod_tap(&scancode_names[0]) {
181+
self.inner().mod_tap_check.set_active(true);
182+
self.inner().mod_tap_mods.set_active_id(Some(mod_name));
183+
}
184+
}
185+
186+
self.inner().mod_tap_signal_blocked.set(false);
187+
188+
for i in scancode_names.iter_mut() {
189+
if let Some((_, kc_name)) = parse_mod_tap(&i) {
190+
*i = kc_name.to_string();
191+
}
192+
}
193+
104194
self.inner().group_box.set_selected(scancode_names);
105195
}
106196

107-
fn key_pressed(&self, name: String) {
108-
let kb = match self.inner().keyboard.borrow().clone() {
197+
fn mod_(&self) -> Option<String> {
198+
if self.inner().mod_tap_box.get_visible() && self.inner().mod_tap_check.is_active() {
199+
Some(self.inner().mod_tap_mods.active_id()?.into())
200+
} else {
201+
None
202+
}
203+
}
204+
205+
fn keyboard(&self) -> Option<Keyboard> {
206+
self.inner().keyboard.borrow().clone()
207+
}
208+
209+
fn key_pressed(&self, mut name: String) {
210+
let kb = match self.keyboard() {
109211
Some(kb) => kb,
110-
None => {
111-
return;
112-
}
212+
None => return,
113213
};
114214
let layer = kb.layer();
115215

216+
if let Some(mod_) = self.mod_() {
217+
name = format!("MT({}, {})", mod_, name);
218+
}
219+
116220
info!("Clicked {} layer {:?}", name, layer);
117221
if let Some(layer) = layer {
118222
let futures = FuturesUnordered::new();
119-
for i in kb.selected().iter() {
120-
let i = *i;
223+
for i in kb.selected().iter().copied() {
121224
futures.push(clone!(@strong kb, @strong name => async move {
122225
kb.keymap_set(i, layer, &name).await;
123226
}));
124227
}
125228
glib::MainContext::default().spawn_local(async { futures.collect::<()>().await });
126229
}
127230
}
231+
232+
fn mod_tap_updated(&self) {
233+
if self.inner().mod_tap_signal_blocked.get() {
234+
return;
235+
}
236+
237+
let kb = match self.keyboard() {
238+
Some(kb) => kb,
239+
None => return,
240+
};
241+
let layer = kb.layer();
242+
243+
if let Some(layer) = layer {
244+
let futures = FuturesUnordered::new();
245+
for i in kb.selected().iter().copied() {
246+
if let Some((_, scancode)) = &kb.board().keys()[i].get_scancode(layer) {
247+
let kc_name = if let Some((_, kc_name)) = parse_mod_tap(scancode) {
248+
kc_name
249+
} else {
250+
scancode
251+
};
252+
253+
let name = if let Some(mod_name) = self.mod_() {
254+
format!("MT({}, {})", mod_name, kc_name)
255+
} else {
256+
kc_name.to_string()
257+
};
258+
259+
futures.push(clone!(@strong kb, @strong name => async move {
260+
kb.keymap_set(i, layer, &name).await;
261+
}));
262+
}
263+
}
264+
glib::MainContext::default().spawn_local(async { futures.collect::<()>().await });
265+
}
266+
}
128267
}
129268

130269
#[cfg(test)]

0 commit comments

Comments
 (0)