Add initial element inspector for Zed development (#31315)
Open inspector with `dev: toggle inspector` from command palette or `cmd-alt-i` on mac or `ctrl-alt-i` on linux. https://github.com/user-attachments/assets/54c43034-d40b-414e-ba9b-190bed2e6d2f * Picking of elements via the mouse, with scroll wheel to inspect occluded elements. * Temporary manipulation of the selected element. * Layout info and JSON-based style manipulation for `Div`. * Navigation to code that constructed the element. Big thanks to @as-cii and @maxdeviant for sorting out how to implement the core of an inspector. Release Notes: - N/A --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Marshall Bowers <git@maxdeviant.com> Co-authored-by: Federico Dionisi <code@fdionisi.me>
This commit is contained in:
parent
685933b5c8
commit
ab59982bf7
74 changed files with 2631 additions and 406 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId,
|
||||
Entity, EntityId, GlobalElementId, IntoElement, LayoutId, PaintIndex, Pixels,
|
||||
PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
|
||||
Entity, EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, PaintIndex,
|
||||
Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
|
||||
};
|
||||
use crate::{Empty, Window};
|
||||
use anyhow::Result;
|
||||
|
@ -33,9 +33,14 @@ impl<V: Render> Element for Entity<V> {
|
|||
Some(ElementId::View(self.entity_id()))
|
||||
}
|
||||
|
||||
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
|
@ -49,6 +54,7 @@ impl<V: Render> Element for Entity<V> {
|
|||
fn prepaint(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
|
@ -61,6 +67,7 @@ impl<V: Render> Element for Entity<V> {
|
|||
fn paint(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::PrepaintState,
|
||||
|
@ -146,22 +153,32 @@ impl Element for AnyView {
|
|||
Some(ElementId::View(self.entity_id()))
|
||||
}
|
||||
|
||||
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
window.with_rendered_view(self.entity_id(), |window| {
|
||||
if let Some(style) = self.cached_style.as_ref() {
|
||||
let mut root_style = Style::default();
|
||||
root_style.refine(style);
|
||||
let layout_id = window.request_layout(root_style, None, cx);
|
||||
(layout_id, None)
|
||||
} else {
|
||||
let mut element = (self.render)(self, window, cx);
|
||||
let layout_id = element.request_layout(window, cx);
|
||||
(layout_id, Some(element))
|
||||
// Disable caching when inspecting so that mouse_hit_test has all hitboxes.
|
||||
let caching_disabled = window.is_inspector_picking(cx);
|
||||
match self.cached_style.as_ref() {
|
||||
Some(style) if !caching_disabled => {
|
||||
let mut root_style = Style::default();
|
||||
root_style.refine(style);
|
||||
let layout_id = window.request_layout(root_style, None, cx);
|
||||
(layout_id, None)
|
||||
}
|
||||
_ => {
|
||||
let mut element = (self.render)(self, window, cx);
|
||||
let layout_id = element.request_layout(window, cx);
|
||||
(layout_id, Some(element))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -169,6 +186,7 @@ impl Element for AnyView {
|
|||
fn prepaint(
|
||||
&mut self,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
bounds: Bounds<Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
|
@ -176,70 +194,69 @@ impl Element for AnyView {
|
|||
) -> Option<AnyElement> {
|
||||
window.set_view_id(self.entity_id());
|
||||
window.with_rendered_view(self.entity_id(), |window| {
|
||||
if self.cached_style.is_some() {
|
||||
window.with_element_state::<AnyViewState, _>(
|
||||
global_id.unwrap(),
|
||||
|element_state, window| {
|
||||
let content_mask = window.content_mask();
|
||||
let text_style = window.text_style();
|
||||
|
||||
if let Some(mut element_state) = element_state {
|
||||
if element_state.cache_key.bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
&& element_state.cache_key.text_style == text_style
|
||||
&& !window.dirty_views.contains(&self.entity_id())
|
||||
&& !window.refreshing
|
||||
{
|
||||
let prepaint_start = window.prepaint_index();
|
||||
window.reuse_prepaint(element_state.prepaint_range.clone());
|
||||
cx.entities
|
||||
.extend_accessed(&element_state.accessed_entities);
|
||||
let prepaint_end = window.prepaint_index();
|
||||
element_state.prepaint_range = prepaint_start..prepaint_end;
|
||||
|
||||
return (None, element_state);
|
||||
}
|
||||
}
|
||||
|
||||
let refreshing = mem::replace(&mut window.refreshing, true);
|
||||
let prepaint_start = window.prepaint_index();
|
||||
let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
|
||||
let mut element = (self.render)(self, window, cx);
|
||||
element.layout_as_root(bounds.size.into(), window, cx);
|
||||
element.prepaint_at(bounds.origin, window, cx);
|
||||
element
|
||||
});
|
||||
|
||||
let prepaint_end = window.prepaint_index();
|
||||
window.refreshing = refreshing;
|
||||
|
||||
(
|
||||
Some(element),
|
||||
AnyViewState {
|
||||
accessed_entities,
|
||||
prepaint_range: prepaint_start..prepaint_end,
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
cache_key: ViewCacheKey {
|
||||
bounds,
|
||||
content_mask,
|
||||
text_style,
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let mut element = element.take().unwrap();
|
||||
if let Some(mut element) = element.take() {
|
||||
element.prepaint(window, cx);
|
||||
|
||||
Some(element)
|
||||
return Some(element);
|
||||
}
|
||||
|
||||
window.with_element_state::<AnyViewState, _>(
|
||||
global_id.unwrap(),
|
||||
|element_state, window| {
|
||||
let content_mask = window.content_mask();
|
||||
let text_style = window.text_style();
|
||||
|
||||
if let Some(mut element_state) = element_state {
|
||||
if element_state.cache_key.bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
&& element_state.cache_key.text_style == text_style
|
||||
&& !window.dirty_views.contains(&self.entity_id())
|
||||
&& !window.refreshing
|
||||
{
|
||||
let prepaint_start = window.prepaint_index();
|
||||
window.reuse_prepaint(element_state.prepaint_range.clone());
|
||||
cx.entities
|
||||
.extend_accessed(&element_state.accessed_entities);
|
||||
let prepaint_end = window.prepaint_index();
|
||||
element_state.prepaint_range = prepaint_start..prepaint_end;
|
||||
|
||||
return (None, element_state);
|
||||
}
|
||||
}
|
||||
|
||||
let refreshing = mem::replace(&mut window.refreshing, true);
|
||||
let prepaint_start = window.prepaint_index();
|
||||
let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
|
||||
let mut element = (self.render)(self, window, cx);
|
||||
element.layout_as_root(bounds.size.into(), window, cx);
|
||||
element.prepaint_at(bounds.origin, window, cx);
|
||||
element
|
||||
});
|
||||
|
||||
let prepaint_end = window.prepaint_index();
|
||||
window.refreshing = refreshing;
|
||||
|
||||
(
|
||||
Some(element),
|
||||
AnyViewState {
|
||||
accessed_entities,
|
||||
prepaint_range: prepaint_start..prepaint_end,
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
cache_key: ViewCacheKey {
|
||||
bounds,
|
||||
content_mask,
|
||||
text_style,
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_: &mut Self::RequestLayoutState,
|
||||
element: &mut Self::PrepaintState,
|
||||
|
@ -247,7 +264,8 @@ impl Element for AnyView {
|
|||
cx: &mut App,
|
||||
) {
|
||||
window.with_rendered_view(self.entity_id(), |window| {
|
||||
if self.cached_style.is_some() {
|
||||
let caching_disabled = window.is_inspector_picking(cx);
|
||||
if self.cached_style.is_some() && !caching_disabled {
|
||||
window.with_element_state::<AnyViewState, _>(
|
||||
global_id.unwrap(),
|
||||
|element_state, window| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue