From 17b8e4a68459b907f8ff8a96ff1eb22c566127e6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 15 Nov 2023 18:19:26 +0100 Subject: [PATCH] Handle clicking folded ranges Co-Authored-By: Nathan Sobo --- crates/editor2/src/element.rs | 695 +++++++++++++++---------------- crates/gpui2/src/element.rs | 46 +- crates/gpui2/src/elements/div.rs | 6 + crates/gpui2/src/window.rs | 2 +- 4 files changed, 374 insertions(+), 375 deletions(-) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 0aee6c301b..f5ea324050 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -18,11 +18,12 @@ use crate::{ use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow, - Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, - ElementInputHandler, Entity, EntityId, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent, - MouseUpEvent, ParentComponent, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, - TextStyle, View, ViewContext, WindowContext, + div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, + BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, + ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveComponent, Line, + MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, + ScrollWheelEvent, Size, StatefulInteractiveComponent, Style, Styled, TextRun, TextStyle, View, + ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; @@ -615,311 +616,341 @@ impl EditorElement { fn paint_text( &mut self, - bounds: Bounds, + text_bounds: Bounds, layout: &mut LayoutState, editor: &mut Editor, cx: &mut ViewContext, ) { let scroll_position = layout.position_map.snapshot.scroll_position(); let start_row = layout.visible_display_row_range.start; - let scroll_top = scroll_position.y * layout.position_map.line_height; - let max_glyph_width = layout.position_map.em_width; - let scroll_left = scroll_position.x * max_glyph_width; - let content_origin = bounds.origin + point(layout.gutter_margin, Pixels::ZERO); + let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO); let line_end_overshoot = 0.15 * layout.position_map.line_height; let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; - cx.with_content_mask(Some(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 - // }, - // }); + cx.with_content_mask( + Some(ContentMask { + bounds: text_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, - // ); + let fold_corner_radius = 0.15 * layout.position_map.line_height; + cx.with_element_id(Some("folds"), |cx| { + let snapshot = &layout.position_map.snapshot; + for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) { + let fold_range = fold.range.clone(); + let display_range = fold.range.start.to_display_point(&snapshot) + ..fold.range.end.to_display_point(&snapshot); + debug_assert_eq!(display_range.start.row(), display_range.end.row()); + let row = display_range.start.row(); - // 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 line_layout = &layout.position_map.line_layouts + [(row - layout.visible_display_row_range.start) as usize] + .line; + let start_x = content_origin.x + + line_layout.x_for_index(display_range.start.column() as usize) + - layout.position_map.scroll_position.x; + let start_y = content_origin.y + + row as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let end_x = content_origin.x + + line_layout.x_for_index(display_range.end.column() as usize) + - layout.position_map.scroll_position.x; - // let display_row = range.start.row(); + let fold_bounds = Bounds { + origin: point(start_x, start_y), + size: size(end_x - start_x, layout.position_map.line_height), + }; - // let buffer_row = DisplayPoint::new(display_row, 0) - // .to_point(&layout.position_map.snapshot.display_snapshot) - // .row; + let fold_background = cx.with_z_index(1, |cx| { + div() + .id(fold.id) + .size_full() + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) + .on_click(move |editor: &mut Editor, _, cx| { + editor.unfold_ranges( + [fold_range.start..fold_range.end], + true, + false, + cx, + ); + cx.stop_propagation(); + }) + .draw( + fold_bounds.origin, + fold_bounds.size, + editor, + cx, + |fold_element_state, cx| { + if fold_element_state.is_active() { + gpui::blue() + } else if fold_bounds.contains_point(&cx.mouse_position()) { + gpui::black() + } else { + gpui::red() + } + }, + ) + }); - // let view_id = cx.view_id(); - // cx.scene().push_mouse_region( - // MouseRegion::new::(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), - // ) - // } - // } + self.paint_highlighted_range( + display_range.clone(), + fold_background, + fold_corner_radius, + fold_corner_radius * 2., + layout, + content_origin, + text_bounds, + cx, + ); + } + }); - 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; 32]>::new(); - - for (selection_style, selections) in &layout.selections { - for selection in selections { + 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, - scroll_left, - bounds, + text_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; 32]>::new(); - 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; + 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, + text_bounds, + cx, + ); - 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; + 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 - layout.position_map.scroll_position.x; + let y = cursor_position.row() as f32 + * layout.position_map.line_height + - layout.position_map.scroll_position.y; + if selection.is_newest { + editor.pixel_position_of_newest_cursor = Some(point( + text_bounds.origin.x + x + block_width / 2., + text_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, + }); } - 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, - }); } } } - } - 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.with_z_index(0, |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, + content_origin, + whitespace_setting, + &invisible_display_ranges, + cx, + ) } - }); - if let Some((position, context_menu)) = layout.context_menu.as_mut() { - cx.with_z_index(1, |cx| { - let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); - let available_space = size( - AvailableSpace::MinContent, - AvailableSpace::Definite( - (12. * line_height).min((bounds.size.height - line_height) / 2.), - ), - ); - let context_menu_size = context_menu.measure(available_space, editor, cx); - - 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.width; - let list_height = context_menu_size.height; - - // 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.viewport_size().width { - list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO); + cx.with_z_index(0, |cx| { + for cursor in cursors { + cursor.paint(content_origin, cx); } + }); - if list_origin.y + list_height > bounds.lower_right().y { - list_origin.y -= layout.position_map.line_height - list_height; - } + if let Some((position, context_menu)) = layout.context_menu.as_mut() { + cx.with_z_index(1, |cx| { + let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); + let available_space = size( + AvailableSpace::MinContent, + AvailableSpace::Definite( + (12. * line_height) + .min((text_bounds.size.height - line_height) / 2.), + ), + ); + let context_menu_size = context_menu.measure(available_space, editor, cx); - context_menu.draw(list_origin, available_space, editor, cx); - }) - } + 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) + - layout.position_map.scroll_position.x; + let y = (position.row() + 1) as f32 * layout.position_map.line_height + - layout.position_map.scroll_position.y; + let mut list_origin = content_origin + point(x, y); + let list_width = context_menu_size.width; + let list_height = context_menu_size.height; - // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { - // cx.scene().push_stacking_context(None, None); + // 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.viewport_size().width { + list_origin.x = + (cx.viewport_size().width - list_width).max(Pixels::ZERO); + } - // // 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; + if list_origin.y + list_height > text_bounds.lower_right().y { + list_origin.y -= layout.position_map.line_height - list_height; + } - // // 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; + context_menu.draw(list_origin, available_space, editor, cx); + }) + } - // // 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 let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { + // cx.scene().push_stacking_context(None, None); - // 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); + // // 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; - // 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); - // } + // // 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; - // hover_popover.paint( - // popover_origin, - // Bounds::::from_points( - // gpui::Point::::zero(), - // point(f32::MAX, f32::MAX), - // ), // Let content bleed outside of editor - // editor, - // cx, - // ); + // // 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); - // 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); + // 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); - // } + // 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::::from_points( - // gpui::Point::::zero(), - // point(f32::MAX, f32::MAX), - // ), // Let content bleed outside of editor - // editor, - // cx, - // ); + // hover_popover.paint( + // popover_origin, + // Bounds::::from_points( + // gpui::Point::::zero(), + // point(f32::MAX, f32::MAX), + // ), // Let content bleed outside of editor + // editor, + // cx, + // ); - // current_y = popover_origin.y + size.y + HOVER_POPOVER_GAP; - // } - // } + // 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); - // cx.scene().pop_stacking_context(); - // } - }) + // 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::::from_points( + // gpui::Point::::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 { @@ -1133,8 +1164,6 @@ impl EditorElement { line_end_overshoot: Pixels, layout: &LayoutState, content_origin: gpui::Point, - scroll_top: Pixels, - scroll_left: Pixels, bounds: Bounds, cx: &mut ViewContext, ) { @@ -1153,7 +1182,7 @@ impl EditorElement { corner_radius, start_y: content_origin.y + row_range.start as f32 * layout.position_map.line_height - - scroll_top, + - layout.position_map.scroll_position.y, lines: row_range .into_iter() .map(|row| { @@ -1163,17 +1192,17 @@ impl EditorElement { start_x: if row == range.start.row() { content_origin.x + line_layout.x_for_index(range.start.column() as usize) - - scroll_left + - layout.position_map.scroll_position.x } else { - content_origin.x - scroll_left + content_origin.x - layout.position_map.scroll_position.x }, end_x: if row == range.end.row() { content_origin.x + line_layout.x_for_index(range.end.column() as usize) - - scroll_left + - layout.position_map.scroll_position.x } else { content_origin.x + line_layout.width + line_end_overshoot - - scroll_left + - layout.position_map.scroll_position.x }, } }) @@ -1567,7 +1596,6 @@ impl EditorElement { let mut selections: Vec<(PlayerColor, Vec)> = Vec::new(); let mut active_rows = BTreeMap::new(); - let mut fold_ranges = Vec::new(); let is_singleton = editor.is_singleton(cx); let highlighted_rows = editor.highlighted_rows(); @@ -1577,19 +1605,6 @@ impl EditorElement { cx.theme().colors(), ); - fold_ranges.extend( - snapshot - .folds_in_range(start_anchor..end_anchor) - .map(|anchor| { - let start = anchor.range.start.to_point(&snapshot.buffer_snapshot); - ( - start.row, - start.to_display_point(&snapshot.display_snapshot) - ..anchor.range.end.to_display_point(&snapshot), - ) - }), - ); - let mut newest_selection_head = None; if editor.show_local_selections { @@ -1698,15 +1713,6 @@ impl EditorElement { ShowScrollbar::Never => false, }; - let fold_ranges: Vec<(BufferRow, Range, Hsla)> = fold_ranges - .into_iter() - .map(|(id, fold)| { - // todo!("change color based on mouse state") - let color = gpui::red(); - (id, fold, color) - }) - .collect(); - let head_for_relative = newest_selection_head.unwrap_or_else(|| { let newest = editor.selections.newest::(cx); SelectionLayout::new( @@ -1908,6 +1914,10 @@ impl EditorElement { mode: editor_mode, position_map: Arc::new(PositionMap { size: bounds.size, + scroll_position: point( + scroll_position.x * em_width, + scroll_position.y * line_height, + ), scroll_max, line_layouts, line_height, @@ -1915,6 +1925,7 @@ impl EditorElement { em_advance, snapshot, }), + visible_anchor_range: start_anchor..end_anchor, visible_display_row_range: start_row..end_row, wrap_guides, gutter_size, @@ -1928,7 +1939,6 @@ impl EditorElement { active_rows, highlighted_rows, highlighted_ranges, - fold_ranges, line_number_layouts, display_hunks, blocks, @@ -2116,11 +2126,13 @@ impl EditorElement { bounds: Bounds, gutter_bounds: Bounds, text_bounds: Bounds, - position_map: &Arc, + layout: &LayoutState, cx: &mut ViewContext, ) { + let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO); + cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &ScrollWheelEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2132,7 +2144,7 @@ impl EditorElement { } }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseDownEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2144,7 +2156,7 @@ impl EditorElement { } }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseUpEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2157,7 +2169,7 @@ impl EditorElement { }); // todo!() // on_down(MouseButton::Right, { - // let position_map = position_map.clone(); + // let position_map = layout.position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_right_down( // editor, @@ -2171,7 +2183,7 @@ impl EditorElement { // } // }); cx.on_mouse_event({ - let position_map = position_map.clone(); + let position_map = layout.position_map.clone(); move |editor, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -2301,18 +2313,16 @@ impl LineWithInvisibles { &self, layout: &LayoutState, row: u32, - scroll_top: Pixels, content_origin: gpui::Point, - scroll_left: Pixels, whitespace_setting: ShowWhitespaceSetting, selection_ranges: &[Range], cx: &mut ViewContext, ) { let line_height = layout.position_map.line_height; - let line_y = line_height * row as f32 - scroll_top; + let line_y = line_height * row as f32 - layout.position_map.scroll_position.y; self.line.paint( - content_origin + gpui::point(-scroll_left, line_y), + content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y), line_height, cx, ); @@ -2321,7 +2331,6 @@ impl LineWithInvisibles { &selection_ranges, layout, content_origin, - scroll_left, line_y, row, line_height, @@ -2335,7 +2344,6 @@ impl LineWithInvisibles { selection_ranges: &[Range], layout: &LayoutState, content_origin: gpui::Point, - scroll_left: Pixels, line_y: Pixels, row: u32, line_height: Pixels, @@ -2357,8 +2365,11 @@ impl LineWithInvisibles { let x_offset = self.line.x_for_index(token_offset); let invisible_offset = (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0; - let origin = - content_origin + gpui::point(-scroll_left + x_offset + invisible_offset, line_y); + let origin = content_origin + + gpui::point( + x_offset + invisible_offset - layout.position_map.scroll_position.x, + line_y, + ); if let Some(allowed_regions) = allowed_invisibles_regions { let invisible_point = DisplayPoint::new(row, token_offset as u32); @@ -2440,13 +2451,6 @@ impl Element for EditorElement { // We call with_z_index to establish a new stacking context. cx.with_z_index(0, |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - self.paint_mouse_listeners( - bounds, - gutter_bounds, - text_bounds, - &layout.position_map, - cx, - ); self.paint_background(gutter_bounds, text_bounds, &layout, cx); if layout.gutter_size.width > Pixels::ZERO { self.paint_gutter(gutter_bounds, &mut layout, editor, cx); @@ -2457,6 +2461,8 @@ impl Element for EditorElement { self.paint_blocks(bounds, &mut layout, editor, cx); } + self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); + let input_handler = ElementInputHandler::new(bounds, cx); cx.handle_input(&editor.focus_handle, input_handler); }); @@ -3080,6 +3086,7 @@ pub struct LayoutState { text_size: gpui::Size, mode: EditorMode, wrap_guides: SmallVec<[(Pixels, bool); 2]>, + visible_anchor_range: Range, visible_display_row_range: Range, active_rows: BTreeMap, highlighted_rows: Option>, @@ -3087,7 +3094,6 @@ pub struct LayoutState { display_hunks: Vec, blocks: Vec, highlighted_ranges: Vec<(Range, Hsla)>, - fold_ranges: Vec<(BufferRow, Range, Hsla)>, selections: Vec<(PlayerColor, Vec)>, scrollbar_row_range: Range, show_scrollbars: bool, @@ -3109,6 +3115,7 @@ struct CodeActionsIndicator { struct PositionMap { size: Size, line_height: Pixels, + scroll_position: gpui::Point, scroll_max: gpui::Point, em_width: Pixels, em_advance: Pixels, @@ -3445,58 +3452,6 @@ impl HighlightedRange { } } -// fn range_to_bounds( -// range: &Range, -// content_origin: gpui::Point, -// scroll_left: f32, -// scroll_top: f32, -// visible_row_range: &Range, -// line_end_overshoot: f32, -// position_map: &PositionMap, -// ) -> impl Iterator> { -// let mut bounds: SmallVec<[Bounds; 1]> = SmallVec::new(); - -// if range.start == range.end { -// return bounds.into_iter(); -// } - -// let start_row = visible_row_range.start; -// let end_row = visible_row_range.end; - -// let row_range = if range.end.column() == 0 { -// cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row) -// } else { -// cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row) -// }; - -// let first_y = -// content_origin.y + row_range.start as f32 * position_map.line_height - scroll_top; - -// for (idx, row) in row_range.enumerate() { -// let line_layout = &position_map.line_layouts[(row - start_row) as usize].line; - -// let start_x = if row == range.start.row() { -// content_origin.x + line_layout.x_for_index(range.start.column() as usize) -// - scroll_left -// } else { -// content_origin.x - scroll_left -// }; - -// let end_x = if row == range.end.row() { -// content_origin.x + line_layout.x_for_index(range.end.column() as usize) - scroll_left -// } else { -// content_origin.x + line_layout.width() + line_end_overshoot - scroll_left -// }; - -// bounds.push(Bounds::::from_points( -// point(start_x, first_y + position_map.line_height * idx as f32), -// point(end_x, first_y + position_map.line_height * (idx + 1) as f32), -// )) -// } - -// bounds.into_iter() -// } - pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 { (delta.pow(1.5) / 100.0).into() } diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 46ea5c6cd2..9db256a572 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -3,7 +3,7 @@ use crate::{ }; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; -use std::{any::Any, mem}; +use std::{any::Any, fmt::Debug, mem}; pub trait Element { type ElementState: 'static; @@ -33,6 +33,42 @@ pub trait Element { element_state: &mut Self::ElementState, cx: &mut ViewContext, ); + + fn draw( + self, + origin: Point, + available_space: Size, + view_state: &mut V, + cx: &mut ViewContext, + f: impl FnOnce(&Self::ElementState, &mut ViewContext) -> R, + ) -> R + where + Self: Sized, + T: Clone + Default + Debug + Into, + { + let mut element = RenderedElement { + element: self, + phase: ElementRenderPhase::Start, + }; + element.draw(origin, available_space.map(Into::into), view_state, cx); + if let ElementRenderPhase::Painted { frame_state } = &element.phase { + if let Some(frame_state) = frame_state.as_ref() { + f(&frame_state, cx) + } else { + let element_id = element + .element + .element_id() + .expect("we either have some frame_state or some element_id"); + cx.with_element_state(element_id, |element_state, cx| { + let element_state = element_state.unwrap(); + let result = f(&element_state, cx); + (result, element_state) + }) + } + } else { + unreachable!() + } + } } #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)] @@ -99,7 +135,9 @@ enum ElementRenderPhase { available_space: Size, frame_state: Option, }, - Painted, + Painted { + frame_state: Option, + }, } /// Internal struct that wraps an element to store Layout and ElementState after the element is rendered. @@ -157,7 +195,7 @@ where ElementRenderPhase::Start => panic!("must call initialize before layout"), ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::LayoutComputed { .. } - | ElementRenderPhase::Painted => { + | ElementRenderPhase::Painted { .. } => { panic!("element rendered twice") } }; @@ -192,7 +230,7 @@ where self.element .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx); } - ElementRenderPhase::Painted + ElementRenderPhase::Painted { frame_state } } _ => panic!("must call layout before paint"), diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index a08d1619ae..447a15b542 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -711,6 +711,12 @@ pub struct DivState { interactive_state: InteractiveElementState, } +impl DivState { + pub fn is_active(&self) -> bool { + self.interactive_state.pending_mouse_down.lock().is_some() + } +} + pub struct Interactivity { pub element_id: Option, pub key_context: KeyContext, diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 9fb9f51314..f209fd4bba 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -216,7 +216,7 @@ pub struct Window { // #[derive(Default)] pub(crate) struct Frame { - element_states: HashMap, + pub(crate) element_states: HashMap, mouse_listeners: HashMap>, pub(crate) dispatch_tree: DispatchTree, pub(crate) focus_listeners: Vec,