@@ -23,6 +23,10 @@ impl DispatchContext {
}
}
+ pub fn is_empty(&self) -> bool {
+ self.set.is_empty() && self.map.is_empty()
+ }
+
pub fn clear(&mut self) {
self.set.clear();
self.map.clear();
@@ -47,17 +51,17 @@ impl DispatchContext {
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub enum ActionContextPredicate {
+pub enum DispatchContextPredicate {
Identifier(SharedString),
Equal(SharedString, SharedString),
NotEqual(SharedString, SharedString),
- Child(Box<ActionContextPredicate>, Box<ActionContextPredicate>),
- Not(Box<ActionContextPredicate>),
- And(Box<ActionContextPredicate>, Box<ActionContextPredicate>),
- Or(Box<ActionContextPredicate>, Box<ActionContextPredicate>),
+ Child(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
+ Not(Box<DispatchContextPredicate>),
+ And(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
+ Or(Box<DispatchContextPredicate>, Box<DispatchContextPredicate>),
}
-impl ActionContextPredicate {
+impl DispatchContextPredicate {
pub fn parse(source: &str) -> Result<Self> {
let source = Self::skip_whitespace(source);
let (predicate, rest) = Self::parse_expr(source, 0)?;
@@ -92,8 +96,10 @@ impl ActionContextPredicate {
}
fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
- type Op =
- fn(ActionContextPredicate, ActionContextPredicate) -> Result<ActionContextPredicate>;
+ type Op = fn(
+ DispatchContextPredicate,
+ DispatchContextPredicate,
+ ) -> Result<DispatchContextPredicate>;
let (mut predicate, rest) = Self::parse_primary(source)?;
source = rest;
@@ -139,7 +145,7 @@ impl ActionContextPredicate {
'!' => {
let source = Self::skip_whitespace(&source[1..]);
let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
- Ok((ActionContextPredicate::Not(Box::new(predicate)), source))
+ Ok((DispatchContextPredicate::Not(Box::new(predicate)), source))
}
_ if next.is_alphanumeric() || next == '_' => {
let len = source
@@ -148,7 +154,7 @@ impl ActionContextPredicate {
let (identifier, rest) = source.split_at(len);
source = Self::skip_whitespace(rest);
Ok((
- ActionContextPredicate::Identifier(identifier.to_string().into()),
+ DispatchContextPredicate::Identifier(identifier.to_string().into()),
source,
))
}
@@ -200,17 +206,17 @@ const PRECEDENCE_NOT: u32 = 5;
#[cfg(test)]
mod tests {
- use super::ActionContextPredicate::{self, *};
+ use super::DispatchContextPredicate::{self, *};
#[test]
fn test_parse_identifiers() {
// Identifiers
assert_eq!(
- ActionContextPredicate::parse("abc12").unwrap(),
+ DispatchContextPredicate::parse("abc12").unwrap(),
Identifier("abc12".into())
);
assert_eq!(
- ActionContextPredicate::parse("_1a").unwrap(),
+ DispatchContextPredicate::parse("_1a").unwrap(),
Identifier("_1a".into())
);
}
@@ -218,11 +224,11 @@ mod tests {
#[test]
fn test_parse_negations() {
assert_eq!(
- ActionContextPredicate::parse("!abc").unwrap(),
+ DispatchContextPredicate::parse("!abc").unwrap(),
Not(Box::new(Identifier("abc".into())))
);
assert_eq!(
- ActionContextPredicate::parse(" ! ! abc").unwrap(),
+ DispatchContextPredicate::parse(" ! ! abc").unwrap(),
Not(Box::new(Not(Box::new(Identifier("abc".into())))))
);
}
@@ -230,15 +236,15 @@ mod tests {
#[test]
fn test_parse_equality_operators() {
assert_eq!(
- ActionContextPredicate::parse("a == b").unwrap(),
+ DispatchContextPredicate::parse("a == b").unwrap(),
Equal("a".into(), "b".into())
);
assert_eq!(
- ActionContextPredicate::parse("c!=d").unwrap(),
+ DispatchContextPredicate::parse("c!=d").unwrap(),
NotEqual("c".into(), "d".into())
);
assert_eq!(
- ActionContextPredicate::parse("c == !d")
+ DispatchContextPredicate::parse("c == !d")
.unwrap_err()
.to_string(),
"operands must be identifiers"
@@ -248,14 +254,14 @@ mod tests {
#[test]
fn test_parse_boolean_operators() {
assert_eq!(
- ActionContextPredicate::parse("a || b").unwrap(),
+ DispatchContextPredicate::parse("a || b").unwrap(),
Or(
Box::new(Identifier("a".into())),
Box::new(Identifier("b".into()))
)
);
assert_eq!(
- ActionContextPredicate::parse("a || !b && c").unwrap(),
+ DispatchContextPredicate::parse("a || !b && c").unwrap(),
Or(
Box::new(Identifier("a".into())),
Box::new(And(
@@ -265,7 +271,7 @@ mod tests {
)
);
assert_eq!(
- ActionContextPredicate::parse("a && b || c&&d").unwrap(),
+ DispatchContextPredicate::parse("a && b || c&&d").unwrap(),
Or(
Box::new(And(
Box::new(Identifier("a".into())),
@@ -278,7 +284,7 @@ mod tests {
)
);
assert_eq!(
- ActionContextPredicate::parse("a == b && c || d == e && f").unwrap(),
+ DispatchContextPredicate::parse("a == b && c || d == e && f").unwrap(),
Or(
Box::new(And(
Box::new(Equal("a".into(), "b".into())),
@@ -291,7 +297,7 @@ mod tests {
)
);
assert_eq!(
- ActionContextPredicate::parse("a && b && c && d").unwrap(),
+ DispatchContextPredicate::parse("a && b && c && d").unwrap(),
And(
Box::new(And(
Box::new(And(
@@ -308,7 +314,7 @@ mod tests {
#[test]
fn test_parse_parenthesized_expressions() {
assert_eq!(
- ActionContextPredicate::parse("a && (b == c || d != e)").unwrap(),
+ DispatchContextPredicate::parse("a && (b == c || d != e)").unwrap(),
And(
Box::new(Identifier("a".into())),
Box::new(Or(
@@ -318,7 +324,7 @@ mod tests {
),
);
assert_eq!(
- ActionContextPredicate::parse(" ( a || b ) ").unwrap(),
+ DispatchContextPredicate::parse(" ( a || b ) ").unwrap(),
Or(
Box::new(Identifier("a".into())),
Box::new(Identifier("b".into())),
@@ -176,6 +176,14 @@ pub trait StatelessInteractive: Element {
self
}
+ fn context(mut self, context: impl Into<DispatchContext>) -> Self
+ where
+ Self: Sized,
+ {
+ self.stateless_interactivity().dispatch_context = context.into();
+ self
+ }
+
fn on_action<A: 'static>(
mut self,
listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
@@ -320,7 +328,10 @@ pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
result
})
} else {
- cx.with_key_listeners(&self.as_stateless().key_listeners, f)
+ let stateless = self.as_stateless();
+ cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
+ cx.with_key_listeners(&stateless.key_listeners, f)
+ })
}
}
@@ -519,6 +530,7 @@ where
}
pub struct StatelessInteraction<V> {
+ pub dispatch_context: DispatchContext,
pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
@@ -583,6 +595,7 @@ pub struct InteractiveElementState {
impl<V> Default for StatelessInteraction<V> {
fn default() -> Self {
Self {
+ dispatch_context: DispatchContext::new(),
mouse_down_listeners: SmallVec::new(),
mouse_up_listeners: SmallVec::new(),
mouse_move_listeners: SmallVec::new(),
@@ -1,11 +1,11 @@
-use crate::{Action, ActionContextPredicate, DispatchContext, KeyMatch, Keystroke};
+use crate::{Action, DispatchContext, DispatchContextPredicate, KeyMatch, Keystroke};
use anyhow::Result;
use smallvec::SmallVec;
pub struct KeyBinding {
action: Box<dyn Action>,
pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
- pub(super) context_predicate: Option<ActionContextPredicate>,
+ pub(super) context_predicate: Option<DispatchContextPredicate>,
}
impl KeyBinding {
@@ -15,7 +15,7 @@ impl KeyBinding {
pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
let context = if let Some(context) = context {
- Some(ActionContextPredicate::parse(context)?)
+ Some(DispatchContextPredicate::parse(context)?)
} else {
None
};
@@ -1,4 +1,4 @@
-use crate::{ActionContextPredicate, KeyBinding, Keystroke};
+use crate::{DispatchContextPredicate, KeyBinding, Keystroke};
use collections::HashSet;
use smallvec::SmallVec;
use std::{any::TypeId, collections::HashMap};
@@ -10,7 +10,8 @@ pub struct KeymapVersion(usize);
pub struct Keymap {
bindings: Vec<KeyBinding>,
binding_indices_by_action_id: HashMap<TypeId, SmallVec<[usize; 3]>>,
- disabled_keystrokes: HashMap<SmallVec<[Keystroke; 2]>, HashSet<Option<ActionContextPredicate>>>,
+ disabled_keystrokes:
+ HashMap<SmallVec<[Keystroke; 2]>, HashSet<Option<DispatchContextPredicate>>>,
version: KeymapVersion,
}