Fix blinking behavior in editor when receiving/losing focus
Co-Authored-By: Marshall <marshall@zed.dev>
This commit is contained in:
parent
738b2ce6c5
commit
2fd8b1f489
4 changed files with 195 additions and 28 deletions
|
@ -85,6 +85,10 @@ impl BlinkManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
|
pub fn enable(&mut self, cx: &mut ModelContext<Self>) {
|
||||||
|
if self.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.enabled = true;
|
self.enabled = true;
|
||||||
// Set cursors as invisible and start blinking: this causes cursors
|
// Set cursors as invisible and start blinking: this causes cursors
|
||||||
// to be visible during the next render.
|
// to be visible during the next render.
|
||||||
|
|
|
@ -1920,9 +1920,15 @@ impl Editor {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let focus_handle = cx.focus_handle();
|
||||||
|
cx.on_focus_in(&focus_handle, Self::handle_focus_in)
|
||||||
|
.detach();
|
||||||
|
cx.on_focus_out(&focus_handle, Self::handle_focus_out)
|
||||||
|
.detach();
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
handle: cx.view().downgrade(),
|
handle: cx.view().downgrade(),
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle,
|
||||||
buffer: buffer.clone(),
|
buffer: buffer.clone(),
|
||||||
display_map: display_map.clone(),
|
display_map: display_map.clone(),
|
||||||
selections,
|
selections,
|
||||||
|
@ -9195,6 +9201,45 @@ impl Editor {
|
||||||
pub fn focus(&self, cx: &mut WindowContext) {
|
pub fn focus(&self, cx: &mut WindowContext) {
|
||||||
cx.focus(&self.focus_handle)
|
cx.focus(&self.focus_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
if self.focus_handle.is_focused(cx) {
|
||||||
|
// todo!()
|
||||||
|
// let focused_event = EditorFocused(cx.handle());
|
||||||
|
// cx.emit_global(focused_event);
|
||||||
|
cx.emit(Event::Focused);
|
||||||
|
}
|
||||||
|
if let Some(rename) = self.pending_rename.as_ref() {
|
||||||
|
let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
|
||||||
|
cx.focus(&rename_editor_focus_handle);
|
||||||
|
} else if self.focus_handle.is_focused(cx) {
|
||||||
|
self.blink_manager.update(cx, BlinkManager::enable);
|
||||||
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.finalize_last_transaction(cx);
|
||||||
|
if self.leader_peer_id.is_none() {
|
||||||
|
buffer.set_active_selections(
|
||||||
|
&self.selections.disjoint_anchors(),
|
||||||
|
self.selections.line_mode,
|
||||||
|
self.cursor_shape,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_focus_out(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
// todo!()
|
||||||
|
// let blurred_event = EditorBlurred(cx.handle());
|
||||||
|
// cx.emit_global(blurred_event);
|
||||||
|
self.blink_manager.update(cx, BlinkManager::disable);
|
||||||
|
self.buffer
|
||||||
|
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
|
||||||
|
self.hide_context_menu(cx);
|
||||||
|
hide_hover(self, cx);
|
||||||
|
cx.emit(Event::Blurred);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CollaborationHub {
|
pub trait CollaborationHub {
|
||||||
|
|
|
@ -518,8 +518,9 @@ impl AppContext {
|
||||||
) {
|
) {
|
||||||
window_handle
|
window_handle
|
||||||
.update(self, |_, cx| {
|
.update(self, |_, cx| {
|
||||||
|
// The window might change focus multiple times in an effect cycle.
|
||||||
|
// We only honor effects for the most recently focused handle.
|
||||||
if cx.window.focus == focused {
|
if cx.window.focus == focused {
|
||||||
let mut listeners = mem::take(&mut cx.window.current_frame.focus_listeners);
|
|
||||||
let focused = focused
|
let focused = focused
|
||||||
.map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap());
|
.map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap());
|
||||||
let blurred = cx
|
let blurred = cx
|
||||||
|
@ -528,15 +529,24 @@ impl AppContext {
|
||||||
.take()
|
.take()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
|
.and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
|
||||||
if focused.is_some() || blurred.is_some() {
|
let focus_changed = focused.is_some() || blurred.is_some();
|
||||||
let event = FocusEvent { focused, blurred };
|
let event = FocusEvent { focused, blurred };
|
||||||
for listener in &listeners {
|
|
||||||
|
let mut listeners = mem::take(&mut cx.window.current_frame.focus_listeners);
|
||||||
|
if focus_changed {
|
||||||
|
for listener in &mut listeners {
|
||||||
listener(&event, cx);
|
listener(&event, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.extend(cx.window.current_frame.focus_listeners.drain(..));
|
listeners.extend(cx.window.current_frame.focus_listeners.drain(..));
|
||||||
cx.window.current_frame.focus_listeners = listeners;
|
cx.window.current_frame.focus_listeners = listeners;
|
||||||
|
|
||||||
|
if focus_changed {
|
||||||
|
cx.window
|
||||||
|
.focus_listeners
|
||||||
|
.clone()
|
||||||
|
.retain(&(), |listener| listener(&event, cx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub enum DispatchPhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
|
type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
|
||||||
type AnyListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
|
type AnyListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
|
||||||
type AnyKeyListener = Box<
|
type AnyKeyListener = Box<
|
||||||
dyn Fn(
|
dyn Fn(
|
||||||
&dyn Any,
|
&dyn Any,
|
||||||
|
@ -71,9 +71,49 @@ type AnyKeyListener = Box<
|
||||||
+ 'static,
|
+ 'static,
|
||||||
>;
|
>;
|
||||||
type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + 'static>;
|
type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + 'static>;
|
||||||
|
type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
|
||||||
|
|
||||||
slotmap::new_key_type! { pub struct FocusId; }
|
slotmap::new_key_type! { pub struct FocusId; }
|
||||||
|
|
||||||
|
impl FocusId {
|
||||||
|
/// Obtains whether the element associated with this handle is currently focused.
|
||||||
|
pub fn is_focused(&self, cx: &WindowContext) -> bool {
|
||||||
|
cx.window.focus == Some(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains whether the element associated with this handle contains the focused
|
||||||
|
/// element or is itself focused.
|
||||||
|
pub fn contains_focused(&self, cx: &WindowContext) -> bool {
|
||||||
|
cx.focused()
|
||||||
|
.map_or(false, |focused| self.contains(focused.id, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains whether the element associated with this handle is contained within the
|
||||||
|
/// focused element or is itself focused.
|
||||||
|
pub fn within_focused(&self, cx: &WindowContext) -> bool {
|
||||||
|
let focused = cx.focused();
|
||||||
|
focused.map_or(false, |focused| focused.id.contains(*self, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains whether this handle contains the given handle in the most recently rendered frame.
|
||||||
|
pub(crate) fn contains(&self, other: Self, cx: &WindowContext) -> bool {
|
||||||
|
let mut ancestor = Some(other);
|
||||||
|
while let Some(ancestor_id) = ancestor {
|
||||||
|
if *self == ancestor_id {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
ancestor = cx
|
||||||
|
.window
|
||||||
|
.current_frame
|
||||||
|
.focus_parents_by_child
|
||||||
|
.get(&ancestor_id)
|
||||||
|
.copied();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A handle which can be used to track and manipulate the focused element in a window.
|
/// A handle which can be used to track and manipulate the focused element in a window.
|
||||||
pub struct FocusHandle {
|
pub struct FocusHandle {
|
||||||
pub(crate) id: FocusId,
|
pub(crate) id: FocusId,
|
||||||
|
@ -108,39 +148,24 @@ impl FocusHandle {
|
||||||
|
|
||||||
/// Obtains whether the element associated with this handle is currently focused.
|
/// Obtains whether the element associated with this handle is currently focused.
|
||||||
pub fn is_focused(&self, cx: &WindowContext) -> bool {
|
pub fn is_focused(&self, cx: &WindowContext) -> bool {
|
||||||
cx.window.focus == Some(self.id)
|
self.id.is_focused(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtains whether the element associated with this handle contains the focused
|
/// Obtains whether the element associated with this handle contains the focused
|
||||||
/// element or is itself focused.
|
/// element or is itself focused.
|
||||||
pub fn contains_focused(&self, cx: &WindowContext) -> bool {
|
pub fn contains_focused(&self, cx: &WindowContext) -> bool {
|
||||||
cx.focused()
|
self.id.contains_focused(cx)
|
||||||
.map_or(false, |focused| self.contains(&focused, cx))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtains whether the element associated with this handle is contained within the
|
/// Obtains whether the element associated with this handle is contained within the
|
||||||
/// focused element or is itself focused.
|
/// focused element or is itself focused.
|
||||||
pub fn within_focused(&self, cx: &WindowContext) -> bool {
|
pub fn within_focused(&self, cx: &WindowContext) -> bool {
|
||||||
let focused = cx.focused();
|
self.id.within_focused(cx)
|
||||||
focused.map_or(false, |focused| focused.contains(self, cx))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtains whether this handle contains the given handle in the most recently rendered frame.
|
/// Obtains whether this handle contains the given handle in the most recently rendered frame.
|
||||||
pub(crate) fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
|
pub(crate) fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
|
||||||
let mut ancestor = Some(other.id);
|
self.id.contains(other.id, cx)
|
||||||
while let Some(ancestor_id) = ancestor {
|
|
||||||
if self.id == ancestor_id {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
ancestor = cx
|
|
||||||
.window
|
|
||||||
.current_frame
|
|
||||||
.focus_parents_by_child
|
|
||||||
.get(&ancestor_id)
|
|
||||||
.copied();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +208,7 @@ pub struct Window {
|
||||||
pub(crate) previous_frame: Frame,
|
pub(crate) previous_frame: Frame,
|
||||||
pub(crate) current_frame: Frame,
|
pub(crate) current_frame: Frame,
|
||||||
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
||||||
|
pub(crate) focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
|
||||||
default_prevented: bool,
|
default_prevented: bool,
|
||||||
mouse_position: Point<Pixels>,
|
mouse_position: Point<Pixels>,
|
||||||
requested_cursor_style: Option<CursorStyle>,
|
requested_cursor_style: Option<CursorStyle>,
|
||||||
|
@ -282,6 +308,7 @@ impl Window {
|
||||||
previous_frame: Frame::default(),
|
previous_frame: Frame::default(),
|
||||||
current_frame: Frame::default(),
|
current_frame: Frame::default(),
|
||||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||||
|
focus_listeners: SubscriberSet::new(),
|
||||||
default_prevented: true,
|
default_prevented: true,
|
||||||
mouse_position,
|
mouse_position,
|
||||||
requested_cursor_style: None,
|
requested_cursor_style: None,
|
||||||
|
@ -1116,7 +1143,7 @@ impl<'a> WindowContext<'a> {
|
||||||
|
|
||||||
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
||||||
// special purposes, such as detecting events outside of a given Bounds.
|
// special purposes, such as detecting events outside of a given Bounds.
|
||||||
for (_, handler) in &handlers {
|
for (_, handler) in &mut handlers {
|
||||||
handler(any_mouse_event, DispatchPhase::Capture, self);
|
handler(any_mouse_event, DispatchPhase::Capture, self);
|
||||||
if !self.app.propagate_event {
|
if !self.app.propagate_event {
|
||||||
break;
|
break;
|
||||||
|
@ -1125,7 +1152,7 @@ impl<'a> WindowContext<'a> {
|
||||||
|
|
||||||
// Bubble phase, where most normal handlers do their work.
|
// Bubble phase, where most normal handlers do their work.
|
||||||
if self.app.propagate_event {
|
if self.app.propagate_event {
|
||||||
for (_, handler) in handlers.iter().rev() {
|
for (_, handler) in handlers.iter_mut().rev() {
|
||||||
handler(any_mouse_event, DispatchPhase::Bubble, self);
|
handler(any_mouse_event, DispatchPhase::Bubble, self);
|
||||||
if !self.app.propagate_event {
|
if !self.app.propagate_event {
|
||||||
break;
|
break;
|
||||||
|
@ -1872,6 +1899,87 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a listener to be called when the given focus handle receives focus.
|
||||||
|
/// Unlike [on_focus_changed], returns a subscription and persists until the subscription
|
||||||
|
/// is dropped.
|
||||||
|
pub fn on_focus(
|
||||||
|
&mut self,
|
||||||
|
handle: &FocusHandle,
|
||||||
|
mut listener: impl FnMut(&mut V, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Subscription {
|
||||||
|
let view = self.view.downgrade();
|
||||||
|
let focus_id = handle.id;
|
||||||
|
self.window.focus_listeners.insert(
|
||||||
|
(),
|
||||||
|
Box::new(move |event, cx| {
|
||||||
|
view.update(cx, |view, cx| {
|
||||||
|
if event.focused.as_ref().map(|focused| focused.id) == Some(focus_id) {
|
||||||
|
listener(view, cx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a listener to be called when the given focus handle or one of its descendants receives focus.
|
||||||
|
/// Unlike [on_focus_changed], returns a subscription and persists until the subscription
|
||||||
|
/// is dropped.
|
||||||
|
pub fn on_focus_in(
|
||||||
|
&mut self,
|
||||||
|
handle: &FocusHandle,
|
||||||
|
mut listener: impl FnMut(&mut V, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Subscription {
|
||||||
|
let view = self.view.downgrade();
|
||||||
|
let focus_id = handle.id;
|
||||||
|
self.window.focus_listeners.insert(
|
||||||
|
(),
|
||||||
|
Box::new(move |event, cx| {
|
||||||
|
view.update(cx, |view, cx| {
|
||||||
|
if event
|
||||||
|
.focused
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |focused| focus_id.contains(focused.id, cx))
|
||||||
|
{
|
||||||
|
listener(view, cx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a listener to be called when the given focus handle or one of its descendants loses focus.
|
||||||
|
/// Unlike [on_focus_changed], returns a subscription and persists until the subscription
|
||||||
|
/// is dropped.
|
||||||
|
pub fn on_focus_out(
|
||||||
|
&mut self,
|
||||||
|
handle: &FocusHandle,
|
||||||
|
mut listener: impl FnMut(&mut V, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Subscription {
|
||||||
|
let view = self.view.downgrade();
|
||||||
|
let focus_id = handle.id;
|
||||||
|
self.window.focus_listeners.insert(
|
||||||
|
(),
|
||||||
|
Box::new(move |event, cx| {
|
||||||
|
view.update(cx, |view, cx| {
|
||||||
|
if event
|
||||||
|
.blurred
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |focused| focus_id.contains(focused.id, cx))
|
||||||
|
{
|
||||||
|
listener(view, cx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a focus listener for the current frame only. It will be cleared
|
||||||
|
/// on the next frame render. You should use this method only from within elements,
|
||||||
|
/// and we may want to enforce that better via a different context type.
|
||||||
|
// todo!() Move this to `FrameContext` to emphasize its individuality?
|
||||||
pub fn on_focus_changed(
|
pub fn on_focus_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
|
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue