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 23:08:59 +00:00
committed by GitHub
co-authored by Antonio Scandurra Marshall Bowers Federico Dionisi
parent 685933b5c8
commit ab59982bf7
74 changed files with 2631 additions and 406 deletions
+10 -3
View File
@@ -1,9 +1,9 @@
use smallvec::SmallVec;
use taffy::style::{Display, Position};
use crate::{
AnyElement, App, Axis, Bounds, Corner, Edges, Element, GlobalElementId, IntoElement, LayoutId,
ParentElement, Pixels, Point, Size, Style, Window, point, px,
AnyElement, App, Axis, Bounds, Corner, Display, Edges, Element, GlobalElementId,
InspectorElementId, IntoElement, LayoutId, ParentElement, Pixels, Point, Position, Size, Style,
Window, point, px,
};
/// The state that the anchored element element uses to track its children.
@@ -91,9 +91,14 @@ impl Element for Anchored {
None
}
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,
) -> (crate::LayoutId, Self::RequestLayoutState) {
@@ -117,6 +122,7 @@ impl Element for Anchored {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -213,6 +219,7 @@ impl Element for Anchored {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
+10 -1
View File
@@ -1,6 +1,8 @@
use std::time::{Duration, Instant};
use crate::{AnyElement, App, Element, ElementId, GlobalElementId, IntoElement, Window};
use crate::{
AnyElement, App, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, Window,
};
pub use easing::*;
use smallvec::SmallVec;
@@ -121,9 +123,14 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
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<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (crate::LayoutId, Self::RequestLayoutState) {
@@ -172,6 +179,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -183,6 +191,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
+9 -2
View File
@@ -1,8 +1,8 @@
use refineable::Refineable as _;
use crate::{
App, Bounds, Element, ElementId, GlobalElementId, IntoElement, Pixels, Style, StyleRefinement,
Styled, Window,
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, Pixels,
Style, StyleRefinement, Styled, Window,
};
/// Construct a canvas element with the given paint callback.
@@ -42,9 +42,14 @@ impl<T: 'static> Element for Canvas<T> {
None
}
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,
) -> (crate::LayoutId, Self::RequestLayoutState) {
@@ -57,6 +62,7 @@ impl<T: 'static> Element for Canvas<T> {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Style,
window: &mut Window,
@@ -68,6 +74,7 @@ impl<T: 'static> Element for Canvas<T> {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
style: &mut Style,
prepaint: &mut Self::PrepaintState,
+9 -1
View File
@@ -1,5 +1,6 @@
use crate::{
AnyElement, App, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, Window,
AnyElement, App, Bounds, Element, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
Pixels, Window,
};
/// Builds a `Deferred` element, which delays the layout and paint of its child.
@@ -35,9 +36,14 @@ impl Element for Deferred {
None
}
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, ()) {
@@ -48,6 +54,7 @@ impl Element for Deferred {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -61,6 +68,7 @@ impl Element for Deferred {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
+151 -33
View File
@@ -18,10 +18,10 @@
use crate::{
Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxId,
IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, ModifiersChangedEvent,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
Visibility, Window, point, px, size,
InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow,
ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
StyleRefinement, Styled, Task, TooltipId, Visibility, Window, point, px, size,
};
use collections::HashMap;
use refineable::Refineable;
@@ -37,7 +37,6 @@ use std::{
sync::Arc,
time::Duration,
};
use taffy::style::Overflow;
use util::ResultExt;
use super::ImageCacheProvider;
@@ -83,6 +82,35 @@ impl<T: 'static> DragMoveEvent<T> {
}
impl Interactivity {
/// Create an `Interactivity`, capturing the caller location in debug mode.
#[cfg(any(feature = "inspector", debug_assertions))]
#[track_caller]
pub fn new() -> Interactivity {
Interactivity {
source_location: Some(core::panic::Location::caller()),
..Default::default()
}
}
/// Create an `Interactivity`, capturing the caller location in debug mode.
#[cfg(not(any(feature = "inspector", debug_assertions)))]
pub fn new() -> Interactivity {
Interactivity::default()
}
/// Gets the source location of construction. Returns `None` when not in debug mode.
pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
#[cfg(any(feature = "inspector", debug_assertions))]
{
self.source_location
}
#[cfg(not(any(feature = "inspector", debug_assertions)))]
{
None
}
}
/// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
/// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
///
@@ -1138,17 +1166,8 @@ pub(crate) type ActionListener =
/// Construct a new [`Div`] element
#[track_caller]
pub fn div() -> Div {
#[cfg(debug_assertions)]
let interactivity = Interactivity {
location: Some(*core::panic::Location::caller()),
..Default::default()
};
#[cfg(not(debug_assertions))]
let interactivity = Interactivity::default();
Div {
interactivity,
interactivity: Interactivity::new(),
children: SmallVec::default(),
prepaint_listener: None,
image_cache: None,
@@ -1191,6 +1210,20 @@ pub struct DivFrameState {
child_layout_ids: SmallVec<[LayoutId; 2]>,
}
/// Interactivity state displayed an manipulated in the inspector.
#[derive(Clone)]
pub struct DivInspectorState {
/// The inspected element's base style. This is used for both inspecting and modifying the
/// state. In the future it will make sense to separate the read and write, possibly tracking
/// the modifications.
#[cfg(any(feature = "inspector", debug_assertions))]
pub base_style: Box<StyleRefinement>,
/// Inspects the bounds of the element.
pub bounds: Bounds<Pixels>,
/// Size of the children of the element, or `bounds.size` if it has no children.
pub content_size: Size<Pixels>,
}
impl Styled for Div {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
@@ -1217,9 +1250,14 @@ impl Element for Div {
self.interactivity.element_id.clone()
}
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
self.interactivity.source_location()
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
@@ -1230,8 +1268,12 @@ impl Element for Div {
.map(|provider| provider.provide(window, cx));
let layout_id = window.with_image_cache(image_cache, |window| {
self.interactivity
.request_layout(global_id, window, cx, |style, window, cx| {
self.interactivity.request_layout(
global_id,
inspector_id,
window,
cx,
|style, window, cx| {
window.with_text_style(style.text_style().cloned(), |window| {
child_layout_ids = self
.children
@@ -1240,7 +1282,8 @@ impl Element for Div {
.collect::<SmallVec<_>>();
window.request_layout(style, child_layout_ids.iter().copied(), cx)
})
})
},
)
});
(layout_id, DivFrameState { child_layout_ids })
@@ -1249,6 +1292,7 @@ impl Element for Div {
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -1294,6 +1338,7 @@ impl Element for Div {
self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
content_size,
window,
@@ -1317,6 +1362,7 @@ impl Element for Div {
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
@@ -1331,6 +1377,7 @@ impl Element for Div {
window.with_image_cache(image_cache, |window| {
self.interactivity.paint(
global_id,
inspector_id,
bounds,
hitbox.as_ref(),
window,
@@ -1403,8 +1450,8 @@ pub struct Interactivity {
pub(crate) tooltip_builder: Option<TooltipBuilder>,
pub(crate) occlude_mouse: bool,
#[cfg(debug_assertions)]
pub(crate) location: Option<core::panic::Location<'static>>,
#[cfg(any(feature = "inspector", debug_assertions))]
pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
#[cfg(any(test, feature = "test-support"))]
pub(crate) debug_selector: Option<String>,
@@ -1415,10 +1462,28 @@ impl Interactivity {
pub fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
) -> LayoutId {
#[cfg(any(feature = "inspector", debug_assertions))]
window.with_inspector_state(
_inspector_id,
cx,
|inspector_state: &mut Option<DivInspectorState>, _window| {
if let Some(inspector_state) = inspector_state {
self.base_style = inspector_state.base_style.clone();
} else {
*inspector_state = Some(DivInspectorState {
base_style: self.base_style.clone(),
bounds: Default::default(),
content_size: Default::default(),
})
}
},
);
window.with_optional_element_state::<InteractiveElementState, _>(
global_id,
|element_state, window| {
@@ -1478,6 +1543,7 @@ impl Interactivity {
pub fn prepaint<R>(
&mut self,
global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
window: &mut Window,
@@ -1485,6 +1551,19 @@ impl Interactivity {
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
) -> R {
self.content_size = content_size;
#[cfg(any(feature = "inspector", debug_assertions))]
window.with_inspector_state(
_inspector_id,
cx,
|inspector_state: &mut Option<DivInspectorState>, _window| {
if let Some(inspector_state) = inspector_state {
inspector_state.bounds = bounds;
inspector_state.content_size = content_size;
}
},
);
if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
window.set_focus_handle(focus_handle, cx);
}
@@ -1514,7 +1593,7 @@ impl Interactivity {
window.with_content_mask(
style.overflow_mask(bounds, window.rem_size()),
|window| {
let hitbox = if self.should_insert_hitbox(&style) {
let hitbox = if self.should_insert_hitbox(&style, window, cx) {
Some(window.insert_hitbox(bounds, self.occlude_mouse))
} else {
None
@@ -1531,7 +1610,7 @@ impl Interactivity {
)
}
fn should_insert_hitbox(&self, style: &Style) -> bool {
fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
self.occlude_mouse
|| style.mouse_cursor.is_some()
|| self.group.is_some()
@@ -1548,6 +1627,7 @@ impl Interactivity {
|| self.drag_listener.is_some()
|| !self.drop_listeners.is_empty()
|| self.tooltip_builder.is_some()
|| window.is_inspector_picking(cx)
}
fn clamp_scroll_position(
@@ -1605,6 +1685,7 @@ impl Interactivity {
pub fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
hitbox: Option<&Hitbox>,
window: &mut Window,
@@ -1672,7 +1753,14 @@ impl Interactivity {
self.paint_keyboard_listeners(window, cx);
f(&style, window, cx);
if hitbox.is_some() {
if let Some(_hitbox) = hitbox {
#[cfg(any(feature = "inspector", debug_assertions))]
window.insert_inspector_hitbox(
_hitbox.id,
_inspector_id,
cx,
);
if let Some(group) = self.group.as_ref() {
GroupHitboxes::pop(group, cx);
}
@@ -1727,7 +1815,7 @@ impl Interactivity {
origin: hitbox.origin,
size: text.size(FONT_SIZE),
};
if self.location.is_some()
if self.source_location.is_some()
&& text_bounds.contains(&window.mouse_position())
&& window.modifiers().secondary()
{
@@ -1758,7 +1846,7 @@ impl Interactivity {
window.on_mouse_event({
let hitbox = hitbox.clone();
let location = self.location.unwrap();
let location = self.source_location.unwrap();
move |e: &crate::MouseDownEvent, phase, window, cx| {
if text_bounds.contains(&e.position)
&& phase.capture()
@@ -2721,37 +2809,52 @@ where
self.element.id()
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
self.element.source_location()
}
fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
self.element.request_layout(id, window, cx)
self.element.request_layout(id, inspector_id, window, cx)
}
fn prepaint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> E::PrepaintState {
self.element.prepaint(id, bounds, state, window, cx)
self.element
.prepaint(id, inspector_id, bounds, state, window, cx)
}
fn paint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
window: &mut Window,
cx: &mut App,
) {
self.element
.paint(id, bounds, request_layout, prepaint, window, cx)
self.element.paint(
id,
inspector_id,
bounds,
request_layout,
prepaint,
window,
cx,
)
}
}
@@ -2818,37 +2921,52 @@ where
self.element.id()
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
self.element.source_location()
}
fn request_layout(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
self.element.request_layout(id, window, cx)
self.element.request_layout(id, inspector_id, window, cx)
}
fn prepaint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> E::PrepaintState {
self.element.prepaint(id, bounds, state, window, cx)
self.element
.prepaint(id, inspector_id, bounds, state, window, cx)
}
fn paint(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
window: &mut Window,
cx: &mut App,
) {
self.element
.paint(id, bounds, request_layout, prepaint, window, cx);
self.element.paint(
id,
inspector_id,
bounds,
request_layout,
prepaint,
window,
cx,
);
}
}
+10 -2
View File
@@ -1,7 +1,8 @@
use crate::{
AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity,
GlobalElementId, ImageAssetLoader, ImageCacheError, IntoElement, LayoutId, ParentElement,
Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Task, Window, hash,
GlobalElementId, ImageAssetLoader, ImageCacheError, InspectorElementId, IntoElement, LayoutId,
ParentElement, Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Task, Window,
hash,
};
use futures::{FutureExt, future::Shared};
@@ -102,9 +103,14 @@ impl Element for ImageCacheElement {
None
}
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) {
@@ -125,6 +131,7 @@ impl Element for ImageCacheElement {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -138,6 +145,7 @@ impl Element for ImageCacheElement {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
+16 -5
View File
@@ -1,9 +1,9 @@
use crate::{
AbsoluteLength, AnyElement, AnyImageCache, App, Asset, AssetLogger, Bounds, DefiniteLength,
Element, ElementId, Entity, GlobalElementId, Hitbox, Image, ImageCache, InteractiveElement,
Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource,
SMOOTH_SVG_SCALE_FACTOR, SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task,
Window, px, swap_rgba_pa_to_bgra,
Element, ElementId, Entity, GlobalElementId, Hitbox, Image, ImageCache, InspectorElementId,
InteractiveElement, Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels,
RenderImage, Resource, SMOOTH_SVG_SCALE_FACTOR, SharedString, SharedUri, StyleRefinement,
Styled, SvgSize, Task, Window, px, swap_rgba_pa_to_bgra,
};
use anyhow::{Context as _, Result};
@@ -194,9 +194,10 @@ pub struct Img {
}
/// Create a new image element.
#[track_caller]
pub fn img(source: impl Into<ImageSource>) -> Img {
Img {
interactivity: Interactivity::default(),
interactivity: Interactivity::new(),
source: source.into(),
style: ImageStyle::default(),
image_cache: None,
@@ -266,9 +267,14 @@ impl Element for Img {
self.interactivity.element_id.clone()
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
self.interactivity.source_location()
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
@@ -290,6 +296,7 @@ impl Element for Img {
let layout_id = self.interactivity.request_layout(
global_id,
inspector_id,
window,
cx,
|mut style, window, cx| {
@@ -408,6 +415,7 @@ impl Element for Img {
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -415,6 +423,7 @@ impl Element for Img {
) -> Self::PrepaintState {
self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
bounds.size,
window,
@@ -432,6 +441,7 @@ impl Element for Img {
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
layout_state: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
@@ -441,6 +451,7 @@ impl Element for Img {
let source = self.source.clone();
self.interactivity.paint(
global_id,
inspector_id,
bounds,
hitbox.as_ref(),
window,
+9 -3
View File
@@ -9,14 +9,13 @@
use crate::{
AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size,
Style, StyleRefinement, Styled, Window, point, px, size,
FocusHandle, GlobalElementId, Hitbox, InspectorElementId, IntoElement, Overflow, Pixels, Point,
ScrollWheelEvent, Size, Style, StyleRefinement, Styled, Window, point, px, size,
};
use collections::VecDeque;
use refineable::Refineable as _;
use std::{cell::RefCell, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree};
use taffy::style::Overflow;
/// Construct a new list element
pub fn list(state: ListState) -> List {
@@ -820,9 +819,14 @@ impl Element for List {
None
}
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,
) -> (crate::LayoutId, Self::RequestLayoutState) {
@@ -890,6 +894,7 @@ impl Element for List {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -938,6 +943,7 @@ impl Element for List {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<crate::Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
+9 -2
View File
@@ -1,6 +1,6 @@
use crate::{
App, Bounds, Element, ElementId, GlobalElementId, IntoElement, LayoutId, ObjectFit, Pixels,
Style, StyleRefinement, Styled, Window,
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
};
#[cfg(target_os = "macos")]
use core_video::pixel_buffer::CVPixelBuffer;
@@ -53,9 +53,14 @@ impl Element for Surface {
None
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
_global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
@@ -68,6 +73,7 @@ impl Element for Surface {
fn prepaint(
&mut self,
_global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_window: &mut Window,
@@ -78,6 +84,7 @@ impl Element for Surface {
fn paint(
&mut self,
_global_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
+22 -9
View File
@@ -1,7 +1,8 @@
use crate::{
App, Bounds, Element, GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement,
LayoutId, Pixels, Point, Radians, SharedString, Size, StyleRefinement, Styled,
TransformationMatrix, Window, geometry::Negate as _, point, px, radians, size,
App, Bounds, Element, GlobalElementId, Hitbox, InspectorElementId, InteractiveElement,
Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,
StyleRefinement, Styled, TransformationMatrix, Window, geometry::Negate as _, point, px,
radians, size,
};
use util::ResultExt;
@@ -13,9 +14,10 @@ pub struct Svg {
}
/// Create a new SVG element.
#[track_caller]
pub fn svg() -> Svg {
Svg {
interactivity: Interactivity::default(),
interactivity: Interactivity::new(),
transformation: None,
path: None,
}
@@ -44,23 +46,31 @@ impl Element for Svg {
self.interactivity.element_id.clone()
}
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
self.interactivity.source_location()
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
let layout_id =
self.interactivity
.request_layout(global_id, window, cx, |style, window, cx| {
window.request_layout(style, None, cx)
});
let layout_id = self.interactivity.request_layout(
global_id,
inspector_id,
window,
cx,
|style, window, cx| window.request_layout(style, None, cx),
);
(layout_id, ())
}
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -68,6 +78,7 @@ impl Element for Svg {
) -> Option<Hitbox> {
self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
bounds.size,
window,
@@ -79,6 +90,7 @@ impl Element for Svg {
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
@@ -89,6 +101,7 @@ impl Element for Svg {
{
self.interactivity.paint(
global_id,
inspector_id,
bounds,
hitbox.as_ref(),
window,
+47 -13
View File
@@ -1,8 +1,9 @@
use crate::{
ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, Point, SharedString, Size, TextOverflow, TextRun, TextStyle, TooltipId, WhiteSpace,
Window, WrappedLine, WrappedLineLayout, register_tooltip_mouse_handlers, set_tooltip_on_window,
HighlightStyle, Hitbox, InspectorElementId, IntoElement, LayoutId, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow, TextRun,
TextStyle, TooltipId, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
register_tooltip_mouse_handlers, set_tooltip_on_window,
};
use anyhow::Context as _;
use smallvec::SmallVec;
@@ -23,9 +24,14 @@ impl Element for &'static str {
None
}
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) {
@@ -37,6 +43,7 @@ impl Element for &'static str {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
text_layout: &mut Self::RequestLayoutState,
_window: &mut Window,
@@ -48,6 +55,7 @@ impl Element for &'static str {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
text_layout: &mut TextLayout,
_: &mut (),
@@ -82,11 +90,14 @@ impl Element for SharedString {
None
}
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) {
@@ -98,6 +109,7 @@ impl Element for SharedString {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
text_layout: &mut Self::RequestLayoutState,
_window: &mut Window,
@@ -109,6 +121,7 @@ impl Element for SharedString {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
text_layout: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -225,9 +238,14 @@ impl Element for StyledText {
None
}
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) {
@@ -244,6 +262,7 @@ impl Element for StyledText {
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_window: &mut Window,
@@ -255,6 +274,7 @@ impl Element for StyledText {
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
@@ -319,8 +339,8 @@ impl TextLayout {
None
};
let (truncate_width, ellipsis) =
if let Some(text_overflow) = text_style.text_overflow {
let (truncate_width, truncation_suffix) =
if let Some(text_overflow) = text_style.text_overflow.clone() {
let width = known_dimensions.width.or(match available_space.width {
crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
Some(max_lines) => Some(x * max_lines),
@@ -330,10 +350,10 @@ impl TextLayout {
});
match text_overflow {
TextOverflow::Ellipsis(s) => (width, Some(s)),
TextOverflow::Truncate(s) => (width, s),
}
} else {
(None, None)
(None, "".into())
};
if let Some(text_layout) = element_state.0.borrow().as_ref() {
@@ -346,7 +366,12 @@ impl TextLayout {
let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size);
let text = if let Some(truncate_width) = truncate_width {
line_wrapper.truncate_line(text.clone(), truncate_width, ellipsis, &mut runs)
line_wrapper.truncate_line(
text.clone(),
truncate_width,
&truncation_suffix,
&mut runs,
)
} else {
text.clone()
};
@@ -673,18 +698,24 @@ impl Element for InteractiveText {
Some(self.element_id.clone())
}
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) {
self.text.request_layout(None, window, cx)
self.text.request_layout(None, inspector_id, window, cx)
}
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
state: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -706,7 +737,8 @@ impl Element for InteractiveText {
}
}
self.text.prepaint(None, bounds, state, window, cx);
self.text
.prepaint(None, inspector_id, bounds, state, window, cx);
let hitbox = window.insert_hitbox(bounds, false);
(hitbox, interactive_state)
},
@@ -716,6 +748,7 @@ impl Element for InteractiveText {
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
hitbox: &mut Hitbox,
@@ -853,7 +886,8 @@ impl Element for InteractiveText {
);
}
self.text.paint(None, bounds, &mut (), &mut (), window, cx);
self.text
.paint(None, inspector_id, bounds, &mut (), &mut (), window, cx);
((), interactive_state)
},
+14 -9
View File
@@ -6,13 +6,12 @@
use crate::{
AnyElement, App, AvailableSpace, Bounds, ContentMask, Context, Element, ElementId, Entity,
GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, IsZero, LayoutId,
ListSizingBehavior, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, Window, point,
size,
GlobalElementId, Hitbox, InspectorElementId, InteractiveElement, Interactivity, IntoElement,
IsZero, LayoutId, ListSizingBehavior, Overflow, Pixels, Render, ScrollHandle, Size,
StyleRefinement, Styled, Window, point, size,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
use taffy::style::Overflow;
use super::ListHorizontalSizingBehavior;
@@ -52,11 +51,7 @@ where
interactivity: Interactivity {
element_id: Some(id),
base_style: Box::new(base_style),
#[cfg(debug_assertions)]
location: Some(*core::panic::Location::caller()),
..Default::default()
..Interactivity::new()
},
scroll_handle: None,
sizing_behavior: ListSizingBehavior::default(),
@@ -166,9 +161,14 @@ impl Element for UniformList {
self.interactivity.element_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<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
@@ -176,6 +176,7 @@ impl Element for UniformList {
let item_size = self.measure_item(None, window, cx);
let layout_id = self.interactivity.request_layout(
global_id,
inspector_id,
window,
cx,
|style, window, cx| match self.sizing_behavior {
@@ -223,6 +224,7 @@ impl Element for UniformList {
fn prepaint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
frame_state: &mut Self::RequestLayoutState,
window: &mut Window,
@@ -271,6 +273,7 @@ impl Element for UniformList {
self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
content_size,
window,
@@ -435,6 +438,7 @@ impl Element for UniformList {
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
bounds: Bounds<crate::Pixels>,
request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
@@ -443,6 +447,7 @@ impl Element for UniformList {
) {
self.interactivity.paint(
global_id,
inspector_id,
bounds,
hitbox.as_ref(),
window,