Detailed changes
@@ -31,7 +31,7 @@ pub use block_map::{
BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
};
-pub use self::fold_map::FoldPoint;
+pub use self::fold_map::{Fold, FoldPoint};
pub use self::inlay_map::{Inlay, InlayOffset, InlayPoint};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -124,7 +124,7 @@ impl DisplayMap {
self.fold(
other
.folds_in_range(0..other.buffer_snapshot.len())
- .map(|fold| fold.to_offset(&other.buffer_snapshot)),
+ .map(|fold| fold.range.to_offset(&other.buffer_snapshot)),
cx,
);
}
@@ -723,7 +723,7 @@ impl DisplaySnapshot {
DisplayPoint(point)
}
- pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
+ pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
where
T: ToOffset,
{
@@ -3,15 +3,16 @@ use super::{
Highlights,
};
use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
-use gpui::{HighlightStyle, Hsla};
+use gpui::{ElementId, HighlightStyle, Hsla};
use language::{Chunk, Edit, Point, TextSummary};
use std::{
any::TypeId,
cmp::{self, Ordering},
iter,
- ops::{Add, AddAssign, Range, Sub},
+ ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
};
use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
+use util::post_inc;
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
pub struct FoldPoint(pub Point);
@@ -90,12 +91,16 @@ impl<'a> FoldMapWriter<'a> {
}
// For now, ignore any ranges that span an excerpt boundary.
- let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
- if fold.0.start.excerpt_id != fold.0.end.excerpt_id {
+ let fold_range =
+ FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
+ if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id {
continue;
}
- folds.push(fold);
+ folds.push(Fold {
+ id: FoldId(post_inc(&mut self.0.next_fold_id.0)),
+ range: fold_range,
+ });
let inlay_range =
snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end);
@@ -106,13 +111,13 @@ impl<'a> FoldMapWriter<'a> {
}
let buffer = &snapshot.buffer;
- folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(a, b, buffer));
+ folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
self.0.snapshot.folds = {
let mut new_tree = SumTree::new();
- let mut cursor = self.0.snapshot.folds.cursor::<Fold>();
+ let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>();
for fold in folds {
- new_tree.append(cursor.slice(&fold, Bias::Right, buffer), buffer);
+ new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer);
new_tree.push(fold, buffer);
}
new_tree.append(cursor.suffix(buffer), buffer);
@@ -138,7 +143,8 @@ impl<'a> FoldMapWriter<'a> {
let mut folds_cursor =
intersecting_folds(&snapshot, &self.0.snapshot.folds, range, inclusive);
while let Some(fold) = folds_cursor.item() {
- let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer);
+ let offset_range =
+ fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer);
if offset_range.end > offset_range.start {
let inlay_range = snapshot.to_inlay_offset(offset_range.start)
..snapshot.to_inlay_offset(offset_range.end);
@@ -175,6 +181,7 @@ impl<'a> FoldMapWriter<'a> {
pub struct FoldMap {
snapshot: FoldSnapshot,
ellipses_color: Option<Hsla>,
+ next_fold_id: FoldId,
}
impl FoldMap {
@@ -197,6 +204,7 @@ impl FoldMap {
ellipses_color: None,
},
ellipses_color: None,
+ next_fold_id: FoldId::default(),
};
let snapshot = this.snapshot.clone();
(this, snapshot)
@@ -242,8 +250,8 @@ impl FoldMap {
while let Some(fold) = folds.next() {
if let Some(next_fold) = folds.peek() {
let comparison = fold
- .0
- .cmp(&next_fold.0, &self.snapshot.inlay_snapshot.buffer);
+ .range
+ .cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer);
assert!(comparison.is_le());
}
}
@@ -304,9 +312,9 @@ impl FoldMap {
let anchor = inlay_snapshot
.buffer
.anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
- let mut folds_cursor = self.snapshot.folds.cursor::<Fold>();
+ let mut folds_cursor = self.snapshot.folds.cursor::<FoldRange>();
folds_cursor.seek(
- &Fold(anchor..Anchor::max()),
+ &FoldRange(anchor..Anchor::max()),
Bias::Left,
&inlay_snapshot.buffer,
);
@@ -315,8 +323,8 @@ impl FoldMap {
let inlay_snapshot = &inlay_snapshot;
move || {
let item = folds_cursor.item().map(|f| {
- let buffer_start = f.0.start.to_offset(&inlay_snapshot.buffer);
- let buffer_end = f.0.end.to_offset(&inlay_snapshot.buffer);
+ let buffer_start = f.range.start.to_offset(&inlay_snapshot.buffer);
+ let buffer_end = f.range.end.to_offset(&inlay_snapshot.buffer);
inlay_snapshot.to_inlay_offset(buffer_start)
..inlay_snapshot.to_inlay_offset(buffer_end)
});
@@ -596,13 +604,13 @@ impl FoldSnapshot {
self.transforms.summary().output.longest_row
}
- pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
+ pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
where
T: ToOffset,
{
let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false);
iter::from_fn(move || {
- let item = folds.item().map(|f| &f.0);
+ let item = folds.item();
folds.next(&self.inlay_snapshot.buffer);
item
})
@@ -830,10 +838,39 @@ impl sum_tree::Summary for TransformSummary {
}
}
-#[derive(Clone, Debug)]
-struct Fold(Range<Anchor>);
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
+pub struct FoldId(usize);
+
+impl Into<ElementId> for FoldId {
+ fn into(self) -> ElementId {
+ ElementId::Integer(self.0)
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Fold {
+ pub id: FoldId,
+ pub range: FoldRange,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct FoldRange(Range<Anchor>);
+
+impl Deref for FoldRange {
+ type Target = Range<Anchor>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for FoldRange {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
-impl Default for Fold {
+impl Default for FoldRange {
fn default() -> Self {
Self(Anchor::min()..Anchor::max())
}
@@ -844,17 +881,17 @@ impl sum_tree::Item for Fold {
fn summary(&self) -> Self::Summary {
FoldSummary {
- start: self.0.start.clone(),
- end: self.0.end.clone(),
- min_start: self.0.start.clone(),
- max_end: self.0.end.clone(),
+ start: self.range.start.clone(),
+ end: self.range.end.clone(),
+ min_start: self.range.start.clone(),
+ max_end: self.range.end.clone(),
count: 1,
}
}
}
#[derive(Clone, Debug)]
-struct FoldSummary {
+pub struct FoldSummary {
start: Anchor,
end: Anchor,
min_start: Anchor,
@@ -900,14 +937,14 @@ impl sum_tree::Summary for FoldSummary {
}
}
-impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
+impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
self.0.start = summary.start.clone();
self.0.end = summary.end.clone();
}
}
-impl<'a> sum_tree::SeekTarget<'a, FoldSummary, Fold> for Fold {
+impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange {
fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
self.0.cmp(&other.0, buffer)
}
@@ -1321,7 +1358,10 @@ mod tests {
let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
let fold_ranges = snapshot
.folds_in_range(Point::new(1, 0)..Point::new(1, 3))
- .map(|fold| fold.start.to_point(&buffer_snapshot)..fold.end.to_point(&buffer_snapshot))
+ .map(|fold| {
+ fold.range.start.to_point(&buffer_snapshot)
+ ..fold.range.end.to_point(&buffer_snapshot)
+ })
.collect::<Vec<_>>();
assert_eq!(
fold_ranges,
@@ -1553,10 +1593,9 @@ mod tests {
.filter(|fold| {
let start = buffer_snapshot.anchor_before(start);
let end = buffer_snapshot.anchor_after(end);
- start.cmp(&fold.0.end, &buffer_snapshot) == Ordering::Less
- && end.cmp(&fold.0.start, &buffer_snapshot) == Ordering::Greater
+ start.cmp(&fold.range.end, &buffer_snapshot) == Ordering::Less
+ && end.cmp(&fold.range.start, &buffer_snapshot) == Ordering::Greater
})
- .map(|fold| fold.0)
.collect::<Vec<_>>();
assert_eq!(
@@ -1639,10 +1678,10 @@ mod tests {
let buffer = &inlay_snapshot.buffer;
let mut folds = self.snapshot.folds.items(buffer);
// Ensure sorting doesn't change how folds get merged and displayed.
- folds.sort_by(|a, b| a.0.cmp(&b.0, buffer));
+ folds.sort_by(|a, b| a.range.cmp(&b.range, buffer));
let mut fold_ranges = folds
.iter()
- .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer))
+ .map(|fold| fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer))
.peekable();
let mut merged_ranges = Vec::new();
@@ -39,12 +39,12 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
- action, actions, div, point, prelude::*, px, relative, rems, render_view, size, uniform_list,
- AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem,
- Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight,
- HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels,
- Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext,
- VisualContext, WeakView, WindowContext,
+ action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement,
+ AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
+ EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
+ InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
+ Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
+ WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -4372,69 +4372,42 @@ impl Editor {
}
}
- // pub fn render_fold_indicators(
- // &self,
- // fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
- // style: &EditorStyle,
- // gutter_hovered: bool,
- // line_height: f32,
- // gutter_margin: f32,
- // cx: &mut ViewContext<Self>,
- // ) -> Vec<Option<AnyElement<Self>>> {
- // enum FoldIndicators {}
-
- // let style = style.folds.clone();
-
- // fold_data
- // .iter()
- // .enumerate()
- // .map(|(ix, fold_data)| {
- // fold_data
- // .map(|(fold_status, buffer_row, active)| {
- // (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
- // MouseEventHandler::new::<FoldIndicators, _>(
- // ix as usize,
- // cx,
- // |mouse_state, _| {
- // Svg::new(match fold_status {
- // FoldStatus::Folded => style.folded_icon.clone(),
- // FoldStatus::Foldable => style.foldable_icon.clone(),
- // })
- // .with_color(
- // style
- // .indicator
- // .in_state(fold_status == FoldStatus::Folded)
- // .style_for(mouse_state)
- // .color,
- // )
- // .constrained()
- // .with_width(gutter_margin * style.icon_margin_scale)
- // .aligned()
- // .constrained()
- // .with_height(line_height)
- // .with_width(gutter_margin)
- // .aligned()
- // },
- // )
- // .with_cursor_style(CursorStyle::PointingHand)
- // .with_padding(Padding::uniform(3.))
- // .on_click(MouseButton::Left, {
- // move |_, editor, cx| match fold_status {
- // FoldStatus::Folded => {
- // editor.unfold_at(&UnfoldAt { buffer_row }, cx);
- // }
- // FoldStatus::Foldable => {
- // editor.fold_at(&FoldAt { buffer_row }, cx);
- // }
- // }
- // })
- // .into_any()
- // })
- // })
- // .flatten()
- // })
- // .collect()
- // }
+ pub fn render_fold_indicators(
+ &self,
+ fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
+ style: &EditorStyle,
+ gutter_hovered: bool,
+ line_height: Pixels,
+ gutter_margin: Pixels,
+ cx: &mut ViewContext<Self>,
+ ) -> Vec<Option<AnyElement<Self>>> {
+ fold_data
+ .iter()
+ .enumerate()
+ .map(|(ix, fold_data)| {
+ fold_data
+ .map(|(fold_status, buffer_row, active)| {
+ (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
+ let icon = match fold_status {
+ FoldStatus::Folded => ui::Icon::ChevronRight,
+ FoldStatus::Foldable => ui::Icon::ChevronDown,
+ };
+ IconButton::new(ix as usize, icon)
+ .on_click(move |editor: &mut Editor, cx| match fold_status {
+ FoldStatus::Folded => {
+ editor.unfold_at(&UnfoldAt { buffer_row }, cx);
+ }
+ FoldStatus::Foldable => {
+ editor.fold_at(&FoldAt { buffer_row }, cx);
+ }
+ })
+ .render()
+ })
+ })
+ .flatten()
+ })
+ .collect()
+ }
pub fn context_menu_visible(&self) -> bool {
self.context_menu
@@ -5330,8 +5303,8 @@ impl Editor {
buffer.anchor_before(range_to_move.start)
..buffer.anchor_after(range_to_move.end),
) {
- let mut start = fold.start.to_point(&buffer);
- let mut end = fold.end.to_point(&buffer);
+ let mut start = fold.range.start.to_point(&buffer);
+ let mut end = fold.range.end.to_point(&buffer);
start.row -= row_delta;
end.row -= row_delta;
refold_ranges.push(start..end);
@@ -5421,8 +5394,8 @@ impl Editor {
buffer.anchor_before(range_to_move.start)
..buffer.anchor_after(range_to_move.end),
) {
- let mut start = fold.start.to_point(&buffer);
- let mut end = fold.end.to_point(&buffer);
+ let mut start = fold.range.start.to_point(&buffer);
+ let mut end = fold.range.end.to_point(&buffer);
start.row += row_delta;
end.row += row_delta;
refold_ranges.push(start..end);
@@ -7804,25 +7777,18 @@ impl Editor {
}
div()
.pl(cx.anchor_x)
- .child(render_view(
+ .child(rename_editor.render_with(EditorElement::new(
&rename_editor,
- EditorElement::new(
- &rename_editor,
- EditorStyle {
- background: cx.theme().system().transparent,
- local_player: cx.editor_style.local_player,
- text: text_style,
- scrollbar_width: cx
- .editor_style
- .scrollbar_width,
- syntax: cx.editor_style.syntax.clone(),
- diagnostic_style: cx
- .editor_style
- .diagnostic_style
- .clone(),
- },
- ),
- ))
+ EditorStyle {
+ background: cx.theme().system().transparent,
+ local_player: cx.editor_style.local_player,
+ text: text_style,
+ scrollbar_width: cx.editor_style.scrollbar_width,
+ syntax: cx.editor_style.syntax.clone(),
+ diagnostic_style:
+ cx.editor_style.diagnostic_style.clone(),
+ },
+ )))
.render()
}
}),
@@ -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;
@@ -487,23 +488,26 @@ impl EditorElement {
}
}
- // todo!("fold indicators")
- // for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
- // if let Some(indicator) = fold_indicator.as_mut() {
- // let position = point(
- // bounds.width() - layout.gutter_padding,
- // ix as f32 * line_height - (scroll_top % line_height),
- // );
- // let centering_offset = point(
- // (layout.gutter_padding + layout.gutter_margin - indicator.size().x) / 2.,
- // (line_height - indicator.size().y) / 2.,
- // );
-
- // let indicator_origin = bounds.origin + position + centering_offset;
+ for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
+ if let Some(fold_indicator) = fold_indicator.as_mut() {
+ let available_space = size(
+ AvailableSpace::MinContent,
+ AvailableSpace::Definite(line_height * 0.55),
+ );
+ let fold_indicator_size = fold_indicator.measure(available_space, editor, cx);
- // indicator.paint(indicator_origin, visible_bounds, editor, cx);
- // }
- // }
+ let position = point(
+ bounds.size.width - layout.gutter_padding,
+ ix as f32 * line_height - (scroll_top % line_height),
+ );
+ let centering_offset = point(
+ (layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width) / 2.,
+ (line_height - fold_indicator_size.height) / 2.,
+ );
+ let origin = bounds.origin + position + centering_offset;
+ fold_indicator.draw(origin, available_space, editor, cx);
+ }
+ }
if let Some(indicator) = layout.code_actions_indicator.as_mut() {
let available_space = size(
@@ -612,311 +616,341 @@ impl EditorElement {
fn paint_text(
&mut self,
- bounds: Bounds<Pixels>,
+ text_bounds: Bounds<Pixels>,
layout: &mut LayoutState,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
) {
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
- // },
- // });
-
- // 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,
- );
- }
+ 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
+ // },
+ // });
+
+ 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();
+
+ 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 fold_bounds = Bounds {
+ origin: point(start_x, start_y),
+ size: size(end_x - start_x, layout.position_map.line_height),
+ };
- 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();
+ 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()
+ }
+ },
+ )
+ });
+
+ self.paint_highlighted_range(
+ display_range.clone(),
+ fold_background,
+ fold_corner_radius,
+ fold_corner_radius * 2.,
+ layout,
+ content_origin,
+ text_bounds,
+ cx,
+ );
+ }
+ });
- 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<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,
+ text_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 - 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,
+ });
}
- 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);
+
+ 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;
+
+ // 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);
+ }
- context_menu.draw(list_origin, available_space, editor, cx);
- })
- }
+ if list_origin.y + list_height > text_bounds.lower_right().y {
+ list_origin.y -= layout.position_map.line_height - list_height;
+ }
- // 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();
- // }
- })
+ context_menu.draw(list_origin, available_space, editor, cx);
+ })
+ }
+
+ // 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 {
@@ -1130,8 +1164,6 @@ impl EditorElement {
line_end_overshoot: Pixels,
layout: &LayoutState,
content_origin: gpui::Point<Pixels>,
- scroll_top: Pixels,
- scroll_left: Pixels,
bounds: Bounds<Pixels>,
cx: &mut ViewContext<Editor>,
) {
@@ -1150,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| {
@@ -1160,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
},
}
})
@@ -1564,7 +1596,6 @@ impl EditorElement {
let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = 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();
@@ -1574,19 +1605,6 @@ impl EditorElement {
cx.theme().colors(),
);
- fold_ranges.extend(
- snapshot
- .folds_in_range(start_anchor..end_anchor)
- .map(|anchor| {
- let start = anchor.start.to_point(&snapshot.buffer_snapshot);
- (
- start.row,
- start.to_display_point(&snapshot.display_snapshot)
- ..anchor.end.to_display_point(&snapshot),
- )
- }),
- );
-
let mut newest_selection_head = None;
if editor.show_local_selections {
@@ -1684,36 +1702,17 @@ impl EditorElement {
ShowScrollbar::Auto => {
// Git
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
- ||
- // Selections
- (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
- // Scrollmanager
- || editor.scroll_manager.scrollbars_visible()
+ ||
+ // Selections
+ (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
+ // Scrollmanager
+ || editor.scroll_manager.scrollbars_visible()
}
ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
ShowScrollbar::Always => true,
ShowScrollbar::Never => false,
};
- let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Hsla)> = Vec::new();
- // todo!()
-
- // fold_ranges
- // .into_iter()
- // .map(|(id, fold)| {
- // // todo!("folds!")
- // // let color = self
- // // .style
- // // .folds
- // // .ellipses
- // // .background
- // // .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
- // // .color;
-
- // // (id, fold, color)
- // })
- // .collect();
-
let head_for_relative = newest_selection_head.unwrap_or_else(|| {
let newest = editor.selections.newest::<Point>(cx);
SelectionLayout::new(
@@ -1754,21 +1753,23 @@ impl EditorElement {
.width;
let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
- let (scroll_width, blocks) = self.layout_blocks(
- start_row..end_row,
- &snapshot,
- bounds.size.width,
- scroll_width,
- gutter_padding,
- gutter_width,
- em_width,
- gutter_width + gutter_margin,
- line_height,
- &style,
- &line_layouts,
- editor,
- cx,
- );
+ let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
+ self.layout_blocks(
+ start_row..end_row,
+ &snapshot,
+ bounds.size.width,
+ scroll_width,
+ gutter_padding,
+ gutter_width,
+ em_width,
+ gutter_width + gutter_margin,
+ line_height,
+ &style,
+ &line_layouts,
+ editor,
+ cx,
+ )
+ });
let scroll_max = point(
f32::from((scroll_width - text_size.width) / em_width).max(0.0),
@@ -1828,15 +1829,16 @@ impl EditorElement {
// );
// let mode = editor.mode;
- // todo!("fold_indicators")
- // let mut fold_indicators = editor.render_fold_indicators(
- // fold_statuses,
- // &style,
- // editor.gutter_hovered,
- // line_height,
- // gutter_margin,
- // cx,
- // );
+ let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+ editor.render_fold_indicators(
+ fold_statuses,
+ &style,
+ editor.gutter_hovered,
+ line_height,
+ gutter_margin,
+ cx,
+ )
+ });
// todo!("context_menu")
// if let Some((_, context_menu)) = context_menu.as_mut() {
@@ -1853,20 +1855,6 @@ impl EditorElement {
// );
// }
- // todo!("fold indicators")
- // for fold_indicator in fold_indicators.iter_mut() {
- // if let Some(indicator) = fold_indicator.as_mut() {
- // indicator.layout(
- // SizeConstraint::strict_along(
- // Axis::Vertical,
- // line_height * style.code_actions.vertical_scale,
- // ),
- // editor,
- // cx,
- // );
- // }
- // }
-
// todo!("hover popovers")
// if let Some((_, hover_popovers)) = hover.as_mut() {
// for hover_popover in hover_popovers.iter_mut() {
@@ -1926,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,
@@ -1933,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,
@@ -1946,14 +1939,13 @@ impl EditorElement {
active_rows,
highlighted_rows,
highlighted_ranges,
- fold_ranges,
line_number_layouts,
display_hunks,
blocks,
selections,
context_menu,
code_actions_indicator,
- // fold_indicators,
+ fold_indicators,
tab_invisible,
space_invisible,
// hover_popovers: hover,
@@ -2019,7 +2011,6 @@ impl EditorElement {
})
}
TransformBlock::ExcerptHeader {
- id,
buffer,
range,
starts_new_buffer,
@@ -2041,9 +2032,7 @@ impl EditorElement {
.map_or(range.context.start, |primary| primary.start);
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
- // todo!("avoid ElementId collision risk here")
- let icon_button_id: usize = id.clone().into();
- IconButton::new(icon_button_id, ui::Icon::ArrowUpRight)
+ IconButton::new(block_id, ui::Icon::ArrowUpRight)
.on_click(move |editor: &mut Editor, cx| {
editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
})
@@ -2137,11 +2126,13 @@ impl EditorElement {
bounds: Bounds<Pixels>,
gutter_bounds: Bounds<Pixels>,
text_bounds: Bounds<Pixels>,
- position_map: &Arc<PositionMap>,
+ layout: &LayoutState,
cx: &mut ViewContext<Editor>,
) {
+ 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;
@@ -2153,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;
@@ -2165,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;
@@ -2178,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,
@@ -2192,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;
@@ -2322,18 +2313,16 @@ impl LineWithInvisibles {
&self,
layout: &LayoutState,
row: u32,
- scroll_top: Pixels,
content_origin: gpui::Point<Pixels>,
- scroll_left: Pixels,
whitespace_setting: ShowWhitespaceSetting,
selection_ranges: &[Range<DisplayPoint>],
cx: &mut ViewContext<Editor>,
) {
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,
);
@@ -60,8 +60,8 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
let folds_end = Point::new(hunk.buffer_range.end + 2, 0);
let folds_range = folds_start..folds_end;
- let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| {
- let fold_point_range = fold_range.to_point(&snapshot.buffer_snapshot);
+ let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
+ let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot);
let fold_point_range = fold_point_range.start..=fold_point_range.end;
let folded_start = fold_point_range.contains(&hunk_start_point);
@@ -72,7 +72,7 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
});
if let Some(fold) = containing_fold {
- let row = fold.start.to_display_point(snapshot).row();
+ let row = fold.range.start.to_display_point(snapshot).row();
DisplayDiffHunk::Folded { display_row: row }
} else {
let start = hunk_start_point.to_display_point(snapshot).row();
@@ -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<V: 'static> {
type ElementState: 'static;
@@ -33,6 +33,42 @@ pub trait Element<V: 'static> {
element_state: &mut Self::ElementState,
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)]
@@ -99,7 +135,9 @@ enum ElementRenderPhase<V> {
available_space: Size<AvailableSpace>,
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.
@@ -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"),
@@ -6,15 +6,15 @@ use crate::{
SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
};
use collections::HashMap;
-use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
+ cell::RefCell,
fmt::Debug,
marker::PhantomData,
mem,
- sync::Arc,
+ rc::Rc,
time::Duration,
};
use taffy::style::Overflow;
@@ -420,7 +420,7 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
self.interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
- self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
+ self.interactivity().tooltip_builder = Some(Rc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
@@ -569,7 +569,7 @@ type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
-pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
+pub type TooltipBuilder<V> = Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
pub type KeyDownListener<V> =
Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
@@ -725,6 +725,12 @@ pub struct DivState {
interactive_state: InteractiveElementState,
}
+impl DivState {
+ pub fn is_active(&self) -> bool {
+ self.interactive_state.pending_mouse_down.borrow().is_some()
+ }
+}
+
pub struct Interactivity<V> {
pub element_id: Option<ElementId>,
pub key_context: KeyContext,
@@ -890,7 +896,7 @@ where
if !click_listeners.is_empty() || drag_listener.is_some() {
let pending_mouse_down = element_state.pending_mouse_down.clone();
- let mouse_down = pending_mouse_down.lock().clone();
+ let mouse_down = pending_mouse_down.borrow().clone();
if let Some(mouse_down) = mouse_down {
if let Some(drag_listener) = drag_listener {
let active_state = element_state.clicked_state.clone();
@@ -904,7 +910,7 @@ where
&& bounds.contains_point(&event.position)
&& (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
{
- *active_state.lock() = ElementClickedState::default();
+ *active_state.borrow_mut() = ElementClickedState::default();
let cursor_offset = event.position - bounds.origin;
let drag = drag_listener(view_state, cursor_offset, cx);
cx.active_drag = Some(drag);
@@ -924,13 +930,13 @@ where
listener(view_state, &mouse_click, cx);
}
}
- *pending_mouse_down.lock() = None;
+ *pending_mouse_down.borrow_mut() = None;
cx.notify();
});
} else {
cx.on_mouse_event(move |_view_state, event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
- *pending_mouse_down.lock() = Some(event.clone());
+ *pending_mouse_down.borrow_mut() = Some(event.clone());
cx.notify();
}
});
@@ -946,8 +952,8 @@ where
return;
}
let is_hovered =
- bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
- let mut was_hovered = was_hovered.lock();
+ bounds.contains_point(&event.position) && has_mouse_down.borrow().is_none();
+ let mut was_hovered = was_hovered.borrow_mut();
if is_hovered != was_hovered.clone() {
*was_hovered = is_hovered;
@@ -968,13 +974,13 @@ where
}
let is_hovered =
- bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none();
+ bounds.contains_point(&event.position) && pending_mouse_down.borrow().is_none();
if !is_hovered {
- active_tooltip.lock().take();
+ active_tooltip.borrow_mut().take();
return;
}
- if active_tooltip.lock().is_none() {
+ if active_tooltip.borrow().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let tooltip_builder = tooltip_builder.clone();
@@ -982,7 +988,7 @@ where
move |view, mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
view.update(&mut cx, move |view_state, cx| {
- active_tooltip.lock().replace(ActiveTooltip {
+ active_tooltip.borrow_mut().replace(ActiveTooltip {
waiting: None,
tooltip: Some(AnyTooltip {
view: tooltip_builder(view_state, cx),
@@ -994,14 +1000,14 @@ where
.ok();
}
});
- active_tooltip.lock().replace(ActiveTooltip {
+ active_tooltip.borrow_mut().replace(ActiveTooltip {
waiting: Some(task),
tooltip: None,
});
}
});
- if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
+ if let Some(active_tooltip) = element_state.active_tooltip.borrow().as_ref() {
if active_tooltip.tooltip.is_some() {
cx.active_tooltip = active_tooltip.tooltip.clone()
}
@@ -1009,10 +1015,10 @@ where
}
let active_state = element_state.clicked_state.clone();
- if !active_state.lock().is_clicked() {
+ if !active_state.borrow().is_clicked() {
cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
- *active_state.lock() = ElementClickedState::default();
+ *active_state.borrow_mut() = ElementClickedState::default();
cx.notify();
}
});
@@ -1027,7 +1033,7 @@ where
.map_or(false, |bounds| bounds.contains_point(&down.position));
let element = bounds.contains_point(&down.position);
if group || element {
- *active_state.lock() = ElementClickedState { group, element };
+ *active_state.borrow_mut() = ElementClickedState { group, element };
cx.notify();
}
}
@@ -1038,14 +1044,14 @@ where
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
let scroll_offset = element_state
.scroll_offset
- .get_or_insert_with(Arc::default)
+ .get_or_insert_with(Rc::default)
.clone();
let line_height = cx.line_height();
let scroll_max = (content_size - bounds.size).max(&Size::default());
cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
- let mut scroll_offset = scroll_offset.lock();
+ let mut scroll_offset = scroll_offset.borrow_mut();
let old_scroll_offset = *scroll_offset;
let delta = event.delta.pixel_delta(line_height);
@@ -1074,7 +1080,7 @@ where
let scroll_offset = element_state
.scroll_offset
.as_ref()
- .map(|scroll_offset| *scroll_offset.lock());
+ .map(|scroll_offset| *scroll_offset.borrow());
cx.with_key_dispatch(
self.key_context.clone(),
@@ -1173,7 +1179,7 @@ where
}
}
- let clicked_state = element_state.clicked_state.lock();
+ let clicked_state = element_state.clicked_state.borrow();
if clicked_state.group {
if let Some(group) = self.group_active_style.as_ref() {
style.refine(&group.style)
@@ -1227,11 +1233,11 @@ impl<V: 'static> Default for Interactivity<V> {
#[derive(Default)]
pub struct InteractiveElementState {
pub focus_handle: Option<FocusHandle>,
- pub clicked_state: Arc<Mutex<ElementClickedState>>,
- pub hover_state: Arc<Mutex<bool>>,
- pub pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
- pub scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
- pub active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
+ pub clicked_state: Rc<RefCell<ElementClickedState>>,
+ pub hover_state: Rc<RefCell<bool>>,
+ pub pending_mouse_down: Rc<RefCell<Option<MouseDownEvent>>>,
+ pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
+ pub active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
}
pub struct ActiveTooltip {
@@ -3,9 +3,8 @@ use crate::{
ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
Point, Size, StyleRefinement, Styled, ViewContext,
};
-use parking_lot::Mutex;
use smallvec::SmallVec;
-use std::{cmp, mem, ops::Range, sync::Arc};
+use std::{cell::RefCell, cmp, mem, ops::Range, rc::Rc};
use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
@@ -61,23 +60,23 @@ pub struct UniformList<V: 'static> {
}
#[derive(Clone, Default)]
-pub struct UniformListScrollHandle(Arc<Mutex<Option<ScrollHandleState>>>);
+pub struct UniformListScrollHandle(Rc<RefCell<Option<ScrollHandleState>>>);
#[derive(Clone, Debug)]
struct ScrollHandleState {
item_height: Pixels,
list_height: Pixels,
- scroll_offset: Arc<Mutex<Point<Pixels>>>,
+ scroll_offset: Rc<RefCell<Point<Pixels>>>,
}
impl UniformListScrollHandle {
pub fn new() -> Self {
- Self(Arc::new(Mutex::new(None)))
+ Self(Rc::new(RefCell::new(None)))
}
pub fn scroll_to_item(&self, ix: usize) {
- if let Some(state) = &*self.0.lock() {
- let mut scroll_offset = state.scroll_offset.lock();
+ if let Some(state) = &*self.0.borrow() {
+ let mut scroll_offset = state.scroll_offset.borrow_mut();
let item_top = state.item_height * ix;
let item_bottom = item_top + state.item_height;
let scroll_top = -scroll_offset.y;
@@ -196,7 +195,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
let shared_scroll_offset = element_state
.interactive
.scroll_offset
- .get_or_insert_with(Arc::default)
+ .get_or_insert_with(Rc::default)
.clone();
interactivity.paint(
@@ -222,7 +221,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
if let Some(scroll_handle) = self.scroll_handle.clone() {
- scroll_handle.0.lock().replace(ScrollHandleState {
+ scroll_handle.0.borrow_mut().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: shared_scroll_offset,
@@ -63,6 +63,16 @@ impl<V: 'static> View<V> {
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
self.model.read(cx)
}
+
+ pub fn render_with<C>(&self, component: C) -> RenderViewWith<C, V>
+ where
+ C: 'static + Component<V>,
+ {
+ RenderViewWith {
+ view: self.clone(),
+ component: Some(component),
+ }
+ }
}
impl<V> Clone for View<V> {
@@ -281,12 +291,12 @@ where
}
}
-pub struct RenderView<C, V> {
+pub struct RenderViewWith<C, V> {
view: View<V>,
component: Option<C>,
}
-impl<C, ParentViewState, ViewState> Component<ParentViewState> for RenderView<C, ViewState>
+impl<C, ParentViewState, ViewState> Component<ParentViewState> for RenderViewWith<C, ViewState>
where
C: 'static + Component<ViewState>,
ParentViewState: 'static,
@@ -297,7 +307,7 @@ where
}
}
-impl<C, ParentViewState, ViewState> Element<ParentViewState> for RenderView<C, ViewState>
+impl<C, ParentViewState, ViewState> Element<ParentViewState> for RenderViewWith<C, ViewState>
where
C: 'static + Component<ViewState>,
ParentViewState: 'static,
@@ -348,17 +358,6 @@ where
}
}
-pub fn render_view<C, V>(view: &View<V>, component: C) -> RenderView<C, V>
-where
- C: 'static + Component<V>,
- V: 'static,
-{
- RenderView {
- view: view.clone(),
- component: Some(component),
- }
-}
-
mod any_view {
use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext};
use std::any::Any;
@@ -216,7 +216,7 @@ pub struct Window {
// #[derive(Default)]
pub(crate) struct Frame {
- element_states: HashMap<GlobalElementId, AnyBox>,
+ pub(crate) element_states: HashMap<GlobalElementId, AnyBox>,
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
pub(crate) dispatch_tree: DispatchTree,
pub(crate) focus_listeners: Vec<AnyFocusListener>,
@@ -2471,7 +2471,7 @@ impl From<SmallVec<[u32; 16]>> for StackingOrder {
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ElementId {
View(EntityId),
- Number(usize),
+ Integer(usize),
Name(SharedString),
FocusHandle(FocusId),
}
@@ -2484,13 +2484,13 @@ impl From<EntityId> for ElementId {
impl From<usize> for ElementId {
fn from(id: usize) -> Self {
- ElementId::Number(id)
+ ElementId::Integer(id)
}
}
impl From<i32> for ElementId {
fn from(id: i32) -> Self {
- Self::Number(id as usize)
+ Self::Integer(id as usize)
}
}