Re-enable scrolling for `EditorElement`

Antonio Scandurra , Julia , and Piotr created

Co-Authored-By: Julia <julia@zed.dev>
Co-Authored-By: Piotr <piotr@zed.dev>

Change summary

crates/editor2/src/editor.rs         |   6 
crates/editor2/src/element.rs        |  81 +++++++-----
crates/editor2/src/scroll/actions.rs | 188 +++++++++++++++--------------
crates/gpui2/src/geometry.rs         |   4 
4 files changed, 151 insertions(+), 128 deletions(-)

Detailed changes

crates/editor2/src/editor.rs 🔗

@@ -36,9 +36,9 @@ pub use element::{
 use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    div, px, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, EventEmitter,
-    FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, Styled, Subscription, Task,
-    TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
+    div, px, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, Entity,
+    EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, Model, Pixels, Render, Styled,
+    Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};

crates/editor2/src/element.rs 🔗

@@ -9,8 +9,9 @@ use anyhow::Result;
 use collections::{BTreeMap, HashMap};
 use gpui::{
     black, hsla, point, px, relative, size, transparent_black, AnyElement, BorrowWindow, Bounds,
-    ContentMask, Corners, Edges, Element, Hsla, Line, Pixels, ShapedGlyph, Size, Style, TextRun,
-    TextStyle, TextSystem, ViewContext, WindowContext,
+    ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, Hsla, Line, Pixels,
+    ScrollWheelEvent, ShapedGlyph, Size, StatefulInteraction, Style, TextRun, TextStyle,
+    TextSystem, ViewContext, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -464,39 +465,41 @@ impl EditorElement {
     //     true
     // }
 
-    // fn scroll(
-    //     editor: &mut Editor,
-    //     position: gpui::Point<Pixels>,
-    //     mut delta: gpui::Point<Pixels>,
-    //     precise: bool,
-    //     position_map: &PositionMap,
-    //     bounds: Bounds<Pixels>,
-    //     cx: &mut ViewContext<Editor>,
-    // ) -> bool {
-    //     if !bounds.contains_point(position) {
-    //         return false;
-    //     }
+    fn scroll(
+        editor: &mut Editor,
+        event: &ScrollWheelEvent,
+        position_map: &PositionMap,
+        bounds: Bounds<Pixels>,
+        cx: &mut ViewContext<Editor>,
+    ) -> bool {
+        if !bounds.contains_point(&event.position) {
+            return false;
+        }
 
-    //     let line_height = position_map.line_height;
-    //     let max_glyph_width = position_map.em_width;
+        let line_height = position_map.line_height;
+        let max_glyph_width = position_map.em_width;
+        let (delta, axis) = match event.delta {
+            gpui::ScrollDelta::Pixels(mut pixels) => {
+                //Trackpad
+                let axis = position_map.snapshot.ongoing_scroll.filter(&mut pixels);
+                (pixels, axis)
+            }
 
-    //     let axis = if precise {
-    //         //Trackpad
-    //         position_map.snapshot.ongoing_scroll.filter(&mut delta)
-    //     } else {
-    //         //Not trackpad
-    //         delta *= point(max_glyph_width, line_height);
-    //         None //Resets ongoing scroll
-    //     };
+            gpui::ScrollDelta::Lines(lines) => {
+                //Not trackpad
+                let pixels = point(lines.x * max_glyph_width, lines.y * line_height);
+                (pixels, None)
+            }
+        };
 
-    //     let scroll_position = position_map.snapshot.scroll_position();
-    //     let x = (scroll_position.x * max_glyph_width - delta.x) / max_glyph_width;
-    //     let y = (scroll_position.y * line_height - delta.y) / line_height;
-    //     let scroll_position = point(x, y).clamp(gpui::Point::<Pixels>::zero(), position_map.scroll_max);
-    //     editor.scroll(scroll_position, axis, cx);
+        let scroll_position = position_map.snapshot.scroll_position();
+        let x = f32::from((scroll_position.x * max_glyph_width - delta.x) / max_glyph_width);
+        let y = f32::from((scroll_position.y * line_height - delta.y) / line_height);
+        let scroll_position = point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
+        editor.scroll(scroll_position, axis, cx);
 
-    //     true
-    // }
+        true
+    }
 
     fn paint_background(
         &self,
@@ -951,7 +954,7 @@ impl EditorElement {
             )
         }
 
-        cx.stack(9999, |cx| {
+        cx.stack(0, |cx| {
             for cursor in cursors {
                 cursor.paint(content_origin, cx);
             }
@@ -2573,6 +2576,20 @@ impl Element<Editor> for EditorElement {
         cx: &mut gpui::ViewContext<Editor>,
     ) {
         let layout = self.compute_layout(editor, cx, bounds);
+
+        cx.on_mouse_event({
+            let position_map = layout.position_map.clone();
+            move |editor, event: &ScrollWheelEvent, phase, cx| {
+                if phase != DispatchPhase::Bubble {
+                    return;
+                }
+
+                if Self::scroll(editor, event, &position_map, bounds, cx) {
+                    cx.stop_propagation();
+                }
+            }
+        });
+
         cx.with_content_mask(ContentMask { bounds }, |cx| {
             let gutter_bounds = Bounds {
                 origin: bounds.origin,

crates/editor2/src/scroll/actions.rs 🔗

@@ -1,4 +1,6 @@
-use gpui::AppContext;
+use super::Axis;
+use crate::Editor;
+use gpui::{AppContext, Point, ViewContext};
 
 // actions!(
 //     editor,
@@ -42,107 +44,107 @@ pub fn init(cx: &mut AppContext) {
     // });
 }
 
-// impl Editor {
-//     pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) -> Option<()> {
-//         if self.take_rename(true, cx).is_some() {
-//             return None;
-//         }
+impl Editor {
+    //     pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) -> Option<()> {
+    //         if self.take_rename(true, cx).is_some() {
+    //             return None;
+    //         }
 
-//         if self.mouse_context_menu.read(cx).visible() {
-//             return None;
-//         }
+    //         if self.mouse_context_menu.read(cx).visible() {
+    //             return None;
+    //         }
 
-//         if matches!(self.mode, EditorMode::SingleLine) {
-//             cx.propagate_action();
-//             return None;
-//         }
-//         self.request_autoscroll(Autoscroll::Next, cx);
-//         Some(())
-//     }
+    //         if matches!(self.mode, EditorMode::SingleLine) {
+    //             cx.propagate_action();
+    //             return None;
+    //         }
+    //         self.request_autoscroll(Autoscroll::Next, cx);
+    //         Some(())
+    //     }
 
-//     pub fn scroll(
-//         &mut self,
-//         scroll_position: Vector2F,
-//         axis: Option<Axis>,
-//         cx: &mut ViewContext<Self>,
-//     ) {
-//         self.scroll_manager.update_ongoing_scroll(axis);
-//         self.set_scroll_position(scroll_position, cx);
-//     }
+    pub fn scroll(
+        &mut self,
+        scroll_position: Point<f32>,
+        axis: Option<Axis>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.scroll_manager.update_ongoing_scroll(axis);
+        self.set_scroll_position(scroll_position, cx);
+    }
 
-//     fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
-//         let snapshot = editor.snapshot(cx).display_snapshot;
-//         let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
+    //     fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
+    //         let snapshot = editor.snapshot(cx).display_snapshot;
+    //         let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
 
-//         let mut new_screen_top = editor.selections.newest_display(cx).head();
-//         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
-//         *new_screen_top.column_mut() = 0;
-//         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-//         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
+    //         let mut new_screen_top = editor.selections.newest_display(cx).head();
+    //         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
+    //         *new_screen_top.column_mut() = 0;
+    //         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
+    //         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-//         editor.set_scroll_anchor(
-//             ScrollAnchor {
-//                 anchor: new_anchor,
-//                 offset: Default::default(),
-//             },
-//             cx,
-//         )
-//     }
+    //         editor.set_scroll_anchor(
+    //             ScrollAnchor {
+    //                 anchor: new_anchor,
+    //                 offset: Default::default(),
+    //             },
+    //             cx,
+    //         )
+    //     }
 
-//     fn scroll_cursor_center(
-//         editor: &mut Editor,
-//         _: &ScrollCursorCenter,
-//         cx: &mut ViewContext<Editor>,
-//     ) {
-//         let snapshot = editor.snapshot(cx).display_snapshot;
-//         let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
-//             visible_rows as u32
-//         } else {
-//             return;
-//         };
+    //     fn scroll_cursor_center(
+    //         editor: &mut Editor,
+    //         _: &ScrollCursorCenter,
+    //         cx: &mut ViewContext<Editor>,
+    //     ) {
+    //         let snapshot = editor.snapshot(cx).display_snapshot;
+    //         let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
+    //             visible_rows as u32
+    //         } else {
+    //             return;
+    //         };
 
-//         let mut new_screen_top = editor.selections.newest_display(cx).head();
-//         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
-//         *new_screen_top.column_mut() = 0;
-//         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-//         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
+    //         let mut new_screen_top = editor.selections.newest_display(cx).head();
+    //         *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
+    //         *new_screen_top.column_mut() = 0;
+    //         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
+    //         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-//         editor.set_scroll_anchor(
-//             ScrollAnchor {
-//                 anchor: new_anchor,
-//                 offset: Default::default(),
-//             },
-//             cx,
-//         )
-//     }
+    //         editor.set_scroll_anchor(
+    //             ScrollAnchor {
+    //                 anchor: new_anchor,
+    //                 offset: Default::default(),
+    //             },
+    //             cx,
+    //         )
+    //     }
 
-//     fn scroll_cursor_bottom(
-//         editor: &mut Editor,
-//         _: &ScrollCursorBottom,
-//         cx: &mut ViewContext<Editor>,
-//     ) {
-//         let snapshot = editor.snapshot(cx).display_snapshot;
-//         let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
-//         let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
-//             visible_rows as u32
-//         } else {
-//             return;
-//         };
+    //     fn scroll_cursor_bottom(
+    //         editor: &mut Editor,
+    //         _: &ScrollCursorBottom,
+    //         cx: &mut ViewContext<Editor>,
+    //     ) {
+    //         let snapshot = editor.snapshot(cx).display_snapshot;
+    //         let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
+    //         let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
+    //             visible_rows as u32
+    //         } else {
+    //             return;
+    //         };
 
-//         let mut new_screen_top = editor.selections.newest_display(cx).head();
-//         *new_screen_top.row_mut() = new_screen_top
-//             .row()
-//             .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
-//         *new_screen_top.column_mut() = 0;
-//         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
-//         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
+    //         let mut new_screen_top = editor.selections.newest_display(cx).head();
+    //         *new_screen_top.row_mut() = new_screen_top
+    //             .row()
+    //             .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
+    //         *new_screen_top.column_mut() = 0;
+    //         let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
+    //         let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
 
-//         editor.set_scroll_anchor(
-//             ScrollAnchor {
-//                 anchor: new_anchor,
-//                 offset: Default::default(),
-//             },
-//             cx,
-//         )
-//     }
-// }
+    //         editor.set_scroll_anchor(
+    //             ScrollAnchor {
+    //                 anchor: new_anchor,
+    //                 offset: Default::default(),
+    //             },
+    //             cx,
+    //         )
+    //     }
+}

crates/gpui2/src/geometry.rs 🔗

@@ -120,6 +120,10 @@ where
             },
         }
     }
+
+    pub fn clamp(&self, min: &Self, max: &Self) -> Self {
+        self.max(min).min(max)
+    }
 }
 
 impl<T: Clone + Default + Debug> Clone for Point<T> {