feat: Add support for action modifier keys like ctrl,shift,alt.
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::hook::HookResponse;
|
||||
@@ -56,6 +58,64 @@ pub enum Action {
|
||||
StreamingDone,
|
||||
}
|
||||
|
||||
/// Modifier keys held during an action. Serializes as an
|
||||
/// array of strings (e.g. `["shift", "ctrl"]`). Empty
|
||||
/// modifiers serialize as `[]`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Modifiers {
|
||||
pub shift: bool,
|
||||
pub ctrl: bool,
|
||||
pub alt: bool,
|
||||
}
|
||||
|
||||
impl Modifiers {
|
||||
/// True when no modifiers are held.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
!self.shift && !self.ctrl && !self.alt
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Modifiers {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let count = self.shift as usize + self.ctrl as usize + self.alt as usize;
|
||||
let mut seq = serializer.serialize_seq(Some(count))?;
|
||||
if self.shift {
|
||||
seq.serialize_element("shift")?;
|
||||
}
|
||||
if self.ctrl {
|
||||
seq.serialize_element("ctrl")?;
|
||||
}
|
||||
if self.alt {
|
||||
seq.serialize_element("alt")?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Action`] paired with the modifier keys that were
|
||||
/// held when it was triggered. Frontends produce these,
|
||||
/// the menu engine consumes them.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ModifiedAction {
|
||||
pub action: Action,
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
impl ModifiedAction {
|
||||
/// Wrap an action with default (empty) modifiers.
|
||||
pub fn new(action: Action) -> Self {
|
||||
Self {
|
||||
action,
|
||||
modifiers: Modifiers::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap an action with specific modifiers.
|
||||
pub fn with_modifiers(action: Action, modifiers: Modifiers) -> Self {
|
||||
Self { action, modifiers }
|
||||
}
|
||||
}
|
||||
|
||||
/// Broadcast from the menu loop to all subscribers
|
||||
/// (frontends, tests).
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -125,10 +185,89 @@ pub enum MenuResult {
|
||||
Selected {
|
||||
items: Vec<(Value, usize)>,
|
||||
filter_text: String,
|
||||
modifiers: Modifiers,
|
||||
},
|
||||
Quicklist {
|
||||
items: Vec<(Value, usize)>,
|
||||
filter_text: String,
|
||||
modifiers: Modifiers,
|
||||
},
|
||||
Cancelled {
|
||||
modifiers: Modifiers,
|
||||
},
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn modifiers_empty_serializes_as_empty_array() {
|
||||
let mods = Modifiers::default();
|
||||
let json = serde_json::to_value(&mods).unwrap_or_default();
|
||||
assert_eq!(json, json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers_single_serializes() {
|
||||
let mods = Modifiers {
|
||||
shift: true,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
};
|
||||
let json = serde_json::to_value(&mods).unwrap_or_default();
|
||||
assert_eq!(json, json!(["shift"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers_multiple_serializes() {
|
||||
let mods = Modifiers {
|
||||
shift: true,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
};
|
||||
let json = serde_json::to_value(&mods).unwrap_or_default();
|
||||
assert_eq!(json, json!(["shift", "ctrl"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers_all_serializes() {
|
||||
let mods = Modifiers {
|
||||
shift: true,
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
};
|
||||
let json = serde_json::to_value(&mods).unwrap_or_default();
|
||||
assert_eq!(json, json!(["shift", "ctrl", "alt"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers_is_empty() {
|
||||
assert!(Modifiers::default().is_empty());
|
||||
assert!(!Modifiers {
|
||||
shift: true,
|
||||
ctrl: false,
|
||||
alt: false
|
||||
}
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modified_action_new_has_empty_modifiers() {
|
||||
let ma = ModifiedAction::new(Action::Confirm);
|
||||
assert!(ma.modifiers.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modified_action_with_modifiers() {
|
||||
let mods = Modifiers {
|
||||
shift: true,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
};
|
||||
let ma = ModifiedAction::with_modifiers(Action::Confirm, mods);
|
||||
assert!(ma.modifiers.shift);
|
||||
assert!(!ma.modifiers.ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user