Implement scrolling for `editor2` (#3251)

Antonio Scandurra created

Release Notes:

- N/A

Change summary

crates/editor2/src/editor.rs         |   6 
crates/editor2/src/element.rs        | 629 +++++++++++++++--------------
crates/editor2/src/scroll/actions.rs | 188 ++++----
crates/gpui2/src/geometry.rs         |   4 
4 files changed, 426 insertions(+), 401 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,
@@ -774,91 +777,72 @@ impl EditorElement {
         let line_end_overshoot = 0.15 * layout.position_map.line_height;
         let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
 
-        // todo!("cursor region")
-        // cx.scene().push_cursor_region(CursorRegion {
-        //     bounds,
-        //     style: if !editor.link_go_to_definition_state.definitions.is_empty {
-        //         CursorStyle::PointingHand
-        //     } else {
-        //         CursorStyle::IBeam
-        //     },
-        // });
-
-        // todo!("fold ranges")
-        // let fold_corner_radius =
-        //     self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
-        // for (id, range, color) in layout.fold_ranges.iter() {
-        //     self.paint_highlighted_range(
-        //         range.clone(),
-        //         *color,
-        //         fold_corner_radius,
-        //         fold_corner_radius * 2.,
-        //         layout,
-        //         content_origin,
-        //         scroll_top,
-        //         scroll_left,
-        //         bounds,
-        //         cx,
-        //     );
-
-        //     for bound in range_to_bounds(
-        //         &range,
-        //         content_origin,
-        //         scroll_left,
-        //         scroll_top,
-        //         &layout.visible_display_row_range,
-        //         line_end_overshoot,
-        //         &layout.position_map,
-        //     ) {
-        //         cx.scene().push_cursor_region(CursorRegion {
-        //             bounds: bound,
-        //             style: CursorStyle::PointingHand,
-        //         });
-
-        //         let display_row = range.start.row();
-
-        //         let buffer_row = DisplayPoint::new(display_row, 0)
-        //             .to_point(&layout.position_map.snapshot.display_snapshot)
-        //             .row;
-
-        //         let view_id = cx.view_id();
-        //         cx.scene().push_mouse_region(
-        //             MouseRegion::new::<FoldMarkers>(view_id, *id as usize, bound)
-        //                 .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| {
-        //                     editor.unfold_at(&UnfoldAt { buffer_row }, cx)
-        //                 })
-        //                 .with_notify_on_hover(true)
-        //                 .with_notify_on_click(true),
-        //         )
-        //     }
-        // }
-
-        for (range, color) in &layout.highlighted_ranges {
-            self.paint_highlighted_range(
-                range.clone(),
-                *color,
-                Pixels::ZERO,
-                line_end_overshoot,
-                layout,
-                content_origin,
-                scroll_top,
-                scroll_left,
-                bounds,
-                cx,
-            );
-        }
-
-        let mut cursors = SmallVec::<[Cursor; 32]>::new();
-        let corner_radius = 0.15 * layout.position_map.line_height;
-        let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
-
-        for (selection_style, selections) in &layout.selections {
-            for selection in selections {
+        cx.with_content_mask(ContentMask { bounds }, |cx| {
+            // todo!("cursor region")
+            // cx.scene().push_cursor_region(CursorRegion {
+            //     bounds,
+            //     style: if !editor.link_go_to_definition_state.definitions.is_empty {
+            //         CursorStyle::PointingHand
+            //     } else {
+            //         CursorStyle::IBeam
+            //     },
+            // });
+
+            // todo!("fold ranges")
+            // let fold_corner_radius =
+            //     self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height;
+            // for (id, range, color) in layout.fold_ranges.iter() {
+            //     self.paint_highlighted_range(
+            //         range.clone(),
+            //         *color,
+            //         fold_corner_radius,
+            //         fold_corner_radius * 2.,
+            //         layout,
+            //         content_origin,
+            //         scroll_top,
+            //         scroll_left,
+            //         bounds,
+            //         cx,
+            //     );
+
+            //     for bound in range_to_bounds(
+            //         &range,
+            //         content_origin,
+            //         scroll_left,
+            //         scroll_top,
+            //         &layout.visible_display_row_range,
+            //         line_end_overshoot,
+            //         &layout.position_map,
+            //     ) {
+            //         cx.scene().push_cursor_region(CursorRegion {
+            //             bounds: bound,
+            //             style: CursorStyle::PointingHand,
+            //         });
+
+            //         let display_row = range.start.row();
+
+            //         let buffer_row = DisplayPoint::new(display_row, 0)
+            //             .to_point(&layout.position_map.snapshot.display_snapshot)
+            //             .row;
+
+            //         let view_id = cx.view_id();
+            //         cx.scene().push_mouse_region(
+            //             MouseRegion::new::<FoldMarkers>(view_id, *id as usize, bound)
+            //                 .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| {
+            //                     editor.unfold_at(&UnfoldAt { buffer_row }, cx)
+            //                 })
+            //                 .with_notify_on_hover(true)
+            //                 .with_notify_on_click(true),
+            //         )
+            //     }
+            // }
+
+            for (range, color) in &layout.highlighted_ranges {
                 self.paint_highlighted_range(
-                    selection.range.clone(),
-                    selection_style.selection,
-                    corner_radius,
-                    corner_radius * 2.,
+                    range.clone(),
+                    *color,
+                    Pixels::ZERO,
+                    line_end_overshoot,
                     layout,
                     content_origin,
                     scroll_top,
@@ -866,205 +850,226 @@ impl EditorElement {
                     bounds,
                     cx,
                 );
+            }
 
-                if selection.is_local && !selection.range.is_empty() {
-                    invisible_display_ranges.push(selection.range.clone());
-                }
+            let mut cursors = SmallVec::<[Cursor; 32]>::new();
+            let corner_radius = 0.15 * layout.position_map.line_height;
+            let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
+
+            for (selection_style, selections) in &layout.selections {
+                for selection in selections {
+                    self.paint_highlighted_range(
+                        selection.range.clone(),
+                        selection_style.selection,
+                        corner_radius,
+                        corner_radius * 2.,
+                        layout,
+                        content_origin,
+                        scroll_top,
+                        scroll_left,
+                        bounds,
+                        cx,
+                    );
 
-                if !selection.is_local || editor.show_local_cursors(cx) {
-                    let cursor_position = selection.head;
-                    if layout
-                        .visible_display_row_range
-                        .contains(&cursor_position.row())
-                    {
-                        let cursor_row_layout = &layout.position_map.line_layouts
-                            [(cursor_position.row() - start_row) as usize]
-                            .line;
-                        let cursor_column = cursor_position.column() as usize;
-
-                        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
-                        let mut block_width =
-                            cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x;
-                        if block_width == Pixels::ZERO {
-                            block_width = layout.position_map.em_width;
-                        }
-                        let block_text = if let CursorShape::Block = selection.cursor_shape {
-                            layout
-                                .position_map
-                                .snapshot
-                                .chars_at(cursor_position)
-                                .next()
-                                .and_then(|(character, _)| {
-                                    let text = character.to_string();
-                                    cx.text_system()
-                                        .layout_text(
-                                            &text,
-                                            cursor_row_layout.font_size,
-                                            &[TextRun {
-                                                len: text.len(),
-                                                font: self.style.text.font(),
-                                                color: self.style.background,
-                                                underline: None,
-                                            }],
-                                            None,
-                                        )
-                                        .unwrap()
-                                        .pop()
-                                })
-                        } else {
-                            None
-                        };
-
-                        let x = cursor_character_x - scroll_left;
-                        let y = cursor_position.row() as f32 * layout.position_map.line_height
-                            - scroll_top;
-                        if selection.is_newest {
-                            editor.pixel_position_of_newest_cursor = Some(point(
-                                bounds.origin.x + x + block_width / 2.,
-                                bounds.origin.y + y + layout.position_map.line_height / 2.,
-                            ));
+                    if selection.is_local && !selection.range.is_empty() {
+                        invisible_display_ranges.push(selection.range.clone());
+                    }
+
+                    if !selection.is_local || editor.show_local_cursors(cx) {
+                        let cursor_position = selection.head;
+                        if layout
+                            .visible_display_row_range
+                            .contains(&cursor_position.row())
+                        {
+                            let cursor_row_layout = &layout.position_map.line_layouts
+                                [(cursor_position.row() - start_row) as usize]
+                                .line;
+                            let cursor_column = cursor_position.column() as usize;
+
+                            let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
+                            let mut block_width = cursor_row_layout.x_for_index(cursor_column + 1)
+                                - cursor_character_x;
+                            if block_width == Pixels::ZERO {
+                                block_width = layout.position_map.em_width;
+                            }
+                            let block_text = if let CursorShape::Block = selection.cursor_shape {
+                                layout
+                                    .position_map
+                                    .snapshot
+                                    .chars_at(cursor_position)
+                                    .next()
+                                    .and_then(|(character, _)| {
+                                        let text = character.to_string();
+                                        cx.text_system()
+                                            .layout_text(
+                                                &text,
+                                                cursor_row_layout.font_size,
+                                                &[TextRun {
+                                                    len: text.len(),
+                                                    font: self.style.text.font(),
+                                                    color: self.style.background,
+                                                    underline: None,
+                                                }],
+                                                None,
+                                            )
+                                            .unwrap()
+                                            .pop()
+                                    })
+                            } else {
+                                None
+                            };
+
+                            let x = cursor_character_x - scroll_left;
+                            let y = cursor_position.row() as f32 * layout.position_map.line_height
+                                - scroll_top;
+                            if selection.is_newest {
+                                editor.pixel_position_of_newest_cursor = Some(point(
+                                    bounds.origin.x + x + block_width / 2.,
+                                    bounds.origin.y + y + layout.position_map.line_height / 2.,
+                                ));
+                            }
+                            cursors.push(Cursor {
+                                color: selection_style.cursor,
+                                block_width,
+                                origin: point(x, y),
+                                line_height: layout.position_map.line_height,
+                                shape: selection.cursor_shape,
+                                block_text,
+                            });
                         }
-                        cursors.push(Cursor {
-                            color: selection_style.cursor,
-                            block_width,
-                            origin: point(x, y),
-                            line_height: layout.position_map.line_height,
-                            shape: selection.cursor_shape,
-                            block_text,
-                        });
                     }
                 }
             }
-        }
-
-        for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
-            let row = start_row + ix as u32;
-            line_with_invisibles.draw(
-                layout,
-                row,
-                scroll_top,
-                content_origin,
-                scroll_left,
-                whitespace_setting,
-                &invisible_display_ranges,
-                cx,
-            )
-        }
 
-        cx.stack(9999, |cx| {
-            for cursor in cursors {
-                cursor.paint(content_origin, cx);
+            for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
+                let row = start_row + ix as u32;
+                line_with_invisibles.draw(
+                    layout,
+                    row,
+                    scroll_top,
+                    content_origin,
+                    scroll_left,
+                    whitespace_setting,
+                    &invisible_display_ranges,
+                    cx,
+                )
             }
-        });
-        // cx.scene().push_layer(Some(bounds));
-
-        // cx.scene().pop_layer();
-
-        // if let Some((position, context_menu)) = layout.context_menu.as_mut() {
-        //     cx.scene().push_stacking_context(None, None);
-        //     let cursor_row_layout =
-        //         &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
-        //     let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
-        //     let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
-        //     let mut list_origin = content_origin + point(x, y);
-        //     let list_width = context_menu.size().x;
-        //     let list_height = context_menu.size().y;
-
-        //     // Snap the right edge of the list to the right edge of the window if
-        //     // its horizontal bounds overflow.
-        //     if list_origin.x + list_width > cx.window_size().x {
-        //         list_origin.set_x((cx.window_size().x - list_width).max(0.));
-        //     }
-
-        //     if list_origin.y + list_height > bounds.max_y {
-        //         list_origin
-        //             .set_y(list_origin.y - layout.position_map.line_height - list_height);
-        //     }
 
-        //     context_menu.paint(
-        //         list_origin,
-        //         Bounds::<Pixels>::from_points(
-        //             gpui::Point::<Pixels>::zero(),
-        //             point(f32::MAX, f32::MAX),
-        //         ), // Let content bleed outside of editor
-        //         editor,
-        //         cx,
-        //     );
-
-        //     cx.scene().pop_stacking_context();
-        // }
-
-        // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
-        //     cx.scene().push_stacking_context(None, None);
-
-        //     // This is safe because we check on layout whether the required row is available
-        //     let hovered_row_layout =
-        //         &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
-
-        //     // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
-        //     // height. This is the size we will use to decide whether to render popovers above or below
-        //     // the hovered line.
-        //     let first_size = hover_popovers[0].size();
-        //     let height_to_reserve = first_size.y
-        //         + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
-
-        //     // Compute Hovered Point
-        //     let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
-        //     let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
-        //     let hovered_point = content_origin + point(x, y);
-
-        //     if hovered_point.y - height_to_reserve > 0.0 {
-        //         // There is enough space above. Render popovers above the hovered point
-        //         let mut current_y = hovered_point.y;
-        //         for hover_popover in hover_popovers {
-        //             let size = hover_popover.size();
-        //             let mut popover_origin = point(hovered_point.x, current_y - size.y);
-
-        //             let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
-        //             if x_out_of_bounds < 0.0 {
-        //                 popover_origin.set_x(popover_origin.x + x_out_of_bounds);
-        //             }
-
-        //             hover_popover.paint(
-        //                 popover_origin,
-        //                 Bounds::<Pixels>::from_points(
-        //                     gpui::Point::<Pixels>::zero(),
-        //                     point(f32::MAX, f32::MAX),
-        //                 ), // Let content bleed outside of editor
-        //                 editor,
-        //                 cx,
-        //             );
-
-        //             current_y = popover_origin.y - HOVER_POPOVER_GAP;
-        //         }
-        //     } else {
-        //         // There is not enough space above. Render popovers below the hovered point
-        //         let mut current_y = hovered_point.y + layout.position_map.line_height;
-        //         for hover_popover in hover_popovers {
-        //             let size = hover_popover.size();
-        //             let mut popover_origin = point(hovered_point.x, current_y);
-
-        //             let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
-        //             if x_out_of_bounds < 0.0 {
-        //                 popover_origin.set_x(popover_origin.x + x_out_of_bounds);
-        //             }
-
-        //             hover_popover.paint(
-        //                 popover_origin,
-        //                 Bounds::<Pixels>::from_points(
-        //                     gpui::Point::<Pixels>::zero(),
-        //                     point(f32::MAX, f32::MAX),
-        //                 ), // Let content bleed outside of editor
-        //                 editor,
-        //                 cx,
-        //             );
-
-        //             current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP;
-        //         }
-        //     }
-
-        //     cx.scene().pop_stacking_context();
-        // }
+            cx.stack(0, |cx| {
+                for cursor in cursors {
+                    cursor.paint(content_origin, cx);
+                }
+            });
+            // cx.scene().push_layer(Some(bounds));
+
+            // cx.scene().pop_layer();
+
+            // if let Some((position, context_menu)) = layout.context_menu.as_mut() {
+            //     cx.scene().push_stacking_context(None, None);
+            //     let cursor_row_layout =
+            //         &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
+            //     let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
+            //     let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
+            //     let mut list_origin = content_origin + point(x, y);
+            //     let list_width = context_menu.size().x;
+            //     let list_height = context_menu.size().y;
+
+            //     // Snap the right edge of the list to the right edge of the window if
+            //     // its horizontal bounds overflow.
+            //     if list_origin.x + list_width > cx.window_size().x {
+            //         list_origin.set_x((cx.window_size().x - list_width).max(0.));
+            //     }
+
+            //     if list_origin.y + list_height > bounds.max_y {
+            //         list_origin
+            //             .set_y(list_origin.y - layout.position_map.line_height - list_height);
+            //     }
+
+            //     context_menu.paint(
+            //         list_origin,
+            //         Bounds::<Pixels>::from_points(
+            //             gpui::Point::<Pixels>::zero(),
+            //             point(f32::MAX, f32::MAX),
+            //         ), // Let content bleed outside of editor
+            //         editor,
+            //         cx,
+            //     );
+
+            //     cx.scene().pop_stacking_context();
+            // }
+
+            // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
+            //     cx.scene().push_stacking_context(None, None);
+
+            //     // This is safe because we check on layout whether the required row is available
+            //     let hovered_row_layout =
+            //         &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
+
+            //     // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
+            //     // height. This is the size we will use to decide whether to render popovers above or below
+            //     // the hovered line.
+            //     let first_size = hover_popovers[0].size();
+            //     let height_to_reserve = first_size.y
+            //         + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height;
+
+            //     // Compute Hovered Point
+            //     let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
+            //     let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
+            //     let hovered_point = content_origin + point(x, y);
+
+            //     if hovered_point.y - height_to_reserve > 0.0 {
+            //         // There is enough space above. Render popovers above the hovered point
+            //         let mut current_y = hovered_point.y;
+            //         for hover_popover in hover_popovers {
+            //             let size = hover_popover.size();
+            //             let mut popover_origin = point(hovered_point.x, current_y - size.y);
+
+            //             let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
+            //             if x_out_of_bounds < 0.0 {
+            //                 popover_origin.set_x(popover_origin.x + x_out_of_bounds);
+            //             }
+
+            //             hover_popover.paint(
+            //                 popover_origin,
+            //                 Bounds::<Pixels>::from_points(
+            //                     gpui::Point::<Pixels>::zero(),
+            //                     point(f32::MAX, f32::MAX),
+            //                 ), // Let content bleed outside of editor
+            //                 editor,
+            //                 cx,
+            //             );
+
+            //             current_y = popover_origin.y - HOVER_POPOVER_GAP;
+            //         }
+            //     } else {
+            //         // There is not enough space above. Render popovers below the hovered point
+            //         let mut current_y = hovered_point.y + layout.position_map.line_height;
+            //         for hover_popover in hover_popovers {
+            //             let size = hover_popover.size();
+            //             let mut popover_origin = point(hovered_point.x, current_y);
+
+            //             let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
+            //             if x_out_of_bounds < 0.0 {
+            //                 popover_origin.set_x(popover_origin.x + x_out_of_bounds);
+            //             }
+
+            //             hover_popover.paint(
+            //                 popover_origin,
+            //                 Bounds::<Pixels>::from_points(
+            //                     gpui::Point::<Pixels>::zero(),
+            //                     point(f32::MAX, f32::MAX),
+            //                 ), // Let content bleed outside of editor
+            //                 editor,
+            //                 cx,
+            //             );
+
+            //             current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP;
+            //         }
+            //     }
+
+            //     cx.scene().pop_stacking_context();
+            // }
+        })
     }
 
     fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
@@ -2573,6 +2578,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 🔗

@@ -124,6 +124,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> {