Refactor editor scrolling and implement scroll commands from vim mode
This commit is contained in:
parent
1920de81d9
commit
cffb064c16
29 changed files with 1244 additions and 683 deletions
|
@ -594,6 +594,9 @@ type ReleaseObservationCallback = Box<dyn FnOnce(&dyn Any, &mut MutableAppContex
|
|||
type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>;
|
||||
type WindowActivationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
|
||||
type WindowFullscreenCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
|
||||
type KeystrokeCallback = Box<
|
||||
dyn FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut MutableAppContext) -> bool,
|
||||
>;
|
||||
type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
|
||||
type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
|
||||
|
||||
|
@ -619,6 +622,7 @@ pub struct MutableAppContext {
|
|||
observations: CallbackCollection<usize, ObservationCallback>,
|
||||
window_activation_observations: CallbackCollection<usize, WindowActivationCallback>,
|
||||
window_fullscreen_observations: CallbackCollection<usize, WindowFullscreenCallback>,
|
||||
keystroke_observations: CallbackCollection<usize, KeystrokeCallback>,
|
||||
|
||||
release_observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>,
|
||||
action_dispatch_observations: Arc<Mutex<BTreeMap<usize, ActionObservationCallback>>>,
|
||||
|
@ -678,6 +682,7 @@ impl MutableAppContext {
|
|||
global_observations: Default::default(),
|
||||
window_activation_observations: Default::default(),
|
||||
window_fullscreen_observations: Default::default(),
|
||||
keystroke_observations: Default::default(),
|
||||
action_dispatch_observations: Default::default(),
|
||||
presenters_and_platform_windows: Default::default(),
|
||||
foreground,
|
||||
|
@ -763,11 +768,11 @@ impl MutableAppContext {
|
|||
.with_context(|| format!("invalid data for action {}", name))
|
||||
}
|
||||
|
||||
pub fn add_action<A, V, F>(&mut self, handler: F)
|
||||
pub fn add_action<A, V, F, R>(&mut self, handler: F)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
|
||||
{
|
||||
self.add_action_internal(handler, false)
|
||||
}
|
||||
|
@ -781,11 +786,11 @@ impl MutableAppContext {
|
|||
self.add_action_internal(handler, true)
|
||||
}
|
||||
|
||||
fn add_action_internal<A, V, F>(&mut self, mut handler: F, capture: bool)
|
||||
fn add_action_internal<A, V, F, R>(&mut self, mut handler: F, capture: bool)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
|
||||
{
|
||||
let handler = Box::new(
|
||||
move |view: &mut dyn AnyView,
|
||||
|
@ -1255,6 +1260,27 @@ impl MutableAppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn observe_keystrokes<F>(&mut self, window_id: usize, callback: F) -> Subscription
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(
|
||||
&Keystroke,
|
||||
&MatchResult,
|
||||
Option<&Box<dyn Action>>,
|
||||
&mut MutableAppContext,
|
||||
) -> bool,
|
||||
{
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.keystroke_observations
|
||||
.add_callback(window_id, subscription_id, Box::new(callback));
|
||||
|
||||
Subscription::KeystrokeObservation {
|
||||
id: subscription_id,
|
||||
window_id,
|
||||
observations: Some(self.keystroke_observations.downgrade()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut MutableAppContext)) {
|
||||
self.pending_effects.push_back(Effect::Deferred {
|
||||
callback: Box::new(callback),
|
||||
|
@ -1538,27 +1564,39 @@ impl MutableAppContext {
|
|||
})
|
||||
.collect();
|
||||
|
||||
match self
|
||||
let match_result = self
|
||||
.keystroke_matcher
|
||||
.push_keystroke(keystroke.clone(), dispatch_path)
|
||||
{
|
||||
.push_keystroke(keystroke.clone(), dispatch_path);
|
||||
let mut handled_by = None;
|
||||
|
||||
let keystroke_handled = match &match_result {
|
||||
MatchResult::None => false,
|
||||
MatchResult::Pending => true,
|
||||
MatchResult::Matches(matches) => {
|
||||
for (view_id, action) in matches {
|
||||
if self.handle_dispatch_action_from_effect(
|
||||
window_id,
|
||||
Some(view_id),
|
||||
Some(*view_id),
|
||||
action.as_ref(),
|
||||
) {
|
||||
self.keystroke_matcher.clear_pending();
|
||||
return true;
|
||||
handled_by = Some(action.boxed_clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
false
|
||||
handled_by.is_some()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.keystroke(
|
||||
window_id,
|
||||
keystroke.clone(),
|
||||
handled_by,
|
||||
match_result.clone(),
|
||||
);
|
||||
keystroke_handled
|
||||
} else {
|
||||
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -2110,6 +2148,12 @@ impl MutableAppContext {
|
|||
} => {
|
||||
self.handle_window_should_close_subscription_effect(window_id, callback)
|
||||
}
|
||||
Effect::Keystroke {
|
||||
window_id,
|
||||
keystroke,
|
||||
handled_by,
|
||||
result,
|
||||
} => self.handle_keystroke_effect(window_id, keystroke, handled_by, result),
|
||||
}
|
||||
self.pending_notifications.clear();
|
||||
self.remove_dropped_entities();
|
||||
|
@ -2188,6 +2232,21 @@ impl MutableAppContext {
|
|||
});
|
||||
}
|
||||
|
||||
fn keystroke(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
keystroke: Keystroke,
|
||||
handled_by: Option<Box<dyn Action>>,
|
||||
result: MatchResult,
|
||||
) {
|
||||
self.pending_effects.push_back(Effect::Keystroke {
|
||||
window_id,
|
||||
keystroke,
|
||||
handled_by,
|
||||
result,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn refresh_windows(&mut self) {
|
||||
self.pending_effects.push_back(Effect::RefreshWindows);
|
||||
}
|
||||
|
@ -2299,6 +2358,21 @@ impl MutableAppContext {
|
|||
});
|
||||
}
|
||||
|
||||
fn handle_keystroke_effect(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
keystroke: Keystroke,
|
||||
handled_by: Option<Box<dyn Action>>,
|
||||
result: MatchResult,
|
||||
) {
|
||||
self.update(|this| {
|
||||
let mut observations = this.keystroke_observations.clone();
|
||||
observations.emit_and_cleanup(window_id, this, {
|
||||
move |callback, this| callback(&keystroke, &result, handled_by.as_ref(), this)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_window_activation_effect(&mut self, window_id: usize, active: bool) {
|
||||
//Short circuit evaluation if we're already g2g
|
||||
if self
|
||||
|
@ -2852,6 +2926,12 @@ pub enum Effect {
|
|||
subscription_id: usize,
|
||||
callback: WindowFullscreenCallback,
|
||||
},
|
||||
Keystroke {
|
||||
window_id: usize,
|
||||
keystroke: Keystroke,
|
||||
handled_by: Option<Box<dyn Action>>,
|
||||
result: MatchResult,
|
||||
},
|
||||
RefreshWindows,
|
||||
DispatchActionFrom {
|
||||
window_id: usize,
|
||||
|
@ -2995,6 +3075,21 @@ impl Debug for Effect {
|
|||
.debug_struct("Effect::WindowShouldCloseSubscription")
|
||||
.field("window_id", window_id)
|
||||
.finish(),
|
||||
Effect::Keystroke {
|
||||
window_id,
|
||||
keystroke,
|
||||
handled_by,
|
||||
result,
|
||||
} => f
|
||||
.debug_struct("Effect::Keystroke")
|
||||
.field("window_id", window_id)
|
||||
.field("keystroke", keystroke)
|
||||
.field(
|
||||
"keystroke",
|
||||
&handled_by.as_ref().map(|handled_by| handled_by.name()),
|
||||
)
|
||||
.field("result", result)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3826,6 +3921,33 @@ impl<'a, T: View> ViewContext<'a, T> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn observe_keystroke<F>(&mut self, mut callback: F) -> Subscription
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(
|
||||
&mut T,
|
||||
&Keystroke,
|
||||
Option<&Box<dyn Action>>,
|
||||
&MatchResult,
|
||||
&mut ViewContext<T>,
|
||||
) -> bool,
|
||||
{
|
||||
let observer = self.weak_handle();
|
||||
self.app.observe_keystrokes(
|
||||
self.window_id(),
|
||||
move |keystroke, result, handled_by, cx| {
|
||||
if let Some(observer) = observer.upgrade(cx) {
|
||||
observer.update(cx, |observer, cx| {
|
||||
callback(observer, keystroke, handled_by, result, cx);
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn emit(&mut self, payload: T::Event) {
|
||||
self.app.pending_effects.push_back(Effect::Event {
|
||||
entity_id: self.view_id,
|
||||
|
@ -5018,6 +5140,11 @@ pub enum Subscription {
|
|||
window_id: usize,
|
||||
observations: Option<Weak<Mapping<usize, WindowFullscreenCallback>>>,
|
||||
},
|
||||
KeystrokeObservation {
|
||||
id: usize,
|
||||
window_id: usize,
|
||||
observations: Option<Weak<Mapping<usize, KeystrokeCallback>>>,
|
||||
},
|
||||
|
||||
ReleaseObservation {
|
||||
id: usize,
|
||||
|
@ -5056,6 +5183,9 @@ impl Subscription {
|
|||
Subscription::ActionObservation { observations, .. } => {
|
||||
observations.take();
|
||||
}
|
||||
Subscription::KeystrokeObservation { observations, .. } => {
|
||||
observations.take();
|
||||
}
|
||||
Subscription::WindowActivationObservation { observations, .. } => {
|
||||
observations.take();
|
||||
}
|
||||
|
@ -5175,6 +5305,27 @@ impl Drop for Subscription {
|
|||
observations.lock().remove(id);
|
||||
}
|
||||
}
|
||||
Subscription::KeystrokeObservation {
|
||||
id,
|
||||
window_id,
|
||||
observations,
|
||||
} => {
|
||||
if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) {
|
||||
match observations
|
||||
.lock()
|
||||
.entry(*window_id)
|
||||
.or_default()
|
||||
.entry(*id)
|
||||
{
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(None);
|
||||
}
|
||||
btree_map::Entry::Occupied(entry) => {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Subscription::WindowActivationObservation {
|
||||
id,
|
||||
window_id,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue