Don't move in paint

Nathan Sobo created

Change summary

crates/editor2/src/element.rs                 |   9 
crates/gpui2/src/element.rs                   |  24 
crates/gpui2/src/elements/canvas.rs           |   8 
crates/gpui2/src/elements/div.rs              | 516 +++++++++++++-------
crates/gpui2/src/elements/img.rs              |   5 
crates/gpui2/src/elements/list.rs             |   4 
crates/gpui2/src/elements/overlay.rs          |   4 
crates/gpui2/src/elements/svg.rs              |   8 
crates/gpui2/src/elements/text.rs             |  15 
crates/gpui2/src/elements/uniform_list.rs     |   6 
crates/gpui2/src/view.rs                      |  16 
crates/terminal_view2/src/terminal_element.rs | 299 ++++++-----
crates/ui2/src/components/popover_menu.rs     |   6 
crates/ui2/src/components/right_click_menu.rs |   8 
crates/workspace2/src/pane_group.rs           |   4 
15 files changed, 538 insertions(+), 394 deletions(-)

Detailed changes

crates/editor2/src/element.rs 🔗

@@ -933,7 +933,7 @@ impl EditorElement {
                                         cx.stop_propagation();
                                     },
                                 ))
-                                .draw(
+                                .draw2(
                                     fold_bounds.origin,
                                     fold_bounds.size,
                                     cx,
@@ -1199,11 +1199,10 @@ impl EditorElement {
                 .child(mouse_context_menu.context_menu.clone())
                 .anchor(AnchorCorner::TopLeft)
                 .snap_to_window();
-            element.draw(
+            element.into_any().draw(
                 gpui::Point::default(),
                 size(AvailableSpace::MinContent, AvailableSpace::MinContent),
                 cx,
-                |_, _| {},
             );
         }
     }
@@ -1496,7 +1495,7 @@ impl EditorElement {
         let scroll_left = scroll_position.x * layout.position_map.em_width;
         let scroll_top = scroll_position.y * layout.position_map.line_height;
 
-        for block in layout.blocks.drain(..) {
+        for mut block in layout.blocks.drain(..) {
             let mut origin = bounds.origin
                 + point(
                     Pixels::ZERO,
@@ -2781,7 +2780,7 @@ impl Element for EditorElement {
     }
 
     fn paint(
-        mut self,
+        &mut self,
         bounds: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
         cx: &mut gpui::WindowContext,

crates/gpui2/src/element.rs 🔗

@@ -23,7 +23,7 @@ pub trait IntoElement: Sized {
         self.into_element().into_any()
     }
 
-    fn draw<T, R>(
+    fn draw2<T, R>(
         self,
         origin: Point<Pixels>,
         available_space: Size<T>,
@@ -92,7 +92,7 @@ pub trait Element: 'static + IntoElement {
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State);
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
 
     fn into_any(self) -> AnyElement {
         AnyElement::new(self)
@@ -150,8 +150,8 @@ impl<C: RenderOnce> Element for Component<C> {
         }
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        let element = state.rendered_element.take().unwrap();
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+        let mut element = state.rendered_element.take().unwrap();
         if let Some(element_id) = element.element_id() {
             cx.with_element_state(element_id, |element_state, cx| {
                 let mut element_state = element_state.unwrap();
@@ -420,7 +420,7 @@ impl AnyElement {
         self.0.layout(cx)
     }
 
-    pub fn paint(mut self, cx: &mut WindowContext) {
+    pub fn paint(&mut self, cx: &mut WindowContext) {
         self.0.paint(cx)
     }
 
@@ -435,7 +435,7 @@ impl AnyElement {
 
     /// Initializes this element and performs layout in the available space, then paints it at the given origin.
     pub fn draw(
-        mut self,
+        &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
         cx: &mut WindowContext,
@@ -465,8 +465,8 @@ impl Element for AnyElement {
         (layout_id, ())
     }
 
-    fn paint(self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
-        self.paint(cx);
+    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
+        self.paint(cx)
     }
 }
 
@@ -508,5 +508,11 @@ impl Element for () {
         (cx.request_layout(&crate::Style::default(), None), ())
     }
 
-    fn paint(self, _bounds: Bounds<Pixels>, _state: &mut Self::State, _cx: &mut WindowContext) {}
+    fn paint(
+        &mut self,
+        _bounds: Bounds<Pixels>,
+        _state: &mut Self::State,
+        _cx: &mut WindowContext,
+    ) {
+    }
 }

crates/gpui2/src/elements/canvas.rs 🔗

@@ -4,13 +4,13 @@ use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled
 
 pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
     Canvas {
-        paint_callback: Box::new(callback),
+        paint_callback: Some(Box::new(callback)),
         style: StyleRefinement::default(),
     }
 }
 
 pub struct Canvas {
-    paint_callback: Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>,
+    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
     style: StyleRefinement,
 }
 
@@ -40,8 +40,8 @@ impl Element for Canvas {
         (layout_id, ())
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, _: &mut (), cx: &mut WindowContext) {
-        (self.paint_callback)(&bounds, cx)
+    fn paint(&mut self, bounds: Bounds<Pixels>, _: &mut (), cx: &mut WindowContext) {
+        (self.paint_callback.take().unwrap())(&bounds, cx)
     }
 }
 

crates/gpui2/src/elements/div.rs 🔗

@@ -35,6 +35,281 @@ pub struct DragMoveEvent<W: Render> {
     pub drag: View<W>,
 }
 
+impl Interactivity {
+    pub fn on_mouse_down(
+        &mut self,
+        button: MouseButton,
+        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_down_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble
+                    && event.button == button
+                    && bounds.visibly_contains(&event.position, cx)
+                {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_any_mouse_down(
+        &mut self,
+        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_down_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_mouse_up(
+        &mut self,
+        button: MouseButton,
+        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_up_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble
+                    && event.button == button
+                    && bounds.visibly_contains(&event.position, cx)
+                {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_any_mouse_up(
+        &mut self,
+        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_up_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_mouse_down_out(
+        &mut self,
+        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_down_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Capture && !bounds.visibly_contains(&event.position, cx)
+                {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_mouse_up_out(
+        &mut self,
+        button: MouseButton,
+        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_up_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Capture
+                    && event.button == button
+                    && !bounds.visibly_contains(&event.position, cx)
+                {
+                    (listener)(event, cx);
+                }
+            }));
+    }
+
+    pub fn on_mouse_move(
+        &mut self,
+        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
+    ) {
+        self.mouse_move_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
+                    (listener)(event, cx);
+                }
+            }));
+    }
+
+    pub fn on_drag_move<W>(
+        &mut self,
+        listener: impl Fn(&DragMoveEvent<W>, &mut WindowContext) + 'static,
+    ) where
+        W: Render,
+    {
+        self.mouse_move_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Capture
+                    && bounds.drag_target_contains(&event.position, cx)
+                {
+                    if let Some(view) = cx.active_drag().and_then(|view| view.downcast::<W>().ok())
+                    {
+                        (listener)(
+                            &DragMoveEvent {
+                                event: event.clone(),
+                                drag: view,
+                            },
+                            cx,
+                        );
+                    }
+                }
+            }));
+    }
+
+    pub fn on_scroll_wheel(
+        &mut self,
+        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
+    ) {
+        self.scroll_wheel_listeners
+            .push(Box::new(move |event, bounds, phase, cx| {
+                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
+                    (listener)(event, cx);
+                }
+            }));
+    }
+
+    pub fn capture_action<A: Action>(
+        &mut self,
+        listener: impl Fn(&A, &mut WindowContext) + 'static,
+    ) {
+        self.action_listeners.push((
+            TypeId::of::<A>(),
+            Box::new(move |action, phase, cx| {
+                let action = action.downcast_ref().unwrap();
+                if phase == DispatchPhase::Capture {
+                    (listener)(action, cx)
+                }
+            }),
+        ));
+    }
+
+    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
+        self.action_listeners.push((
+            TypeId::of::<A>(),
+            Box::new(move |action, phase, cx| {
+                let action = action.downcast_ref().unwrap();
+                if phase == DispatchPhase::Bubble {
+                    (listener)(action, cx)
+                }
+            }),
+        ));
+    }
+
+    pub fn on_boxed_action(
+        &mut self,
+        action: &Box<dyn Action>,
+        listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
+    ) {
+        let action = action.boxed_clone();
+        self.action_listeners.push((
+            (*action).type_id(),
+            Box::new(move |_, phase, cx| {
+                if phase == DispatchPhase::Bubble {
+                    (listener)(&action, cx)
+                }
+            }),
+        ));
+    }
+
+    pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
+        self.key_down_listeners
+            .push(Box::new(move |event, phase, cx| {
+                if phase == DispatchPhase::Bubble {
+                    (listener)(event, cx)
+                }
+            }));
+    }
+
+    pub fn capture_key_down(
+        &mut self,
+        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
+    ) {
+        self.key_down_listeners
+            .push(Box::new(move |event, phase, cx| {
+                if phase == DispatchPhase::Capture {
+                    listener(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
+        self.key_up_listeners
+            .push(Box::new(move |event, phase, cx| {
+                if phase == DispatchPhase::Bubble {
+                    listener(event, cx)
+                }
+            }));
+    }
+
+    pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
+        self.key_up_listeners
+            .push(Box::new(move |event, phase, cx| {
+                if phase == DispatchPhase::Capture {
+                    listener(event, cx)
+                }
+            }));
+    }
+
+    pub fn on_drop<W: 'static>(
+        &mut self,
+        listener: impl Fn(&View<W>, &mut WindowContext) + 'static,
+    ) {
+        self.drop_listeners.push((
+            TypeId::of::<W>(),
+            Box::new(move |dragged_view, cx| {
+                listener(&dragged_view.downcast().unwrap(), cx);
+            }),
+        ));
+    }
+
+    pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
+    where
+        Self: Sized,
+    {
+        self.click_listeners
+            .push(Box::new(move |event, cx| listener(event, cx)));
+    }
+
+    pub fn on_drag<W>(&mut self, constructor: impl Fn(&mut WindowContext) -> View<W> + 'static)
+    where
+        Self: Sized,
+        W: 'static + Render,
+    {
+        debug_assert!(
+            self.drag_listener.is_none(),
+            "calling on_drag more than once on the same element is not supported"
+        );
+        self.drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag {
+            view: constructor(cx).into(),
+            cursor_offset,
+        }));
+    }
+
+    pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
+    where
+        Self: Sized,
+    {
+        debug_assert!(
+            self.hover_listener.is_none(),
+            "calling on_hover more than once on the same element is not supported"
+        );
+        self.hover_listener = Some(Box::new(listener));
+    }
+
+    pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
+    where
+        Self: Sized,
+    {
+        debug_assert!(
+            self.tooltip_builder.is_none(),
+            "calling tooltip more than once on the same element is not supported"
+        );
+        self.tooltip_builder = Some(Rc::new(build_tooltip));
+    }
+}
+
 pub trait InteractiveElement: Sized {
     fn interactivity(&mut self) -> &mut Interactivity;
 
@@ -92,16 +367,7 @@ pub trait InteractiveElement: Sized {
         button: MouseButton,
         listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().mouse_down_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && event.button == button
-                    && bounds.visibly_contains(&event.position, cx)
-                {
-                    (listener)(event, cx)
-                }
-            },
-        ));
+        self.interactivity().on_mouse_down(button, listener);
         self
     }
 
@@ -109,13 +375,7 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().mouse_down_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
-                    (listener)(event, cx)
-                }
-            },
-        ));
+        self.interactivity().on_any_mouse_down(listener);
         self
     }
 
@@ -124,30 +384,7 @@ pub trait InteractiveElement: Sized {
         button: MouseButton,
         listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity()
-            .mouse_up_listeners
-            .push(Box::new(move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && event.button == button
-                    && bounds.visibly_contains(&event.position, cx)
-                {
-                    (listener)(event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_any_mouse_up(
-        mut self,
-        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
-    ) -> Self {
-        self.interactivity()
-            .mouse_up_listeners
-            .push(Box::new(move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
-                    (listener)(event, cx)
-                }
-            }));
+        self.interactivity().on_mouse_up(button, listener);
         self
     }
 
@@ -155,14 +392,7 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().mouse_down_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Capture && !bounds.visibly_contains(&event.position, cx)
-                {
-                    (listener)(event, cx)
-                }
-            },
-        ));
+        self.interactivity().on_mouse_down_out(listener);
         self
     }
 
@@ -171,16 +401,7 @@ pub trait InteractiveElement: Sized {
         button: MouseButton,
         listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity()
-            .mouse_up_listeners
-            .push(Box::new(move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Capture
-                    && event.button == button
-                    && !bounds.visibly_contains(&event.position, cx)
-                {
-                    (listener)(event, cx);
-                }
-            }));
+        self.interactivity().on_mouse_up_out(button, listener);
         self
     }
 
@@ -188,13 +409,7 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().mouse_move_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
-                    (listener)(event, cx);
-                }
-            },
-        ));
+        self.interactivity().on_mouse_move(listener);
         self
     }
 
@@ -205,24 +420,7 @@ pub trait InteractiveElement: Sized {
     where
         W: Render,
     {
-        self.interactivity().mouse_move_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Capture
-                    && bounds.drag_target_contains(&event.position, cx)
-                {
-                    if let Some(view) = cx.active_drag().and_then(|view| view.downcast::<W>().ok())
-                    {
-                        (listener)(
-                            &DragMoveEvent {
-                                event: event.clone(),
-                                drag: view,
-                            },
-                            cx,
-                        );
-                    }
-                }
-            },
-        ));
+        self.interactivity().on_drag_move(listener);
         self
     }
 
@@ -230,13 +428,7 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().scroll_wheel_listeners.push(Box::new(
-            move |event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
-                    (listener)(event, cx);
-                }
-            },
-        ));
+        self.interactivity().on_scroll_wheel(listener);
         self
     }
 
@@ -245,29 +437,13 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&A, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity().action_listeners.push((
-            TypeId::of::<A>(),
-            Box::new(move |action, phase, cx| {
-                let action = action.downcast_ref().unwrap();
-                if phase == DispatchPhase::Capture {
-                    (listener)(action, cx)
-                }
-            }),
-        ));
+        self.interactivity().capture_action(listener);
         self
     }
 
     /// Add a listener for the given action, fires during the bubble event phase
     fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
-        self.interactivity().action_listeners.push((
-            TypeId::of::<A>(),
-            Box::new(move |action, phase, cx| {
-                let action = action.downcast_ref().unwrap();
-                if phase == DispatchPhase::Bubble {
-                    (listener)(action, cx)
-                }
-            }),
-        ));
+        self.interactivity().on_action(listener);
         self
     }
 
@@ -276,15 +452,7 @@ pub trait InteractiveElement: Sized {
         action: &Box<dyn Action>,
         listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
     ) -> Self {
-        let action = action.boxed_clone();
-        self.interactivity().action_listeners.push((
-            (*action).type_id(),
-            Box::new(move |_, phase, cx| {
-                if phase == DispatchPhase::Bubble {
-                    (listener)(&action, cx)
-                }
-            }),
-        ));
+        self.interactivity().on_boxed_action(action, listener);
         self
     }
 
@@ -292,13 +460,7 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity()
-            .key_down_listeners
-            .push(Box::new(move |event, phase, cx| {
-                if phase == DispatchPhase::Bubble {
-                    (listener)(event, cx)
-                }
-            }));
+        self.interactivity().on_key_down(listener);
         self
     }
 
@@ -306,24 +468,12 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity()
-            .key_down_listeners
-            .push(Box::new(move |event, phase, cx| {
-                if phase == DispatchPhase::Capture {
-                    listener(event, cx)
-                }
-            }));
+        self.interactivity().capture_key_down(listener);
         self
     }
 
     fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
-        self.interactivity()
-            .key_up_listeners
-            .push(Box::new(move |event, phase, cx| {
-                if phase == DispatchPhase::Bubble {
-                    listener(event, cx)
-                }
-            }));
+        self.interactivity().on_key_up(listener);
         self
     }
 
@@ -331,13 +481,15 @@ pub trait InteractiveElement: Sized {
         mut self,
         listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.interactivity()
-            .key_up_listeners
-            .push(Box::new(move |event, phase, cx| {
-                if phase == DispatchPhase::Capture {
-                    listener(event, cx)
-                }
-            }));
+        self.interactivity().capture_key_up(listener);
+        self
+    }
+
+    fn on_drop<W: 'static>(
+        mut self,
+        listener: impl Fn(&View<W>, &mut WindowContext) + 'static,
+    ) -> Self {
+        self.interactivity().on_drop(listener);
         self
     }
 
@@ -362,19 +514,6 @@ pub trait InteractiveElement: Sized {
         ));
         self
     }
-
-    fn on_drop<W: 'static>(
-        mut self,
-        listener: impl Fn(&View<W>, &mut WindowContext) + 'static,
-    ) -> Self {
-        self.interactivity().drop_listeners.push((
-            TypeId::of::<W>(),
-            Box::new(move |dragged_view, cx| {
-                listener(&dragged_view.downcast().unwrap(), cx);
-            }),
-        ));
-        self
-    }
 }
 
 pub trait StatefulInteractiveElement: InteractiveElement {
@@ -431,9 +570,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .click_listeners
-            .push(Box::new(move |event, cx| listener(event, cx)));
+        self.interactivity().on_click(listener);
         self
     }
 
@@ -442,14 +579,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
         Self: Sized,
         W: 'static + Render,
     {
-        debug_assert!(
-            self.interactivity().drag_listener.is_none(),
-            "calling on_drag more than once on the same element is not supported"
-        );
-        self.interactivity().drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag {
-            view: constructor(cx).into(),
-            cursor_offset,
-        }));
+        self.interactivity().on_drag(constructor);
         self
     }
 
@@ -457,11 +587,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     where
         Self: Sized,
     {
-        debug_assert!(
-            self.interactivity().hover_listener.is_none(),
-            "calling on_hover more than once on the same element is not supported"
-        );
-        self.interactivity().hover_listener = Some(Box::new(listener));
+        self.interactivity().on_hover(listener);
         self
     }
 
@@ -469,11 +595,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     where
         Self: Sized,
     {
-        debug_assert!(
-            self.interactivity().tooltip_builder.is_none(),
-            "calling tooltip more than once on the same element is not supported"
-        );
-        self.interactivity().tooltip_builder = Some(Rc::new(build_tooltip));
+        self.interactivity().tooltip(build_tooltip);
         self
     }
 }
@@ -529,6 +651,7 @@ pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext
 
 #[track_caller]
 pub fn div() -> Div {
+    #[allow(unused_mut)]
     let mut div = Div {
         interactivity: Interactivity::default(),
         children: SmallVec::default(),
@@ -598,7 +721,7 @@ impl Element for Div {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
@@ -649,7 +772,7 @@ impl Element for Div {
                         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 self.children {
+                                    for child in &mut self.children {
                                         child.paint(cx);
                                     }
                                 })
@@ -769,7 +892,7 @@ impl Interactivity {
     }
 
     pub fn paint(
-        mut self,
+        &mut self,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
         element_state: &mut InteractiveElementState,
@@ -788,7 +911,7 @@ impl Interactivity {
             && bounds.contains(&cx.mouse_position())
         {
             const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
-            let element_id = format!("{:?}", self.element_id.unwrap());
+            let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
             let str_len = element_id.len();
 
             let render_debug_text = |cx: &mut WindowContext| {
@@ -934,28 +1057,28 @@ impl Interactivity {
             });
         }
 
-        for listener in self.mouse_down_listeners {
+        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 {
+        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 {
+        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 {
+        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);
@@ -1024,8 +1147,8 @@ impl Interactivity {
             }
         }
 
-        let click_listeners = self.click_listeners;
-        let drag_listener = self.drag_listener;
+        let click_listeners = mem::take(&mut self.click_listeners);
+        let drag_listener = mem::take(&mut self.drag_listener);
 
         if !click_listeners.is_empty() || drag_listener.is_some() {
             let pending_mouse_down = element_state
@@ -1267,23 +1390,26 @@ impl Interactivity {
             .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 self.key_down_listeners {
+                for listener in key_down_listeners {
                     cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
                         listener(event, phase, cx);
                     })
                 }
 
-                for listener in self.key_up_listeners {
+                for listener in key_up_listeners {
                     cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
                         listener(event, phase, cx);
                     })
                 }
 
-                for (action_type, listener) in self.action_listeners {
+                for (action_type, listener) in action_listeners {
                     cx.on_action(action_type, listener)
                 }
 
@@ -1522,7 +1648,7 @@ where
         self.element.layout(state, cx)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
         self.element.paint(bounds, state, cx)
     }
 }
@@ -1596,7 +1722,7 @@ where
         self.element.layout(state, cx)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
         self.element.paint(bounds, state, cx)
     }
 }

crates/gpui2/src/elements/img.rs 🔗

@@ -81,11 +81,12 @@ impl Element for Img {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
     ) {
+        let source = self.source.clone();
         self.interactivity.paint(
             bounds,
             bounds.size,
@@ -94,7 +95,7 @@ impl Element for Img {
             |style, _scroll_offset, cx| {
                 let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
                 cx.with_z_index(1, |cx| {
-                    match self.source {
+                    match source {
                         ImageSource::Uri(uri) => {
                             let image_future = cx.image_cache.get(uri.clone());
                             if let Some(data) = image_future

crates/gpui2/src/elements/list.rs 🔗

@@ -257,7 +257,7 @@ impl Element for List {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: crate::Bounds<crate::Pixels>,
         _state: &mut Self::State,
         cx: &mut crate::WindowContext,
@@ -385,7 +385,7 @@ impl Element for List {
         // Paint the visible items
         let mut item_origin = bounds.origin;
         item_origin.y -= scroll_top.offset_in_item;
-        for mut item_element in item_elements {
+        for item_element in &mut item_elements {
             let item_height = item_element.measure(available_item_space, cx).height;
             item_element.draw(item_origin, available_item_space, cx);
             item_origin.y += item_height;

crates/gpui2/src/elements/overlay.rs 🔗

@@ -81,7 +81,7 @@ impl Element for Overlay {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: crate::Bounds<crate::Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
@@ -149,7 +149,7 @@ impl Element for Overlay {
 
         cx.with_element_offset(desired.origin - bounds.origin, |cx| {
             cx.break_content_mask(|cx| {
-                for child in self.children {
+                for child in &mut self.children {
                     child.paint(cx);
                 }
             })

crates/gpui2/src/elements/svg.rs 🔗

@@ -36,8 +36,12 @@ impl Element for Svg {
         })
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, element_state: &mut Self::State, cx: &mut WindowContext)
-    where
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        element_state: &mut Self::State,
+        cx: &mut WindowContext,
+    ) where
         Self: Sized,
     {
         self.interactivity

crates/gpui2/src/elements/text.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
 use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
 use smallvec::SmallVec;
-use std::{cell::Cell, ops::Range, rc::Rc, sync::Arc};
+use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
 use util::ResultExt;
 
 impl Element for &'static str {
@@ -22,7 +22,7 @@ impl Element for &'static str {
         (layout_id, state)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
         state.paint(bounds, self, cx)
     }
 }
@@ -52,7 +52,7 @@ impl Element for SharedString {
         (layout_id, state)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
         let text_str: &str = self.as_ref();
         state.paint(bounds, text_str, cx)
     }
@@ -128,7 +128,7 @@ impl Element for StyledText {
         (layout_id, state)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
         state.paint(bounds, &self.text, cx)
     }
 }
@@ -356,8 +356,8 @@ impl Element for InteractiveText {
         }
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        if let Some(click_listener) = self.click_listener {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+        if let Some(click_listener) = self.click_listener.take() {
             if let Some(ix) = state
                 .text_state
                 .index_for_position(bounds, cx.mouse_position())
@@ -374,13 +374,14 @@ impl Element for InteractiveText {
             let text_state = state.text_state.clone();
             let mouse_down = state.mouse_down_index.clone();
             if let Some(mouse_down_index) = mouse_down.get() {
+                let clickable_ranges = mem::take(&mut self.clickable_ranges);
                 cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
                     if phase == DispatchPhase::Bubble {
                         if let Some(mouse_up_index) =
                             text_state.index_for_position(bounds, event.position)
                         {
                             click_listener(
-                                &self.clickable_ranges,
+                                &clickable_ranges,
                                 InteractiveTextClickEvent {
                                     mouse_down_index,
                                     mouse_up_index,

crates/gpui2/src/elements/uniform_list.rs 🔗

@@ -155,7 +155,7 @@ impl Element for UniformList {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: Bounds<crate::Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
@@ -220,11 +220,11 @@ impl Element for UniformList {
                             let visible_range = first_visible_element_ix
                                 ..cmp::min(last_visible_element_ix, self.item_count);
 
-                            let items = (self.render_items)(visible_range.clone(), cx);
+                            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.into_iter().zip(visible_range) {
+                                    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);
                                         let available_space = size(

crates/gpui2/src/view.rs 🔗

@@ -90,7 +90,7 @@ impl<V: Render> Element for View<V> {
         (layout_id, Some(element))
     }
 
-    fn paint(self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
         element.take().unwrap().paint(cx);
     }
 }
@@ -170,7 +170,7 @@ impl<V> Eq for WeakView<V> {}
 pub struct AnyView {
     model: AnyModel,
     layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
-    paint: fn(&AnyView, AnyElement, &mut WindowContext),
+    paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
 }
 
 impl AnyView {
@@ -209,7 +209,7 @@ impl AnyView {
     ) {
         cx.with_absolute_element_offset(origin, |cx| {
             let start_time = std::time::Instant::now();
-            let (layout_id, rendered_element) = (self.layout)(self, cx);
+            let (layout_id, mut rendered_element) = (self.layout)(self, cx);
             let duration = start_time.elapsed();
             println!("request layout: {:?}", duration);
 
@@ -219,7 +219,7 @@ impl AnyView {
             println!("compute layout: {:?}", duration);
 
             let start_time = std::time::Instant::now();
-            (self.paint)(self, rendered_element, cx);
+            (self.paint)(self, &mut rendered_element, cx);
             let duration = start_time.elapsed();
             println!("paint: {:?}", duration);
         })
@@ -248,12 +248,12 @@ impl Element for AnyView {
         (layout_id, Some(state))
     }
 
-    fn paint(self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
         debug_assert!(
             state.is_some(),
             "state is None. Did you include an AnyView twice in the tree?"
         );
-        (self.paint)(&self, state.take().unwrap(), cx)
+        (self.paint)(&self, state.as_mut().unwrap(), cx)
     }
 }
 
@@ -284,7 +284,7 @@ impl IntoElement for AnyView {
 pub struct AnyWeakView {
     model: AnyWeakModel,
     layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
-    paint: fn(&AnyView, AnyElement, &mut WindowContext),
+    paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
 }
 
 impl AnyWeakView {
@@ -335,7 +335,7 @@ mod any_view {
 
     pub(crate) fn paint<V: 'static + Render>(
         _view: &AnyView,
-        element: AnyElement,
+        element: &mut AnyElement,
         cx: &mut WindowContext,
     ) {
         element.paint(cx);

crates/terminal_view2/src/terminal_element.rs 🔗

@@ -2,8 +2,8 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
 use gpui::{
     black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
     Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle,
-    FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, IntoElement,
-    LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
+    FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, Interactivity,
+    IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
     PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled,
     TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
 };
@@ -145,11 +145,11 @@ pub struct TerminalElement {
     focused: bool,
     cursor_visible: bool,
     can_navigate_to_selected_word: bool,
-    interactivity: gpui::Interactivity,
+    interactivity: Interactivity,
 }
 
 impl InteractiveElement for TerminalElement {
-    fn interactivity(&mut self) -> &mut gpui::Interactivity {
+    fn interactivity(&mut self) -> &mut Interactivity {
         &mut self.interactivity
     }
 }
@@ -605,141 +605,157 @@ impl TerminalElement {
     }
 
     fn register_mouse_listeners(
-        self,
+        &mut self,
         origin: Point<Pixels>,
         mode: TermMode,
         bounds: Bounds<Pixels>,
         cx: &mut WindowContext,
-    ) -> Self {
+    ) {
         let focus = self.focus.clone();
-        let connection = self.terminal.clone();
-
-        let mut this = self
-            .on_mouse_down(MouseButton::Left, {
-                let connection = connection.clone();
-                let focus = focus.clone();
-                move |e, cx| {
-                    cx.focus(&focus);
-                    //todo!(context menu)
-                    // v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
-                    connection.update(cx, |terminal, cx| {
-                        terminal.mouse_down(&e, origin);
+        let terminal = self.terminal.clone();
+
+        self.interactivity.on_mouse_down(MouseButton::Left, {
+            let terminal = terminal.clone();
+            let focus = focus.clone();
+            move |e, cx| {
+                cx.focus(&focus);
+                //todo!(context menu)
+                // v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
+                terminal.update(cx, |terminal, cx| {
+                    terminal.mouse_down(&e, origin);
 
+                    cx.notify();
+                })
+            }
+        });
+        self.interactivity.on_mouse_move({
+            let terminal = terminal.clone();
+            let focus = focus.clone();
+            move |e, cx| {
+                if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() {
+                    terminal.update(cx, |terminal, cx| {
+                        terminal.mouse_drag(e, origin, bounds);
                         cx.notify();
                     })
                 }
-            })
-            .on_mouse_move({
-                let connection = connection.clone();
-                let focus = focus.clone();
-                move |e, cx| {
-                    if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() {
-                        connection.update(cx, |terminal, cx| {
-                            terminal.mouse_drag(e, origin, bounds);
-                            cx.notify();
-                        })
-                    }
-                }
-            })
-            .on_mouse_up(
-                MouseButton::Left,
-                TerminalElement::generic_button_handler(
-                    connection.clone(),
-                    origin,
-                    focus.clone(),
-                    move |terminal, origin, e, cx| {
-                        terminal.mouse_up(&e, origin, cx);
-                    },
-                ),
-            )
-            .on_click({
-                let connection = connection.clone();
-                move |e, cx| {
-                    if e.down.button == MouseButton::Right {
-                        let mouse_mode = connection.update(cx, |terminal, _cx| {
-                            terminal.mouse_mode(e.down.modifiers.shift)
-                        });
-
-                        if !mouse_mode {
-                            //todo!(context menu)
-                            // view.deploy_context_menu(e.position, cx);
-                        }
-                    }
-                }
-            })
-            .on_mouse_move({
-                let connection = connection.clone();
-                let focus = focus.clone();
-                move |e, cx| {
-                    if focus.is_focused(cx) {
-                        connection.update(cx, |terminal, cx| {
-                            terminal.mouse_move(&e, origin);
-                            cx.notify();
-                        })
+            }
+        });
+        self.interactivity.on_mouse_up(
+            MouseButton::Left,
+            TerminalElement::generic_button_handler(
+                terminal.clone(),
+                origin,
+                focus.clone(),
+                move |terminal, origin, e, cx| {
+                    terminal.mouse_up(&e, origin, cx);
+                },
+            ),
+        );
+        self.interactivity.on_click({
+            let terminal = terminal.clone();
+            move |e, cx| {
+                if e.down.button == MouseButton::Right {
+                    let mouse_mode = terminal.update(cx, |terminal, _cx| {
+                        terminal.mouse_mode(e.down.modifiers.shift)
+                    });
+
+                    if !mouse_mode {
+                        //todo!(context menu)
+                        // view.deploy_context_menu(e.position, cx);
                     }
                 }
-            })
-            .on_scroll_wheel({
-                let connection = connection.clone();
-                move |e, cx| {
-                    connection.update(cx, |terminal, cx| {
-                        terminal.scroll_wheel(e, origin);
+            }
+        });
+
+        self.interactivity.on_mouse_move({
+            let terminal = terminal.clone();
+            let focus = focus.clone();
+            move |e, cx| {
+                if focus.is_focused(cx) {
+                    terminal.update(cx, |terminal, cx| {
+                        terminal.mouse_move(&e, origin);
                         cx.notify();
                     })
                 }
-            });
+            }
+        });
+        self.interactivity.on_scroll_wheel({
+            let terminal = terminal.clone();
+            move |e, cx| {
+                terminal.update(cx, |terminal, cx| {
+                    terminal.scroll_wheel(e, origin);
+                    cx.notify();
+                })
+            }
+        });
+
+        self.interactivity.on_drop::<ExternalPaths>({
+            let focus = focus.clone();
+            let terminal = terminal.clone();
+            move |external_paths, cx| {
+                cx.focus(&focus);
+                let mut new_text = external_paths
+                    .read(cx)
+                    .paths()
+                    .iter()
+                    .map(|path| format!(" {path:?}"))
+                    .join("");
+                new_text.push(' ');
+                terminal.update(cx, |terminal, _| {
+                    // todo!() long paths are not displayed properly albeit the text is there
+                    terminal.paste(&new_text);
+                });
+            }
+        });
 
         // Mouse mode handlers:
         // All mouse modes need the extra click handlers
         if mode.intersects(TermMode::MOUSE_MODE) {
-            this = this
-                .on_mouse_down(
-                    MouseButton::Right,
-                    TerminalElement::generic_button_handler(
-                        connection.clone(),
-                        origin,
-                        focus.clone(),
-                        move |terminal, origin, e, _cx| {
-                            terminal.mouse_down(&e, origin);
-                        },
-                    ),
-                )
-                .on_mouse_down(
-                    MouseButton::Middle,
-                    TerminalElement::generic_button_handler(
-                        connection.clone(),
-                        origin,
-                        focus.clone(),
-                        move |terminal, origin, e, _cx| {
-                            terminal.mouse_down(&e, origin);
-                        },
-                    ),
-                )
-                .on_mouse_up(
-                    MouseButton::Right,
-                    TerminalElement::generic_button_handler(
-                        connection.clone(),
-                        origin,
-                        focus.clone(),
-                        move |terminal, origin, e, cx| {
-                            terminal.mouse_up(&e, origin, cx);
-                        },
-                    ),
-                )
-                .on_mouse_up(
-                    MouseButton::Middle,
-                    TerminalElement::generic_button_handler(
-                        connection,
-                        origin,
-                        focus,
-                        move |terminal, origin, e, cx| {
-                            terminal.mouse_up(&e, origin, cx);
-                        },
-                    ),
-                )
+            self.interactivity.on_mouse_down(
+                MouseButton::Right,
+                TerminalElement::generic_button_handler(
+                    terminal.clone(),
+                    origin,
+                    focus.clone(),
+                    move |terminal, origin, e, _cx| {
+                        terminal.mouse_down(&e, origin);
+                    },
+                ),
+            );
+            self.interactivity.on_mouse_down(
+                MouseButton::Middle,
+                TerminalElement::generic_button_handler(
+                    terminal.clone(),
+                    origin,
+                    focus.clone(),
+                    move |terminal, origin, e, _cx| {
+                        terminal.mouse_down(&e, origin);
+                    },
+                ),
+            );
+            self.interactivity.on_mouse_up(
+                MouseButton::Right,
+                TerminalElement::generic_button_handler(
+                    terminal.clone(),
+                    origin,
+                    focus.clone(),
+                    move |terminal, origin, e, cx| {
+                        terminal.mouse_up(&e, origin, cx);
+                    },
+                ),
+            );
+            self.interactivity.on_mouse_up(
+                MouseButton::Middle,
+                TerminalElement::generic_button_handler(
+                    terminal,
+                    origin,
+                    focus,
+                    move |terminal, origin, e, cx| {
+                        terminal.mouse_up(&e, origin, cx);
+                    },
+                ),
+            );
         }
-
-        this
     }
 }
 
@@ -764,7 +780,12 @@ impl Element for TerminalElement {
         (layout_id, interactive_state)
     }
 
-    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext<'_>) {
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        state: &mut Self::State,
+        cx: &mut WindowContext<'_>,
+    ) {
         let mut layout = self.compute_layout(bounds, cx);
 
         let theme = cx.theme();
@@ -783,33 +804,19 @@ impl Element for TerminalElement {
 
         let terminal_focus_handle = self.focus.clone();
         let terminal_handle = self.terminal.clone();
-        let mut this: TerminalElement = self
-            .register_mouse_listeners(origin, layout.mode, bounds, cx)
-            .drag_over::<ExternalPaths>(|style| {
-                // todo!() why does not it work? z-index of elements?
-                style.bg(cx.theme().colors().ghost_element_hover)
-            })
-            .on_drop::<ExternalPaths>(move |external_paths, cx| {
-                cx.focus(&terminal_focus_handle);
-                let mut new_text = external_paths
-                    .read(cx)
-                    .paths()
-                    .iter()
-                    .map(|path| format!(" {path:?}"))
-                    .join("");
-                new_text.push(' ');
-                terminal_handle.update(cx, |terminal, _| {
-                    // todo!() long paths are not displayed properly albeit the text is there
-                    terminal.paste(&new_text);
-                });
-            });
+        self.register_mouse_listeners(origin, layout.mode, bounds, cx);
 
-        let interactivity = mem::take(&mut this.interactivity);
+        // todo!(change this to work in terms of on_drag_move or some such)
+        // .drag_over::<ExternalPaths>(|style| {
+        //     // todo!() why does not it work? z-index of elements?
+        //     style.bg(cx.theme().colors().ghost_element_hover)
+        // })
 
+        let mut interactivity = mem::take(&mut self.interactivity);
         interactivity.paint(bounds, bounds.size, state, cx, |_, _, cx| {
-            cx.handle_input(&this.focus, terminal_input_handler);
+            cx.handle_input(&self.focus, terminal_input_handler);
 
-            this.register_key_listeners(cx);
+            self.register_key_listeners(cx);
 
             for rect in &layout.rects {
                 rect.paint(origin, &layout, cx);
@@ -840,7 +847,7 @@ impl Element for TerminalElement {
                 }
             });
 
-            if this.cursor_visible {
+            if self.cursor_visible {
                 cx.with_z_index(3, |cx| {
                     if let Some(cursor) = &layout.cursor {
                         cursor.paint(origin, cx);
@@ -848,7 +855,7 @@ impl Element for TerminalElement {
                 });
             }
 
-            if let Some(element) = layout.hyperlink_tooltip.take() {
+            if let Some(mut element) = layout.hyperlink_tooltip.take() {
                 let width: AvailableSpace = bounds.size.width.into();
                 let height: AvailableSpace = bounds.size.height.into();
                 element.draw(origin, Size { width, height }, cx)

crates/ui2/src/components/popover_menu.rs 🔗

@@ -182,12 +182,12 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
     }
 
     fn paint(
-        self,
+        &mut self,
         _: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
     ) {
-        if let Some(child) = element_state.child_element.take() {
+        if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);
         }
 
@@ -195,7 +195,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
             element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
         }
 
-        if let Some(menu) = element_state.menu_element.take() {
+        if let Some(mut menu) = element_state.menu_element.take() {
             menu.paint(cx);
 
             if let Some(child_bounds) = element_state.child_bounds {

crates/ui2/src/components/right_click_menu.rs 🔗

@@ -112,21 +112,21 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
     }
 
     fn paint(
-        self,
+        &mut self,
         bounds: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
         cx: &mut WindowContext,
     ) {
-        if let Some(child) = element_state.child_element.take() {
+        if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);
         }
 
-        if let Some(menu) = element_state.menu_element.take() {
+        if let Some(mut menu) = element_state.menu_element.take() {
             menu.paint(cx);
             return;
         }
 
-        let Some(builder) = self.menu_builder else {
+        let Some(builder) = self.menu_builder.take() else {
             return;
         };
         let menu = element_state.menu.clone();

crates/workspace2/src/pane_group.rs 🔗

@@ -896,7 +896,7 @@ mod element {
         }
 
         fn paint(
-            self,
+            &mut self,
             bounds: gpui::Bounds<ui::prelude::Pixels>,
             state: &mut Self::State,
             cx: &mut ui::prelude::WindowContext,
@@ -912,7 +912,7 @@ mod element {
             let mut bounding_boxes = self.bounding_boxes.lock();
             bounding_boxes.clear();
 
-            for (ix, child) in self.children.into_iter().enumerate() {
+            for (ix, mut child) in self.children.iter_mut().enumerate() {
                 //todo!(active_pane_magnification)
                 // If usign active pane magnification, need to switch to using
                 // 1 for all non-active panes, and then the magnification for the