Handle clicking folded ranges

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-11-15 18:19:26 +01:00
parent 3ff8c78b58
commit 17b8e4a684
4 changed files with 374 additions and 375 deletions

View file

@ -18,11 +18,12 @@ use crate::{
use anyhow::Result; use anyhow::Result;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
use gpui::{ use gpui::{
point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow, div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId, BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element,
ElementInputHandler, Entity, EntityId, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent, ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveComponent, Line,
MouseUpEvent, ParentComponent, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels,
TextStyle, View, ViewContext, WindowContext, ScrollWheelEvent, Size, StatefulInteractiveComponent, Style, Styled, TextRun, TextStyle, View,
ViewContext, WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting; use language::language_settings::ShowWhitespaceSetting;
@ -615,311 +616,341 @@ impl EditorElement {
fn paint_text( fn paint_text(
&mut self, &mut self,
bounds: Bounds<Pixels>, text_bounds: Bounds<Pixels>,
layout: &mut LayoutState, layout: &mut LayoutState,
editor: &mut Editor, editor: &mut Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let scroll_position = layout.position_map.snapshot.scroll_position(); let scroll_position = layout.position_map.snapshot.scroll_position();
let start_row = layout.visible_display_row_range.start; let start_row = layout.visible_display_row_range.start;
let scroll_top = scroll_position.y * layout.position_map.line_height; let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
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 line_end_overshoot = 0.15 * layout.position_map.line_height; let line_end_overshoot = 0.15 * layout.position_map.line_height;
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { cx.with_content_mask(
// todo!("cursor region") Some(ContentMask {
// cx.scene().push_cursor_region(CursorRegion { bounds: text_bounds,
// bounds, }),
// style: if !editor.link_go_to_definition_state.definitions.is_empty { |cx| {
// CursorStyle::PointingHand // todo!("cursor region")
// } else { // cx.scene().push_cursor_region(CursorRegion {
// CursorStyle::IBeam // bounds,
// }, // style: if !editor.link_go_to_definition_state.definitions.is_empty {
// }); // CursorStyle::PointingHand
// } else {
// CursorStyle::IBeam
// },
// });
// todo!("fold ranges") let fold_corner_radius = 0.15 * layout.position_map.line_height;
// let fold_corner_radius = cx.with_element_id(Some("folds"), |cx| {
// self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; let snapshot = &layout.position_map.snapshot;
// for (id, range, color) in layout.fold_ranges.iter() { for fold in snapshot.folds_in_range(layout.visible_anchor_range.clone()) {
// self.paint_highlighted_range( let fold_range = fold.range.clone();
// range.clone(), let display_range = fold.range.start.to_display_point(&snapshot)
// *color, ..fold.range.end.to_display_point(&snapshot);
// fold_corner_radius, debug_assert_eq!(display_range.start.row(), display_range.end.row());
// fold_corner_radius * 2., let row = display_range.start.row();
// layout,
// content_origin,
// scroll_top,
// scroll_left,
// bounds,
// cx,
// );
// for bound in range_to_bounds( let line_layout = &layout.position_map.line_layouts
// &range, [(row - layout.visible_display_row_range.start) as usize]
// content_origin, .line;
// scroll_left, let start_x = content_origin.x
// scroll_top, + line_layout.x_for_index(display_range.start.column() as usize)
// &layout.visible_display_row_range, - layout.position_map.scroll_position.x;
// line_end_overshoot, let start_y = content_origin.y
// &layout.position_map, + row as f32 * layout.position_map.line_height
// ) { - layout.position_map.scroll_position.y;
// cx.scene().push_cursor_region(CursorRegion { let end_x = content_origin.x
// bounds: bound, + line_layout.x_for_index(display_range.end.column() as usize)
// style: CursorStyle::PointingHand, - 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) let fold_background = cx.with_z_index(1, |cx| {
// .to_point(&layout.position_map.snapshot.display_snapshot) div()
// .row; .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(); self.paint_highlighted_range(
// cx.scene().push_mouse_region( display_range.clone(),
// MouseRegion::new::<FoldMarkers>(view_id, *id as usize, bound) fold_background,
// .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { fold_corner_radius,
// editor.unfold_at(&UnfoldAt { buffer_row }, cx) fold_corner_radius * 2.,
// }) layout,
// .with_notify_on_hover(true) content_origin,
// .with_notify_on_click(true), text_bounds,
// ) cx,
// } );
// } }
});
for (range, color) in &layout.highlighted_ranges { 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 {
self.paint_highlighted_range( self.paint_highlighted_range(
selection.range.clone(), range.clone(),
selection_style.selection, *color,
corner_radius, Pixels::ZERO,
corner_radius * 2., line_end_overshoot,
layout, layout,
content_origin, content_origin,
scroll_top, text_bounds,
scroll_left,
bounds,
cx, cx,
); );
}
if selection.is_local && !selection.range.is_empty() { let mut cursors = SmallVec::<[Cursor; 32]>::new();
invisible_display_ranges.push(selection.range.clone()); let corner_radius = 0.15 * layout.position_map.line_height;
} let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
if !selection.is_local || editor.show_local_cursors(cx) { for (selection_style, selections) in &layout.selections {
let cursor_position = selection.head; for selection in selections {
if layout self.paint_highlighted_range(
.visible_display_row_range selection.range.clone(),
.contains(&cursor_position.row()) selection_style.selection,
{ corner_radius,
let cursor_row_layout = &layout.position_map.line_layouts corner_radius * 2.,
[(cursor_position.row() - start_row) as usize] layout,
.line; content_origin,
let cursor_column = cursor_position.column() as usize; text_bounds,
cx,
);
let cursor_character_x = cursor_row_layout.x_for_index(cursor_column); if selection.is_local && !selection.range.is_empty() {
let mut block_width = cursor_row_layout.x_for_index(cursor_column + 1) invisible_display_ranges.push(selection.range.clone());
- cursor_character_x; }
if block_width == Pixels::ZERO {
block_width = layout.position_map.em_width; 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() { for (ix, line_with_invisibles) in
let row = start_row + ix as u32; layout.position_map.line_layouts.iter().enumerate()
line_with_invisibles.draw( {
layout, let row = start_row + ix as u32;
row, line_with_invisibles.draw(
scroll_top, layout,
content_origin, row,
scroll_left, content_origin,
whitespace_setting, whitespace_setting,
&invisible_display_ranges, &invisible_display_ranges,
cx, cx,
) )
}
cx.with_z_index(0, |cx| {
for cursor in cursors {
cursor.paint(content_origin, cx);
} }
});
if let Some((position, context_menu)) = layout.context_menu.as_mut() { cx.with_z_index(0, |cx| {
cx.with_z_index(1, |cx| { for cursor in cursors {
let line_height = self.style.text.line_height_in_pixels(cx.rem_size()); cursor.paint(content_origin, cx);
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);
} }
});
if list_origin.y + list_height > bounds.lower_right().y { if let Some((position, context_menu)) = layout.context_menu.as_mut() {
list_origin.y -= layout.position_map.line_height - list_height; 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() { // Snap the right edge of the list to the right edge of the window if
// cx.scene().push_stacking_context(None, None); // 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 if list_origin.y + list_height > text_bounds.lower_right().y {
// let hovered_row_layout = list_origin.y -= layout.position_map.line_height - list_height;
// &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 context_menu.draw(list_origin, available_space, editor, cx);
// // 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 // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
// let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; // cx.scene().push_stacking_context(None, None);
// 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 { // // This is safe because we check on layout whether the required row is available
// // There is enough space above. Render popovers above the hovered point // let hovered_row_layout =
// let mut current_y = hovered_point.y; // &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
// 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); // // Minimum required size: Take the first popover, and add 1.5 times the minimum popover
// if x_out_of_bounds < 0.0 { // // height. This is the size we will use to decide whether to render popovers above or below
// popover_origin.set_x(popover_origin.x + x_out_of_bounds); // // 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( // // Compute Hovered Point
// popover_origin, // let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left;
// Bounds::<Pixels>::from_points( // let y = position.row() as f32 * layout.position_map.line_height - scroll_top;
// gpui::Point::<Pixels>::zero(), // let hovered_point = content_origin + point(x, y);
// point(f32::MAX, f32::MAX),
// ), // Let content bleed outside of editor
// editor,
// cx,
// );
// current_y = popover_origin.y - HOVER_POPOVER_GAP; // if hovered_point.y - height_to_reserve > 0.0 {
// } // // There is enough space above. Render popovers above the hovered point
// } else { // let mut current_y = hovered_point.y;
// // There is not enough space above. Render popovers below the hovered point // for hover_popover in hover_popovers {
// let mut current_y = hovered_point.y + layout.position_map.line_height; // let size = hover_popover.size();
// for hover_popover in hover_popovers { // let mut popover_origin = point(hovered_point.x, current_y - size.y);
// 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); // let x_out_of_bounds = bounds.max_x - (popover_origin.x + size.x);
// if x_out_of_bounds < 0.0 { // if x_out_of_bounds < 0.0 {
// popover_origin.set_x(popover_origin.x + x_out_of_bounds); // popover_origin.set_x(popover_origin.x + x_out_of_bounds);
// } // }
// hover_popover.paint( // hover_popover.paint(
// popover_origin, // popover_origin,
// Bounds::<Pixels>::from_points( // Bounds::<Pixels>::from_points(
// gpui::Point::<Pixels>::zero(), // gpui::Point::<Pixels>::zero(),
// point(f32::MAX, f32::MAX), // point(f32::MAX, f32::MAX),
// ), // Let content bleed outside of editor // ), // Let content bleed outside of editor
// editor, // editor,
// cx, // 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::<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 { fn scrollbar_left(&self, bounds: &Bounds<Pixels>) -> Pixels {
@ -1133,8 +1164,6 @@ impl EditorElement {
line_end_overshoot: Pixels, line_end_overshoot: Pixels,
layout: &LayoutState, layout: &LayoutState,
content_origin: gpui::Point<Pixels>, content_origin: gpui::Point<Pixels>,
scroll_top: Pixels,
scroll_left: Pixels,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
@ -1153,7 +1182,7 @@ impl EditorElement {
corner_radius, corner_radius,
start_y: content_origin.y start_y: content_origin.y
+ row_range.start as f32 * layout.position_map.line_height + row_range.start as f32 * layout.position_map.line_height
- scroll_top, - layout.position_map.scroll_position.y,
lines: row_range lines: row_range
.into_iter() .into_iter()
.map(|row| { .map(|row| {
@ -1163,17 +1192,17 @@ impl EditorElement {
start_x: if row == range.start.row() { start_x: if row == range.start.row() {
content_origin.x content_origin.x
+ line_layout.x_for_index(range.start.column() as usize) + line_layout.x_for_index(range.start.column() as usize)
- scroll_left - layout.position_map.scroll_position.x
} else { } else {
content_origin.x - scroll_left content_origin.x - layout.position_map.scroll_position.x
}, },
end_x: if row == range.end.row() { end_x: if row == range.end.row() {
content_origin.x content_origin.x
+ line_layout.x_for_index(range.end.column() as usize) + line_layout.x_for_index(range.end.column() as usize)
- scroll_left - layout.position_map.scroll_position.x
} else { } else {
content_origin.x + line_layout.width + line_end_overshoot 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<SelectionLayout>)> = Vec::new(); let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
let mut active_rows = BTreeMap::new(); let mut active_rows = BTreeMap::new();
let mut fold_ranges = Vec::new();
let is_singleton = editor.is_singleton(cx); let is_singleton = editor.is_singleton(cx);
let highlighted_rows = editor.highlighted_rows(); let highlighted_rows = editor.highlighted_rows();
@ -1577,19 +1605,6 @@ impl EditorElement {
cx.theme().colors(), 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; let mut newest_selection_head = None;
if editor.show_local_selections { if editor.show_local_selections {
@ -1698,15 +1713,6 @@ impl EditorElement {
ShowScrollbar::Never => false, ShowScrollbar::Never => false,
}; };
let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, 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 head_for_relative = newest_selection_head.unwrap_or_else(|| {
let newest = editor.selections.newest::<Point>(cx); let newest = editor.selections.newest::<Point>(cx);
SelectionLayout::new( SelectionLayout::new(
@ -1908,6 +1914,10 @@ impl EditorElement {
mode: editor_mode, mode: editor_mode,
position_map: Arc::new(PositionMap { position_map: Arc::new(PositionMap {
size: bounds.size, size: bounds.size,
scroll_position: point(
scroll_position.x * em_width,
scroll_position.y * line_height,
),
scroll_max, scroll_max,
line_layouts, line_layouts,
line_height, line_height,
@ -1915,6 +1925,7 @@ impl EditorElement {
em_advance, em_advance,
snapshot, snapshot,
}), }),
visible_anchor_range: start_anchor..end_anchor,
visible_display_row_range: start_row..end_row, visible_display_row_range: start_row..end_row,
wrap_guides, wrap_guides,
gutter_size, gutter_size,
@ -1928,7 +1939,6 @@ impl EditorElement {
active_rows, active_rows,
highlighted_rows, highlighted_rows,
highlighted_ranges, highlighted_ranges,
fold_ranges,
line_number_layouts, line_number_layouts,
display_hunks, display_hunks,
blocks, blocks,
@ -2116,11 +2126,13 @@ impl EditorElement {
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
gutter_bounds: Bounds<Pixels>, gutter_bounds: Bounds<Pixels>,
text_bounds: Bounds<Pixels>, text_bounds: Bounds<Pixels>,
position_map: &Arc<PositionMap>, layout: &LayoutState,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
cx.on_mouse_event({ cx.on_mouse_event({
let position_map = position_map.clone(); let position_map = layout.position_map.clone();
move |editor, event: &ScrollWheelEvent, phase, cx| { move |editor, event: &ScrollWheelEvent, phase, cx| {
if phase != DispatchPhase::Bubble { if phase != DispatchPhase::Bubble {
return; return;
@ -2132,7 +2144,7 @@ impl EditorElement {
} }
}); });
cx.on_mouse_event({ cx.on_mouse_event({
let position_map = position_map.clone(); let position_map = layout.position_map.clone();
move |editor, event: &MouseDownEvent, phase, cx| { move |editor, event: &MouseDownEvent, phase, cx| {
if phase != DispatchPhase::Bubble { if phase != DispatchPhase::Bubble {
return; return;
@ -2144,7 +2156,7 @@ impl EditorElement {
} }
}); });
cx.on_mouse_event({ cx.on_mouse_event({
let position_map = position_map.clone(); let position_map = layout.position_map.clone();
move |editor, event: &MouseUpEvent, phase, cx| { move |editor, event: &MouseUpEvent, phase, cx| {
if phase != DispatchPhase::Bubble { if phase != DispatchPhase::Bubble {
return; return;
@ -2157,7 +2169,7 @@ impl EditorElement {
}); });
// todo!() // todo!()
// on_down(MouseButton::Right, { // on_down(MouseButton::Right, {
// let position_map = position_map.clone(); // let position_map = layout.position_map.clone();
// move |event, editor, cx| { // move |event, editor, cx| {
// if !Self::mouse_right_down( // if !Self::mouse_right_down(
// editor, // editor,
@ -2171,7 +2183,7 @@ impl EditorElement {
// } // }
// }); // });
cx.on_mouse_event({ cx.on_mouse_event({
let position_map = position_map.clone(); let position_map = layout.position_map.clone();
move |editor, event: &MouseMoveEvent, phase, cx| { move |editor, event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble { if phase != DispatchPhase::Bubble {
return; return;
@ -2301,18 +2313,16 @@ impl LineWithInvisibles {
&self, &self,
layout: &LayoutState, layout: &LayoutState,
row: u32, row: u32,
scroll_top: Pixels,
content_origin: gpui::Point<Pixels>, content_origin: gpui::Point<Pixels>,
scroll_left: Pixels,
whitespace_setting: ShowWhitespaceSetting, whitespace_setting: ShowWhitespaceSetting,
selection_ranges: &[Range<DisplayPoint>], selection_ranges: &[Range<DisplayPoint>],
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let line_height = layout.position_map.line_height; 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( 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, line_height,
cx, cx,
); );
@ -2321,7 +2331,6 @@ impl LineWithInvisibles {
&selection_ranges, &selection_ranges,
layout, layout,
content_origin, content_origin,
scroll_left,
line_y, line_y,
row, row,
line_height, line_height,
@ -2335,7 +2344,6 @@ impl LineWithInvisibles {
selection_ranges: &[Range<DisplayPoint>], selection_ranges: &[Range<DisplayPoint>],
layout: &LayoutState, layout: &LayoutState,
content_origin: gpui::Point<Pixels>, content_origin: gpui::Point<Pixels>,
scroll_left: Pixels,
line_y: Pixels, line_y: Pixels,
row: u32, row: u32,
line_height: Pixels, line_height: Pixels,
@ -2357,8 +2365,11 @@ impl LineWithInvisibles {
let x_offset = self.line.x_for_index(token_offset); let x_offset = self.line.x_for_index(token_offset);
let invisible_offset = let invisible_offset =
(layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0; (layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
let origin = let origin = content_origin
content_origin + gpui::point(-scroll_left + x_offset + invisible_offset, line_y); + gpui::point(
x_offset + invisible_offset - layout.position_map.scroll_position.x,
line_y,
);
if let Some(allowed_regions) = allowed_invisibles_regions { if let Some(allowed_regions) = allowed_invisibles_regions {
let invisible_point = DisplayPoint::new(row, token_offset as u32); let invisible_point = DisplayPoint::new(row, token_offset as u32);
@ -2440,13 +2451,6 @@ impl Element<Editor> for EditorElement {
// We call with_z_index to establish a new stacking context. // We call with_z_index to establish a new stacking context.
cx.with_z_index(0, |cx| { cx.with_z_index(0, |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |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); self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO { if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_bounds, &mut layout, editor, cx); self.paint_gutter(gutter_bounds, &mut layout, editor, cx);
@ -2457,6 +2461,8 @@ impl Element<Editor> for EditorElement {
self.paint_blocks(bounds, &mut layout, editor, cx); 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); let input_handler = ElementInputHandler::new(bounds, cx);
cx.handle_input(&editor.focus_handle, input_handler); cx.handle_input(&editor.focus_handle, input_handler);
}); });
@ -3080,6 +3086,7 @@ pub struct LayoutState {
text_size: gpui::Size<Pixels>, text_size: gpui::Size<Pixels>,
mode: EditorMode, mode: EditorMode,
wrap_guides: SmallVec<[(Pixels, bool); 2]>, wrap_guides: SmallVec<[(Pixels, bool); 2]>,
visible_anchor_range: Range<Anchor>,
visible_display_row_range: Range<u32>, visible_display_row_range: Range<u32>,
active_rows: BTreeMap<u32, bool>, active_rows: BTreeMap<u32, bool>,
highlighted_rows: Option<Range<u32>>, highlighted_rows: Option<Range<u32>>,
@ -3087,7 +3094,6 @@ pub struct LayoutState {
display_hunks: Vec<DisplayDiffHunk>, display_hunks: Vec<DisplayDiffHunk>,
blocks: Vec<BlockLayout>, blocks: Vec<BlockLayout>,
highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>, highlighted_ranges: Vec<(Range<DisplayPoint>, Hsla)>,
fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Hsla)>,
selections: Vec<(PlayerColor, Vec<SelectionLayout>)>, selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
scrollbar_row_range: Range<f32>, scrollbar_row_range: Range<f32>,
show_scrollbars: bool, show_scrollbars: bool,
@ -3109,6 +3115,7 @@ struct CodeActionsIndicator {
struct PositionMap { struct PositionMap {
size: Size<Pixels>, size: Size<Pixels>,
line_height: Pixels, line_height: Pixels,
scroll_position: gpui::Point<Pixels>,
scroll_max: gpui::Point<f32>, scroll_max: gpui::Point<f32>,
em_width: Pixels, em_width: Pixels,
em_advance: Pixels, em_advance: Pixels,
@ -3445,58 +3452,6 @@ impl HighlightedRange {
} }
} }
// fn range_to_bounds(
// range: &Range<DisplayPoint>,
// content_origin: gpui::Point<Pixels>,
// scroll_left: f32,
// scroll_top: f32,
// visible_row_range: &Range<u32>,
// line_end_overshoot: f32,
// position_map: &PositionMap,
// ) -> impl Iterator<Item = Bounds<Pixels>> {
// let mut bounds: SmallVec<[Bounds<Pixels>; 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::<Pixels>::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 { pub fn scale_vertical_mouse_autoscroll_delta(delta: Pixels) -> f32 {
(delta.pow(1.5) / 100.0).into() (delta.pow(1.5) / 100.0).into()
} }

View file

@ -3,7 +3,7 @@ use crate::{
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec; pub(crate) use smallvec::SmallVec;
use std::{any::Any, mem}; use std::{any::Any, fmt::Debug, mem};
pub trait Element<V: 'static> { pub trait Element<V: 'static> {
type ElementState: 'static; type ElementState: 'static;
@ -33,6 +33,42 @@ pub trait Element<V: 'static> {
element_state: &mut Self::ElementState, element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>, cx: &mut ViewContext<V>,
); );
fn draw<T, R>(
self,
origin: Point<Pixels>,
available_space: Size<T>,
view_state: &mut V,
cx: &mut ViewContext<V>,
f: impl FnOnce(&Self::ElementState, &mut ViewContext<V>) -> R,
) -> R
where
Self: Sized,
T: Clone + Default + Debug + Into<AvailableSpace>,
{
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)] #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
@ -99,7 +135,9 @@ enum ElementRenderPhase<V> {
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
frame_state: Option<V>, frame_state: Option<V>,
}, },
Painted, Painted {
frame_state: Option<V>,
},
} }
/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered. /// 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::Start => panic!("must call initialize before layout"),
ElementRenderPhase::LayoutRequested { .. } ElementRenderPhase::LayoutRequested { .. }
| ElementRenderPhase::LayoutComputed { .. } | ElementRenderPhase::LayoutComputed { .. }
| ElementRenderPhase::Painted => { | ElementRenderPhase::Painted { .. } => {
panic!("element rendered twice") panic!("element rendered twice")
} }
}; };
@ -192,7 +230,7 @@ where
self.element self.element
.paint(bounds, view_state, frame_state.as_mut().unwrap(), cx); .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
} }
ElementRenderPhase::Painted ElementRenderPhase::Painted { frame_state }
} }
_ => panic!("must call layout before paint"), _ => panic!("must call layout before paint"),

View file

@ -711,6 +711,12 @@ pub struct DivState {
interactive_state: InteractiveElementState, interactive_state: InteractiveElementState,
} }
impl DivState {
pub fn is_active(&self) -> bool {
self.interactive_state.pending_mouse_down.lock().is_some()
}
}
pub struct Interactivity<V> { pub struct Interactivity<V> {
pub element_id: Option<ElementId>, pub element_id: Option<ElementId>,
pub key_context: KeyContext, pub key_context: KeyContext,

View file

@ -216,7 +216,7 @@ pub struct Window {
// #[derive(Default)] // #[derive(Default)]
pub(crate) struct Frame { pub(crate) struct Frame {
element_states: HashMap<GlobalElementId, AnyBox>, pub(crate) element_states: HashMap<GlobalElementId, AnyBox>,
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>, mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
pub(crate) dispatch_tree: DispatchTree, pub(crate) dispatch_tree: DispatchTree,
pub(crate) focus_listeners: Vec<AnyFocusListener>, pub(crate) focus_listeners: Vec<AnyFocusListener>,