DOCK WORKING!
Update editor element to use mouse regions instead of dispatch event for mouse events Fix bug in presenter where mouse region handlers were stored on click and called instead of more up to date handlers from subsequent renders Changed MouseRegion to require discriminants in all cases Add scroll wheel event to MouseRegion Polished a bunch of dock inconsistencies Co-Authored-By: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
co-authored by
Mikayla Maki
parent
59fd967793
commit
69ecbb644d
@@ -4028,7 +4028,7 @@ pub struct RenderParams {
|
||||
pub view_id: usize,
|
||||
pub titlebar_height: f32,
|
||||
pub hovered_region_ids: HashSet<MouseRegionId>,
|
||||
pub clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
|
||||
pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||
pub refreshing: bool,
|
||||
}
|
||||
|
||||
@@ -4037,7 +4037,7 @@ pub struct RenderContext<'a, T: View> {
|
||||
pub(crate) view_id: usize,
|
||||
pub(crate) view_type: PhantomData<T>,
|
||||
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
|
||||
pub(crate) clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||
pub app: &'a mut MutableAppContext,
|
||||
pub titlebar_height: f32,
|
||||
pub refreshing: bool,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
geometry::vector::Vector2F, presenter::MeasurementContext, CursorRegion, DebugContext, Element,
|
||||
ElementBox, Event, EventContext, LayoutContext, MouseButton, MouseButtonEvent, MouseRegion,
|
||||
NavigationDirection, PaintContext, SizeConstraint,
|
||||
geometry::vector::Vector2F, presenter::MeasurementContext, scene::HandlerSet, CursorRegion,
|
||||
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseButton,
|
||||
MouseButtonEvent, MouseRegion, NavigationDirection, PaintContext, SizeConstraint,
|
||||
};
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use serde_json::json;
|
||||
@@ -82,11 +82,13 @@ impl Element for EventHandler {
|
||||
bounds: visible_bounds,
|
||||
style: Default::default(),
|
||||
});
|
||||
cx.scene.push_mouse_region(MouseRegion::handle_all(
|
||||
cx.current_view_id(),
|
||||
Some(discriminant),
|
||||
visible_bounds,
|
||||
));
|
||||
cx.scene.push_mouse_region(MouseRegion {
|
||||
view_id: cx.current_view_id(),
|
||||
discriminant,
|
||||
bounds: visible_bounds,
|
||||
handlers: HandlerSet::capture_all(),
|
||||
hoverable: true,
|
||||
});
|
||||
cx.scene.pop_stacking_context();
|
||||
}
|
||||
self.child.paint(bounds.origin(), visible_bounds, cx);
|
||||
|
||||
@@ -167,15 +167,13 @@ impl Element for MouseEventHandler {
|
||||
});
|
||||
}
|
||||
|
||||
cx.scene.push_mouse_region(
|
||||
MouseRegion::from_handlers(
|
||||
cx.current_view_id(),
|
||||
Some(self.discriminant),
|
||||
hit_bounds,
|
||||
self.handlers.clone(),
|
||||
)
|
||||
.with_hoverable(self.hoverable),
|
||||
);
|
||||
cx.scene.push_mouse_region(MouseRegion {
|
||||
view_id: cx.current_view_id(),
|
||||
discriminant: self.discriminant,
|
||||
bounds: hit_bounds,
|
||||
handlers: self.handlers.clone(),
|
||||
hoverable: self.hoverable,
|
||||
});
|
||||
|
||||
self.child.paint(bounds.origin(), visible_bounds, cx);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::ops::Range;
|
||||
use std::{any::TypeId, ops::Range};
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
@@ -78,10 +78,13 @@ impl Element for Overlay {
|
||||
cx.scene.push_stacking_context(None);
|
||||
|
||||
if self.hoverable {
|
||||
enum OverlayHoverCapture {}
|
||||
cx.scene.push_mouse_region(MouseRegion {
|
||||
view_id: cx.current_view_id(),
|
||||
bounds,
|
||||
..Default::default()
|
||||
discriminant: (TypeId::of::<OverlayHoverCapture>(), cx.current_view_id()),
|
||||
handlers: Default::default(),
|
||||
hoverable: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::{
|
||||
platform::{CursorStyle, Event},
|
||||
scene::{
|
||||
ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragRegionEvent,
|
||||
HoverRegionEvent, MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
|
||||
HoverRegionEvent, MouseRegionEvent, MoveRegionEvent, ScrollWheelRegionEvent,
|
||||
UpOutRegionEvent, UpRegionEvent,
|
||||
},
|
||||
text_layout::TextLayoutCache,
|
||||
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity,
|
||||
@@ -36,7 +37,7 @@ pub struct Presenter {
|
||||
asset_cache: Arc<AssetCache>,
|
||||
last_mouse_moved_event: Option<Event>,
|
||||
hovered_region_ids: HashSet<MouseRegionId>,
|
||||
clicked_regions: Vec<MouseRegion>,
|
||||
clicked_region_ids: HashSet<MouseRegionId>,
|
||||
clicked_button: Option<MouseButton>,
|
||||
mouse_position: Vector2F,
|
||||
titlebar_height: f32,
|
||||
@@ -61,7 +62,7 @@ impl Presenter {
|
||||
asset_cache,
|
||||
last_mouse_moved_event: None,
|
||||
hovered_region_ids: Default::default(),
|
||||
clicked_regions: Vec::new(),
|
||||
clicked_region_ids: Default::default(),
|
||||
clicked_button: None,
|
||||
mouse_position: vec2f(0., 0.),
|
||||
titlebar_height,
|
||||
@@ -75,7 +76,6 @@ impl Presenter {
|
||||
) {
|
||||
cx.start_frame();
|
||||
for view_id in &invalidation.removed {
|
||||
invalidation.updated.remove(view_id);
|
||||
self.rendered_views.remove(view_id);
|
||||
}
|
||||
for view_id in &invalidation.updated {
|
||||
@@ -86,15 +86,9 @@ impl Presenter {
|
||||
view_id: *view_id,
|
||||
titlebar_height: self.titlebar_height,
|
||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||
clicked_region_ids: self.clicked_button.map(|button| {
|
||||
(
|
||||
self.clicked_regions
|
||||
.iter()
|
||||
.filter_map(MouseRegion::id)
|
||||
.collect(),
|
||||
button,
|
||||
)
|
||||
}),
|
||||
clicked_region_ids: self
|
||||
.clicked_button
|
||||
.map(|button| (self.clicked_region_ids.clone(), button)),
|
||||
refreshing: false,
|
||||
})
|
||||
.unwrap(),
|
||||
@@ -112,15 +106,9 @@ impl Presenter {
|
||||
view_id: *view_id,
|
||||
titlebar_height: self.titlebar_height,
|
||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||
clicked_region_ids: self.clicked_button.map(|button| {
|
||||
(
|
||||
self.clicked_regions
|
||||
.iter()
|
||||
.filter_map(MouseRegion::id)
|
||||
.collect(),
|
||||
button,
|
||||
)
|
||||
}),
|
||||
clicked_region_ids: self
|
||||
.clicked_button
|
||||
.map(|button| (self.clicked_region_ids.clone(), button)),
|
||||
refreshing: true,
|
||||
})
|
||||
.unwrap();
|
||||
@@ -184,15 +172,9 @@ impl Presenter {
|
||||
view_stack: Vec::new(),
|
||||
refreshing,
|
||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||
clicked_region_ids: self.clicked_button.map(|button| {
|
||||
(
|
||||
self.clicked_regions
|
||||
.iter()
|
||||
.filter_map(MouseRegion::id)
|
||||
.collect(),
|
||||
button,
|
||||
)
|
||||
}),
|
||||
clicked_region_ids: self
|
||||
.clicked_button
|
||||
.map(|button| (self.clicked_region_ids.clone(), button)),
|
||||
titlebar_height: self.titlebar_height,
|
||||
window_size,
|
||||
app: cx,
|
||||
@@ -248,14 +230,15 @@ impl Presenter {
|
||||
|
||||
// If there is already clicked_button stored, don't replace it.
|
||||
if self.clicked_button.is_none() {
|
||||
self.clicked_regions = self
|
||||
self.clicked_region_ids = self
|
||||
.mouse_regions
|
||||
.iter()
|
||||
.filter_map(|(region, _)| {
|
||||
region
|
||||
.bounds
|
||||
.contains_point(e.position)
|
||||
.then(|| region.clone())
|
||||
if region.bounds.contains_point(e.position) {
|
||||
Some(region.id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.clicked_button = Some(e.button);
|
||||
@@ -341,6 +324,12 @@ impl Presenter {
|
||||
|
||||
self.last_mouse_moved_event = Some(event.clone());
|
||||
}
|
||||
Event::ScrollWheel(e) => {
|
||||
events_to_send.push(MouseRegionEvent::ScrollWheel(ScrollWheelRegionEvent {
|
||||
region: Default::default(),
|
||||
platform_event: e.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
@@ -375,23 +364,21 @@ impl Presenter {
|
||||
top_most_depth = Some(depth);
|
||||
}
|
||||
|
||||
if let Some(region_id) = region.id() {
|
||||
// This unwrap relies on short circuiting boolean expressions
|
||||
// The right side of the && is only executed when contains_mouse
|
||||
// is true, and we know above that when contains_mouse is true
|
||||
// top_most_depth is set
|
||||
if contains_mouse && depth == top_most_depth.unwrap() {
|
||||
//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.view_id);
|
||||
}
|
||||
} else {
|
||||
// Ensure that hover exit events aren't sent twice
|
||||
if self.hovered_region_ids.remove(®ion_id) {
|
||||
valid_regions.push(region.clone());
|
||||
invalidated_views.insert(region.view_id);
|
||||
}
|
||||
// This unwrap relies on short circuiting boolean expressions
|
||||
// The right side of the && is only executed when contains_mouse
|
||||
// is true, and we know above that when contains_mouse is true
|
||||
// top_most_depth is set
|
||||
if contains_mouse && depth == top_most_depth.unwrap() {
|
||||
//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.view_id);
|
||||
}
|
||||
} else {
|
||||
// Ensure that hover exit events aren't sent twice
|
||||
if self.hovered_region_ids.remove(®ion.id()) {
|
||||
valid_regions.push(region.clone());
|
||||
invalidated_views.insert(region.view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,21 +391,23 @@ impl Presenter {
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Clear clicked regions and clicked button
|
||||
let clicked_regions =
|
||||
std::mem::replace(&mut self.clicked_regions, Vec::new());
|
||||
let clicked_region_ids =
|
||||
std::mem::replace(&mut self.clicked_region_ids, Default::default());
|
||||
self.clicked_button = None;
|
||||
|
||||
// Find regions which still overlap with the mouse since the last MouseDown happened
|
||||
for clicked_region in clicked_regions.into_iter().rev() {
|
||||
if clicked_region.bounds.contains_point(e.position) {
|
||||
valid_regions.push(clicked_region);
|
||||
for (mouse_region, _) in self.mouse_regions.iter().rev() {
|
||||
if clicked_region_ids.contains(&mouse_region.id()) {
|
||||
valid_regions.push(mouse_region.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseRegionEvent::Drag(_) => {
|
||||
for clicked_region in self.clicked_regions.iter().rev() {
|
||||
valid_regions.push(clicked_region.clone());
|
||||
for (mouse_region, _) in self.mouse_regions.iter().rev() {
|
||||
if self.clicked_region_ids.contains(&mouse_region.id()) {
|
||||
valid_regions.push(mouse_region.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,10 +436,7 @@ impl Presenter {
|
||||
|
||||
region_event.set_region(valid_region.bounds);
|
||||
if let MouseRegionEvent::Hover(e) = &mut region_event {
|
||||
e.started = valid_region
|
||||
.id()
|
||||
.map(|region_id| hovered_region_ids.contains(®ion_id))
|
||||
.unwrap_or(false)
|
||||
e.started = hovered_region_ids.contains(&valid_region.id())
|
||||
}
|
||||
// Handle Down events if the MouseRegion has a Click handler. This makes the api more intuitive as you would
|
||||
// not expect a MouseRegion to be transparent to Down events if it also has a Click handler.
|
||||
@@ -546,7 +532,7 @@ pub struct LayoutContext<'a> {
|
||||
pub window_size: Vector2F,
|
||||
titlebar_height: f32,
|
||||
hovered_region_ids: HashSet<MouseRegionId>,
|
||||
clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
|
||||
clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||
}
|
||||
|
||||
impl<'a> LayoutContext<'a> {
|
||||
|
||||
@@ -536,11 +536,11 @@ impl ToJson for Border {
|
||||
}
|
||||
|
||||
impl MouseRegion {
|
||||
pub fn id(&self) -> Option<MouseRegionId> {
|
||||
self.discriminant.map(|discriminant| MouseRegionId {
|
||||
pub fn id(&self) -> MouseRegionId {
|
||||
MouseRegionId {
|
||||
view_id: self.view_id,
|
||||
discriminant,
|
||||
})
|
||||
discriminant: self.discriminant,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,54 +6,51 @@ use pathfinder_geometry::rect::RectF;
|
||||
|
||||
use crate::{EventContext, MouseButton};
|
||||
|
||||
use super::mouse_region_event::{
|
||||
ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragRegionEvent, HoverRegionEvent,
|
||||
MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
|
||||
use super::{
|
||||
mouse_region_event::{
|
||||
ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragRegionEvent, HoverRegionEvent,
|
||||
MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
|
||||
},
|
||||
ScrollWheelRegionEvent,
|
||||
};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct MouseRegion {
|
||||
pub view_id: usize,
|
||||
pub discriminant: Option<(TypeId, usize)>,
|
||||
pub discriminant: (TypeId, usize),
|
||||
pub bounds: RectF,
|
||||
pub handlers: HandlerSet,
|
||||
pub hoverable: bool,
|
||||
}
|
||||
|
||||
impl MouseRegion {
|
||||
pub fn new(view_id: usize, discriminant: Option<(TypeId, usize)>, bounds: RectF) -> Self {
|
||||
Self::from_handlers(view_id, discriminant, bounds, Default::default())
|
||||
/// Region ID is used to track semantically equivalent mouse regions across render passes.
|
||||
/// e.g. if you have mouse handlers attached to a list item type, then each item of the list
|
||||
/// should pass a different (consistent) region_id. If you have one big region that covers your
|
||||
/// whole component, just pass the view_id again.
|
||||
pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, Default::default())
|
||||
}
|
||||
|
||||
pub fn from_handlers(
|
||||
pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, HandlerSet::capture_all())
|
||||
}
|
||||
|
||||
pub fn from_handlers<Tag: 'static>(
|
||||
view_id: usize,
|
||||
discriminant: Option<(TypeId, usize)>,
|
||||
region_id: usize,
|
||||
bounds: RectF,
|
||||
handlers: HandlerSet,
|
||||
) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
discriminant,
|
||||
discriminant: (TypeId::of::<Tag>(), region_id),
|
||||
bounds,
|
||||
handlers,
|
||||
hoverable: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_all(
|
||||
view_id: usize,
|
||||
discriminant: Option<(TypeId, usize)>,
|
||||
bounds: RectF,
|
||||
) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
discriminant,
|
||||
bounds,
|
||||
handlers: HandlerSet::capture_all(),
|
||||
hoverable: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_down(
|
||||
mut self,
|
||||
button: MouseButton,
|
||||
@@ -124,6 +121,14 @@ impl MouseRegion {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_scroll(
|
||||
mut self,
|
||||
handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
|
||||
) -> Self {
|
||||
self.handlers = self.handlers.on_scroll(handler);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
|
||||
self.hoverable = is_hoverable;
|
||||
self
|
||||
@@ -345,4 +350,22 @@ impl HandlerSet {
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_scroll(
|
||||
mut self,
|
||||
handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
|
||||
) -> Self {
|
||||
self.set.insert((MouseRegionEvent::scroll_wheel_disc(), None),
|
||||
Rc::new(move |region_event, cx| {
|
||||
if let MouseRegionEvent::ScrollWheel(e) = region_event {
|
||||
handler(e, cx);
|
||||
} else {
|
||||
panic!(
|
||||
"Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
|
||||
region_event
|
||||
);
|
||||
}
|
||||
}));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ impl MouseRegionEvent {
|
||||
pub fn is_capturable(&self) -> bool {
|
||||
match self {
|
||||
MouseRegionEvent::Move(_) => true,
|
||||
MouseRegionEvent::Drag(_) => false,
|
||||
MouseRegionEvent::Drag(_) => true,
|
||||
MouseRegionEvent::Hover(_) => false,
|
||||
MouseRegionEvent::Down(_) => true,
|
||||
MouseRegionEvent::Up(_) => true,
|
||||
|
||||
Reference in New Issue
Block a user