Checkpoint
This commit is contained in:
@@ -10,14 +10,14 @@ pub trait Action: Any + Send + Sync {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct ActionContext {
|
||||
pub struct DispatchContext {
|
||||
set: HashSet<SharedString>,
|
||||
map: HashMap<SharedString, SharedString>,
|
||||
}
|
||||
|
||||
impl ActionContext {
|
||||
impl DispatchContext {
|
||||
pub fn new() -> Self {
|
||||
ActionContext {
|
||||
DispatchContext {
|
||||
set: HashSet::default(),
|
||||
map: HashMap::default(),
|
||||
}
|
||||
@@ -68,7 +68,7 @@ impl ActionContextPredicate {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(&self, contexts: &[ActionContext]) -> bool {
|
||||
pub fn eval(&self, contexts: &[&DispatchContext]) -> bool {
|
||||
let Some(context) = contexts.first() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -435,11 +435,11 @@ where
|
||||
if let Some(global_id) = global_id {
|
||||
key_listeners.push((
|
||||
TypeId::of::<KeyDownEvent>(),
|
||||
Arc::new(move |_, key_down, phase, cx| {
|
||||
Arc::new(move |_, key_down, context, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble {
|
||||
let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
|
||||
if let KeyMatch::Some(action) =
|
||||
cx.match_keystroke(&global_id, &key_down.keystroke)
|
||||
cx.match_keystroke(&global_id, &key_down.keystroke, context)
|
||||
{
|
||||
return Some(action);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
point, Action, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point,
|
||||
ViewContext,
|
||||
point, Action, Bounds, DispatchContext, DispatchPhase, FocusHandle, Keystroke, Modifiers,
|
||||
Pixels, Point, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
@@ -256,7 +256,13 @@ pub type ScrollWheelListener<V> = Arc<
|
||||
>;
|
||||
|
||||
pub type KeyListener<V> = Arc<
|
||||
dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) -> Option<Box<dyn Action>>
|
||||
dyn Fn(
|
||||
&mut V,
|
||||
&dyn Any,
|
||||
&[&DispatchContext],
|
||||
DispatchPhase,
|
||||
&mut ViewContext<V>,
|
||||
) -> Option<Box<dyn Action>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
|
||||
@@ -160,7 +160,7 @@ pub trait Interactive: Element {
|
||||
{
|
||||
self.listeners().key.push((
|
||||
TypeId::of::<KeyDownEvent>(),
|
||||
Arc::new(move |view, event, phase, cx| {
|
||||
Arc::new(move |view, event, _, phase, cx| {
|
||||
let event = event.downcast_ref().unwrap();
|
||||
listener(view, event, phase, cx);
|
||||
None
|
||||
@@ -181,7 +181,7 @@ pub trait Interactive: Element {
|
||||
{
|
||||
self.listeners().key.push((
|
||||
TypeId::of::<KeyUpEvent>(),
|
||||
Arc::new(move |view, event, phase, cx| {
|
||||
Arc::new(move |view, event, _, phase, cx| {
|
||||
let event = event.downcast_ref().unwrap();
|
||||
listener(view, event, phase, cx);
|
||||
None
|
||||
@@ -202,7 +202,7 @@ pub trait Interactive: Element {
|
||||
{
|
||||
self.listeners().key.push((
|
||||
TypeId::of::<A>(),
|
||||
Arc::new(move |view, event, phase, cx| {
|
||||
Arc::new(move |view, event, _, phase, cx| {
|
||||
let event = event.downcast_ref().unwrap();
|
||||
listener(view, event, phase, cx);
|
||||
None
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Action, ActionContext, ActionContextPredicate, KeyMatch, Keystroke};
|
||||
use crate::{Action, ActionContextPredicate, DispatchContext, KeyMatch, Keystroke};
|
||||
use anyhow::Result;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@@ -32,7 +32,7 @@ impl KeyBinding {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn matches_context(&self, contexts: &[ActionContext]) -> bool {
|
||||
pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool {
|
||||
self.context_predicate
|
||||
.as_ref()
|
||||
.map(|predicate| predicate.eval(contexts))
|
||||
@@ -42,7 +42,7 @@ impl KeyBinding {
|
||||
pub fn match_keystrokes(
|
||||
&self,
|
||||
pending_keystrokes: &[Keystroke],
|
||||
contexts: &[ActionContext],
|
||||
contexts: &[&DispatchContext],
|
||||
) -> KeyMatch {
|
||||
if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
|
||||
&& self.matches_context(contexts)
|
||||
@@ -61,7 +61,7 @@ impl KeyBinding {
|
||||
pub fn keystrokes_for_action(
|
||||
&self,
|
||||
action: &dyn Action,
|
||||
contexts: &[ActionContext],
|
||||
contexts: &[&DispatchContext],
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
if self.action.eq(action) && self.matches_context(contexts) {
|
||||
Some(self.keystrokes.clone())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Action, ActionContext, Keymap, KeymapVersion, Keystroke};
|
||||
use crate::{Action, DispatchContext, Keymap, KeymapVersion, Keystroke};
|
||||
use parking_lot::RwLock;
|
||||
use smallvec::SmallVec;
|
||||
use std::sync::Arc;
|
||||
@@ -44,7 +44,7 @@ impl KeyMatcher {
|
||||
pub fn match_keystroke(
|
||||
&mut self,
|
||||
keystroke: &Keystroke,
|
||||
context_stack: &[ActionContext],
|
||||
context_stack: &[&DispatchContext],
|
||||
) -> KeyMatch {
|
||||
let keymap = self.keymap.read();
|
||||
// Clear pending keystrokes if the keymap has changed since the last matched keystroke.
|
||||
@@ -86,7 +86,7 @@ impl KeyMatcher {
|
||||
pub fn keystrokes_for_action(
|
||||
&self,
|
||||
action: &dyn Action,
|
||||
contexts: &[ActionContext],
|
||||
contexts: &[&DispatchContext],
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
self.keymap
|
||||
.read()
|
||||
|
||||
+143
-52
@@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
||||
BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect,
|
||||
Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla,
|
||||
ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
|
||||
MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform,
|
||||
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
|
||||
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
|
||||
Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
|
||||
WindowOptions, SUBPIXEL_VARIANTS,
|
||||
BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext,
|
||||
DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId,
|
||||
GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher,
|
||||
Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path,
|
||||
Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference,
|
||||
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow,
|
||||
SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
|
||||
WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
@@ -47,7 +47,12 @@ pub enum DispatchPhase {
|
||||
|
||||
type AnyListener = Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
|
||||
type AnyKeyListener = Arc<
|
||||
dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) -> Option<Box<dyn Action>>
|
||||
dyn Fn(
|
||||
&dyn Any,
|
||||
&[&DispatchContext],
|
||||
DispatchPhase,
|
||||
&mut WindowContext,
|
||||
) -> Option<Box<dyn Action>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
@@ -155,8 +160,8 @@ pub struct Window {
|
||||
z_index_stack: StackingOrder,
|
||||
content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
|
||||
key_listeners: Vec<(TypeId, AnyKeyListener)>,
|
||||
key_events_enabled: bool,
|
||||
key_dispatch_stack: Vec<KeyDispatchStackFrame>,
|
||||
freeze_key_dispatch_stack: bool,
|
||||
focus_stack: Vec<FocusId>,
|
||||
focus_parents_by_child: HashMap<FocusId, FocusId>,
|
||||
pub(crate) focus_listeners: Vec<AnyFocusListener>,
|
||||
@@ -230,24 +235,32 @@ impl Window {
|
||||
z_index_stack: StackingOrder(SmallVec::new()),
|
||||
content_mask_stack: Vec::new(),
|
||||
mouse_listeners: HashMap::default(),
|
||||
key_listeners: Vec::new(),
|
||||
key_events_enabled: true,
|
||||
key_dispatch_stack: Vec::new(),
|
||||
freeze_key_dispatch_stack: false,
|
||||
focus_stack: Vec::new(),
|
||||
focus_parents_by_child: HashMap::default(),
|
||||
focus_listeners: Vec::new(),
|
||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||
propagate: true,
|
||||
default_prevented: true,
|
||||
mouse_position,
|
||||
scale_factor,
|
||||
scene_builder: SceneBuilder::new(),
|
||||
dirty: true,
|
||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||
last_blur: None,
|
||||
focus: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum KeyDispatchStackFrame {
|
||||
Listener {
|
||||
event_type: TypeId,
|
||||
listener: AnyKeyListener,
|
||||
},
|
||||
Context(DispatchContext),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub struct ContentMask<P: Clone + Default + Debug> {
|
||||
@@ -833,9 +846,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
// Clear focus state, because we determine what is focused when the new elements
|
||||
// in the upcoming frame are initialized.
|
||||
window.focus_listeners.clear();
|
||||
window.key_listeners.clear();
|
||||
window.key_dispatch_stack.clear();
|
||||
window.focus_parents_by_child.clear();
|
||||
window.key_events_enabled = true;
|
||||
window.freeze_key_dispatch_stack = false;
|
||||
}
|
||||
|
||||
fn dispatch_event(&mut self, event: InputEvent) -> bool {
|
||||
@@ -888,36 +901,67 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
.insert(any_mouse_event.type_id(), handlers);
|
||||
}
|
||||
} else if let Some(any_key_event) = event.keyboard_event() {
|
||||
let key_listeners = mem::take(&mut self.window.key_listeners);
|
||||
let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack);
|
||||
let key_event_type = any_key_event.type_id();
|
||||
let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new();
|
||||
|
||||
for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate() {
|
||||
if key_event_type == *listener_event_type {
|
||||
if let Some(action) = listener(any_key_event, DispatchPhase::Capture, self) {
|
||||
self.dispatch_action(action, &key_listeners[..ix]);
|
||||
for (ix, frame) in key_dispatch_stack.iter().enumerate() {
|
||||
match frame {
|
||||
KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener,
|
||||
} => {
|
||||
if key_event_type == *event_type {
|
||||
if let Some(action) = listener(
|
||||
any_key_event,
|
||||
&context_stack,
|
||||
DispatchPhase::Capture,
|
||||
self,
|
||||
) {
|
||||
self.dispatch_action(action, &key_dispatch_stack[..ix]);
|
||||
}
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
KeyDispatchStackFrame::Context(context) => {
|
||||
context_stack.push(&context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.window.propagate {
|
||||
for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate().rev()
|
||||
{
|
||||
if key_event_type == *listener_event_type {
|
||||
if let Some(action) = listener(any_key_event, DispatchPhase::Bubble, self) {
|
||||
self.dispatch_action(action, &key_listeners[..ix]);
|
||||
}
|
||||
for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() {
|
||||
match frame {
|
||||
KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener,
|
||||
} => {
|
||||
if key_event_type == *event_type {
|
||||
if let Some(action) = listener(
|
||||
any_key_event,
|
||||
&context_stack,
|
||||
DispatchPhase::Bubble,
|
||||
self,
|
||||
) {
|
||||
self.dispatch_action(action, &key_dispatch_stack[..ix]);
|
||||
}
|
||||
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyDispatchStackFrame::Context(_) => {
|
||||
context_stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.window.key_listeners = key_listeners;
|
||||
drop(context_stack);
|
||||
self.window.key_dispatch_stack = key_dispatch_stack;
|
||||
}
|
||||
|
||||
true
|
||||
@@ -927,13 +971,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
&mut self,
|
||||
element_id: &GlobalElementId,
|
||||
keystroke: &Keystroke,
|
||||
context_stack: &[&DispatchContext],
|
||||
) -> KeyMatch {
|
||||
let key_match = self
|
||||
.window
|
||||
.key_matchers
|
||||
.get_mut(element_id)
|
||||
.unwrap()
|
||||
.match_keystroke(keystroke, &[]);
|
||||
.match_keystroke(keystroke, context_stack);
|
||||
|
||||
if key_match.is_some() {
|
||||
for matcher in self.window.key_matchers.values_mut() {
|
||||
@@ -944,23 +989,39 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
key_match
|
||||
}
|
||||
|
||||
fn dispatch_action(&mut self, action: Box<dyn Action>, listeners: &[(TypeId, AnyKeyListener)]) {
|
||||
fn dispatch_action(
|
||||
&mut self,
|
||||
action: Box<dyn Action>,
|
||||
dispatch_stack: &[KeyDispatchStackFrame],
|
||||
) {
|
||||
let action_type = action.as_any().type_id();
|
||||
for (event_type, listener) in listeners {
|
||||
if action_type == *event_type {
|
||||
listener(action.as_any(), DispatchPhase::Capture, self);
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
for stack_frame in dispatch_stack {
|
||||
if let KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener,
|
||||
} = stack_frame
|
||||
{
|
||||
if action_type == *event_type {
|
||||
listener(action.as_any(), &[], DispatchPhase::Capture, self);
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.window.propagate {
|
||||
for (event_type, listener) in listeners.iter().rev() {
|
||||
if action_type == *event_type {
|
||||
listener(action.as_any(), DispatchPhase::Bubble, self);
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
for stack_frame in dispatch_stack.iter().rev() {
|
||||
if let KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener,
|
||||
} = stack_frame
|
||||
{
|
||||
if action_type == *event_type {
|
||||
listener(action.as_any(), &[], DispatchPhase::Bubble, self);
|
||||
if !self.window.propagate {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1287,26 +1348,56 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
|
||||
key_listeners: &[(TypeId, KeyListener<V>)],
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if self.window.key_events_enabled {
|
||||
if !self.window.freeze_key_dispatch_stack {
|
||||
for (event_type, listener) in key_listeners.iter().cloned() {
|
||||
let handle = self.handle();
|
||||
let listener = Arc::new(
|
||||
move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_, '_>| {
|
||||
move |event: &dyn Any,
|
||||
context_stack: &[&DispatchContext],
|
||||
phase: DispatchPhase,
|
||||
cx: &mut WindowContext<'_, '_>| {
|
||||
handle
|
||||
.update(cx, |view, cx| listener(view, event, phase, cx))
|
||||
.update(cx, |view, cx| {
|
||||
listener(view, event, context_stack, phase, cx)
|
||||
})
|
||||
.log_err()
|
||||
.flatten()
|
||||
},
|
||||
);
|
||||
self.window.key_listeners.push((event_type, listener));
|
||||
self.window
|
||||
.key_dispatch_stack
|
||||
.push(KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let result = f(self);
|
||||
|
||||
if self.window.key_events_enabled {
|
||||
let prev_len = self.window.key_listeners.len() - key_listeners.len();
|
||||
self.window.key_listeners.truncate(prev_len);
|
||||
if !self.window.freeze_key_dispatch_stack {
|
||||
let prev_len = self.window.key_dispatch_stack.len() - key_listeners.len();
|
||||
self.window.key_dispatch_stack.truncate(prev_len);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn with_key_dispatch_context<R>(
|
||||
&mut self,
|
||||
context: DispatchContext,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
if !self.window.freeze_key_dispatch_stack {
|
||||
self.window
|
||||
.key_dispatch_stack
|
||||
.push(KeyDispatchStackFrame::Context(context));
|
||||
}
|
||||
|
||||
let result = f(self);
|
||||
|
||||
if !self.window.freeze_key_dispatch_stack {
|
||||
self.window.key_dispatch_stack.pop();
|
||||
}
|
||||
|
||||
result
|
||||
@@ -1325,7 +1416,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
|
||||
self.window.focus_stack.push(focus_handle.id);
|
||||
|
||||
if Some(focus_handle.id) == self.window.focus {
|
||||
self.window.key_events_enabled = false;
|
||||
self.window.freeze_key_dispatch_stack = true;
|
||||
}
|
||||
|
||||
let result = f(self);
|
||||
|
||||
Reference in New Issue
Block a user