Skip to content

Commit

Permalink
Support multiple modifier keys
Browse files Browse the repository at this point in the history
Alfred supports combinations of keys as modifiers. This adds support
for `ctrl+shift` and similar combinations.
  • Loading branch information
rossmacarthur committed Dec 31, 2023
1 parent 887ae3a commit ac9ae7a
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
1 change: 0 additions & 1 deletion crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ fn init(manifest_dir: &Path, name: Option<OsString>) -> Result<()> {

// Add workflow/<binary> to the gitignore file (if it exists)
if let Ok(mut file) = fs::OpenOptions::new()
.write(true)
.append(true)
.open(manifest_dir.join(".gitignore"))
{
Expand Down
5 changes: 4 additions & 1 deletion examples/hello-alfred/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ fn main() -> Result<(), Box<dyn Error>> {
.large_type_text("this text will be displayed with ⌘L")
.modifier(Modifier::new(Key::Command).subtitle("⌘ changes the subtitle"))
.modifier(Modifier::new(Key::Option).arg("/path/to/modified.jpg"))
.modifier(Modifier::new(Key::Control).icon(Icon::with_image("/path/to/file.png")))
.modifier(
Modifier::new_multi([Key::Control, Key::Shift])
.icon(Icon::with_image("/path/to/file.png")),
)
.modifier(Modifier::new(Key::Shift).valid(false))
.quicklook_url("https://example.com")
.action(value!({
Expand Down
51 changes: 48 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ pub enum Key {
Function,
}

/// A keyboard modifier combination.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[serde(untagged)]
enum Keys {
One(Key),
#[serde(serialize_with = "serialize_many_keys")]
Many(Vec<Key>),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum IconInner {
/// Load an image from a path.
Expand Down Expand Up @@ -146,7 +155,7 @@ struct Data {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Modifier {
/// The modifier key.
key: Key,
key: Keys,

/// The modifier data.
data: Data,
Expand Down Expand Up @@ -192,7 +201,7 @@ pub struct Item {

/// Control how the modifier keys react.
#[serde(rename = "mods", skip_serializing_if = "HashMap::is_empty")]
modifiers: HashMap<Key, Data>,
modifiers: HashMap<Keys, Data>,

/// Defines the copied or large type text for this item.
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -228,6 +237,26 @@ pub struct Output {
// Implementations
////////////////////////////////////////////////////////////////////////////////

fn serialize_many_keys<S>(duration: &[Key], s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = String::with_capacity(5 * duration.len());
for (i, key) in duration.iter().enumerate() {
if i != 0 {
out.push('+');
}
out.push_str(match key {
Key::Command => "cmd",
Key::Option => "alt",
Key::Control => "ctrl",
Key::Shift => "shift",
Key::Function => "fn",
});
}
s.serialize_str(&out)
}

impl Serialize for Icon {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match &self.0 {
Expand Down Expand Up @@ -322,7 +351,23 @@ impl Modifier {
#[must_use]
pub fn new(key: Key) -> Self {
Self {
key,
key: Keys::One(key),
data: Data::default(),
}
}

/// Create a new modifier with multiple keys.
///
/// # Examples
///
/// ```
/// # use powerpack::{Key, Modifier};
/// let m = Modifier::multi([Key::Command, Key::Option]);
/// ```
#[must_use]
pub fn new_multi(keys: impl IntoIterator<Item = Key>) -> Self {
Self {
key: Keys::Many(keys.into_iter().collect()),
data: Data::default(),
}
}
Expand Down
5 changes: 4 additions & 1 deletion tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ fn all() {
.large_type_text("this text will be displayed with ⌘L")
.modifier(Modifier::new(Key::Command).subtitle("⌘ changes the subtitle"))
.modifier(Modifier::new(Key::Option).arg("/path/to/modified.jpg"))
.modifier(Modifier::new(Key::Control).icon(Icon::with_image("/path/to/file.png")))
.modifier(
Modifier::new_multi([Key::Control, Key::Shift])
.icon(Icon::with_image("/path/to/file.png")),
)
.modifier(Modifier::new(Key::Shift).valid(false))
.quicklook_url("https://example.com")
.action(value!({
Expand Down
8 changes: 4 additions & 4 deletions tests/testdata/all.golden
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
"autocomplete": "to this",
"type": "file:skipcheck",
"mods": {
"cmd": {
"subtitle": "⌘ changes the subtitle"
},
"alt": {
"arg": "/path/to/modified.jpg"
},
"ctrl": {
"ctrl+shift": {
"icon": {
"path": "/path/to/file.png"
}
},
"shift": {
"valid": false
},
"cmd": {
"subtitle": "⌘ changes the subtitle"
}
},
"text": {
Expand Down

0 comments on commit ac9ae7a

Please sign in to comment.