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:
Michael Sloan 2025-05-23 17:08:59 -06:00 committed by GitHub
parent 685933b5c8
commit ab59982bf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 2631 additions and 406 deletions

View file

@ -41,7 +41,7 @@ impl RenderOnce for SplitButton {
)
.child(self.right)
.bg(ElevationIndex::Surface.on_elevation_bg(cx))
.shadow(smallvec::smallvec![BoxShadow {
.shadow(vec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.16),
offset: point(px(0.), px(1.)),
blur_radius: px(0.),

View file

@ -227,9 +227,14 @@ mod uniform_list {
None
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
_id: Option<&gpui::GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
@ -239,6 +244,7 @@ mod uniform_list {
fn prepaint(
&mut self,
_id: Option<&gpui::GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@ -264,6 +270,7 @@ mod uniform_list {
fn paint(
&mut self,
_id: Option<&gpui::GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,

View file

@ -1,7 +1,6 @@
use crate::KeyBinding;
use crate::{h_flex, prelude::*};
use gpui::{AnyElement, App, BoxShadow, FontStyle, Hsla, IntoElement, Window, point};
use smallvec::smallvec;
use theme::Appearance;
/// Represents a hint for a keybinding, optionally with a prefix and suffix.
@ -193,7 +192,7 @@ impl RenderOnce for KeybindingHint {
.border_1()
.border_color(border_color)
.bg(bg_color)
.shadow(smallvec![BoxShadow {
.shadow(vec![BoxShadow {
color: shadow_color,
offset: point(px(0.), px(1.)),
blur_radius: px(0.),

View file

@ -316,9 +316,14 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
Some(self.id.clone())
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
@ -394,6 +399,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
_bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@ -422,6 +428,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
_: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
child_hitbox: &mut Option<HitboxId>,

View file

@ -72,7 +72,7 @@ impl RenderOnce for ProgressBar {
.py(px(2.0))
.px(px(4.0))
.bg(self.bg_color)
.shadow(smallvec::smallvec![gpui::BoxShadow {
.shadow(vec![gpui::BoxShadow {
color: gpui::black().opacity(0.08),
offset: point(px(0.), px(1.)),
blur_radius: px(0.),

View file

@ -116,9 +116,14 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
Some(self.id.clone())
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
@ -174,6 +179,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@ -200,6 +206,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
fn paint(
&mut self,
id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
_bounds: Bounds<gpui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint_state: &mut Self::PrepaintState,

View file

@ -162,16 +162,20 @@ impl Scrollbar {
impl Element for Scrollbar {
type RequestLayoutState = ();
type PrepaintState = Hitbox;
fn id(&self) -> Option<ElementId> {
None
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
@ -193,6 +197,7 @@ impl Element for Scrollbar {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@ -206,6 +211,7 @@ impl Element for Scrollbar {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&gpui::InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,

View file

@ -1,7 +1,6 @@
use std::fmt::{self, Display, Formatter};
use gpui::{App, BoxShadow, Hsla, hsla, point, px};
use smallvec::{SmallVec, smallvec};
use theme::{ActiveTheme, Appearance};
/// Today, elevation is primarily used to add shadows to elements, and set the correct background for elements like buttons.
@ -40,14 +39,14 @@ impl Display for ElevationIndex {
impl ElevationIndex {
/// Returns an appropriate shadow for the given elevation index.
pub fn shadow(self, cx: &App) -> SmallVec<[BoxShadow; 2]> {
pub fn shadow(self, cx: &App) -> Vec<BoxShadow> {
let is_light = cx.theme().appearance() == Appearance::Light;
match self {
ElevationIndex::Surface => smallvec![],
ElevationIndex::EditorSurface => smallvec![],
ElevationIndex::Surface => vec![],
ElevationIndex::EditorSurface => vec![],
ElevationIndex::ElevatedSurface => smallvec![
ElevationIndex::ElevatedSurface => vec![
BoxShadow {
color: hsla(0., 0., 0., 0.12),
offset: point(px(0.), px(2.)),
@ -59,10 +58,10 @@ impl ElevationIndex {
offset: point(px(1.), px(1.)),
blur_radius: px(0.),
spread_radius: px(0.),
}
},
],
ElevationIndex::ModalSurface => smallvec![
ElevationIndex::ModalSurface => vec![
BoxShadow {
color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.12 }),
offset: point(px(0.), px(2.)),
@ -89,7 +88,7 @@ impl ElevationIndex {
},
],
_ => smallvec![],
_ => vec![],
}
}

View file

@ -50,33 +50,41 @@ impl Element for WithRemSize {
Element::id(&self.div)
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
Element::source_location(&self.div)
}
fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
window.with_rem_size(Some(self.rem_size), |window| {
self.div.request_layout(id, window, cx)
self.div.request_layout(id, inspector_id, window, cx)
})
}
fn prepaint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState {
window.with_rem_size(Some(self.rem_size), |window| {
self.div.prepaint(id, bounds, request_layout, window, cx)
self.div
.prepaint(id, inspector_id, bounds, request_layout, window, cx)
})
}
fn paint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
@ -84,8 +92,15 @@ impl Element for WithRemSize {
cx: &mut App,
) {
window.with_rem_size(Some(self.rem_size), |window| {
self.div
.paint(id, bounds, request_layout, prepaint, window, cx)
self.div.paint(
id,
inspector_id,
bounds,
request_layout,
prepaint,
window,
cx,
)
})
}
}