Ensure that notify observations are sent during Window::draw() (#4236)
This PR fixes a bug in GPUI where the observation API would not be triggered if a focus event caused a `notify()` Release Notes: - N/A
This commit is contained in:
@@ -136,6 +136,25 @@ impl Render for () {
|
|||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {}
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A quick way to create a [`Render`]able view without having to define a new type.
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub struct TestView(Box<dyn FnMut(&mut ViewContext<TestView>) -> AnyElement>);
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
impl TestView {
|
||||||
|
/// Construct a TestView from a render closure.
|
||||||
|
pub fn new<F: FnMut(&mut ViewContext<TestView>) -> AnyElement + 'static>(f: F) -> Self {
|
||||||
|
Self(Box::new(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
impl Render for TestView {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
(self.0)(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// You can derive [`IntoElement`] on any type that implements this trait.
|
/// You can derive [`IntoElement`] on any type that implements this trait.
|
||||||
/// It is used to construct reusable `components` out of plain data. Think of
|
/// It is used to construct reusable `components` out of plain data. Think of
|
||||||
/// components as a recipe for a certain pattern of elements. RenderOnce allows
|
/// components as a recipe for a certain pattern of elements. RenderOnce allows
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
|
|||||||
|
|
||||||
type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
|
type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct FocusEvent {
|
struct FocusEvent {
|
||||||
previous_focus_path: SmallVec<[FocusId; 8]>,
|
previous_focus_path: SmallVec<[FocusId; 8]>,
|
||||||
current_focus_path: SmallVec<[FocusId; 8]>,
|
current_focus_path: SmallVec<[FocusId; 8]>,
|
||||||
@@ -2015,11 +2016,12 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always emit a notify effect, so that handlers fire correctly
|
||||||
|
self.window_cx.app.push_effect(Effect::Notify {
|
||||||
|
emitter: self.view.model.entity_id,
|
||||||
|
});
|
||||||
if !self.window.drawing {
|
if !self.window.drawing {
|
||||||
self.window_cx.window.dirty = true;
|
self.window_cx.window.dirty = true;
|
||||||
self.window_cx.app.push_effect(Effect::Notify {
|
|
||||||
emitter: self.view.model.entity_id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2751,3 +2753,59 @@ pub fn outline(bounds: impl Into<Bounds<Pixels>>, border_color: impl Into<Hsla>)
|
|||||||
border_color: border_color.into(),
|
border_color: border_color.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
self as gpui, div, FocusHandle, InteractiveElement, IntoElement, Render, TestAppContext,
|
||||||
|
ViewContext, VisualContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_notify_on_focus(cx: &mut TestAppContext) {
|
||||||
|
struct TestFocusView {
|
||||||
|
handle: FocusHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for TestFocusView {
|
||||||
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
div().id("test").track_focus(&self.handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let notify_counter = Rc::new(RefCell::new(0));
|
||||||
|
|
||||||
|
let (notify_producer, cx) = cx.add_window_view(|cx| {
|
||||||
|
cx.activate_window();
|
||||||
|
let handle = cx.focus_handle();
|
||||||
|
|
||||||
|
cx.on_focus(&handle, |_, cx| {
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
TestFocusView { handle }
|
||||||
|
});
|
||||||
|
|
||||||
|
let focus_handle = cx.update(|cx| notify_producer.read(cx).handle.clone());
|
||||||
|
|
||||||
|
let _notify_consumer = cx.new_view({
|
||||||
|
|cx| {
|
||||||
|
let notify_counter = notify_counter.clone();
|
||||||
|
cx.observe(¬ify_producer, move |_, _, _| {
|
||||||
|
*notify_counter.borrow_mut() += 1;
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.update(|cx| {
|
||||||
|
cx.focus(&focus_handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(*notify_counter.borrow(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user