Ensure editor elements invalidate their parent views on notify

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Conrad Irwin <conrad@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-01-12 17:36:11 +01:00
parent a32ad3f907
commit 817b641c17
2 changed files with 124 additions and 109 deletions

View file

@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus;
use gpui::{ use gpui::{
div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action, div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent, InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
TextStyle, View, ViewContext, WindowContext, Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting; use language::language_settings::ShowWhitespaceSetting;
@ -2801,44 +2801,49 @@ impl Element for EditorElement {
_element_state: Option<Self::State>, _element_state: Option<Self::State>,
cx: &mut gpui::WindowContext, cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) { ) -> (gpui::LayoutId, Self::State) {
self.editor.update(cx, |editor, cx| { cx.with_view_id(self.editor.entity_id(), |cx| {
editor.set_style(self.style.clone(), cx); self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
let layout_id = match editor.mode { let layout_id = match editor.mode {
EditorMode::SingleLine => { EditorMode::SingleLine => {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
let mut style = Style::default(); let mut style = Style::default();
style.size.width = relative(1.).into(); style.size.width = relative(1.).into();
style.size.height = self.style.text.line_height_in_pixels(rem_size).into(); style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
cx.request_layout(&style, None) cx.request_layout(&style, None)
} }
EditorMode::AutoHeight { max_lines } => { EditorMode::AutoHeight { max_lines } => {
let editor_handle = cx.view().clone(); let editor_handle = cx.view().clone();
let max_line_number_width = let max_line_number_width =
self.max_line_number_width(&editor.snapshot(cx), cx); self.max_line_number_width(&editor.snapshot(cx), cx);
cx.request_measured_layout(Style::default(), move |known_dimensions, _, cx| { cx.request_measured_layout(
editor_handle Style::default(),
.update(cx, |editor, cx| { move |known_dimensions, _, cx| {
compute_auto_height_layout( editor_handle
editor, .update(cx, |editor, cx| {
max_lines, compute_auto_height_layout(
max_line_number_width, editor,
known_dimensions, max_lines,
cx, max_line_number_width,
) known_dimensions,
}) cx,
.unwrap_or_default() )
}) })
} .unwrap_or_default()
EditorMode::Full => { },
let mut style = Style::default(); )
style.size.width = relative(1.).into(); }
style.size.height = relative(1.).into(); EditorMode::Full => {
cx.request_layout(&style, None) let mut style = Style::default();
} style.size.width = relative(1.).into();
}; style.size.height = relative(1.).into();
cx.request_layout(&style, None)
}
};
(layout_id, ()) (layout_id, ())
})
}) })
} }
@ -2850,65 +2855,67 @@ impl Element for EditorElement {
) { ) {
let editor = self.editor.clone(); let editor = self.editor.clone();
cx.with_text_style( cx.paint_view(self.editor.entity_id(), |cx| {
Some(gpui::TextStyleRefinement { cx.with_text_style(
font_size: Some(self.style.text.font_size), Some(gpui::TextStyleRefinement {
..Default::default() font_size: Some(self.style.text.font_size),
}), ..Default::default()
|cx| { }),
let mut layout = self.compute_layout(bounds, cx); |cx| {
let gutter_bounds = Bounds { let mut layout = self.compute_layout(bounds, cx);
origin: bounds.origin, let gutter_bounds = Bounds {
size: layout.gutter_size, origin: bounds.origin,
}; size: layout.gutter_size,
let text_bounds = Bounds { };
origin: gutter_bounds.upper_right(), let text_bounds = Bounds {
size: layout.text_size, origin: gutter_bounds.upper_right(),
}; size: layout.text_size,
};
let focus_handle = editor.focus_handle(cx); let focus_handle = editor.focus_handle(cx);
let key_context = self.editor.read(cx).key_context(cx); let key_context = self.editor.read(cx).key_context(cx);
cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| { cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
self.register_actions(cx); self.register_actions(cx);
self.register_key_listeners(cx); self.register_key_listeners(cx);
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let input_handler = let input_handler =
ElementInputHandler::new(bounds, self.editor.clone(), cx); ElementInputHandler::new(bounds, self.editor.clone(), cx);
cx.handle_input(&focus_handle, input_handler); cx.handle_input(&focus_handle, input_handler);
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, cx); self.paint_gutter(gutter_bounds, &mut layout, cx);
} }
self.paint_text(text_bounds, &mut layout, cx); self.paint_text(text_bounds, &mut layout, cx);
cx.with_z_index(0, |cx| {
self.paint_mouse_listeners(
bounds,
gutter_bounds,
text_bounds,
&layout,
cx,
);
});
if !layout.blocks.is_empty() {
cx.with_z_index(0, |cx| { cx.with_z_index(0, |cx| {
cx.with_element_id(Some("editor_blocks"), |cx| { self.paint_mouse_listeners(
self.paint_blocks(bounds, &mut layout, cx); bounds,
}); gutter_bounds,
}) text_bounds,
} &layout,
cx,
);
});
if !layout.blocks.is_empty() {
cx.with_z_index(0, |cx| {
cx.with_element_id(Some("editor_blocks"), |cx| {
self.paint_blocks(bounds, &mut layout, cx);
});
})
}
cx.with_z_index(1, |cx| { cx.with_z_index(1, |cx| {
self.paint_overlays(text_bounds, &mut layout, cx); self.paint_overlays(text_bounds, &mut layout, cx);
});
cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
}); });
})
cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); },
}); )
}) })
},
);
} }
} }
@ -3588,10 +3595,8 @@ mod tests {
// Don't panic. // Don't panic.
let bounds = Bounds::<Pixels>::new(Default::default(), size); let bounds = Bounds::<Pixels>::new(Default::default(), size);
cx.update_window(window.into(), |view, cx| { cx.update_window(window.into(), |view, cx| element.paint(bounds, &mut (), cx))
cx.paint_view(view.entity_id(), |cx| element.paint(bounds, &mut (), cx)) .unwrap()
})
.unwrap()
} }
#[gpui::test] #[gpui::test]

View file

@ -2009,24 +2009,34 @@ impl<'a> WindowContext<'a> {
pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
let text_system = self.text_system().clone(); let text_system = self.text_system().clone();
text_system.with_view(view_id, || { text_system.with_view(view_id, || {
self.window.next_frame.view_stack.push(view_id); if self.window.next_frame.view_stack.last() == Some(&view_id) {
let result = f(self); return f(self);
self.window.next_frame.view_stack.pop(); } else {
result self.window.next_frame.view_stack.push(view_id);
let result = f(self);
self.window.next_frame.view_stack.pop();
result
}
}) })
} }
/// Invoke the given function with the given view id present on the view stack. /// Invoke the given function with the given view id present on the view stack.
/// This is a fairly low-level method used to paint views. /// This is a fairly low-level method used to paint views.
pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
self.with_view_id(view_id, |cx| { let text_system = self.text_system().clone();
cx.window text_system.with_view(view_id, || {
.next_frame if self.window.next_frame.view_stack.last() == Some(&view_id) {
.dispatch_tree return f(self);
.push_node(None, None, Some(view_id)); } else {
let result = f(cx); self.window.next_frame.view_stack.push(view_id);
cx.window.next_frame.dispatch_tree.pop_node(); self.window
result .next_frame
.dispatch_tree
.push_node(None, None, Some(view_id));
let result = f(self);
self.window.next_frame.view_stack.pop();
result
}
}) })
} }