Skip to content

Commit 0e50865

Browse files
authored
Merge pull request #772 from multiplex55/codex/add-bindingkind-enum-and-update-bindings
Add BindingKind enum, migrate legacy mouse gesture bindings, and update UI
2 parents 098d40d + e503771 commit 0e50865

8 files changed

Lines changed: 678 additions & 366 deletions

File tree

src/gui/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2041,7 +2041,21 @@ impl LauncherApp {
20412041
self.query.starts_with("timer list") || self.query.starts_with("alarm list");
20422042
self.search();
20432043
}
2044+
let mut focus_after_launcher = false;
2045+
if a.action == "launcher:show" {
2046+
if let Some(query) = a.args.as_ref() {
2047+
self.query = query.to_string();
2048+
self.last_timer_query =
2049+
query.starts_with("timer list") || query.starts_with("alarm list");
2050+
self.search();
2051+
self.move_cursor_end = true;
2052+
focus_after_launcher = true;
2053+
}
2054+
}
20442055
if self.handle_launcher_action(&a.action) {
2056+
if focus_after_launcher {
2057+
self.focus_input();
2058+
}
20452059
return;
20462060
}
20472061
let current = self.query.clone();
@@ -2488,7 +2502,7 @@ impl LauncherApp {
24882502
} else if a.action != "help:show" {
24892503
self.record_history_usage(&a, &current, source);
24902504
}
2491-
} else if let Err(e) = launch_action(&a) {
2505+
} else if let Err(e) = execute_action(&a) {
24922506
if a.desc == "Fav" && !a.action.starts_with("fav:") {
24932507
tracing::error!(?e, fav=%a.label, "failed to run favorite");
24942508
}

src/gui/mouse_gesture_settings_dialog.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,7 @@ impl MouseGestureSettingsDialog {
257257
}
258258
}
259259

260-
fn cancel_behavior_label(
261-
value: crate::mouse_gestures::service::CancelBehavior,
262-
) -> &'static str {
260+
fn cancel_behavior_label(value: crate::mouse_gestures::service::CancelBehavior) -> &'static str {
263261
match value {
264262
crate::mouse_gestures::service::CancelBehavior::DoNothing => "Do nothing",
265263
crate::mouse_gestures::service::CancelBehavior::PassThroughClick => {
@@ -268,9 +266,7 @@ fn cancel_behavior_label(
268266
}
269267
}
270268

271-
fn no_match_behavior_label(
272-
value: crate::mouse_gestures::service::NoMatchBehavior,
273-
) -> &'static str {
269+
fn no_match_behavior_label(value: crate::mouse_gestures::service::NoMatchBehavior) -> &'static str {
274270
match value {
275271
crate::mouse_gestures::service::NoMatchBehavior::DoNothing => "Do nothing",
276272
crate::mouse_gestures::service::NoMatchBehavior::PassThroughClick => {

src/gui/mouse_gestures_dialog.rs

Lines changed: 393 additions & 343 deletions
Large diffs are not rendered by default.

src/mouse_gestures/db.rs

Lines changed: 158 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,29 @@ use std::collections::{HashMap, HashSet};
55
use std::sync::{Arc, Mutex};
66

77
pub 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)]
1127
pub 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

421443
impl 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

447527
pub 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+
519668
fn fuzzy_score(needle: &str, haystack: &str) -> Option<f32> {
520669
let mut matched = 0_usize;
521670
let mut start_index = 0_usize;

0 commit comments

Comments
 (0)