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::{
div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds,
InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
TextStyle, View, ViewContext, WindowContext,
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@ -2801,44 +2801,49 @@ impl Element for EditorElement {
_element_state: Option<Self::State>,
cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
cx.with_view_id(self.editor.entity_id(), |cx| {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
let layout_id = match editor.mode {
EditorMode::SingleLine => {
let rem_size = cx.rem_size();
let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
cx.request_layout(&style, None)
}
EditorMode::AutoHeight { max_lines } => {
let editor_handle = cx.view().clone();
let max_line_number_width =
self.max_line_number_width(&editor.snapshot(cx), cx);
cx.request_measured_layout(Style::default(), move |known_dimensions, _, cx| {
editor_handle
.update(cx, |editor, cx| {
compute_auto_height_layout(
editor,
max_lines,
max_line_number_width,
known_dimensions,
cx,
)
})
.unwrap_or_default()
})
}
EditorMode::Full => {
let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = relative(1.).into();
cx.request_layout(&style, None)
}
};
let layout_id = match editor.mode {
EditorMode::SingleLine => {
let rem_size = cx.rem_size();
let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
cx.request_layout(&style, None)
}
EditorMode::AutoHeight { max_lines } => {
let editor_handle = cx.view().clone();
let max_line_number_width =
self.max_line_number_width(&editor.snapshot(cx), cx);
cx.request_measured_layout(
Style::default(),
move |known_dimensions, _, cx| {
editor_handle
.update(cx, |editor, cx| {
compute_auto_height_layout(
editor,
max_lines,
max_line_number_width,
known_dimensions,
cx,
)
})
.unwrap_or_default()
},
)
}
EditorMode::Full => {
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();
cx.with_text_style(
Some(gpui::TextStyleRefinement {
font_size: Some(self.style.text.font_size),
..Default::default()
}),
|cx| {
let mut layout = self.compute_layout(bounds, cx);
let gutter_bounds = Bounds {
origin: bounds.origin,
size: layout.gutter_size,
};
let text_bounds = Bounds {
origin: gutter_bounds.upper_right(),
size: layout.text_size,
};
cx.paint_view(self.editor.entity_id(), |cx| {
cx.with_text_style(
Some(gpui::TextStyleRefinement {
font_size: Some(self.style.text.font_size),
..Default::default()
}),
|cx| {
let mut layout = self.compute_layout(bounds, cx);
let gutter_bounds = Bounds {
origin: bounds.origin,
size: layout.gutter_size,
};
let text_bounds = Bounds {
origin: gutter_bounds.upper_right(),
size: layout.text_size,
};
let focus_handle = editor.focus_handle(cx);
let key_context = self.editor.read(cx).key_context(cx);
cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
self.register_actions(cx);
self.register_key_listeners(cx);
let focus_handle = editor.focus_handle(cx);
let key_context = self.editor.read(cx).key_context(cx);
cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
self.register_actions(cx);
self.register_key_listeners(cx);
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let input_handler =
ElementInputHandler::new(bounds, self.editor.clone(), cx);
cx.handle_input(&focus_handle, input_handler);
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let input_handler =
ElementInputHandler::new(bounds, self.editor.clone(), cx);
cx.handle_input(&focus_handle, input_handler);
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_bounds, &mut layout, cx);
}
self.paint_text(text_bounds, &mut layout, cx);
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_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_element_id(Some("editor_blocks"), |cx| {
self.paint_blocks(bounds, &mut layout, 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_element_id(Some("editor_blocks"), |cx| {
self.paint_blocks(bounds, &mut layout, cx);
});
})
}
cx.with_z_index(1, |cx| {
self.paint_overlays(text_bounds, &mut layout, cx);
cx.with_z_index(1, |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.
let bounds = Bounds::<Pixels>::new(Default::default(), size);
cx.update_window(window.into(), |view, cx| {
cx.paint_view(view.entity_id(), |cx| element.paint(bounds, &mut (), cx))
})
.unwrap()
cx.update_window(window.into(), |view, cx| element.paint(bounds, &mut (), cx))
.unwrap()
}
#[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 {
let text_system = self.text_system().clone();
text_system.with_view(view_id, || {
self.window.next_frame.view_stack.push(view_id);
let result = f(self);
self.window.next_frame.view_stack.pop();
result
if self.window.next_frame.view_stack.last() == Some(&view_id) {
return f(self);
} else {
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.
/// 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 {
self.with_view_id(view_id, |cx| {
cx.window
.next_frame
.dispatch_tree
.push_node(None, None, Some(view_id));
let result = f(cx);
cx.window.next_frame.dispatch_tree.pop_node();
result
let text_system = self.text_system().clone();
text_system.with_view(view_id, || {
if self.window.next_frame.view_stack.last() == Some(&view_id) {
return f(self);
} else {
self.window.next_frame.view_stack.push(view_id);
self.window
.next_frame
.dispatch_tree
.push_node(None, None, Some(view_id));
let result = f(self);
self.window.next_frame.view_stack.pop();
result
}
})
}