@@ -4,7 +4,11 @@ use glib::clone;
4
4
use gtk:: prelude:: * ;
5
5
use gtk:: subclass:: prelude:: * ;
6
6
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
+ } ;
8
12
9
13
use crate :: Keyboard ;
10
14
use backend:: DerefCell ;
@@ -28,10 +32,23 @@ pub static SCANCODE_LABELS: Lazy<HashMap<String, String>> = Lazy::new(|| {
28
32
labels
29
33
} ) ;
30
34
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
+
31
44
#[ derive( Default ) ]
32
45
pub struct PickerInner {
33
46
group_box : DerefCell < PickerGroupBox > ,
34
47
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 > ,
35
52
}
36
53
37
54
#[ glib:: object_subclass]
@@ -52,13 +69,50 @@ impl ObjectImpl for PickerInner {
52
69
} ) ) ;
53
70
} ;
54
71
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
+
55
103
cascade ! {
56
104
picker;
105
+ ..set_spacing( 18 ) ;
106
+ ..set_orientation( gtk:: Orientation :: Vertical ) ;
57
107
..add( & group_box) ;
108
+ ..add( & mod_tap_box) ;
58
109
..show_all( ) ;
59
110
} ;
60
111
61
112
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) ;
62
116
}
63
117
}
64
118
@@ -82,49 +136,134 @@ impl Picker {
82
136
PickerInner :: from_instance ( self )
83
137
}
84
138
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
+
85
154
pub ( crate ) fn set_keyboard ( & self , keyboard : Option < Keyboard > ) {
86
155
if let Some ( old_kb) = & * self . inner ( ) . keyboard . borrow ( ) {
87
156
old_kb. set_picker ( None ) ;
88
157
}
89
158
90
159
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
- } ) ;
97
160
kb. set_picker ( Some ( & self ) ) ;
161
+
162
+ self . inner ( )
163
+ . mod_tap_box
164
+ . set_visible ( kb. layout ( ) . meta . has_mod_tap ) ;
98
165
}
99
166
100
167
* self . inner ( ) . keyboard . borrow_mut ( ) = keyboard;
168
+ self . update_key_visibility ( ) ;
101
169
}
102
170
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
+
104
194
self . inner ( ) . group_box . set_selected ( scancode_names) ;
105
195
}
106
196
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 ( ) {
109
211
Some ( kb) => kb,
110
- None => {
111
- return ;
112
- }
212
+ None => return ,
113
213
} ;
114
214
let layer = kb. layer ( ) ;
115
215
216
+ if let Some ( mod_) = self . mod_ ( ) {
217
+ name = format ! ( "MT({}, {})" , mod_, name) ;
218
+ }
219
+
116
220
info ! ( "Clicked {} layer {:?}" , name, layer) ;
117
221
if let Some ( layer) = layer {
118
222
let futures = FuturesUnordered :: new ( ) ;
119
- for i in kb. selected ( ) . iter ( ) {
120
- let i = * i;
223
+ for i in kb. selected ( ) . iter ( ) . copied ( ) {
121
224
futures. push ( clone ! ( @strong kb, @strong name => async move {
122
225
kb. keymap_set( i, layer, & name) . await ;
123
226
} ) ) ;
124
227
}
125
228
glib:: MainContext :: default ( ) . spawn_local ( async { futures. collect :: < ( ) > ( ) . await } ) ;
126
229
}
127
230
}
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
+ }
128
267
}
129
268
130
269
#[ cfg( test) ]
0 commit comments