Eliminate GPUI View, ViewContext, and WindowContext types (#22632)

There's still a bit more work to do on this, but this PR is compiling
(with warnings) after eliminating the key types. When the tasks below
are complete, this will be the new narrative for GPUI:

- `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit
of state, and if `T` implements `Render`, then `Entity<T>` implements
`Element`.
- `&mut App` This replaces `AppContext` and represents the app.
- `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It
is provided by the framework when updating an entity.
- `&mut Window` Broken out of `&mut WindowContext` which no longer
exists. Every method that once took `&mut WindowContext` now takes `&mut
Window, &mut App` and every method that took `&mut ViewContext<T>` now
takes `&mut Window, &mut Context<T>`

Not pictured here are the two other failed attempts. It's been quite a
month!

Tasks:

- [x] Remove `View`, `ViewContext`, `WindowContext` and thread through
`Window`
- [x] [@cole-miller @mikayla-maki] Redraw window when entities change
- [x] [@cole-miller @mikayla-maki] Get examples and Zed running
- [x] [@cole-miller @mikayla-maki] Fix Zed rendering
- [x] [@mikayla-maki] Fix todo! macros and comments
- [x] Fix a bug where the editor would not be redrawn because of view
caching
- [x] remove publicness window.notify() and replace with
`AppContext::notify`
- [x] remove `observe_new_window_models`, replace with
`observe_new_models` with an optional window
- [x] Fix a bug where the project panel would not be redrawn because of
the wrong refresh() call being used
- [x] Fix the tests
- [x] Fix warnings by eliminating `Window` params or using `_`
- [x] Fix conflicts
- [x] Simplify generic code where possible
- [x] Rename types
- [ ] Update docs

### issues post merge

- [x] Issues switching between normal and insert mode
- [x] Assistant re-rendering failure
- [x] Vim test failures
- [x] Mac build issue



Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Joseph <joseph@zed.dev>
Co-authored-by: max <max@zed.dev>
Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local>
Co-authored-by: Mikayla <mikayla.c.maki@gmail.com>
Co-authored-by: joão <joao@zed.dev>
This commit is contained in:
Nathan Sobo 2025-01-25 20:02:45 -07:00 committed by GitHub
parent 21b4a0d50e
commit 6fca1d2b0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
648 changed files with 36248 additions and 28208 deletions

View file

@ -32,8 +32,8 @@
//! your own custom layout algorithm or rendering a code editor.
use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, FocusHandle,
LayoutId, Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
util::FluentBuilder, App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ElementId,
FocusHandle, LayoutId, Pixels, Point, Size, Style, Window, ELEMENT_ARENA,
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
@ -64,7 +64,8 @@ pub trait Element: 'static + IntoElement {
fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState);
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
@ -74,7 +75,8 @@ pub trait Element: 'static + IntoElement {
id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState;
/// Once layout has been completed, this method will be called to paint the element to the screen.
@ -85,7 +87,8 @@ pub trait Element: 'static + IntoElement {
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
);
/// Convert this element into a dynamically-typed [`AnyElement`].
@ -115,11 +118,11 @@ impl<T: IntoElement> FluentBuilder for T {}
/// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
pub trait Render: 'static + Sized {
/// Render this view into an element tree.
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement;
}
impl Render for Empty {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
Empty
}
}
@ -133,7 +136,7 @@ pub trait RenderOnce: 'static {
/// Render this component into an element tree. Note that this method
/// takes ownership of self, as compared to [`Render::render()`] method
/// which takes a mutable reference.
fn render(self, cx: &mut WindowContext) -> impl IntoElement;
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
}
/// This is a helper trait to provide a uniform interface for constructing elements that
@ -184,10 +187,11 @@ impl<C: RenderOnce> Element for Component<C> {
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
let mut element = self.0.take().unwrap().render(cx).into_any_element();
let layout_id = element.request_layout(cx);
let mut element = self.0.take().unwrap().render(window, cx).into_any_element();
let layout_id = element.request_layout(window, cx);
(layout_id, element)
}
@ -196,9 +200,10 @@ impl<C: RenderOnce> Element for Component<C> {
_id: Option<&GlobalElementId>,
_: Bounds<Pixels>,
element: &mut AnyElement,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) {
element.prepaint(cx);
element.prepaint(window, cx);
}
fn paint(
@ -207,9 +212,10 @@ impl<C: RenderOnce> Element for Component<C> {
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) {
element.paint(cx);
element.paint(window, cx);
}
}
@ -228,16 +234,17 @@ pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
trait ElementObject {
fn inner_element(&mut self) -> &mut dyn Any;
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId;
fn prepaint(&mut self, cx: &mut WindowContext);
fn prepaint(&mut self, window: &mut Window, cx: &mut App);
fn paint(&mut self, cx: &mut WindowContext);
fn paint(&mut self, window: &mut Window, cx: &mut App);
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Size<Pixels>;
}
@ -282,19 +289,19 @@ impl<E: Element> Drawable<E> {
}
}
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
match mem::take(&mut self.phase) {
ElementDrawPhase::Start => {
let global_id = self.element.id().map(|element_id| {
cx.window.element_id_stack.push(element_id);
GlobalElementId(cx.window.element_id_stack.clone())
window.element_id_stack.push(element_id);
GlobalElementId(window.element_id_stack.clone())
});
let (layout_id, request_layout) =
self.element.request_layout(global_id.as_ref(), cx);
self.element.request_layout(global_id.as_ref(), window, cx);
if global_id.is_some() {
cx.window.element_id_stack.pop();
window.element_id_stack.pop();
}
self.phase = ElementDrawPhase::RequestLayout {
@ -308,7 +315,7 @@ impl<E: Element> Drawable<E> {
}
}
pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
match mem::take(&mut self.phase) {
ElementDrawPhase::RequestLayout {
layout_id,
@ -322,19 +329,23 @@ impl<E: Element> Drawable<E> {
..
} => {
if let Some(element_id) = self.element.id() {
cx.window.element_id_stack.push(element_id);
debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
window.element_id_stack.push(element_id);
debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);
}
let bounds = cx.layout_bounds(layout_id);
let node_id = cx.window.next_frame.dispatch_tree.push_node();
let prepaint =
self.element
.prepaint(global_id.as_ref(), bounds, &mut request_layout, cx);
cx.window.next_frame.dispatch_tree.pop_node();
let bounds = window.layout_bounds(layout_id);
let node_id = window.next_frame.dispatch_tree.push_node();
let prepaint = self.element.prepaint(
global_id.as_ref(),
bounds,
&mut request_layout,
window,
cx,
);
window.next_frame.dispatch_tree.pop_node();
if global_id.is_some() {
cx.window.element_id_stack.pop();
window.element_id_stack.pop();
}
self.phase = ElementDrawPhase::Prepaint {
@ -351,7 +362,8 @@ impl<E: Element> Drawable<E> {
pub(crate) fn paint(
&mut self,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (E::RequestLayoutState, E::PrepaintState) {
match mem::take(&mut self.phase) {
ElementDrawPhase::Prepaint {
@ -363,21 +375,22 @@ impl<E: Element> Drawable<E> {
..
} => {
if let Some(element_id) = self.element.id() {
cx.window.element_id_stack.push(element_id);
debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
window.element_id_stack.push(element_id);
debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);
}
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
window.next_frame.dispatch_tree.set_active_node(node_id);
self.element.paint(
global_id.as_ref(),
bounds,
&mut request_layout,
&mut prepaint,
window,
cx,
);
if global_id.is_some() {
cx.window.element_id_stack.pop();
window.element_id_stack.pop();
}
self.phase = ElementDrawPhase::Painted;
@ -390,10 +403,11 @@ impl<E: Element> Drawable<E> {
pub(crate) fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx);
self.request_layout(window, cx);
}
let layout_id = match mem::take(&mut self.phase) {
@ -402,7 +416,7 @@ impl<E: Element> Drawable<E> {
global_id,
request_layout,
} => {
cx.compute_layout(layout_id, available_space);
window.compute_layout(layout_id, available_space, cx);
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
global_id,
@ -418,7 +432,7 @@ impl<E: Element> Drawable<E> {
request_layout,
} => {
if available_space != prev_available_space {
cx.compute_layout(layout_id, available_space);
window.compute_layout(layout_id, available_space, cx);
}
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
@ -431,7 +445,7 @@ impl<E: Element> Drawable<E> {
_ => panic!("cannot measure after painting"),
};
cx.layout_bounds(layout_id).size
window.layout_bounds(layout_id).size
}
}
@ -444,24 +458,25 @@ where
&mut self.element
}
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
Drawable::request_layout(self, cx)
fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
Drawable::request_layout(self, window, cx)
}
fn prepaint(&mut self, cx: &mut WindowContext) {
Drawable::prepaint(self, cx);
fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
Drawable::prepaint(self, window, cx);
}
fn paint(&mut self, cx: &mut WindowContext) {
Drawable::paint(self, cx);
fn paint(&mut self, window: &mut Window, cx: &mut App) {
Drawable::paint(self, window, cx);
}
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Size<Pixels> {
Drawable::layout_as_root(self, available_space, cx)
Drawable::layout_as_root(self, available_space, window, cx)
}
}
@ -487,19 +502,19 @@ impl AnyElement {
/// Request the layout ID of the element stored in this `AnyElement`.
/// Used for laying out child elements in a parent element.
pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
self.0.request_layout(cx)
pub fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
self.0.request_layout(window, cx)
}
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
/// request autoscroll before the final paint pass is confirmed.
pub fn prepaint(&mut self, cx: &mut WindowContext) -> Option<FocusHandle> {
let focus_assigned = cx.window.next_frame.focus.is_some();
pub fn prepaint(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusHandle> {
let focus_assigned = window.next_frame.focus.is_some();
self.0.prepaint(cx);
self.0.prepaint(window, cx);
if !focus_assigned {
if let Some(focus_id) = cx.window.next_frame.focus {
if let Some(focus_id) = window.next_frame.focus {
return FocusHandle::for_id(focus_id, &cx.focus_handles);
}
}
@ -508,17 +523,18 @@ impl AnyElement {
}
/// Paints the element stored in this `AnyElement`.
pub fn paint(&mut self, cx: &mut WindowContext) {
self.0.paint(cx);
pub fn paint(&mut self, window: &mut Window, cx: &mut App) {
self.0.paint(window, cx);
}
/// Performs layout for this element within the given available space and returns its size.
pub fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Size<Pixels> {
self.0.layout_as_root(available_space, cx)
self.0.layout_as_root(available_space, window, cx)
}
/// Prepaints this element at the given absolute origin.
@ -526,9 +542,10 @@ impl AnyElement {
pub fn prepaint_at(
&mut self,
origin: Point<Pixels>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Option<FocusHandle> {
cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
}
/// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
@ -537,10 +554,11 @@ impl AnyElement {
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Option<FocusHandle> {
self.layout_as_root(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
self.layout_as_root(available_space, window, cx);
window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
}
}
@ -555,9 +573,10 @@ impl Element for AnyElement {
fn request_layout(
&mut self,
_: Option<&GlobalElementId>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self.request_layout(cx);
let layout_id = self.request_layout(window, cx);
(layout_id, ())
}
@ -566,9 +585,10 @@ impl Element for AnyElement {
_: Option<&GlobalElementId>,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) {
self.prepaint(cx);
self.prepaint(window, cx);
}
fn paint(
@ -577,9 +597,10 @@ impl Element for AnyElement {
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) {
self.paint(cx);
self.paint(window, cx);
}
}
@ -617,9 +638,10 @@ impl Element for Empty {
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
(cx.request_layout(Style::default(), None), ())
(window.request_layout(Style::default(), None, cx), ())
}
fn prepaint(
@ -627,7 +649,8 @@ impl Element for Empty {
_id: Option<&GlobalElementId>,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
_cx: &mut WindowContext,
_window: &mut Window,
_cx: &mut App,
) {
}
@ -637,7 +660,8 @@ impl Element for Empty {
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
_cx: &mut WindowContext,
_window: &mut Window,
_cx: &mut App,
) {
}
}