mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 11:31:47 +00:00
keymap: add Keymap::conflicts_with and document the edit calls
conflicts_with lists the commands that share one command's chords, with that command as a, without filtering every conflict. The README shows set_bindings with one binding for an editor with one shortcut per command; assign adds a binding.
This commit is contained in:
1 parent
5208562c28
commit
be51941628
3 files changed
+62
-1
No files matched your search
+14
-1
@@ -113,10 +113,23 @@ let mut keymap = Keymap::new(catalogue, Scopes::new(), Platform::current()).with
|
||||
let _problems = keymap.problems();
|
||||
|
||||
// After each change in the shortcut editor:
|
||||
keymap.assign(&"file.save".into(), Binding::parse("Cmd+Alt+S").unwrap(), Policy::Steal).ok();
|
||||
keymap.set_bindings(&"file.save".into(), vec![Binding::parse("Cmd+Alt+S").unwrap()], Policy::Steal).ok();
|
||||
store.update(|p| p.shortcuts = keymap.overrides().clone());
|
||||
```
|
||||
|
||||
## Editing
|
||||
|
||||
- `set_bindings(&command, bindings, policy)` replaces a command's bindings. An editor with one
|
||||
shortcut per command calls it with the one binding (`vec![binding]`); `assign` adds a binding
|
||||
to those the command has.
|
||||
- `remove` takes one binding away, `clear` leaves the command without shortcuts, `reset` gives it
|
||||
its defaults back and `reset_all` does so for every command.
|
||||
- `Policy` says what happens to other commands holding the chord: `Steal` takes it from them,
|
||||
`Keep` lets both keep it and reports the clash, `Refuse` changes nothing and returns
|
||||
`AssignError::Taken`.
|
||||
- `conflicts()` lists every pair of commands on one chord; `conflicts_with(&command)` lists the
|
||||
commands that share one command's chords, with that command as `a`.
|
||||
|
||||
## Importing a shortcut set
|
||||
|
||||
The app's reader turns a file (an InDesign Show Set, an Illustrator `.kys`) into an
|
||||
|
||||
@@ -594,6 +594,32 @@ impl Keymap {
|
||||
out
|
||||
}
|
||||
|
||||
/// The conflicts [`Keymap::conflicts`] reports for one command's bindings, with that command
|
||||
/// as `a`, in the order of its bindings, then catalogue order. Empty for an unknown command.
|
||||
pub fn conflicts_with(&self, command: &CommandRef) -> Vec<Conflict> {
|
||||
let Some(i) = self.find(command) else { return Vec::new() };
|
||||
let mut out = Vec::new();
|
||||
for a in &self.current[i] {
|
||||
let chord = a.0.effective(self.platform);
|
||||
let Some(holders) = self.by_trigger.get(&chord) else { continue };
|
||||
for &(j, k) in holders {
|
||||
if j == i || self.same_group(i, j) {
|
||||
continue;
|
||||
}
|
||||
let Some(b) = self.at(j, k) else { continue };
|
||||
let Some(clash) = self.conflict_relation(&a.1, &b.1) else { continue };
|
||||
out.push(Conflict {
|
||||
chord,
|
||||
a: (self.catalogue[i].command.clone(), self.public(i, a)),
|
||||
b: (self.catalogue[j].command.clone(), self.public(j, b)),
|
||||
kind: if clash { ConflictKind::Clash } else { ConflictKind::Shadow },
|
||||
in_defaults: self.defaults[i].contains(a) && self.defaults[j].contains(b),
|
||||
});
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// What is wrong with the stored overrides: load notes, unknown commands, texts that do not
|
||||
/// parse, reserved chords, unknown scopes, duplicate entries.
|
||||
pub fn problems(&self) -> Vec<Problem> {
|
||||
|
||||
@@ -246,6 +246,28 @@ fn conflicts_tell_clashes_from_shadows() {
|
||||
assert!(c("edit.copy", "color.copy") < c("edit.copy", "timeline.copy"), "{order:?}");
|
||||
assert!(c("edit.copy", "timeline.copy") < c("color.copy", "timeline.copy"), "{order:?}");
|
||||
|
||||
// One command's conflicts, with that command as `a`.
|
||||
let with = |id: &str| -> Vec<(String, String, ConflictKind)> {
|
||||
km.conflicts_with(&cmd(id)).into_iter().map(|c| (c.chord.to_string(), c.b.0.id, c.kind)).collect()
|
||||
};
|
||||
let w = |chord: &str, other: &str, kind| (chord.to_string(), other.to_string(), kind);
|
||||
assert_eq!(with("edit.copy"), [w("Cmd+C", "color.copy", ConflictKind::Clash), w("Cmd+C", "timeline.copy", ConflictKind::Shadow)]);
|
||||
assert_eq!(with("edit.cut"), [w("Cmd+X", "text.cut", ConflictKind::Shadow), w("Cmd+D", "edit.find", ConflictKind::Clash)]);
|
||||
assert!(with("nope").is_empty());
|
||||
for entry in km.catalogue() {
|
||||
let id = entry.command.id.as_str();
|
||||
let mut direct: Vec<_> = km.conflicts_with(&entry.command).into_iter().map(|c| (c.chord, c.a.0.id, c.b.0.id, c.kind, c.in_defaults)).collect();
|
||||
let mut filtered: Vec<_> = km
|
||||
.conflicts()
|
||||
.into_iter()
|
||||
.filter(|c| c.a.0.id == id || c.b.0.id == id)
|
||||
.map(|c| if c.a.0.id == id { (c.chord, c.a.0.id, c.b.0.id, c.kind, c.in_defaults) } else { (c.chord, c.b.0.id, c.a.0.id, c.kind, c.in_defaults) })
|
||||
.collect();
|
||||
direct.sort();
|
||||
filtered.sort();
|
||||
assert_eq!(direct, filtered, "{id}");
|
||||
}
|
||||
|
||||
let strict = Keymap::new(km.catalogue().to_vec(), scopes().with_panel_fallthrough(false), Platform::Mac);
|
||||
assert!(!strict.conflicts().iter().any(|c| c.chord.to_string() == "Cmd+R"));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user