diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index cf34863ba6..8ccae59172 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -564,8 +564,6 @@ impl EditorElement { ); } } - - cx.stop_propagation(); } else { update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx); hover_at(editor, None, cx); diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 631ffc43c6..0d477fc62d 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -151,9 +151,7 @@ impl Interactivity { { self.mouse_move_listeners .push(Box::new(move |event, bounds, phase, cx| { - if phase == DispatchPhase::Capture - && bounds.drag_target_contains(&event.position, cx) - { + if phase == DispatchPhase::Capture { if cx .active_drag .as_ref() @@ -780,20 +778,16 @@ impl Element for Div { &mut element_state.interactive_state, cx, |style, scroll_offset, cx| { - let z_index = style.z_index.unwrap_or(0); - - cx.with_z_index(z_index, |cx| { - style.paint(bounds, cx, |cx| { - cx.with_text_style(style.text_style().cloned(), |cx| { - cx.with_content_mask(style.overflow_mask(bounds), |cx| { - cx.with_element_offset(scroll_offset, |cx| { - for child in &mut self.children { - child.paint(cx); - } - }) + style.paint(bounds, cx, |cx| { + cx.with_text_style(style.text_style().cloned(), |cx| { + cx.with_content_mask(style.overflow_mask(bounds), |cx| { + cx.with_element_offset(scroll_offset, |cx| { + for child in &mut self.children { + child.paint(cx); + } }) }) - }); + }) }) }, ); @@ -920,531 +914,547 @@ impl Interactivity { return; } - #[cfg(debug_assertions)] - if self.element_id.is_some() - && (style.debug || style.debug_below || cx.has_global::()) - && bounds.contains(&cx.mouse_position()) - { - const FONT_SIZE: crate::Pixels = crate::Pixels(10.); - let element_id = format!("{:?}", self.element_id.as_ref().unwrap()); - let str_len = element_id.len(); + let z_index = style.z_index.unwrap_or(0); + cx.with_z_index(z_index, |cx| { + #[cfg(debug_assertions)] + if self.element_id.is_some() + && (style.debug || style.debug_below || cx.has_global::()) + && bounds.contains(&cx.mouse_position()) + { + const FONT_SIZE: crate::Pixels = crate::Pixels(10.); + let element_id = format!("{:?}", self.element_id.as_ref().unwrap()); + let str_len = element_id.len(); - let render_debug_text = |cx: &mut WindowContext| { - if let Some(text) = cx - .text_system() - .shape_text( - &element_id, - FONT_SIZE, - &[cx.text_style().to_run(str_len)], - None, - ) - .ok() - .map(|mut text| text.pop()) - .flatten() - { - text.paint(bounds.origin, FONT_SIZE, cx).ok(); - - let text_bounds = crate::Bounds { - origin: bounds.origin, - size: text.size(FONT_SIZE), - }; - if self.location.is_some() - && text_bounds.contains(&cx.mouse_position()) - && cx.modifiers().command + let render_debug_text = |cx: &mut WindowContext| { + if let Some(text) = cx + .text_system() + .shape_text( + &element_id, + FONT_SIZE, + &[cx.text_style().to_run(str_len)], + None, + ) + .ok() + .map(|mut text| text.pop()) + .flatten() { - let command_held = cx.modifiers().command; - cx.on_key_event({ - let text_bounds = text_bounds.clone(); - move |e: &crate::ModifiersChangedEvent, _phase, cx| { - if e.modifiers.command != command_held - && text_bounds.contains(&cx.mouse_position()) - { - cx.notify(); + text.paint(bounds.origin, FONT_SIZE, cx).ok(); + + let text_bounds = crate::Bounds { + origin: bounds.origin, + size: text.size(FONT_SIZE), + }; + if self.location.is_some() + && text_bounds.contains(&cx.mouse_position()) + && cx.modifiers().command + { + let command_held = cx.modifiers().command; + cx.on_key_event({ + let text_bounds = text_bounds.clone(); + move |e: &crate::ModifiersChangedEvent, _phase, cx| { + if e.modifiers.command != command_held + && text_bounds.contains(&cx.mouse_position()) + { + cx.notify(); + } } - } - }); + }); - let hovered = bounds.contains(&cx.mouse_position()); - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if bounds.contains(&event.position) != hovered { - cx.notify(); + let hovered = bounds.contains(&cx.mouse_position()); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains(&event.position) != hovered { + cx.notify(); + } } - } - }); + }); - cx.on_mouse_event({ - let location = self.location.clone().unwrap(); - let text_bounds = text_bounds.clone(); - move |e: &crate::MouseDownEvent, phase, cx| { - if text_bounds.contains(&e.position) && phase.capture() { - cx.stop_propagation(); - let Ok(dir) = std::env::current_dir() else { - return; - }; + cx.on_mouse_event({ + let location = self.location.clone().unwrap(); + let text_bounds = text_bounds.clone(); + move |e: &crate::MouseDownEvent, phase, cx| { + if text_bounds.contains(&e.position) && phase.capture() { + cx.stop_propagation(); + let Ok(dir) = std::env::current_dir() else { + return; + }; - eprintln!( - "This element is created at:\n{}:{}:{}", - location.file(), - location.line(), - location.column() - ); - - std::process::Command::new("zed") - .arg(format!( - "{}/{}:{}:{}", - dir.to_string_lossy(), + eprintln!( + "This element is created at:\n{}:{}:{}", location.file(), location.line(), location.column() - )) - .spawn() - .ok(); + ); + + std::process::Command::new("zed") + .arg(format!( + "{}/{}:{}:{}", + dir.to_string_lossy(), + location.file(), + location.line(), + location.column() + )) + .spawn() + .ok(); + } } - } - }); - cx.paint_quad(crate::outline( - crate::Bounds { - origin: bounds.origin - + crate::point(crate::px(0.), FONT_SIZE - px(2.)), - size: crate::Size { - width: text_bounds.size.width, - height: crate::px(1.), + }); + cx.paint_quad(crate::outline( + crate::Bounds { + origin: bounds.origin + + crate::point(crate::px(0.), FONT_SIZE - px(2.)), + size: crate::Size { + width: text_bounds.size.width, + height: crate::px(1.), + }, }, - }, - crate::red(), - )) + crate::red(), + )) + } } - } + }; + + cx.with_z_index(1, |cx| { + cx.with_text_style( + Some(crate::TextStyleRefinement { + color: Some(crate::red()), + line_height: Some(FONT_SIZE.into()), + background_color: Some(crate::white()), + ..Default::default() + }), + render_debug_text, + ) + }); + } + + if style + .background + .as_ref() + .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) + { + cx.add_opaque_layer(bounds) + } + + let interactive_bounds = InteractiveBounds { + bounds: bounds.intersect(&cx.content_mask().bounds), + stacking_order: cx.stacking_order().clone(), }; - cx.with_z_index(1, |cx| { - cx.with_text_style( - Some(crate::TextStyleRefinement { - color: Some(crate::red()), - line_height: Some(FONT_SIZE.into()), - background_color: Some(crate::white()), - ..Default::default() - }), - render_debug_text, - ) - }); - } - - if style - .background - .as_ref() - .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) - { - cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds)) - } - - let interactive_bounds = InteractiveBounds { - bounds: bounds.intersect(&cx.content_mask().bounds), - stacking_order: cx.stacking_order().clone(), - }; - - if let Some(mouse_cursor) = style.mouse_cursor { - let mouse_position = &cx.mouse_position(); - let hovered = interactive_bounds.visibly_contains(mouse_position, cx); - if hovered { - cx.set_cursor_style(mouse_cursor); + if let Some(mouse_cursor) = style.mouse_cursor { + let mouse_position = &cx.mouse_position(); + let hovered = interactive_bounds.visibly_contains(mouse_position, cx); + if hovered { + cx.set_cursor_style(mouse_cursor); + } } - } - // If this element can be focused, register a mouse down listener - // that will automatically transfer focus when hitting the element. - // This behavior can be suppressed by using `cx.prevent_default()`. - if let Some(focus_handle) = element_state.focus_handle.clone() { - cx.on_mouse_event({ - let interactive_bounds = interactive_bounds.clone(); - move |event: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble - && !cx.default_prevented() - && interactive_bounds.visibly_contains(&event.position, cx) - { - cx.focus(&focus_handle); - // If there is a parent that is also focusable, prevent it - // from transferring focus because we already did so. - cx.prevent_default(); - } - } - }); - } - - for listener in self.mouse_down_listeners.drain(..) { - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { - listener(event, &interactive_bounds, phase, cx); - }) - } - - for listener in self.mouse_up_listeners.drain(..) { - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| { - listener(event, &interactive_bounds, phase, cx); - }) - } - - for listener in self.mouse_move_listeners.drain(..) { - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - listener(event, &interactive_bounds, phase, cx); - }) - } - - for listener in self.scroll_wheel_listeners.drain(..) { - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| { - listener(event, &interactive_bounds, phase, cx); - }) - } - - let hover_group_bounds = self - .group_hover_style - .as_ref() - .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); - - if let Some(group_bounds) = hover_group_bounds { - let hovered = group_bounds.contains(&cx.mouse_position()); - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if group_bounds.contains(&event.position) != hovered { - cx.notify(); - } - } - }); - } - - if self.hover_style.is_some() - || self.base_style.mouse_cursor.is_some() - || cx.active_drag.is_some() && !self.drag_over_styles.is_empty() - { - let bounds = bounds.intersect(&cx.content_mask().bounds); - let hovered = bounds.contains(&cx.mouse_position()); - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if bounds.contains(&event.position) != hovered { - cx.notify(); - } - } - }); - } - - if cx.active_drag.is_some() { - let drop_listeners = mem::take(&mut self.drop_listeners); - let interactive_bounds = interactive_bounds.clone(); - if !drop_listeners.is_empty() { - cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Bubble - && interactive_bounds.drag_target_contains(&event.position, cx) - { - if let Some(drag_state_type) = cx - .active_drag - .as_ref() - .map(|drag| drag.value.as_ref().type_id()) + // If this element can be focused, register a mouse down listener + // that will automatically transfer focus when hitting the element. + // This behavior can be suppressed by using `cx.prevent_default()`. + if let Some(focus_handle) = element_state.focus_handle.clone() { + cx.on_mouse_event({ + let interactive_bounds = interactive_bounds.clone(); + move |event: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble + && !cx.default_prevented() + && interactive_bounds.visibly_contains(&event.position, cx) { - for (drop_state_type, listener) in &drop_listeners { - if *drop_state_type == drag_state_type { - let drag = cx - .active_drag - .take() - .expect("checked for type drag state type above"); + cx.focus(&focus_handle); + // If there is a parent that is also focusable, prevent it + // from transferring focus because we already did so. + cx.prevent_default(); + } + } + }); + } - listener(drag.value.as_ref(), cx); + for listener in self.mouse_down_listeners.drain(..) { + let interactive_bounds = interactive_bounds.clone(); + cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { + listener(event, &interactive_bounds, phase, cx); + }) + } + + for listener in self.mouse_up_listeners.drain(..) { + let interactive_bounds = interactive_bounds.clone(); + cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| { + listener(event, &interactive_bounds, phase, cx); + }) + } + + for listener in self.mouse_move_listeners.drain(..) { + let interactive_bounds = interactive_bounds.clone(); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + listener(event, &interactive_bounds, phase, cx); + }) + } + + for listener in self.scroll_wheel_listeners.drain(..) { + let interactive_bounds = interactive_bounds.clone(); + cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| { + listener(event, &interactive_bounds, phase, cx); + }) + } + + let hover_group_bounds = self + .group_hover_style + .as_ref() + .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); + + if let Some(group_bounds) = hover_group_bounds { + let hovered = group_bounds.contains(&cx.mouse_position()); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if group_bounds.contains(&event.position) != hovered { + cx.notify(); + } + } + }); + } + + if self.hover_style.is_some() + || self.base_style.mouse_cursor.is_some() + || cx.active_drag.is_some() && !self.drag_over_styles.is_empty() + { + let bounds = bounds.intersect(&cx.content_mask().bounds); + let hovered = bounds.contains(&cx.mouse_position()); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains(&event.position) != hovered { + cx.notify(); + } + } + }); + } + + let mut drag_listener = mem::take(&mut self.drag_listener); + let drop_listeners = mem::take(&mut self.drop_listeners); + let click_listeners = mem::take(&mut self.click_listeners); + + if !drop_listeners.is_empty() { + cx.on_mouse_event({ + let interactive_bounds = interactive_bounds.clone(); + move |event: &MouseUpEvent, phase, cx| { + if let Some(drag) = &cx.active_drag { + if phase == DispatchPhase::Bubble + && interactive_bounds.drag_target_contains(&event.position, cx) + { + let drag_state_type = drag.value.as_ref().type_id(); + for (drop_state_type, listener) in &drop_listeners { + if *drop_state_type == drag_state_type { + let drag = cx + .active_drag + .take() + .expect("checked for type drag state type above"); + + listener(drag.value.as_ref(), cx); + cx.notify(); + cx.stop_propagation(); + } + } + } + } + } + }); + } + + if !click_listeners.is_empty() || drag_listener.is_some() { + let pending_mouse_down = element_state + .pending_mouse_down + .get_or_insert_with(Default::default) + .clone(); + + let active_state = element_state + .clicked_state + .get_or_insert_with(Default::default) + .clone(); + + cx.on_mouse_event({ + let interactive_bounds = interactive_bounds.clone(); + let pending_mouse_down = pending_mouse_down.clone(); + move |event: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble + && event.button == MouseButton::Left + && interactive_bounds.visibly_contains(&event.position, cx) + { + *pending_mouse_down.borrow_mut() = Some(event.clone()); + cx.notify(); + } + } + }); + + cx.on_mouse_event({ + let pending_mouse_down = pending_mouse_down.clone(); + move |event: &MouseMoveEvent, phase, cx| { + let mut pending_mouse_down = pending_mouse_down.borrow_mut(); + + if let Some(mouse_down) = pending_mouse_down.clone() { + if cx.active_drag.is_some() { + if phase == DispatchPhase::Capture { + cx.notify(); + } + } else if phase == DispatchPhase::Bubble + && (event.position - mouse_down.position).magnitude() + > DRAG_THRESHOLD + { + if let Some((drag_value, drag_listener)) = drag_listener.take() { + *active_state.borrow_mut() = ElementClickedState::default(); + let cursor_offset = event.position - bounds.origin; + let drag = (drag_listener)(drag_value.as_ref(), cx); + cx.active_drag = Some(AnyDrag { + view: drag, + value: drag_value, + cursor_offset, + }); + pending_mouse_down.take(); cx.notify(); cx.stop_propagation(); } } - } else { - cx.active_drag = None; + } + } + }); + + cx.on_mouse_event({ + let interactive_bounds = interactive_bounds.clone(); + let mut captured_mouse_down = None; + move |event: &MouseUpEvent, phase, cx| match phase { + // Clear the pending mouse down during the capture phase, + // so that it happens even if another event handler stops + // propagation. + DispatchPhase::Capture => { + let mut pending_mouse_down = pending_mouse_down.borrow_mut(); + if pending_mouse_down.is_some() { + captured_mouse_down = pending_mouse_down.take(); + cx.notify(); + } + } + // Fire click handlers during the bubble phase. + DispatchPhase::Bubble => { + if let Some(mouse_down) = captured_mouse_down.take() { + if interactive_bounds.visibly_contains(&event.position, cx) { + let mouse_click = ClickEvent { + down: mouse_down, + up: event.clone(), + }; + for listener in &click_listeners { + listener(&mouse_click, cx); + } + } + } } } }); } - } - let click_listeners = mem::take(&mut self.click_listeners); - let mut drag_listener = mem::take(&mut self.drag_listener); + if let Some(hover_listener) = self.hover_listener.take() { + let was_hovered = element_state + .hover_state + .get_or_insert_with(Default::default) + .clone(); + let has_mouse_down = element_state + .pending_mouse_down + .get_or_insert_with(Default::default) + .clone(); + let interactive_bounds = interactive_bounds.clone(); - if !click_listeners.is_empty() || drag_listener.is_some() { - let pending_mouse_down = element_state - .pending_mouse_down + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase != DispatchPhase::Bubble { + return; + } + let is_hovered = interactive_bounds.visibly_contains(&event.position, cx) + && has_mouse_down.borrow().is_none(); + let mut was_hovered = was_hovered.borrow_mut(); + + if is_hovered != was_hovered.clone() { + *was_hovered = is_hovered; + drop(was_hovered); + + hover_listener(&is_hovered, cx); + } + }); + } + + if let Some(tooltip_builder) = self.tooltip_builder.take() { + let active_tooltip = element_state + .active_tooltip + .get_or_insert_with(Default::default) + .clone(); + let pending_mouse_down = element_state + .pending_mouse_down + .get_or_insert_with(Default::default) + .clone(); + let interactive_bounds = interactive_bounds.clone(); + + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + let is_hovered = interactive_bounds.visibly_contains(&event.position, cx) + && pending_mouse_down.borrow().is_none(); + if !is_hovered { + active_tooltip.borrow_mut().take(); + return; + } + + if phase != DispatchPhase::Bubble { + return; + } + + if active_tooltip.borrow().is_none() { + let task = cx.spawn({ + let active_tooltip = active_tooltip.clone(); + let tooltip_builder = tooltip_builder.clone(); + + move |mut cx| async move { + cx.background_executor().timer(TOOLTIP_DELAY).await; + cx.update(|_, cx| { + active_tooltip.borrow_mut().replace(ActiveTooltip { + tooltip: Some(AnyTooltip { + view: tooltip_builder(cx), + cursor_offset: cx.mouse_position(), + }), + _task: None, + }); + cx.notify(); + }) + .ok(); + } + }); + active_tooltip.borrow_mut().replace(ActiveTooltip { + tooltip: None, + _task: Some(task), + }); + } + }); + + let active_tooltip = element_state + .active_tooltip + .get_or_insert_with(Default::default) + .clone(); + cx.on_mouse_event(move |_: &MouseDownEvent, _, _| { + active_tooltip.borrow_mut().take(); + }); + + if let Some(active_tooltip) = element_state + .active_tooltip + .get_or_insert_with(Default::default) + .borrow() + .as_ref() + { + if active_tooltip.tooltip.is_some() { + cx.active_tooltip = active_tooltip.tooltip.clone() + } + } + } + + let active_state = element_state + .clicked_state .get_or_insert_with(Default::default) .clone(); - let mouse_down = pending_mouse_down.borrow().clone(); - if let Some(mouse_down) = mouse_down { - if drag_listener.is_some() { - let active_state = element_state - .clicked_state - .get_or_insert_with(Default::default) - .clone(); - let interactive_bounds = interactive_bounds.clone(); + if active_state.borrow().is_clicked() { + cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Capture { + *active_state.borrow_mut() = ElementClickedState::default(); + cx.notify(); + } + }); + } else { + let active_group_bounds = self + .group_active_style + .as_ref() + .and_then(|group_active| GroupBounds::get(&group_active.group, cx)); + let interactive_bounds = interactive_bounds.clone(); + cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble && !cx.default_prevented() { + let group = active_group_bounds + .map_or(false, |bounds| bounds.contains(&down.position)); + let element = interactive_bounds.visibly_contains(&down.position, cx); + if group || element { + *active_state.borrow_mut() = ElementClickedState { group, element }; + cx.notify(); + } + } + }); + } - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if cx.active_drag.is_some() { - if phase == DispatchPhase::Capture { - cx.notify(); - } - } else if phase == DispatchPhase::Bubble - && interactive_bounds.visibly_contains(&event.position, cx) - && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD - { - let (drag_value, drag_listener) = drag_listener - .take() - .expect("The notify below should invalidate this callback"); + let overflow = style.overflow; + if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll { + if let Some(scroll_handle) = &self.scroll_handle { + scroll_handle.0.borrow_mut().overflow = overflow; + } - *active_state.borrow_mut() = ElementClickedState::default(); - let cursor_offset = event.position - bounds.origin; - let drag = (drag_listener)(drag_value.as_ref(), cx); - cx.active_drag = Some(AnyDrag { - view: drag, - value: drag_value, - cursor_offset, - }); + let scroll_offset = element_state + .scroll_offset + .get_or_insert_with(Rc::default) + .clone(); + let line_height = cx.line_height(); + let scroll_max = (content_size - bounds.size).max(&Size::default()); + let interactive_bounds = interactive_bounds.clone(); + + cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| { + if phase == DispatchPhase::Bubble + && interactive_bounds.visibly_contains(&event.position, cx) + { + let mut scroll_offset = scroll_offset.borrow_mut(); + let old_scroll_offset = *scroll_offset; + let delta = event.delta.pixel_delta(line_height); + + if overflow.x == Overflow::Scroll { + scroll_offset.x = + (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.)); + } + + if overflow.y == Overflow::Scroll { + scroll_offset.y = + (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.)); + } + + if *scroll_offset != old_scroll_offset { cx.notify(); cx.stop_propagation(); } - }); - } - - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Bubble - && interactive_bounds.visibly_contains(&event.position, cx) - { - let mouse_click = ClickEvent { - down: mouse_down.clone(), - up: event.clone(), - }; - for listener in &click_listeners { - listener(&mouse_click, cx); - } - } - *pending_mouse_down.borrow_mut() = None; - cx.notify(); - }); - } else { - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble - && event.button == MouseButton::Left - && interactive_bounds.visibly_contains(&event.position, cx) - { - *pending_mouse_down.borrow_mut() = Some(event.clone()); - cx.notify(); } }); } - } - if let Some(hover_listener) = self.hover_listener.take() { - let was_hovered = element_state - .hover_state - .get_or_insert_with(Default::default) - .clone(); - let has_mouse_down = element_state - .pending_mouse_down - .get_or_insert_with(Default::default) - .clone(); - let interactive_bounds = interactive_bounds.clone(); - - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if phase != DispatchPhase::Bubble { - return; - } - let is_hovered = interactive_bounds.visibly_contains(&event.position, cx) - && has_mouse_down.borrow().is_none(); - let mut was_hovered = was_hovered.borrow_mut(); - - if is_hovered != was_hovered.clone() { - *was_hovered = is_hovered; - drop(was_hovered); - - hover_listener(&is_hovered, cx); - } - }); - } - - if let Some(tooltip_builder) = self.tooltip_builder.take() { - let active_tooltip = element_state - .active_tooltip - .get_or_insert_with(Default::default) - .clone(); - let pending_mouse_down = element_state - .pending_mouse_down - .get_or_insert_with(Default::default) - .clone(); - let interactive_bounds = interactive_bounds.clone(); - - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - let is_hovered = interactive_bounds.visibly_contains(&event.position, cx) - && pending_mouse_down.borrow().is_none(); - if !is_hovered { - active_tooltip.borrow_mut().take(); - return; - } - - if phase != DispatchPhase::Bubble { - return; - } - - if active_tooltip.borrow().is_none() { - let task = cx.spawn({ - let active_tooltip = active_tooltip.clone(); - let tooltip_builder = tooltip_builder.clone(); - - move |mut cx| async move { - cx.background_executor().timer(TOOLTIP_DELAY).await; - cx.update(|_, cx| { - active_tooltip.borrow_mut().replace(ActiveTooltip { - tooltip: Some(AnyTooltip { - view: tooltip_builder(cx), - cursor_offset: cx.mouse_position(), - }), - _task: None, - }); - cx.notify(); - }) - .ok(); - } - }); - active_tooltip.borrow_mut().replace(ActiveTooltip { - tooltip: None, - _task: Some(task), - }); - } - }); - - let active_tooltip = element_state - .active_tooltip - .get_or_insert_with(Default::default) - .clone(); - cx.on_mouse_event(move |_: &MouseDownEvent, _, _| { - active_tooltip.borrow_mut().take(); - }); - - if let Some(active_tooltip) = element_state - .active_tooltip - .get_or_insert_with(Default::default) - .borrow() - .as_ref() - { - if active_tooltip.tooltip.is_some() { - cx.active_tooltip = active_tooltip.tooltip.clone() - } - } - } - - let active_state = element_state - .clicked_state - .get_or_insert_with(Default::default) - .clone(); - if active_state.borrow().is_clicked() { - cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Capture { - *active_state.borrow_mut() = ElementClickedState::default(); - cx.notify(); - } - }); - } else { - let active_group_bounds = self - .group_active_style - .as_ref() - .and_then(|group_active| GroupBounds::get(&group_active.group, cx)); - let interactive_bounds = interactive_bounds.clone(); - cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble && !cx.default_prevented() { - let group = - active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position)); - let element = interactive_bounds.visibly_contains(&down.position, cx); - if group || element { - *active_state.borrow_mut() = ElementClickedState { group, element }; - cx.notify(); - } - } - }); - } - - let overflow = style.overflow; - if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll { - if let Some(scroll_handle) = &self.scroll_handle { - scroll_handle.0.borrow_mut().overflow = overflow; + if let Some(group) = self.group.clone() { + GroupBounds::push(group, bounds, cx); } let scroll_offset = element_state .scroll_offset - .get_or_insert_with(Rc::default) - .clone(); - let line_height = cx.line_height(); - let scroll_max = (content_size - bounds.size).max(&Size::default()); - let interactive_bounds = interactive_bounds.clone(); + .as_ref() + .map(|scroll_offset| *scroll_offset.borrow()); - cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| { - if phase == DispatchPhase::Bubble - && interactive_bounds.visibly_contains(&event.position, cx) - { - let mut scroll_offset = scroll_offset.borrow_mut(); - let old_scroll_offset = *scroll_offset; - let delta = event.delta.pixel_delta(line_height); - - if overflow.x == Overflow::Scroll { - scroll_offset.x = - (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.)); + let key_down_listeners = mem::take(&mut self.key_down_listeners); + let key_up_listeners = mem::take(&mut self.key_up_listeners); + let action_listeners = mem::take(&mut self.action_listeners); + cx.with_key_dispatch( + self.key_context.clone(), + element_state.focus_handle.clone(), + |_, cx| { + for listener in key_down_listeners { + cx.on_key_event(move |event: &KeyDownEvent, phase, cx| { + listener(event, phase, cx); + }) } - if overflow.y == Overflow::Scroll { - scroll_offset.y = - (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.)); + for listener in key_up_listeners { + cx.on_key_event(move |event: &KeyUpEvent, phase, cx| { + listener(event, phase, cx); + }) } - if *scroll_offset != old_scroll_offset { - cx.notify(); - cx.stop_propagation(); + for (action_type, listener) in action_listeners { + cx.on_action(action_type, listener) } - } - }); - } - if let Some(group) = self.group.clone() { - GroupBounds::push(group, bounds, cx); - } + f(style, scroll_offset.unwrap_or_default(), cx) + }, + ); - let scroll_offset = element_state - .scroll_offset - .as_ref() - .map(|scroll_offset| *scroll_offset.borrow()); - - let key_down_listeners = mem::take(&mut self.key_down_listeners); - let key_up_listeners = mem::take(&mut self.key_up_listeners); - let action_listeners = mem::take(&mut self.action_listeners); - cx.with_key_dispatch( - self.key_context.clone(), - element_state.focus_handle.clone(), - |_, cx| { - for listener in key_down_listeners { - cx.on_key_event(move |event: &KeyDownEvent, phase, cx| { - listener(event, phase, cx); - }) - } - - for listener in key_up_listeners { - cx.on_key_event(move |event: &KeyUpEvent, phase, cx| { - listener(event, phase, cx); - }) - } - - for (action_type, listener) in action_listeners { - cx.on_action(action_type, listener) - } - - f(style, scroll_offset.unwrap_or_default(), cx) - }, - ); - - if let Some(group) = self.group.as_ref() { - GroupBounds::pop(group, cx); - } + if let Some(group) = self.group.as_ref() { + GroupBounds::pop(group, cx); + } + }); } pub fn compute_style( diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 26b1df4cda..7fba7ef477 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -200,58 +200,56 @@ impl Element for UniformList { bounds.lower_right() - point(border.right + padding.right, border.bottom), ); - cx.with_z_index(style.z_index.unwrap_or(0), |cx| { - style.paint(bounds, cx, |cx| { - if self.item_count > 0 { - let content_height = - item_height * self.item_count + padding.top + padding.bottom; - let min_scroll_offset = padded_bounds.size.height - content_height; - let is_scrolled = scroll_offset.y != px(0.); + style.paint(bounds, cx, |cx| { + if self.item_count > 0 { + let content_height = + item_height * self.item_count + padding.top + padding.bottom; + let min_scroll_offset = padded_bounds.size.height - content_height; + let is_scrolled = scroll_offset.y != px(0.); - if is_scrolled && scroll_offset.y < min_scroll_offset { - shared_scroll_offset.borrow_mut().y = min_scroll_offset; - scroll_offset.y = min_scroll_offset; - } + if is_scrolled && scroll_offset.y < min_scroll_offset { + shared_scroll_offset.borrow_mut().y = min_scroll_offset; + scroll_offset.y = min_scroll_offset; + } - if let Some(scroll_handle) = self.scroll_handle.clone() { - scroll_handle.0.borrow_mut().replace(ScrollHandleState { - item_height, - list_height: padded_bounds.size.height, - scroll_offset: shared_scroll_offset, - }); - } - - let first_visible_element_ix = - (-(scroll_offset.y + padding.top) / item_height).floor() as usize; - let last_visible_element_ix = - ((-scroll_offset.y + padded_bounds.size.height) / item_height) - .ceil() as usize; - let visible_range = first_visible_element_ix - ..cmp::min(last_visible_element_ix, self.item_count); - - let mut items = (self.render_items)(visible_range.clone(), cx); - cx.with_z_index(1, |cx| { - let content_mask = ContentMask { bounds }; - cx.with_content_mask(Some(content_mask), |cx| { - for (item, ix) in items.iter_mut().zip(visible_range) { - let item_origin = padded_bounds.origin - + point( - px(0.), - item_height * ix + scroll_offset.y + padding.top, - ); - let available_space = size( - AvailableSpace::Definite(padded_bounds.size.width), - AvailableSpace::Definite(item_height), - ); - item.draw(item_origin, available_space, cx); - } - }); + if let Some(scroll_handle) = self.scroll_handle.clone() { + scroll_handle.0.borrow_mut().replace(ScrollHandleState { + item_height, + list_height: padded_bounds.size.height, + scroll_offset: shared_scroll_offset, }); } - }); - }) + + let first_visible_element_ix = + (-(scroll_offset.y + padding.top) / item_height).floor() as usize; + let last_visible_element_ix = + ((-scroll_offset.y + padded_bounds.size.height) / item_height).ceil() + as usize; + let visible_range = first_visible_element_ix + ..cmp::min(last_visible_element_ix, self.item_count); + + let mut items = (self.render_items)(visible_range.clone(), cx); + cx.with_z_index(1, |cx| { + let content_mask = ContentMask { bounds }; + cx.with_content_mask(Some(content_mask), |cx| { + for (item, ix) in items.iter_mut().zip(visible_range) { + let item_origin = padded_bounds.origin + + point( + px(0.), + item_height * ix + scroll_offset.y + padding.top, + ); + let available_space = size( + AvailableSpace::Definite(padded_bounds.size.width), + AvailableSpace::Definite(item_height), + ); + item.draw(item_origin, available_space, cx); + } + }); + }); + } + }); }, - ); + ) } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index ca91a806ad..71799a6e00 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -946,16 +946,20 @@ impl<'a> WindowContext<'a> { } } - /// Returns true if the top-most opaque layer painted over this point was part of the - /// same layer as the given stacking order. + /// Returns true if there is no opaque layer containing the given point + /// on top of the given level. Layers whose level is an extension of the + /// level are not considered to be on top of the level. pub fn was_top_layer(&self, point: &Point, level: &StackingOrder) -> bool { - for (stack, bounds) in self.window.rendered_frame.depth_map.iter() { - if bounds.contains(point) { - return level.starts_with(stack) || stack.starts_with(level); + for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() { + if level >= opaque_level { + break; + } + + if bounds.contains(point) && !opaque_level.starts_with(level) { + return false; } } - - false + true } pub fn was_top_layer_under_active_drag( @@ -963,16 +967,19 @@ impl<'a> WindowContext<'a> { point: &Point, level: &StackingOrder, ) -> bool { - for (stack, bounds) in self.window.rendered_frame.depth_map.iter() { - if stack.starts_with(&[ACTIVE_DRAG_Z_INDEX]) { + for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() { + if level >= opaque_level { + break; + } + if opaque_level.starts_with(&[ACTIVE_DRAG_Z_INDEX]) { continue; } - if bounds.contains(point) { - return level.starts_with(stack) || stack.starts_with(level); + + if bounds.contains(point) && !opaque_level.starts_with(level) { + return false; } } - - false + true } /// Called during painting to get the current stacking order. diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index e44471a871..d0aba998b9 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -490,11 +490,13 @@ impl Render for Dock { let mut handle = div() .id("resize-handle") .on_drag(DraggedDock(position), |dock, cx| { + cx.stop_propagation(); cx.build_view(|_| dock.clone()) }) .on_click(cx.listener(|v, e: &ClickEvent, cx| { if e.down.button == MouseButton::Left && e.down.click_count == 2 { - v.resize_active_panel(None, cx) + v.resize_active_panel(None, cx); + cx.stop_propagation(); } })) .z_index(1); @@ -525,8 +527,8 @@ impl Render for Dock { .absolute() .top(px(0.)) .left(px(0.)) - .w_full() - .h(HANDLE_SIZE) + .h_full() + .w(HANDLE_SIZE) .cursor_col_resize(); } }