@@ -5,11 +5,29 @@ use std::collections::{HashMap, HashSet};
55use std:: sync:: { Arc , Mutex } ;
66
77pub const GESTURES_FILE : & str = "mouse_gestures.json" ;
8- pub const SCHEMA_VERSION : u32 = 1 ;
8+ pub const SCHEMA_VERSION : u32 = 2 ;
9+ const LEGACY_SCHEMA_VERSION : u32 = 1 ;
10+
11+ #[ derive( Debug , Clone , Copy , Serialize , Deserialize , PartialEq , Eq ) ]
12+ #[ serde( rename_all = "snake_case" ) ]
13+ pub enum BindingKind {
14+ Execute ,
15+ SetQuery ,
16+ SetQueryAndShow ,
17+ ToggleLauncher ,
18+ }
19+
20+ impl Default for BindingKind {
21+ fn default ( ) -> Self {
22+ BindingKind :: Execute
23+ }
24+ }
925
1026#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , Eq ) ]
1127pub struct BindingEntry {
1228 pub label : String ,
29+ #[ serde( default ) ]
30+ pub kind : BindingKind ,
1331 pub action : String ,
1432 #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
1533 pub args : Option < String > ,
@@ -134,7 +152,11 @@ impl GestureDb {
134152 if binding. label . to_lowercase ( ) . contains ( & query_lower) {
135153 fields. push ( BindingMatchField :: BindingLabel ) ;
136154 }
137- if binding. action . to_lowercase ( ) . contains ( & query_lower) {
155+ if binding
156+ . display_target ( )
157+ . to_lowercase ( )
158+ . contains ( & query_lower)
159+ {
138160 fields. push ( BindingMatchField :: Action ) ;
139161 }
140162 if binding
@@ -178,7 +200,7 @@ impl GestureDb {
178200 for gesture in self . gestures . iter ( ) . filter ( |gesture| gesture. enabled ) {
179201 for binding in gesture. bindings . iter ( ) . filter ( |binding| binding. enabled ) {
180202 if binding
181- . action
203+ . action_string ( )
182204 . to_lowercase ( )
183205 . starts_with ( & action_prefix)
184206 {
@@ -419,12 +441,41 @@ impl GestureDb {
419441}
420442
421443impl BindingEntry {
444+ pub fn action_string ( & self ) -> String {
445+ match self . kind {
446+ BindingKind :: Execute => self . action . clone ( ) ,
447+ BindingKind :: SetQuery => format ! ( "query:{}" , self . action) ,
448+ BindingKind :: SetQueryAndShow => "launcher:show" . to_string ( ) ,
449+ BindingKind :: ToggleLauncher => "launcher:toggle" . to_string ( ) ,
450+ }
451+ }
452+
453+ pub fn display_target ( & self ) -> String {
454+ match self . kind {
455+ BindingKind :: Execute => match & self . args {
456+ Some ( args) => format ! ( "{} {}" , self . action, args) ,
457+ None => self . action . clone ( ) ,
458+ } ,
459+ BindingKind :: SetQuery => format ! ( "query:{}" , self . action) ,
460+ BindingKind :: SetQueryAndShow => format ! ( "launcher:show (query: {})" , self . action) ,
461+ BindingKind :: ToggleLauncher => "launcher:toggle" . to_string ( ) ,
462+ }
463+ }
464+
422465 pub fn to_action ( & self , gesture_label : & str ) -> Action {
466+ let ( action, args) = match self . kind {
467+ BindingKind :: Execute => ( self . action . clone ( ) , self . args . clone ( ) ) ,
468+ BindingKind :: SetQuery => ( format ! ( "query:{}" , self . action) , None ) ,
469+ BindingKind :: SetQueryAndShow => {
470+ ( "launcher:show" . to_string ( ) , Some ( self . action . clone ( ) ) )
471+ }
472+ BindingKind :: ToggleLauncher => ( "launcher:toggle" . to_string ( ) , None ) ,
473+ } ;
423474 Action {
424475 label : self . label . clone ( ) ,
425476 desc : format ! ( "Mouse gesture: {gesture_label}" ) ,
426- action : self . action . clone ( ) ,
427- args : self . args . clone ( ) ,
477+ action,
478+ args,
428479 }
429480 }
430481}
@@ -434,14 +485,43 @@ pub fn load_gestures(path: &str) -> anyhow::Result<GestureDb> {
434485 if content. trim ( ) . is_empty ( ) {
435486 return Ok ( GestureDb :: default ( ) ) ;
436487 }
437- let db: GestureDb = serde_json:: from_str ( & content) ?;
438- if db. schema_version != SCHEMA_VERSION {
488+ let raw: serde_json:: Value = serde_json:: from_str ( & content) ?;
489+ let version = raw
490+ . get ( "schema_version" )
491+ . and_then ( |v| v. as_u64 ( ) )
492+ . unwrap_or ( LEGACY_SCHEMA_VERSION as u64 ) as u32 ;
493+ if version == SCHEMA_VERSION {
494+ let db: GestureDb = serde_json:: from_value ( raw) ?;
495+ return Ok ( db) ;
496+ }
497+ if version != LEGACY_SCHEMA_VERSION {
439498 return Err ( anyhow:: anyhow!(
440499 "Unsupported gesture schema version {}" ,
441- db . schema_version
500+ version
442501 ) ) ;
443502 }
444- Ok ( db)
503+
504+ let legacy: LegacyGestureDb = serde_json:: from_value ( raw) ?;
505+ let gestures = legacy
506+ . gestures
507+ . into_iter ( )
508+ . map ( |gesture| GestureEntry {
509+ label : gesture. label ,
510+ tokens : gesture. tokens ,
511+ dir_mode : gesture. dir_mode ,
512+ stroke : gesture. stroke ,
513+ enabled : gesture. enabled ,
514+ bindings : gesture
515+ . bindings
516+ . into_iter ( )
517+ . map ( |binding| binding. into_binding ( ) )
518+ . collect ( ) ,
519+ } )
520+ . collect ( ) ;
521+ Ok ( GestureDb {
522+ schema_version : SCHEMA_VERSION ,
523+ gestures,
524+ } )
445525}
446526
447527pub fn save_gestures ( path : & str , db : & GestureDb ) -> anyhow:: Result < ( ) > {
@@ -516,6 +596,75 @@ fn default_schema_version() -> u32 {
516596 SCHEMA_VERSION
517597}
518598
599+ fn default_legacy_schema_version ( ) -> u32 {
600+ LEGACY_SCHEMA_VERSION
601+ }
602+
603+ #[ derive( Debug , Clone , Deserialize ) ]
604+ struct LegacyGestureDb {
605+ #[ serde( default = "default_legacy_schema_version" ) ]
606+ schema_version : u32 ,
607+ #[ serde( default ) ]
608+ gestures : Vec < LegacyGestureEntry > ,
609+ }
610+
611+ #[ derive( Debug , Clone , Deserialize ) ]
612+ struct LegacyGestureEntry {
613+ label : String ,
614+ tokens : String ,
615+ dir_mode : DirMode ,
616+ #[ serde( default , skip_serializing_if = "Vec::is_empty" ) ]
617+ stroke : Vec < [ i16 ; 2 ] > ,
618+ #[ serde( default = "default_enabled" ) ]
619+ enabled : bool ,
620+ #[ serde( default ) ]
621+ bindings : Vec < LegacyBindingEntry > ,
622+ }
623+
624+ #[ derive( Debug , Clone , Deserialize ) ]
625+ struct LegacyBindingEntry {
626+ label : String ,
627+ action : String ,
628+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
629+ args : Option < String > ,
630+ #[ serde( default = "default_enabled" ) ]
631+ enabled : bool ,
632+ #[ serde( default ) ]
633+ use_query : bool ,
634+ }
635+
636+ impl LegacyBindingEntry {
637+ fn into_binding ( self ) -> BindingEntry {
638+ let action = self . action . trim ( ) . to_string ( ) ;
639+ if action == "launcher:toggle" {
640+ return BindingEntry {
641+ label : self . label ,
642+ kind : BindingKind :: ToggleLauncher ,
643+ action : String :: new ( ) ,
644+ args : None ,
645+ enabled : self . enabled ,
646+ } ;
647+ }
648+ let ( kind, action) = if self . use_query || action. starts_with ( "query:" ) {
649+ let action = action
650+ . strip_prefix ( "query:" )
651+ . unwrap_or ( & action)
652+ . trim ( )
653+ . to_string ( ) ;
654+ ( BindingKind :: SetQuery , action)
655+ } else {
656+ ( BindingKind :: Execute , action)
657+ } ;
658+ BindingEntry {
659+ label : self . label ,
660+ kind,
661+ action,
662+ args : self . args ,
663+ enabled : self . enabled ,
664+ }
665+ }
666+ }
667+
519668fn fuzzy_score ( needle : & str , haystack : & str ) -> Option < f32 > {
520669 let mut matched = 0_usize ;
521670 let mut start_index = 0_usize ;
0 commit comments