Only invalidate parent view on click/hover if we read that state when rendering

Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Nathan Sobo
2022-10-14 18:09:15 -06:00
co-authored by Max Brunsfeld
parent 934474f87e
commit bc03592912
19 changed files with 124 additions and 55 deletions
+27 -3
View File
@@ -3774,10 +3774,32 @@ pub struct RenderContext<'a, T: View> {
pub refreshing: bool,
}
#[derive(Clone, Copy, Default)]
#[derive(Clone, Default)]
pub struct MouseState {
pub hovered: bool,
pub clicked: Option<MouseButton>,
hovered: bool,
clicked: Option<MouseButton>,
accessed_hovered: bool,
accessed_clicked: bool,
}
impl MouseState {
pub fn hovered(&mut self) -> bool {
self.accessed_hovered = true;
self.hovered
}
pub fn clicked(&mut self) -> Option<MouseButton> {
self.accessed_clicked = true;
self.clicked
}
pub fn accessed_hovered(&self) -> bool {
self.accessed_hovered
}
pub fn accessed_clicked(&self) -> bool {
self.accessed_clicked
}
}
impl<'a, V: View> RenderContext<'a, V> {
@@ -3818,6 +3840,8 @@ impl<'a, V: View> RenderContext<'a, V> {
None
}
}),
accessed_hovered: false,
accessed_clicked: false,
}
}
@@ -22,6 +22,8 @@ pub struct MouseEventHandler<Tag: 'static> {
cursor_style: Option<CursorStyle>,
handlers: HandlerSet,
hoverable: bool,
notify_on_hover: bool,
notify_on_click: bool,
padding: Padding,
_tag: PhantomData<Tag>,
}
@@ -30,13 +32,19 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
where
V: View,
F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox,
{
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
let child = render_child(&mut mouse_state, cx);
let notify_on_hover = mouse_state.accessed_hovered();
let notify_on_click = mouse_state.accessed_clicked();
Self {
child: render_child(cx.mouse_state::<Tag>(region_id), cx),
child,
region_id,
cursor_style: None,
handlers: Default::default(),
notify_on_hover,
notify_on_click,
hoverable: true,
padding: Default::default(),
_tag: PhantomData,
@@ -185,7 +193,9 @@ impl<Tag> Element for MouseEventHandler<Tag> {
hit_bounds,
self.handlers.clone(),
)
.with_hoverable(self.hoverable),
.with_hoverable(self.hoverable)
.with_notify_on_hover(self.notify_on_hover)
.with_notify_on_click(self.notify_on_click),
);
self.child.paint(bounds.origin(), visible_bounds, cx);
+24 -20
View File
@@ -231,7 +231,7 @@ impl Presenter {
) -> bool {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
let mut events_to_send = Vec::new();
let mut invalidated_views: HashSet<usize> = Default::default();
let mut notified_views: HashSet<usize> = Default::default();
// 1. Allocate the correct set of GPUI events generated from the platform events
// -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
@@ -257,11 +257,6 @@ impl Presenter {
})
.collect();
// Clicked status is used when rendering views via the RenderContext.
// So when it changes, these views need to be rerendered
for clicked_region_id in self.clicked_region_ids.iter() {
invalidated_views.insert(clicked_region_id.view_id());
}
self.clicked_button = Some(e.button);
}
@@ -392,17 +387,31 @@ impl Presenter {
//Ensure that hover entrance events aren't sent twice
if self.hovered_region_ids.insert(region.id()) {
valid_regions.push(region.clone());
invalidated_views.insert(region.id().view_id());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());
}
}
} else {
// Ensure that hover exit events aren't sent twice
if self.hovered_region_ids.remove(&region.id()) {
valid_regions.push(region.clone());
invalidated_views.insert(region.id().view_id());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());
}
}
}
}
}
MouseRegionEvent::Down(_) | MouseRegionEvent::Up(_) => {
for (region, _) in self.mouse_regions.iter().rev() {
if region.bounds.contains_point(self.mouse_position) {
if region.notify_on_click {
notified_views.insert(region.id().view_id());
}
valid_regions.push(region.clone());
}
}
}
MouseRegionEvent::Click(e) => {
// Only raise click events if the released button is the same as the one stored
if self
@@ -413,11 +422,6 @@ impl Presenter {
// Clear clicked regions and clicked button
let clicked_region_ids =
std::mem::replace(&mut self.clicked_region_ids, Default::default());
// Clicked status is used when rendering views via the RenderContext.
// So when it changes, these views need to be rerendered
for clicked_region_id in clicked_region_ids.iter() {
invalidated_views.insert(clicked_region_id.view_id());
}
self.clicked_button = None;
// Find regions which still overlap with the mouse since the last MouseDown happened
@@ -459,7 +463,7 @@ impl Presenter {
//3. Fire region events
let hovered_region_ids = self.hovered_region_ids.clone();
for valid_region in valid_regions.into_iter() {
let mut event_cx = self.build_event_context(&mut invalidated_views, cx);
let mut event_cx = self.build_event_context(&mut notified_views, cx);
region_event.set_region(valid_region.bounds);
if let MouseRegionEvent::Hover(e) = &mut region_event {
@@ -500,11 +504,11 @@ impl Presenter {
}
if !any_event_handled && !event_reused {
let mut event_cx = self.build_event_context(&mut invalidated_views, cx);
let mut event_cx = self.build_event_context(&mut notified_views, cx);
any_event_handled = event_cx.dispatch_event(root_view_id, &event);
}
for view_id in invalidated_views {
for view_id in notified_views {
cx.notify_view(self.window_id, view_id);
}
@@ -516,7 +520,7 @@ impl Presenter {
pub fn build_event_context<'a>(
&'a mut self,
invalidated_views: &'a mut HashSet<usize>,
notified_views: &'a mut HashSet<usize>,
cx: &'a mut MutableAppContext,
) -> EventContext<'a> {
EventContext {
@@ -524,7 +528,7 @@ impl Presenter {
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
view_stack: Default::default(),
invalidated_views,
notified_views,
notify_count: 0,
handled: false,
window_id: self.window_id,
@@ -747,7 +751,7 @@ pub struct EventContext<'a> {
pub notify_count: usize,
view_stack: Vec<usize>,
handled: bool,
invalidated_views: &'a mut HashSet<usize>,
notified_views: &'a mut HashSet<usize>,
}
impl<'a> EventContext<'a> {
@@ -806,7 +810,7 @@ impl<'a> EventContext<'a> {
pub fn notify(&mut self) {
self.notify_count += 1;
if let Some(view_id) = self.view_stack.last() {
self.invalidated_views.insert(*view_id);
self.notified_views.insert(*view_id);
}
}
+14
View File
@@ -20,6 +20,8 @@ pub struct MouseRegion {
pub bounds: RectF,
pub handlers: HandlerSet,
pub hoverable: bool,
pub notify_on_hover: bool,
pub notify_on_click: bool,
}
impl MouseRegion {
@@ -52,6 +54,8 @@ impl MouseRegion {
bounds,
handlers,
hoverable: true,
notify_on_hover: false,
notify_on_click: false,
}
}
@@ -137,6 +141,16 @@ impl MouseRegion {
self.hoverable = is_hoverable;
self
}
pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
self.notify_on_hover = notify;
self
}
pub fn with_notify_on_click(mut self, notify: bool) -> Self {
self.notify_on_click = notify;
self
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+2 -2
View File
@@ -113,7 +113,7 @@ impl View for Select {
Container::new((self.render_item)(
self.selected_item_ix,
ItemType::Header,
mouse_state.hovered,
mouse_state.hovered(),
cx,
))
.with_style(style.header)
@@ -145,7 +145,7 @@ impl View for Select {
} else {
ItemType::Unselected
},
mouse_state.hovered,
mouse_state.hovered(),
cx,
)
})