Checkpoint
This commit is contained in:
+31
-25
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -1388,6 +1388,10 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
|
||||
context: DispatchContext,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if context.is_empty() {
|
||||
return f(self);
|
||||
}
|
||||
|
||||
if !self.window.freeze_key_dispatch_stack {
|
||||
self.window
|
||||
.key_dispatch_stack
|
||||
|
||||
Reference in New Issue
Block a user