Fix panel resize (#3707)

Max Brunsfeld created

* [x] Reposition right dock handle
* [x] Handle mouse events correctly for drag and drop
* [x] Prevent drag events from passing through the resize handle to the
draggable items in the panels (channels, files)
* [x] Stop the editor gutter from stealing mouse move events

Change summary

crates/editor2/src/element.rs             |   2 
crates/gpui2/src/elements/div.rs          | 934 ++++++++++++------------
crates/gpui2/src/elements/uniform_list.rs |  96 +-
crates/gpui2/src/window.rs                |  33 
crates/workspace2/src/dock.rs             |   8 
5 files changed, 544 insertions(+), 529 deletions(-)

Detailed changes

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);

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::<crate::DebugBelow>())
-            && 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 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::<crate::DebugBelow>())
+                && 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()
                     {
-                        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 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 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();
+                                    }
                                 }
-                            }
-                        });
+                            });
 
-                        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;
-                                    };
+                            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();
+                                    }
+                                }
+                            });
 
-                                    eprintln!(
-                                        "This element is created at:\n{}:{}:{}",
-                                        location.file(),
-                                        location.line(),
-                                        location.column()
-                                    );
-
-                                    std::process::Command::new("zed")
-                                        .arg(format!(
-                                            "{}/{}:{}:{}",
-                                            dir.to_string_lossy(),
+                            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()
-                                        ))
-                                        .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,
-                )
-            });
-        }
+                };
+
+                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))
-        }
+            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(),
-        };
+            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();
+            // 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_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_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.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);
-            })
-        }
+            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));
+            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 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 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 mut drag_listener = mem::take(&mut self.drag_listener);
             let drop_listeners = mem::take(&mut self.drop_listeners);
-            let interactive_bounds = interactive_bounds.clone();
+            let click_listeners = mem::take(&mut self.click_listeners);
+
             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())
-                        {
-                            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();
+                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();
+                                    }
                                 }
                             }
-                        } else {
-                            cx.active_drag = None;
                         }
                     }
                 });
             }
-        }
 
-        let click_listeners = mem::take(&mut self.click_listeners);
-        let mut drag_listener = mem::take(&mut self.drag_listener);
+            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();
 
-        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 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();
+                let active_state = element_state
+                    .clicked_state
+                    .get_or_insert_with(Default::default)
+                    .clone();
 
-                    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
+                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)
-                            && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
                         {
-                            let (drag_value, drag_listener) = drag_listener
-                                .take()
-                                .expect("The notify below should invalidate this callback");
-
-                            *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.borrow_mut() = Some(event.clone());
                             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);
+                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();
+                                }
+                            }
                         }
                     }
-                    *pending_mouse_down.borrow_mut() = None;
-                    cx.notify();
                 });
-            } else {
+
+                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);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                });
+            }
+
+            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: &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(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(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 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| {
-                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();
+                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 is_hovered != was_hovered.clone() {
-                    *was_hovered = is_hovered;
-                    drop(was_hovered);
+                    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();
+                });
 
-                    hover_listener(&is_hovered, cx);
+                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()
+                    }
                 }
-            });
-        }
+            }
 
-        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
+            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| {
-                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;
+            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 phase != DispatchPhase::Bubble {
-                    return;
-                }
+                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();
 
-                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),
-                    });
-                }
-            });
+                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);
 
-            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 overflow.x == Overflow::Scroll {
+                            scroll_offset.x =
+                                (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
+                        }
 
-            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()
-                }
-            }
-        }
+                        if overflow.y == Overflow::Scroll {
+                            scroll_offset.y =
+                                (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
+                        }
 
-        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();
+                        if *scroll_offset != old_scroll_offset {
+                            cx.notify();
+                            cx.stop_propagation();
+                        }
                     }
-                }
-            });
-        }
+                });
+            }
 
-        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();
-
-            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.));
+                .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);
+                        })
                     }
 
-                    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);
-        }
-
-        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);
-                    })
-                }
+                    f(style, scroll_offset.unwrap_or_default(), 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(

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.);
-
-                            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);
-                                    }
-                                });
+                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 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);
+                                }
+                            });
+                        });
+                    }
+                });
             },
-        );
+        )
     }
 }
 

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<Pixels>, 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;
             }
-        }
 
-        false
+            if bounds.contains(point) && !opaque_level.starts_with(level) {
+                return false;
+            }
+        }
+        true
     }
 
     pub fn was_top_layer_under_active_drag(
@@ -963,16 +967,19 @@ impl<'a> WindowContext<'a> {
         point: &Point<Pixels>,
         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.

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();
                 }
             }