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
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -112,7 +112,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_json_lenient",
|
"serde_json_lenient",
|
||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
|
||||||
"smol",
|
"smol",
|
||||||
"streaming_diff",
|
"streaming_diff",
|
||||||
"telemetry",
|
"telemetry",
|
||||||
|
@ -8159,6 +8158,26 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inspector_ui"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"command_palette_hooks",
|
||||||
|
"editor",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"project",
|
||||||
|
"serde_json",
|
||||||
|
"serde_json_lenient",
|
||||||
|
"theme",
|
||||||
|
"ui",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
"workspace-hack",
|
||||||
|
"zed_actions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "install_cli"
|
name = "install_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -8931,8 +8950,10 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"rope",
|
"rope",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_json_lenient",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"snippet_provider",
|
"snippet_provider",
|
||||||
|
@ -19750,6 +19771,7 @@ dependencies = [
|
||||||
"image_viewer",
|
"image_viewer",
|
||||||
"indoc",
|
"indoc",
|
||||||
"inline_completion_button",
|
"inline_completion_button",
|
||||||
|
"inspector_ui",
|
||||||
"install_cli",
|
"install_cli",
|
||||||
"jj_ui",
|
"jj_ui",
|
||||||
"journal",
|
"journal",
|
||||||
|
|
|
@ -73,6 +73,7 @@ members = [
|
||||||
"crates/indexed_docs",
|
"crates/indexed_docs",
|
||||||
"crates/inline_completion",
|
"crates/inline_completion",
|
||||||
"crates/inline_completion_button",
|
"crates/inline_completion_button",
|
||||||
|
"crates/inspector_ui",
|
||||||
"crates/install_cli",
|
"crates/install_cli",
|
||||||
"crates/jj",
|
"crates/jj",
|
||||||
"crates/jj_ui",
|
"crates/jj_ui",
|
||||||
|
@ -279,6 +280,7 @@ image_viewer = { path = "crates/image_viewer" }
|
||||||
indexed_docs = { path = "crates/indexed_docs" }
|
indexed_docs = { path = "crates/indexed_docs" }
|
||||||
inline_completion = { path = "crates/inline_completion" }
|
inline_completion = { path = "crates/inline_completion" }
|
||||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||||
|
inspector_ui = { path = "crates/inspector_ui" }
|
||||||
install_cli = { path = "crates/install_cli" }
|
install_cli = { path = "crates/install_cli" }
|
||||||
jj = { path = "crates/jj" }
|
jj = { path = "crates/jj" }
|
||||||
jj_ui = { path = "crates/jj_ui" }
|
jj_ui = { path = "crates/jj_ui" }
|
||||||
|
@ -447,6 +449,7 @@ futures-batch = "0.6.1"
|
||||||
futures-lite = "1.13"
|
futures-lite = "1.13"
|
||||||
git2 = { version = "0.20.1", default-features = false }
|
git2 = { version = "0.20.1", default-features = false }
|
||||||
globset = "0.4"
|
globset = "0.4"
|
||||||
|
hashbrown = "0.15.3"
|
||||||
handlebars = "4.3"
|
handlebars = "4.3"
|
||||||
heck = "0.5"
|
heck = "0.5"
|
||||||
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
|
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
|
||||||
|
|
|
@ -675,7 +675,7 @@
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
|
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
|
||||||
"ctrl-alt-i": "zed::DebugElements"
|
"ctrl-alt-i": "dev::ToggleInspector"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -735,7 +735,7 @@
|
||||||
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
|
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
|
||||||
// TODO: Move this to a dock open action
|
// TODO: Move this to a dock open action
|
||||||
"cmd-shift-c": "collab_panel::ToggleFocus",
|
"cmd-shift-c": "collab_panel::ToggleFocus",
|
||||||
"cmd-alt-i": "zed::DebugElements"
|
"cmd-alt-i": "dev::ToggleInspector"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,6 @@ serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde_json_lenient.workspace = true
|
serde_json_lenient.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smallvec.workspace = true
|
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
streaming_diff.workspace = true
|
streaming_diff.workspace = true
|
||||||
telemetry.workspace = true
|
telemetry.workspace = true
|
||||||
|
|
|
@ -842,7 +842,7 @@ impl MessageEditor {
|
||||||
.border_b_0()
|
.border_b_0()
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.rounded_t_md()
|
.rounded_t_md()
|
||||||
.shadow(smallvec::smallvec![gpui::BoxShadow {
|
.shadow(vec![gpui::BoxShadow {
|
||||||
color: gpui::black().opacity(0.15),
|
color: gpui::black().opacity(0.15),
|
||||||
offset: point(px(1.), px(-1.)),
|
offset: point(px(1.), px(-1.)),
|
||||||
blur_radius: px(3.),
|
blur_radius: px(3.),
|
||||||
|
|
|
@ -7698,7 +7698,7 @@ impl Editor {
|
||||||
.gap_1()
|
.gap_1()
|
||||||
// Workaround: For some reason, there's a gap if we don't do this
|
// Workaround: For some reason, there's a gap if we don't do this
|
||||||
.ml(-BORDER_WIDTH)
|
.ml(-BORDER_WIDTH)
|
||||||
.shadow(smallvec![gpui::BoxShadow {
|
.shadow(vec![gpui::BoxShadow {
|
||||||
color: gpui::black().opacity(0.05),
|
color: gpui::black().opacity(0.05),
|
||||||
offset: point(px(1.), px(1.)),
|
offset: point(px(1.), px(1.)),
|
||||||
blur_radius: px(2.),
|
blur_radius: px(2.),
|
||||||
|
|
|
@ -138,7 +138,6 @@ pub use git::blame::BlameRenderer;
|
||||||
pub use proposed_changes_editor::{
|
pub use proposed_changes_editor::{
|
||||||
ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
|
ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
|
||||||
};
|
};
|
||||||
use smallvec::smallvec;
|
|
||||||
use std::{cell::OnceCell, iter::Peekable, ops::Not};
|
use std::{cell::OnceCell, iter::Peekable, ops::Not};
|
||||||
use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
|
use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
|
||||||
|
|
||||||
|
@ -176,7 +175,7 @@ use selections_collection::{
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
|
use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{SmallVec, smallvec};
|
||||||
use snippet::Snippet;
|
use snippet::Snippet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -7993,7 +7992,7 @@ impl Editor {
|
||||||
.gap_1()
|
.gap_1()
|
||||||
// Workaround: For some reason, there's a gap if we don't do this
|
// Workaround: For some reason, there's a gap if we don't do this
|
||||||
.ml(-BORDER_WIDTH)
|
.ml(-BORDER_WIDTH)
|
||||||
.shadow(smallvec![gpui::BoxShadow {
|
.shadow(vec![gpui::BoxShadow {
|
||||||
color: gpui::black().opacity(0.05),
|
color: gpui::black().opacity(0.05),
|
||||||
offset: point(px(1.), px(1.)),
|
offset: point(px(1.), px(1.)),
|
||||||
blur_radius: px(2.),
|
blur_radius: px(2.),
|
||||||
|
@ -16708,7 +16707,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
|
pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
|
||||||
let mut wrap_guides = smallvec::smallvec![];
|
let mut wrap_guides = smallvec![];
|
||||||
|
|
||||||
if self.show_wrap_guides == Some(false) {
|
if self.show_wrap_guides == Some(false) {
|
||||||
return wrap_guides;
|
return wrap_guides;
|
||||||
|
|
|
@ -7181,9 +7181,14 @@ impl Element for EditorElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
__inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, ()) {
|
) -> (gpui::LayoutId, ()) {
|
||||||
|
@ -7290,6 +7295,7 @@ impl Element for EditorElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -7761,7 +7767,7 @@ impl Element for EditorElement {
|
||||||
// If the fold widths have changed, we need to prepaint
|
// If the fold widths have changed, we need to prepaint
|
||||||
// the element again to account for any changes in
|
// the element again to account for any changes in
|
||||||
// wrapping.
|
// wrapping.
|
||||||
return self.prepaint(None, bounds, &mut (), window, cx);
|
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let longest_line_blame_width = self
|
let longest_line_blame_width = self
|
||||||
|
@ -7846,7 +7852,7 @@ impl Element for EditorElement {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.resize_blocks(resized_blocks, autoscroll_request, cx)
|
editor.resize_blocks(resized_blocks, autoscroll_request, cx)
|
||||||
});
|
});
|
||||||
return self.prepaint(None, bounds, &mut (), window, cx);
|
return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8345,6 +8351,7 @@ impl Element for EditorElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
__inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<gpui::Pixels>,
|
bounds: Bounds<gpui::Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::PrepaintState,
|
layout: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1135,7 +1135,7 @@ impl SerializableItem for Editor {
|
||||||
mtime,
|
mtime,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let project_item = project.update(cx, |project, cx| {
|
let opened_buffer = project.update(cx, |project, cx| {
|
||||||
let (worktree, path) = project.find_worktree(&abs_path, cx)?;
|
let (worktree, path) = project.find_worktree(&abs_path, cx)?;
|
||||||
let project_path = ProjectPath {
|
let project_path = ProjectPath {
|
||||||
worktree_id: worktree.read(cx).id(),
|
worktree_id: worktree.read(cx).id(),
|
||||||
|
@ -1144,13 +1144,10 @@ impl SerializableItem for Editor {
|
||||||
Some(project.open_path(project_path, cx))
|
Some(project.open_path(project_path, cx))
|
||||||
});
|
});
|
||||||
|
|
||||||
match project_item {
|
match opened_buffer {
|
||||||
Some(project_item) => {
|
Some(opened_buffer) => {
|
||||||
window.spawn(cx, async move |cx| {
|
window.spawn(cx, async move |cx| {
|
||||||
let (_, project_item) = project_item.await?;
|
let (_, buffer) = opened_buffer.await?;
|
||||||
let buffer = project_item.downcast::<Buffer>().map_err(|_| {
|
|
||||||
anyhow!("Project item at stored path was not a buffer")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// This is a bit wasteful: we're loading the whole buffer from
|
// This is a bit wasteful: we're loading the whole buffer from
|
||||||
// disk and then overwrite the content.
|
// disk and then overwrite the content.
|
||||||
|
|
|
@ -22,6 +22,7 @@ test-support = [
|
||||||
"wayland",
|
"wayland",
|
||||||
"x11",
|
"x11",
|
||||||
]
|
]
|
||||||
|
inspector = []
|
||||||
leak-detection = ["backtrace"]
|
leak-detection = ["backtrace"]
|
||||||
runtime_shaders = []
|
runtime_shaders = []
|
||||||
macos-blade = [
|
macos-blade = [
|
||||||
|
|
|
@ -404,16 +404,20 @@ impl IntoElement for TextElement {
|
||||||
|
|
||||||
impl Element for TextElement {
|
impl Element for TextElement {
|
||||||
type RequestLayoutState = ();
|
type RequestLayoutState = ();
|
||||||
|
|
||||||
type PrepaintState = PrepaintState;
|
type PrepaintState = PrepaintState;
|
||||||
|
|
||||||
fn id(&self) -> Option<ElementId> {
|
fn id(&self) -> Option<ElementId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -426,6 +430,7 @@ impl Element for TextElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -523,6 +528,7 @@ impl Element for TextElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -121,7 +121,7 @@ impl Render for HelloWorld {
|
||||||
.bg(gpui::blue())
|
.bg(gpui::blue())
|
||||||
.border_3()
|
.border_3()
|
||||||
.border_color(gpui::black())
|
.border_color(gpui::black())
|
||||||
.shadow(smallvec::smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.5),
|
color: hsla(0.0, 0.0, 0.0, 0.5),
|
||||||
blur_radius: px(1.0),
|
blur_radius: px(1.0),
|
||||||
spread_radius: px(5.0),
|
spread_radius: px(5.0),
|
||||||
|
|
|
@ -3,8 +3,6 @@ use gpui::{
|
||||||
WindowOptions, div, hsla, point, prelude::*, px, relative, rgb, size,
|
WindowOptions, div, hsla, point, prelude::*, px, relative, rgb, size,
|
||||||
};
|
};
|
||||||
|
|
||||||
use smallvec::smallvec;
|
|
||||||
|
|
||||||
struct Shadow {}
|
struct Shadow {}
|
||||||
|
|
||||||
impl Shadow {
|
impl Shadow {
|
||||||
|
@ -103,7 +101,7 @@ impl Render for Shadow {
|
||||||
example(
|
example(
|
||||||
"Square",
|
"Square",
|
||||||
Shadow::square()
|
Shadow::square()
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -113,7 +111,7 @@ impl Render for Shadow {
|
||||||
example(
|
example(
|
||||||
"Rounded 4",
|
"Rounded 4",
|
||||||
Shadow::rounded_small()
|
Shadow::rounded_small()
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -123,7 +121,7 @@ impl Render for Shadow {
|
||||||
example(
|
example(
|
||||||
"Rounded 8",
|
"Rounded 8",
|
||||||
Shadow::rounded_medium()
|
Shadow::rounded_medium()
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -133,7 +131,7 @@ impl Render for Shadow {
|
||||||
example(
|
example(
|
||||||
"Rounded 16",
|
"Rounded 16",
|
||||||
Shadow::rounded_large()
|
Shadow::rounded_large()
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -143,7 +141,7 @@ impl Render for Shadow {
|
||||||
example(
|
example(
|
||||||
"Circle",
|
"Circle",
|
||||||
Shadow::base()
|
Shadow::base()
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -175,7 +173,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Blur 0",
|
"Blur 0",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(0.),
|
blur_radius: px(0.),
|
||||||
|
@ -184,7 +182,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Blur 2",
|
"Blur 2",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(2.),
|
blur_radius: px(2.),
|
||||||
|
@ -193,7 +191,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Blur 4",
|
"Blur 4",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(4.),
|
blur_radius: px(4.),
|
||||||
|
@ -202,7 +200,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Blur 8",
|
"Blur 8",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -211,7 +209,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Blur 16",
|
"Blur 16",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(16.),
|
blur_radius: px(16.),
|
||||||
|
@ -227,7 +225,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Spread 0",
|
"Spread 0",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -236,7 +234,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Spread 2",
|
"Spread 2",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -245,7 +243,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Spread 4",
|
"Spread 4",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -254,7 +252,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Spread 8",
|
"Spread 8",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -263,7 +261,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Spread 16",
|
"Spread 16",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -279,7 +277,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Square Spread 0",
|
"Square Spread 0",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -288,7 +286,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Spread 8",
|
"Square Spread 8",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -297,7 +295,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Spread 16",
|
"Square Spread 16",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -313,7 +311,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Rounded Large Spread 0",
|
"Rounded Large Spread 0",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -322,7 +320,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Spread 8",
|
"Rounded Large Spread 8",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -331,7 +329,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Spread 16",
|
"Rounded Large Spread 16",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.3),
|
color: hsla(0.0, 0.0, 0.0, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -347,7 +345,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Left",
|
"Left",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(-8.), px(0.)),
|
offset: point(px(-8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -356,7 +354,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Right",
|
"Right",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(8.), px(0.)),
|
offset: point(px(8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -365,7 +363,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Top",
|
"Top",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(-8.)),
|
offset: point(px(0.), px(-8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -374,7 +372,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Bottom",
|
"Bottom",
|
||||||
Shadow::base().shadow(smallvec![BoxShadow {
|
Shadow::base().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -390,7 +388,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Square Left",
|
"Square Left",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(-8.), px(0.)),
|
offset: point(px(-8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -399,7 +397,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Right",
|
"Square Right",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(8.), px(0.)),
|
offset: point(px(8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -408,7 +406,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Top",
|
"Square Top",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(-8.)),
|
offset: point(px(0.), px(-8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -417,7 +415,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Bottom",
|
"Square Bottom",
|
||||||
Shadow::square().shadow(smallvec![BoxShadow {
|
Shadow::square().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -433,7 +431,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Rounded Large Left",
|
"Rounded Large Left",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(-8.), px(0.)),
|
offset: point(px(-8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -442,7 +440,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Right",
|
"Rounded Large Right",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(8.), px(0.)),
|
offset: point(px(8.), px(0.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -451,7 +449,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Top",
|
"Rounded Large Top",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(-8.)),
|
offset: point(px(0.), px(-8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -460,7 +458,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Bottom",
|
"Rounded Large Bottom",
|
||||||
Shadow::rounded_large().shadow(smallvec![BoxShadow {
|
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.5, 0.5, 0.3),
|
color: hsla(0.0, 0.5, 0.5, 0.3),
|
||||||
offset: point(px(0.), px(8.)),
|
offset: point(px(0.), px(8.)),
|
||||||
blur_radius: px(8.),
|
blur_radius: px(8.),
|
||||||
|
@ -476,7 +474,7 @@ impl Render for Shadow {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example(
|
example(
|
||||||
"Circle Multiple",
|
"Circle Multiple",
|
||||||
Shadow::base().shadow(smallvec![
|
Shadow::base().shadow(vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
||||||
offset: point(px(0.), px(-12.)),
|
offset: point(px(0.), px(-12.)),
|
||||||
|
@ -505,7 +503,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Square Multiple",
|
"Square Multiple",
|
||||||
Shadow::square().shadow(smallvec![
|
Shadow::square().shadow(vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
||||||
offset: point(px(0.), px(-12.)),
|
offset: point(px(0.), px(-12.)),
|
||||||
|
@ -534,7 +532,7 @@ impl Render for Shadow {
|
||||||
),
|
),
|
||||||
example(
|
example(
|
||||||
"Rounded Large Multiple",
|
"Rounded Large Multiple",
|
||||||
Shadow::rounded_large().shadow(smallvec![
|
Shadow::rounded_large().shadow(vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
|
||||||
offset: point(px(0.), px(-12.)),
|
offset: point(px(0.), px(-12.)),
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl Render for HelloWorld {
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
.text_xl()
|
.text_xl()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.text_overflow(TextOverflow::Ellipsis(""))
|
.text_overflow(TextOverflow::Truncate("".into()))
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(gpui::green())
|
.border_color(gpui::green())
|
||||||
.child("TRUNCATE: ".to_owned() + text),
|
.child("TRUNCATE: ".to_owned() + text),
|
||||||
|
@ -83,7 +83,7 @@ impl Render for HelloWorld {
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
.text_xl()
|
.text_xl()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.text_overflow(TextOverflow::Ellipsis(""))
|
.text_overflow(TextOverflow::Truncate("".into()))
|
||||||
.line_clamp(3)
|
.line_clamp(3)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(gpui::green())
|
.border_color(gpui::green())
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl Render for WindowShadow {
|
||||||
.when(!tiling.left, |div| div.border_l(border_size))
|
.when(!tiling.left, |div| div.border_l(border_size))
|
||||||
.when(!tiling.right, |div| div.border_r(border_size))
|
.when(!tiling.right, |div| div.border_r(border_size))
|
||||||
.when(!tiling.is_tiled(), |div| {
|
.when(!tiling.is_tiled(), |div| {
|
||||||
div.shadow(smallvec::smallvec![gpui::BoxShadow {
|
div.shadow(vec![gpui::BoxShadow {
|
||||||
color: Hsla {
|
color: Hsla {
|
||||||
h: 0.,
|
h: 0.,
|
||||||
s: 0.,
|
s: 0.,
|
||||||
|
@ -144,7 +144,7 @@ impl Render for WindowShadow {
|
||||||
.w(px(200.0))
|
.w(px(200.0))
|
||||||
.h(px(100.0))
|
.h(px(100.0))
|
||||||
.bg(green())
|
.bg(green())
|
||||||
.shadow(smallvec::smallvec![gpui::BoxShadow {
|
.shadow(vec![gpui::BoxShadow {
|
||||||
color: Hsla {
|
color: Hsla {
|
||||||
h: 0.,
|
h: 0.,
|
||||||
s: 0.,
|
s: 0.,
|
||||||
|
|
|
@ -30,6 +30,8 @@ use smallvec::SmallVec;
|
||||||
pub use test_context::*;
|
pub use test_context::*;
|
||||||
use util::{ResultExt, debug_panic};
|
use util::{ResultExt, debug_panic};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
use crate::InspectorElementRegistry;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset,
|
Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Asset,
|
||||||
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
||||||
|
@ -281,6 +283,10 @@ pub struct App {
|
||||||
pub(crate) window_invalidators_by_entity:
|
pub(crate) window_invalidators_by_entity:
|
||||||
FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
|
FxHashMap<EntityId, FxHashMap<WindowId, WindowInvalidator>>,
|
||||||
pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
|
pub(crate) tracked_entities: FxHashMap<WindowId, FxHashSet<EntityId>>,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub(crate) inspector_renderer: Option<crate::InspectorRenderer>,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub(crate) inspector_element_registry: InspectorElementRegistry,
|
||||||
#[cfg(any(test, feature = "test-support", debug_assertions))]
|
#[cfg(any(test, feature = "test-support", debug_assertions))]
|
||||||
pub(crate) name: Option<&'static str>,
|
pub(crate) name: Option<&'static str>,
|
||||||
quitting: bool,
|
quitting: bool,
|
||||||
|
@ -345,6 +351,10 @@ impl App {
|
||||||
layout_id_buffer: Default::default(),
|
layout_id_buffer: Default::default(),
|
||||||
propagate_event: true,
|
propagate_event: true,
|
||||||
prompt_builder: Some(PromptBuilder::Default),
|
prompt_builder: Some(PromptBuilder::Default),
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
inspector_renderer: None,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
inspector_element_registry: InspectorElementRegistry::default(),
|
||||||
quitting: false,
|
quitting: false,
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support", debug_assertions))]
|
#[cfg(any(test, feature = "test-support", debug_assertions))]
|
||||||
|
@ -1669,6 +1679,21 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the renderer for the inspector.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) {
|
||||||
|
self.inspector_renderer = Some(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a renderer specific to an inspector state.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub fn register_inspector_element<T: 'static, R: crate::IntoElement>(
|
||||||
|
&mut self,
|
||||||
|
f: impl 'static + Fn(crate::InspectorElementId, &T, &mut Window, &mut App) -> R,
|
||||||
|
) {
|
||||||
|
self.inspector_element_registry.register(f);
|
||||||
|
}
|
||||||
|
|
||||||
/// Initializes gpui's default colors for the application.
|
/// Initializes gpui's default colors for the application.
|
||||||
///
|
///
|
||||||
/// These colors can be accessed through `cx.default_colors()`.
|
/// These colors can be accessed through `cx.default_colors()`.
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
use anyhow::{Context as _, bail};
|
use anyhow::{Context as _, bail};
|
||||||
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
use schemars::{JsonSchema, SchemaGenerator, schema::Schema};
|
||||||
|
use serde::{
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
de::{self, Visitor},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
|
@ -94,12 +98,48 @@ impl Visitor<'_> for RgbaVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for Rgba {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
"Rgba".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
|
||||||
|
use schemars::schema::{InstanceType, SchemaObject, StringValidation};
|
||||||
|
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
string: Some(Box::new(StringValidation {
|
||||||
|
pattern: Some(
|
||||||
|
r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$".to_string(),
|
||||||
|
),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Rgba {
|
impl<'de> Deserialize<'de> for Rgba {
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
deserializer.deserialize_str(RgbaVisitor)
|
deserializer.deserialize_str(RgbaVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for Rgba {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let r = (self.r * 255.0).round() as u8;
|
||||||
|
let g = (self.g * 255.0).round() as u8;
|
||||||
|
let b = (self.b * 255.0).round() as u8;
|
||||||
|
let a = (self.a * 255.0).round() as u8;
|
||||||
|
|
||||||
|
let s = format!("#{r:02x}{g:02x}{b:02x}{a:02x}");
|
||||||
|
serializer.serialize_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Hsla> for Rgba {
|
impl From<Hsla> for Rgba {
|
||||||
fn from(color: Hsla) -> Self {
|
fn from(color: Hsla) -> Self {
|
||||||
let h = color.h;
|
let h = color.h;
|
||||||
|
@ -588,20 +628,35 @@ impl From<Rgba> for Hsla {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for Hsla {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
Rgba::schema_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
|
||||||
|
Rgba::json_schema(generator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Hsla {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
Rgba::from(*self).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Hsla {
|
impl<'de> Deserialize<'de> for Hsla {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
// First, deserialize it into Rgba
|
Ok(Rgba::deserialize(deserializer)?.into())
|
||||||
let rgba = Rgba::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
// Then, use the From<Rgba> for Hsla implementation to convert it
|
|
||||||
Ok(Hsla::from(rgba))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub(crate) enum BackgroundTag {
|
pub(crate) enum BackgroundTag {
|
||||||
Solid = 0,
|
Solid = 0,
|
||||||
|
@ -614,7 +669,7 @@ pub(crate) enum BackgroundTag {
|
||||||
/// References:
|
/// References:
|
||||||
/// - <https://developer.mozilla.org/en-US/docs/Web/CSS/color-interpolation-method>
|
/// - <https://developer.mozilla.org/en-US/docs/Web/CSS/color-interpolation-method>
|
||||||
/// - <https://www.w3.org/TR/css-color-4/#typedef-color-space>
|
/// - <https://www.w3.org/TR/css-color-4/#typedef-color-space>
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum ColorSpace {
|
pub enum ColorSpace {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -634,7 +689,7 @@ impl Display for ColorSpace {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A background color, which can be either a solid color or a linear gradient.
|
/// A background color, which can be either a solid color or a linear gradient.
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Background {
|
pub struct Background {
|
||||||
pub(crate) tag: BackgroundTag,
|
pub(crate) tag: BackgroundTag,
|
||||||
|
@ -727,7 +782,7 @@ pub fn linear_gradient(
|
||||||
/// A color stop in a linear gradient.
|
/// A color stop in a linear gradient.
|
||||||
///
|
///
|
||||||
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient#linear-color-stop>
|
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient#linear-color-stop>
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct LinearColorStop {
|
pub struct LinearColorStop {
|
||||||
/// The color of the color stop.
|
/// The color of the color stop.
|
||||||
|
|
|
@ -33,11 +33,16 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ELEMENT_ARENA, ElementId,
|
App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ELEMENT_ARENA, ElementId,
|
||||||
FocusHandle, LayoutId, Pixels, Point, Size, Style, Window, util::FluentBuilder,
|
FocusHandle, InspectorElementId, LayoutId, Pixels, Point, Size, Style, Window,
|
||||||
|
util::FluentBuilder,
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub(crate) use smallvec::SmallVec;
|
pub(crate) use smallvec::SmallVec;
|
||||||
use std::{any::Any, fmt::Debug, mem};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
fmt::{self, Debug, Display},
|
||||||
|
mem, panic,
|
||||||
|
};
|
||||||
|
|
||||||
/// Implemented by types that participate in laying out and painting the contents of a window.
|
/// Implemented by types that participate in laying out and painting the contents of a window.
|
||||||
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
|
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
|
||||||
|
@ -59,11 +64,16 @@ pub trait Element: 'static + IntoElement {
|
||||||
/// frames. This id must be unique among children of the first containing element with an id.
|
/// frames. This id must be unique among children of the first containing element with an id.
|
||||||
fn id(&self) -> Option<ElementId>;
|
fn id(&self) -> Option<ElementId>;
|
||||||
|
|
||||||
|
/// Source location where this element was constructed, used to disambiguate elements in the
|
||||||
|
/// inspector and navigate to their source code.
|
||||||
|
fn source_location(&self) -> Option<&'static panic::Location<'static>>;
|
||||||
|
|
||||||
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
||||||
/// Use this method to request a layout from Taffy and initialize the element's state.
|
/// Use this method to request a layout from Taffy and initialize the element's state.
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState);
|
) -> (LayoutId, Self::RequestLayoutState);
|
||||||
|
@ -73,6 +83,7 @@ pub trait Element: 'static + IntoElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -84,6 +95,7 @@ pub trait Element: 'static + IntoElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
@ -167,12 +179,21 @@ pub trait ParentElement {
|
||||||
/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
|
/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
|
||||||
/// for [`RenderOnce`]
|
/// for [`RenderOnce`]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct Component<C: RenderOnce>(Option<C>);
|
pub struct Component<C: RenderOnce> {
|
||||||
|
component: Option<C>,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
source: &'static core::panic::Location<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<C: RenderOnce> Component<C> {
|
impl<C: RenderOnce> Component<C> {
|
||||||
/// Create a new component from the given RenderOnce type.
|
/// Create a new component from the given RenderOnce type.
|
||||||
|
#[track_caller]
|
||||||
pub fn new(component: C) -> Self {
|
pub fn new(component: C) -> Self {
|
||||||
Component(Some(component))
|
Component {
|
||||||
|
component: Some(component),
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
source: core::panic::Location::caller(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,13 +205,27 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
return Some(self.source);
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let mut element = self.0.take().unwrap().render(window, cx).into_any_element();
|
let mut element = self
|
||||||
|
.component
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.render(window, cx)
|
||||||
|
.into_any_element();
|
||||||
let layout_id = element.request_layout(window, cx);
|
let layout_id = element.request_layout(window, cx);
|
||||||
(layout_id, element)
|
(layout_id, element)
|
||||||
}
|
}
|
||||||
|
@ -198,6 +233,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut AnyElement,
|
element: &mut AnyElement,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -209,6 +245,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
@ -231,6 +268,18 @@ impl<C: RenderOnce> IntoElement for Component<C> {
|
||||||
#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]
|
#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
|
pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
|
||||||
|
|
||||||
|
impl Display for GlobalElementId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for (i, element_id) in self.0.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ".")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", element_id)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait ElementObject {
|
trait ElementObject {
|
||||||
fn inner_element(&mut self) -> &mut dyn Any;
|
fn inner_element(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
|
@ -262,17 +311,20 @@ enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
|
||||||
RequestLayout {
|
RequestLayout {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
global_id: Option<GlobalElementId>,
|
global_id: Option<GlobalElementId>,
|
||||||
|
inspector_id: Option<InspectorElementId>,
|
||||||
request_layout: RequestLayoutState,
|
request_layout: RequestLayoutState,
|
||||||
},
|
},
|
||||||
LayoutComputed {
|
LayoutComputed {
|
||||||
layout_id: LayoutId,
|
layout_id: LayoutId,
|
||||||
global_id: Option<GlobalElementId>,
|
global_id: Option<GlobalElementId>,
|
||||||
|
inspector_id: Option<InspectorElementId>,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
request_layout: RequestLayoutState,
|
request_layout: RequestLayoutState,
|
||||||
},
|
},
|
||||||
Prepaint {
|
Prepaint {
|
||||||
node_id: DispatchNodeId,
|
node_id: DispatchNodeId,
|
||||||
global_id: Option<GlobalElementId>,
|
global_id: Option<GlobalElementId>,
|
||||||
|
inspector_id: Option<InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: RequestLayoutState,
|
request_layout: RequestLayoutState,
|
||||||
prepaint: PrepaintState,
|
prepaint: PrepaintState,
|
||||||
|
@ -297,8 +349,28 @@ impl<E: Element> Drawable<E> {
|
||||||
GlobalElementId(window.element_id_stack.clone())
|
GlobalElementId(window.element_id_stack.clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
let (layout_id, request_layout) =
|
let inspector_id;
|
||||||
self.element.request_layout(global_id.as_ref(), window, cx);
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
{
|
||||||
|
inspector_id = self.element.source_location().map(|source| {
|
||||||
|
let path = crate::InspectorElementPath {
|
||||||
|
global_id: GlobalElementId(window.element_id_stack.clone()),
|
||||||
|
source_location: source,
|
||||||
|
};
|
||||||
|
window.build_inspector_element_id(path)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(not(any(feature = "inspector", debug_assertions)))]
|
||||||
|
{
|
||||||
|
inspector_id = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (layout_id, request_layout) = self.element.request_layout(
|
||||||
|
global_id.as_ref(),
|
||||||
|
inspector_id.as_ref(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
if global_id.is_some() {
|
if global_id.is_some() {
|
||||||
window.element_id_stack.pop();
|
window.element_id_stack.pop();
|
||||||
|
@ -307,6 +379,7 @@ impl<E: Element> Drawable<E> {
|
||||||
self.phase = ElementDrawPhase::RequestLayout {
|
self.phase = ElementDrawPhase::RequestLayout {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
request_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
layout_id
|
layout_id
|
||||||
|
@ -320,11 +393,13 @@ impl<E: Element> Drawable<E> {
|
||||||
ElementDrawPhase::RequestLayout {
|
ElementDrawPhase::RequestLayout {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
mut request_layout,
|
mut request_layout,
|
||||||
}
|
}
|
||||||
| ElementDrawPhase::LayoutComputed {
|
| ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
mut request_layout,
|
mut request_layout,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
@ -337,6 +412,7 @@ impl<E: Element> Drawable<E> {
|
||||||
let node_id = window.next_frame.dispatch_tree.push_node();
|
let node_id = window.next_frame.dispatch_tree.push_node();
|
||||||
let prepaint = self.element.prepaint(
|
let prepaint = self.element.prepaint(
|
||||||
global_id.as_ref(),
|
global_id.as_ref(),
|
||||||
|
inspector_id.as_ref(),
|
||||||
bounds,
|
bounds,
|
||||||
&mut request_layout,
|
&mut request_layout,
|
||||||
window,
|
window,
|
||||||
|
@ -351,6 +427,7 @@ impl<E: Element> Drawable<E> {
|
||||||
self.phase = ElementDrawPhase::Prepaint {
|
self.phase = ElementDrawPhase::Prepaint {
|
||||||
node_id,
|
node_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
request_layout,
|
request_layout,
|
||||||
prepaint,
|
prepaint,
|
||||||
|
@ -369,6 +446,7 @@ impl<E: Element> Drawable<E> {
|
||||||
ElementDrawPhase::Prepaint {
|
ElementDrawPhase::Prepaint {
|
||||||
node_id,
|
node_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
mut request_layout,
|
mut request_layout,
|
||||||
mut prepaint,
|
mut prepaint,
|
||||||
|
@ -382,6 +460,7 @@ impl<E: Element> Drawable<E> {
|
||||||
window.next_frame.dispatch_tree.set_active_node(node_id);
|
window.next_frame.dispatch_tree.set_active_node(node_id);
|
||||||
self.element.paint(
|
self.element.paint(
|
||||||
global_id.as_ref(),
|
global_id.as_ref(),
|
||||||
|
inspector_id.as_ref(),
|
||||||
bounds,
|
bounds,
|
||||||
&mut request_layout,
|
&mut request_layout,
|
||||||
&mut prepaint,
|
&mut prepaint,
|
||||||
|
@ -414,12 +493,14 @@ impl<E: Element> Drawable<E> {
|
||||||
ElementDrawPhase::RequestLayout {
|
ElementDrawPhase::RequestLayout {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
request_layout,
|
request_layout,
|
||||||
} => {
|
} => {
|
||||||
window.compute_layout(layout_id, available_space, cx);
|
window.compute_layout(layout_id, available_space, cx);
|
||||||
self.phase = ElementDrawPhase::LayoutComputed {
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
available_space,
|
available_space,
|
||||||
request_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
|
@ -428,6 +509,7 @@ impl<E: Element> Drawable<E> {
|
||||||
ElementDrawPhase::LayoutComputed {
|
ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
available_space: prev_available_space,
|
available_space: prev_available_space,
|
||||||
request_layout,
|
request_layout,
|
||||||
} => {
|
} => {
|
||||||
|
@ -437,6 +519,7 @@ impl<E: Element> Drawable<E> {
|
||||||
self.phase = ElementDrawPhase::LayoutComputed {
|
self.phase = ElementDrawPhase::LayoutComputed {
|
||||||
layout_id,
|
layout_id,
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
available_space,
|
available_space,
|
||||||
request_layout,
|
request_layout,
|
||||||
};
|
};
|
||||||
|
@ -570,9 +653,14 @@ impl Element for AnyElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -583,6 +671,7 @@ impl Element for AnyElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -594,6 +683,7 @@ impl Element for AnyElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Option<&GlobalElementId>,
|
_: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
@ -635,9 +725,14 @@ impl Element for Empty {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -647,6 +742,7 @@ impl Element for Empty {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::RequestLayoutState,
|
_state: &mut Self::RequestLayoutState,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
|
@ -657,6 +753,7 @@ impl Element for Empty {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use taffy::style::{Display, Position};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, App, Axis, Bounds, Corner, Edges, Element, GlobalElementId, IntoElement, LayoutId,
|
AnyElement, App, Axis, Bounds, Corner, Display, Edges, Element, GlobalElementId,
|
||||||
ParentElement, Pixels, Point, Size, Style, Window, point, px,
|
InspectorElementId, IntoElement, LayoutId, ParentElement, Pixels, Point, Position, Size, Style,
|
||||||
|
Window, point, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The state that the anchored element element uses to track its children.
|
/// The state that the anchored element element uses to track its children.
|
||||||
|
@ -91,9 +91,14 @@ impl Element for Anchored {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -117,6 +122,7 @@ impl Element for Anchored {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -213,6 +219,7 @@ impl Element for Anchored {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::time::{Duration, Instant};
|
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::*;
|
pub use easing::*;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -121,9 +123,14 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||||
Some(self.id.clone())
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -172,6 +179,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -183,6 +191,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: crate::Bounds<crate::Pixels>,
|
_bounds: crate::Bounds<crate::Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use refineable::Refineable as _;
|
use refineable::Refineable as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
App, Bounds, Element, ElementId, GlobalElementId, IntoElement, Pixels, Style, StyleRefinement,
|
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, Pixels,
|
||||||
Styled, Window,
|
Style, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Construct a canvas element with the given paint callback.
|
/// Construct a canvas element with the given paint callback.
|
||||||
|
@ -42,9 +42,14 @@ impl<T: 'static> Element for Canvas<T> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -57,6 +62,7 @@ impl<T: 'static> Element for Canvas<T> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Style,
|
_request_layout: &mut Style,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -68,6 +74,7 @@ impl<T: 'static> Element for Canvas<T> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
style: &mut Style,
|
style: &mut Style,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
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.
|
/// Builds a `Deferred` element, which delays the layout and paint of its child.
|
||||||
|
@ -35,9 +36,14 @@ impl Element for Deferred {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, ()) {
|
) -> (LayoutId, ()) {
|
||||||
|
@ -48,6 +54,7 @@ impl Element for Deferred {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -61,6 +68,7 @@ impl Element for Deferred {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
|
Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
|
||||||
Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxId,
|
Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox, HitboxId,
|
||||||
IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, ModifiersChangedEvent,
|
InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
|
||||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
|
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow,
|
||||||
Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
|
ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
|
||||||
Visibility, Window, point, px, size,
|
StyleRefinement, Styled, Task, TooltipId, Visibility, Window, point, px, size,
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
@ -37,7 +37,6 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use taffy::style::Overflow;
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use super::ImageCacheProvider;
|
use super::ImageCacheProvider;
|
||||||
|
@ -83,6 +82,35 @@ impl<T: 'static> DragMoveEvent<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interactivity {
|
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
|
/// 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`]
|
/// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
|
||||||
///
|
///
|
||||||
|
@ -1138,17 +1166,8 @@ pub(crate) type ActionListener =
|
||||||
/// Construct a new [`Div`] element
|
/// Construct a new [`Div`] element
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn div() -> Div {
|
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 {
|
Div {
|
||||||
interactivity,
|
interactivity: Interactivity::new(),
|
||||||
children: SmallVec::default(),
|
children: SmallVec::default(),
|
||||||
prepaint_listener: None,
|
prepaint_listener: None,
|
||||||
image_cache: None,
|
image_cache: None,
|
||||||
|
@ -1191,6 +1210,20 @@ pub struct DivFrameState {
|
||||||
child_layout_ids: SmallVec<[LayoutId; 2]>,
|
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 {
|
impl Styled for Div {
|
||||||
fn style(&mut self) -> &mut StyleRefinement {
|
fn style(&mut self) -> &mut StyleRefinement {
|
||||||
&mut self.interactivity.base_style
|
&mut self.interactivity.base_style
|
||||||
|
@ -1217,9 +1250,14 @@ impl Element for Div {
|
||||||
self.interactivity.element_id.clone()
|
self.interactivity.element_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||||
|
self.interactivity.source_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -1230,8 +1268,12 @@ impl Element for Div {
|
||||||
.map(|provider| provider.provide(window, cx));
|
.map(|provider| provider.provide(window, cx));
|
||||||
|
|
||||||
let layout_id = window.with_image_cache(image_cache, |window| {
|
let layout_id = window.with_image_cache(image_cache, |window| {
|
||||||
self.interactivity
|
self.interactivity.request_layout(
|
||||||
.request_layout(global_id, window, cx, |style, window, cx| {
|
global_id,
|
||||||
|
inspector_id,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
|style, window, cx| {
|
||||||
window.with_text_style(style.text_style().cloned(), |window| {
|
window.with_text_style(style.text_style().cloned(), |window| {
|
||||||
child_layout_ids = self
|
child_layout_ids = self
|
||||||
.children
|
.children
|
||||||
|
@ -1240,7 +1282,8 @@ impl Element for Div {
|
||||||
.collect::<SmallVec<_>>();
|
.collect::<SmallVec<_>>();
|
||||||
window.request_layout(style, child_layout_ids.iter().copied(), cx)
|
window.request_layout(style, child_layout_ids.iter().copied(), cx)
|
||||||
})
|
})
|
||||||
})
|
},
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
(layout_id, DivFrameState { child_layout_ids })
|
(layout_id, DivFrameState { child_layout_ids })
|
||||||
|
@ -1249,6 +1292,7 @@ impl Element for Div {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -1294,6 +1338,7 @@ impl Element for Div {
|
||||||
|
|
||||||
self.interactivity.prepaint(
|
self.interactivity.prepaint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
content_size,
|
content_size,
|
||||||
window,
|
window,
|
||||||
|
@ -1317,6 +1362,7 @@ impl Element for Div {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
|
@ -1331,6 +1377,7 @@ impl Element for Div {
|
||||||
window.with_image_cache(image_cache, |window| {
|
window.with_image_cache(image_cache, |window| {
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
hitbox.as_ref(),
|
hitbox.as_ref(),
|
||||||
window,
|
window,
|
||||||
|
@ -1403,8 +1450,8 @@ pub struct Interactivity {
|
||||||
pub(crate) tooltip_builder: Option<TooltipBuilder>,
|
pub(crate) tooltip_builder: Option<TooltipBuilder>,
|
||||||
pub(crate) occlude_mouse: bool,
|
pub(crate) occlude_mouse: bool,
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
pub(crate) location: Option<core::panic::Location<'static>>,
|
pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub(crate) debug_selector: Option<String>,
|
pub(crate) debug_selector: Option<String>,
|
||||||
|
@ -1415,10 +1462,28 @@ impl Interactivity {
|
||||||
pub fn request_layout(
|
pub fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
|
f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
|
||||||
) -> 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, _>(
|
window.with_optional_element_state::<InteractiveElementState, _>(
|
||||||
global_id,
|
global_id,
|
||||||
|element_state, window| {
|
|element_state, window| {
|
||||||
|
@ -1478,6 +1543,7 @@ impl Interactivity {
|
||||||
pub fn prepaint<R>(
|
pub fn prepaint<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
content_size: Size<Pixels>,
|
content_size: Size<Pixels>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -1485,6 +1551,19 @@ impl Interactivity {
|
||||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
|
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
self.content_size = content_size;
|
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() {
|
if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
|
||||||
window.set_focus_handle(focus_handle, cx);
|
window.set_focus_handle(focus_handle, cx);
|
||||||
}
|
}
|
||||||
|
@ -1514,7 +1593,7 @@ impl Interactivity {
|
||||||
window.with_content_mask(
|
window.with_content_mask(
|
||||||
style.overflow_mask(bounds, window.rem_size()),
|
style.overflow_mask(bounds, window.rem_size()),
|
||||||
|window| {
|
|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))
|
Some(window.insert_hitbox(bounds, self.occlude_mouse))
|
||||||
} else {
|
} else {
|
||||||
None
|
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
|
self.occlude_mouse
|
||||||
|| style.mouse_cursor.is_some()
|
|| style.mouse_cursor.is_some()
|
||||||
|| self.group.is_some()
|
|| self.group.is_some()
|
||||||
|
@ -1548,6 +1627,7 @@ impl Interactivity {
|
||||||
|| self.drag_listener.is_some()
|
|| self.drag_listener.is_some()
|
||||||
|| !self.drop_listeners.is_empty()
|
|| !self.drop_listeners.is_empty()
|
||||||
|| self.tooltip_builder.is_some()
|
|| self.tooltip_builder.is_some()
|
||||||
|
|| window.is_inspector_picking(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_scroll_position(
|
fn clamp_scroll_position(
|
||||||
|
@ -1605,6 +1685,7 @@ impl Interactivity {
|
||||||
pub fn paint(
|
pub fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
hitbox: Option<&Hitbox>,
|
hitbox: Option<&Hitbox>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -1672,7 +1753,14 @@ impl Interactivity {
|
||||||
self.paint_keyboard_listeners(window, cx);
|
self.paint_keyboard_listeners(window, cx);
|
||||||
f(&style, 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() {
|
if let Some(group) = self.group.as_ref() {
|
||||||
GroupHitboxes::pop(group, cx);
|
GroupHitboxes::pop(group, cx);
|
||||||
}
|
}
|
||||||
|
@ -1727,7 +1815,7 @@ impl Interactivity {
|
||||||
origin: hitbox.origin,
|
origin: hitbox.origin,
|
||||||
size: text.size(FONT_SIZE),
|
size: text.size(FONT_SIZE),
|
||||||
};
|
};
|
||||||
if self.location.is_some()
|
if self.source_location.is_some()
|
||||||
&& text_bounds.contains(&window.mouse_position())
|
&& text_bounds.contains(&window.mouse_position())
|
||||||
&& window.modifiers().secondary()
|
&& window.modifiers().secondary()
|
||||||
{
|
{
|
||||||
|
@ -1758,7 +1846,7 @@ impl Interactivity {
|
||||||
|
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
let location = self.location.unwrap();
|
let location = self.source_location.unwrap();
|
||||||
move |e: &crate::MouseDownEvent, phase, window, cx| {
|
move |e: &crate::MouseDownEvent, phase, window, cx| {
|
||||||
if text_bounds.contains(&e.position)
|
if text_bounds.contains(&e.position)
|
||||||
&& phase.capture()
|
&& phase.capture()
|
||||||
|
@ -2721,37 +2809,52 @@ where
|
||||||
self.element.id()
|
self.element.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
self.element.source_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.element.request_layout(id, window, cx)
|
self.element.request_layout(id, inspector_id, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::RequestLayoutState,
|
state: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> E::PrepaintState {
|
) -> E::PrepaintState {
|
||||||
self.element.prepaint(id, bounds, state, window, cx)
|
self.element
|
||||||
|
.prepaint(id, inspector_id, bounds, state, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
self.element
|
self.element.paint(
|
||||||
.paint(id, bounds, request_layout, prepaint, window, cx)
|
id,
|
||||||
|
inspector_id,
|
||||||
|
bounds,
|
||||||
|
request_layout,
|
||||||
|
prepaint,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2818,37 +2921,52 @@ where
|
||||||
self.element.id()
|
self.element.id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
self.element.source_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.element.request_layout(id, window, cx)
|
self.element.request_layout(id, inspector_id, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::RequestLayoutState,
|
state: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> E::PrepaintState {
|
) -> E::PrepaintState {
|
||||||
self.element.prepaint(id, bounds, state, window, cx)
|
self.element
|
||||||
|
.prepaint(id, inspector_id, bounds, state, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
self.element
|
self.element.paint(
|
||||||
.paint(id, bounds, request_layout, prepaint, window, cx);
|
id,
|
||||||
|
inspector_id,
|
||||||
|
bounds,
|
||||||
|
request_layout,
|
||||||
|
prepaint,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity,
|
AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity,
|
||||||
GlobalElementId, ImageAssetLoader, ImageCacheError, IntoElement, LayoutId, ParentElement,
|
GlobalElementId, ImageAssetLoader, ImageCacheError, InspectorElementId, IntoElement, LayoutId,
|
||||||
Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Task, Window, hash,
|
ParentElement, Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Task, Window,
|
||||||
|
hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::{FutureExt, future::Shared};
|
use futures::{FutureExt, future::Shared};
|
||||||
|
@ -102,9 +103,14 @@ impl Element for ImageCacheElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -125,6 +131,7 @@ impl Element for ImageCacheElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -138,6 +145,7 @@ impl Element for ImageCacheElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AbsoluteLength, AnyElement, AnyImageCache, App, Asset, AssetLogger, Bounds, DefiniteLength,
|
AbsoluteLength, AnyElement, AnyImageCache, App, Asset, AssetLogger, Bounds, DefiniteLength,
|
||||||
Element, ElementId, Entity, GlobalElementId, Hitbox, Image, ImageCache, InteractiveElement,
|
Element, ElementId, Entity, GlobalElementId, Hitbox, Image, ImageCache, InspectorElementId,
|
||||||
Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource,
|
InteractiveElement, Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels,
|
||||||
SMOOTH_SVG_SCALE_FACTOR, SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task,
|
RenderImage, Resource, SMOOTH_SVG_SCALE_FACTOR, SharedString, SharedUri, StyleRefinement,
|
||||||
Window, px, swap_rgba_pa_to_bgra,
|
Styled, SvgSize, Task, Window, px, swap_rgba_pa_to_bgra,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
|
|
||||||
|
@ -194,9 +194,10 @@ pub struct Img {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new image element.
|
/// Create a new image element.
|
||||||
|
#[track_caller]
|
||||||
pub fn img(source: impl Into<ImageSource>) -> Img {
|
pub fn img(source: impl Into<ImageSource>) -> Img {
|
||||||
Img {
|
Img {
|
||||||
interactivity: Interactivity::default(),
|
interactivity: Interactivity::new(),
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
style: ImageStyle::default(),
|
style: ImageStyle::default(),
|
||||||
image_cache: None,
|
image_cache: None,
|
||||||
|
@ -266,9 +267,14 @@ impl Element for Img {
|
||||||
self.interactivity.element_id.clone()
|
self.interactivity.element_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
self.interactivity.source_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -290,6 +296,7 @@ impl Element for Img {
|
||||||
|
|
||||||
let layout_id = self.interactivity.request_layout(
|
let layout_id = self.interactivity.request_layout(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|mut style, window, cx| {
|
|mut style, window, cx| {
|
||||||
|
@ -408,6 +415,7 @@ impl Element for Img {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -415,6 +423,7 @@ impl Element for Img {
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
self.interactivity.prepaint(
|
self.interactivity.prepaint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
bounds.size,
|
bounds.size,
|
||||||
window,
|
window,
|
||||||
|
@ -432,6 +441,7 @@ impl Element for Img {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
layout_state: &mut Self::RequestLayoutState,
|
layout_state: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Self::PrepaintState,
|
hitbox: &mut Self::PrepaintState,
|
||||||
|
@ -441,6 +451,7 @@ impl Element for Img {
|
||||||
let source = self.source.clone();
|
let source = self.source.clone();
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
hitbox.as_ref(),
|
hitbox.as_ref(),
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -9,14 +9,13 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
|
AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
|
||||||
FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size,
|
FocusHandle, GlobalElementId, Hitbox, InspectorElementId, IntoElement, Overflow, Pixels, Point,
|
||||||
Style, StyleRefinement, Styled, Window, point, px, size,
|
ScrollWheelEvent, Size, Style, StyleRefinement, Styled, Window, point, px, size,
|
||||||
};
|
};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use refineable::Refineable as _;
|
use refineable::Refineable as _;
|
||||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree};
|
||||||
use taffy::style::Overflow;
|
|
||||||
|
|
||||||
/// Construct a new list element
|
/// Construct a new list element
|
||||||
pub fn list(state: ListState) -> List {
|
pub fn list(state: ListState) -> List {
|
||||||
|
@ -820,9 +819,14 @@ impl Element for List {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
) -> (crate::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -890,6 +894,7 @@ impl Element for List {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -938,6 +943,7 @@ impl Element for List {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<crate::Pixels>,
|
bounds: Bounds<crate::Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
App, Bounds, Element, ElementId, GlobalElementId, IntoElement, LayoutId, ObjectFit, Pixels,
|
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
|
||||||
Style, StyleRefinement, Styled, Window,
|
ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use core_video::pixel_buffer::CVPixelBuffer;
|
use core_video::pixel_buffer::CVPixelBuffer;
|
||||||
|
@ -53,9 +53,14 @@ impl Element for Surface {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_global_id: Option<&GlobalElementId>,
|
_global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -68,6 +73,7 @@ impl Element for Surface {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_global_id: Option<&GlobalElementId>,
|
_global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
|
@ -78,6 +84,7 @@ impl Element for Surface {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_global_id: Option<&GlobalElementId>,
|
_global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds<Pixels>,
|
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
App, Bounds, Element, GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement,
|
App, Bounds, Element, GlobalElementId, Hitbox, InspectorElementId, InteractiveElement,
|
||||||
LayoutId, Pixels, Point, Radians, SharedString, Size, StyleRefinement, Styled,
|
Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,
|
||||||
TransformationMatrix, Window, geometry::Negate as _, point, px, radians, size,
|
StyleRefinement, Styled, TransformationMatrix, Window, geometry::Negate as _, point, px,
|
||||||
|
radians, size,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -13,9 +14,10 @@ pub struct Svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new SVG element.
|
/// Create a new SVG element.
|
||||||
|
#[track_caller]
|
||||||
pub fn svg() -> Svg {
|
pub fn svg() -> Svg {
|
||||||
Svg {
|
Svg {
|
||||||
interactivity: Interactivity::default(),
|
interactivity: Interactivity::new(),
|
||||||
transformation: None,
|
transformation: None,
|
||||||
path: None,
|
path: None,
|
||||||
}
|
}
|
||||||
|
@ -44,23 +46,31 @@ impl Element for Svg {
|
||||||
self.interactivity.element_id.clone()
|
self.interactivity.element_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||||
|
self.interactivity.source_location()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
let layout_id =
|
let layout_id = self.interactivity.request_layout(
|
||||||
self.interactivity
|
global_id,
|
||||||
.request_layout(global_id, window, cx, |style, window, cx| {
|
inspector_id,
|
||||||
window.request_layout(style, None, cx)
|
window,
|
||||||
});
|
cx,
|
||||||
|
|style, window, cx| window.request_layout(style, None, cx),
|
||||||
|
);
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -68,6 +78,7 @@ impl Element for Svg {
|
||||||
) -> Option<Hitbox> {
|
) -> Option<Hitbox> {
|
||||||
self.interactivity.prepaint(
|
self.interactivity.prepaint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
bounds.size,
|
bounds.size,
|
||||||
window,
|
window,
|
||||||
|
@ -79,6 +90,7 @@ impl Element for Svg {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
|
@ -89,6 +101,7 @@ impl Element for Svg {
|
||||||
{
|
{
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
hitbox.as_ref(),
|
hitbox.as_ref(),
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
|
ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
|
||||||
HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
HighlightStyle, Hitbox, InspectorElementId, IntoElement, LayoutId, MouseDownEvent,
|
||||||
Pixels, Point, SharedString, Size, TextOverflow, TextRun, TextStyle, TooltipId, WhiteSpace,
|
MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow, TextRun,
|
||||||
Window, WrappedLine, WrappedLineLayout, register_tooltip_mouse_handlers, set_tooltip_on_window,
|
TextStyle, TooltipId, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
|
||||||
|
register_tooltip_mouse_handlers, set_tooltip_on_window,
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -23,9 +24,14 @@ impl Element for &'static str {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -37,6 +43,7 @@ impl Element for &'static str {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
text_layout: &mut Self::RequestLayoutState,
|
text_layout: &mut Self::RequestLayoutState,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
|
@ -48,6 +55,7 @@ impl Element for &'static str {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
text_layout: &mut TextLayout,
|
text_layout: &mut TextLayout,
|
||||||
_: &mut (),
|
_: &mut (),
|
||||||
|
@ -82,11 +90,14 @@ impl Element for SharedString {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -98,6 +109,7 @@ impl Element for SharedString {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
text_layout: &mut Self::RequestLayoutState,
|
text_layout: &mut Self::RequestLayoutState,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
|
@ -109,6 +121,7 @@ impl Element for SharedString {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
text_layout: &mut Self::RequestLayoutState,
|
text_layout: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
@ -225,9 +238,14 @@ impl Element for StyledText {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -244,6 +262,7 @@ impl Element for StyledText {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
|
@ -255,6 +274,7 @@ impl Element for StyledText {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
@ -319,8 +339,8 @@ impl TextLayout {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let (truncate_width, ellipsis) =
|
let (truncate_width, truncation_suffix) =
|
||||||
if let Some(text_overflow) = text_style.text_overflow {
|
if let Some(text_overflow) = text_style.text_overflow.clone() {
|
||||||
let width = known_dimensions.width.or(match available_space.width {
|
let width = known_dimensions.width.or(match available_space.width {
|
||||||
crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
|
crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
|
||||||
Some(max_lines) => Some(x * max_lines),
|
Some(max_lines) => Some(x * max_lines),
|
||||||
|
@ -330,10 +350,10 @@ impl TextLayout {
|
||||||
});
|
});
|
||||||
|
|
||||||
match text_overflow {
|
match text_overflow {
|
||||||
TextOverflow::Ellipsis(s) => (width, Some(s)),
|
TextOverflow::Truncate(s) => (width, s),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
(None, "".into())
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(text_layout) = element_state.0.borrow().as_ref() {
|
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 mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size);
|
||||||
let text = if let Some(truncate_width) = truncate_width {
|
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 {
|
} else {
|
||||||
text.clone()
|
text.clone()
|
||||||
};
|
};
|
||||||
|
@ -673,18 +698,24 @@ impl Element for InteractiveText {
|
||||||
Some(self.element_id.clone())
|
Some(self.element_id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
self.text.request_layout(None, window, cx)
|
self.text.request_layout(None, inspector_id, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
state: &mut Self::RequestLayoutState,
|
state: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
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);
|
let hitbox = window.insert_hitbox(bounds, false);
|
||||||
(hitbox, interactive_state)
|
(hitbox, interactive_state)
|
||||||
},
|
},
|
||||||
|
@ -716,6 +748,7 @@ impl Element for InteractiveText {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Hitbox,
|
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)
|
((), interactive_state)
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,13 +6,12 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, App, AvailableSpace, Bounds, ContentMask, Context, Element, ElementId, Entity,
|
AnyElement, App, AvailableSpace, Bounds, ContentMask, Context, Element, ElementId, Entity,
|
||||||
GlobalElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, IsZero, LayoutId,
|
GlobalElementId, Hitbox, InspectorElementId, InteractiveElement, Interactivity, IntoElement,
|
||||||
ListSizingBehavior, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, Window, point,
|
IsZero, LayoutId, ListSizingBehavior, Overflow, Pixels, Render, ScrollHandle, Size,
|
||||||
size,
|
StyleRefinement, Styled, Window, point, size,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||||
use taffy::style::Overflow;
|
|
||||||
|
|
||||||
use super::ListHorizontalSizingBehavior;
|
use super::ListHorizontalSizingBehavior;
|
||||||
|
|
||||||
|
@ -52,11 +51,7 @@ where
|
||||||
interactivity: Interactivity {
|
interactivity: Interactivity {
|
||||||
element_id: Some(id),
|
element_id: Some(id),
|
||||||
base_style: Box::new(base_style),
|
base_style: Box::new(base_style),
|
||||||
|
..Interactivity::new()
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
location: Some(*core::panic::Location::caller()),
|
|
||||||
|
|
||||||
..Default::default()
|
|
||||||
},
|
},
|
||||||
scroll_handle: None,
|
scroll_handle: None,
|
||||||
sizing_behavior: ListSizingBehavior::default(),
|
sizing_behavior: ListSizingBehavior::default(),
|
||||||
|
@ -166,9 +161,14 @@ impl Element for UniformList {
|
||||||
self.interactivity.element_id.clone()
|
self.interactivity.element_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -176,6 +176,7 @@ impl Element for UniformList {
|
||||||
let item_size = self.measure_item(None, window, cx);
|
let item_size = self.measure_item(None, window, cx);
|
||||||
let layout_id = self.interactivity.request_layout(
|
let layout_id = self.interactivity.request_layout(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|style, window, cx| match self.sizing_behavior {
|
|style, window, cx| match self.sizing_behavior {
|
||||||
|
@ -223,6 +224,7 @@ impl Element for UniformList {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
frame_state: &mut Self::RequestLayoutState,
|
frame_state: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -271,6 +273,7 @@ impl Element for UniformList {
|
||||||
|
|
||||||
self.interactivity.prepaint(
|
self.interactivity.prepaint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
content_size,
|
content_size,
|
||||||
window,
|
window,
|
||||||
|
@ -435,6 +438,7 @@ impl Element for UniformList {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<crate::Pixels>,
|
bounds: Bounds<crate::Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Option<Hitbox>,
|
hitbox: &mut Option<Hitbox>,
|
||||||
|
@ -443,6 +447,7 @@ impl Element for UniformList {
|
||||||
) {
|
) {
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
hitbox.as_ref(),
|
hitbox.as_ref(),
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
//! can be used to describe common units, concepts, and the relationships
|
//! can be used to describe common units, concepts, and the relationships
|
||||||
//! between them.
|
//! between them.
|
||||||
|
|
||||||
|
use anyhow::{Context as _, anyhow};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
|
use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use schemars::{JsonSchema, SchemaGenerator, schema::Schema};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, PartialOrd},
|
cmp::{self, PartialOrd},
|
||||||
fmt,
|
fmt::{self, Display},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
ops::{Add, Div, Mul, MulAssign, Neg, Sub},
|
ops::{Add, Div, Mul, MulAssign, Neg, Sub},
|
||||||
};
|
};
|
||||||
|
@ -71,9 +73,10 @@ pub trait Along {
|
||||||
Eq,
|
Eq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
|
JsonSchema,
|
||||||
Hash,
|
Hash,
|
||||||
)]
|
)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Point<T: Default + Clone + Debug> {
|
pub struct Point<T: Default + Clone + Debug> {
|
||||||
/// The x coordinate of the point.
|
/// The x coordinate of the point.
|
||||||
|
@ -375,12 +378,18 @@ impl<T: Clone + Default + Debug> Clone for Point<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Clone + Debug + Display> Display for Point<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A structure representing a two-dimensional size with width and height in a given unit.
|
/// A structure representing a two-dimensional size with width and height in a given unit.
|
||||||
///
|
///
|
||||||
/// This struct is generic over the type `T`, which can be any type that implements `Clone`, `Default`, and `Debug`.
|
/// This struct is generic over the type `T`, which can be any type that implements `Clone`, `Default`, and `Debug`.
|
||||||
/// It is commonly used to specify dimensions for elements in a UI, such as a window or element.
|
/// It is commonly used to specify dimensions for elements in a UI, such as a window or element.
|
||||||
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
|
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Size<T: Clone + Default + Debug> {
|
pub struct Size<T: Clone + Default + Debug> {
|
||||||
/// The width component of the size.
|
/// The width component of the size.
|
||||||
|
@ -649,6 +658,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Clone + Debug + Display> Display for Size<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} × {}", self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Clone + Default + Debug> From<Point<T>> for Size<T> {
|
impl<T: Clone + Default + Debug> From<Point<T>> for Size<T> {
|
||||||
fn from(point: Point<T>) -> Self {
|
fn from(point: Point<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -1541,6 +1556,18 @@ impl<T: PartialOrd + Default + Debug + Clone> Bounds<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Clone + Debug + Display + Add<T, Output = T>> Display for Bounds<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} - {} (size {})",
|
||||||
|
self.origin,
|
||||||
|
self.bottom_right(),
|
||||||
|
self.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Size<DevicePixels> {
|
impl Size<DevicePixels> {
|
||||||
/// Converts the size from physical to logical pixels.
|
/// Converts the size from physical to logical pixels.
|
||||||
pub(crate) fn to_pixels(self, scale_factor: f32) -> Size<Pixels> {
|
pub(crate) fn to_pixels(self, scale_factor: f32) -> Size<Pixels> {
|
||||||
|
@ -1647,7 +1674,7 @@ impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
|
||||||
/// assert_eq!(edges.left, 40.0);
|
/// assert_eq!(edges.left, 40.0);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Edges<T: Clone + Default + Debug> {
|
pub struct Edges<T: Clone + Default + Debug> {
|
||||||
/// The size of the top edge.
|
/// The size of the top edge.
|
||||||
|
@ -2124,7 +2151,7 @@ impl Corner {
|
||||||
///
|
///
|
||||||
/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
|
/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
|
||||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Corners<T: Clone + Default + Debug> {
|
pub struct Corners<T: Clone + Default + Debug> {
|
||||||
/// The value associated with the top left corner.
|
/// The value associated with the top left corner.
|
||||||
|
@ -2508,16 +2535,11 @@ impl From<Percentage> for Radians {
|
||||||
PartialEq,
|
PartialEq,
|
||||||
Serialize,
|
Serialize,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
|
JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Pixels(pub f32);
|
pub struct Pixels(pub f32);
|
||||||
|
|
||||||
impl std::fmt::Display for Pixels {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_fmt(format_args!("{}px", self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div for Pixels {
|
impl Div for Pixels {
|
||||||
type Output = f32;
|
type Output = f32;
|
||||||
|
|
||||||
|
@ -2584,6 +2606,30 @@ impl MulAssign<f32> for Pixels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Pixels {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}px", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Pixels {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&'_ str> for Pixels {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||||
|
value
|
||||||
|
.strip_suffix("px")
|
||||||
|
.context("expected 'px' suffix")
|
||||||
|
.and_then(|number| Ok(number.parse()?))
|
||||||
|
.map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pixels {
|
impl Pixels {
|
||||||
/// Represents zero pixels.
|
/// Represents zero pixels.
|
||||||
pub const ZERO: Pixels = Pixels(0.0);
|
pub const ZERO: Pixels = Pixels(0.0);
|
||||||
|
@ -2706,12 +2752,6 @@ impl From<f32> for Pixels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Pixels {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{} px", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Pixels> for f32 {
|
impl From<Pixels> for f32 {
|
||||||
fn from(pixels: Pixels) -> Self {
|
fn from(pixels: Pixels) -> Self {
|
||||||
pixels.0
|
pixels.0
|
||||||
|
@ -2910,7 +2950,7 @@ impl Ord for ScaledPixels {
|
||||||
|
|
||||||
impl Debug for ScaledPixels {
|
impl Debug for ScaledPixels {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{} px (scaled)", self.0)
|
write!(f, "{}px (scaled)", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3032,9 +3072,27 @@ impl Mul<Pixels> for Rems {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Rems {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}rem", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Rems {
|
impl Debug for Rems {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{} rem", self.0)
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&'_ str> for Rems {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||||
|
value
|
||||||
|
.strip_suffix("rem")
|
||||||
|
.context("expected 'rem' suffix")
|
||||||
|
.and_then(|number| Ok(number.parse()?))
|
||||||
|
.map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3044,7 +3102,7 @@ impl Debug for Rems {
|
||||||
/// affected by the current font size, or a number of rems, which is relative to the font size of
|
/// affected by the current font size, or a number of rems, which is relative to the font size of
|
||||||
/// the root element. It is used for specifying dimensions that are either independent of or
|
/// the root element. It is used for specifying dimensions that are either independent of or
|
||||||
/// related to the typographic scale.
|
/// related to the typographic scale.
|
||||||
#[derive(Clone, Copy, Debug, Neg, PartialEq)]
|
#[derive(Clone, Copy, Neg, PartialEq)]
|
||||||
pub enum AbsoluteLength {
|
pub enum AbsoluteLength {
|
||||||
/// A length in pixels.
|
/// A length in pixels.
|
||||||
Pixels(Pixels),
|
Pixels(Pixels),
|
||||||
|
@ -3126,6 +3184,87 @@ impl Default for AbsoluteLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for AbsoluteLength {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Pixels(pixels) => write!(f, "{pixels}"),
|
||||||
|
Self::Rems(rems) => write!(f, "{rems}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AbsoluteLength {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXPECTED_ABSOLUTE_LENGTH: &str = "number with 'px' or 'rem' suffix";
|
||||||
|
|
||||||
|
impl TryFrom<&'_ str> for AbsoluteLength {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||||
|
if let Ok(pixels) = value.try_into() {
|
||||||
|
Ok(Self::Pixels(pixels))
|
||||||
|
} else if let Ok(rems) = value.try_into() {
|
||||||
|
Ok(Self::Rems(rems))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"invalid AbsoluteLength '{value}', expected {EXPECTED_ABSOLUTE_LENGTH}"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for AbsoluteLength {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
"AbsoluteLength".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
|
||||||
|
use schemars::schema::{InstanceType, SchemaObject, StringValidation};
|
||||||
|
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
string: Some(Box::new(StringValidation {
|
||||||
|
pattern: Some(r"^-?\d+(\.\d+)?(px|rem)$".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for AbsoluteLength {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
struct StringVisitor;
|
||||||
|
|
||||||
|
impl de::Visitor<'_> for StringVisitor {
|
||||||
|
type Value = AbsoluteLength;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{EXPECTED_ABSOLUTE_LENGTH}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||||
|
AbsoluteLength::try_from(value).map_err(E::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_str(StringVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for AbsoluteLength {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{self}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
|
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
|
||||||
///
|
///
|
||||||
/// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
|
/// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
|
||||||
|
@ -3180,14 +3319,89 @@ impl DefiniteLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for DefiniteLength {
|
impl Debug for DefiniteLength {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DefiniteLength {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
DefiniteLength::Absolute(length) => Debug::fmt(length, f),
|
DefiniteLength::Absolute(length) => write!(f, "{length}"),
|
||||||
DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
|
DefiniteLength::Fraction(fraction) => write!(f, "{}%", (fraction * 100.0) as i32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EXPECTED_DEFINITE_LENGTH: &str = "expected number with 'px', 'rem', or '%' suffix";
|
||||||
|
|
||||||
|
impl TryFrom<&'_ str> for DefiniteLength {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||||
|
if let Some(percentage) = value.strip_suffix('%') {
|
||||||
|
let fraction: f32 = percentage.parse::<f32>().with_context(|| {
|
||||||
|
format!("invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}")
|
||||||
|
})?;
|
||||||
|
Ok(DefiniteLength::Fraction(fraction / 100.0))
|
||||||
|
} else if let Ok(absolute_length) = value.try_into() {
|
||||||
|
Ok(DefiniteLength::Absolute(absolute_length))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for DefiniteLength {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
"DefiniteLength".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
|
||||||
|
use schemars::schema::{InstanceType, SchemaObject, StringValidation};
|
||||||
|
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
string: Some(Box::new(StringValidation {
|
||||||
|
pattern: Some(r"^-?\d+(\.\d+)?(px|rem|%)$".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for DefiniteLength {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
struct StringVisitor;
|
||||||
|
|
||||||
|
impl de::Visitor<'_> for StringVisitor {
|
||||||
|
type Value = DefiniteLength;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{EXPECTED_DEFINITE_LENGTH}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||||
|
DefiniteLength::try_from(value).map_err(E::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_str(StringVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DefiniteLength {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{self}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Pixels> for DefiniteLength {
|
impl From<Pixels> for DefiniteLength {
|
||||||
fn from(pixels: Pixels) -> Self {
|
fn from(pixels: Pixels) -> Self {
|
||||||
Self::Absolute(pixels.into())
|
Self::Absolute(pixels.into())
|
||||||
|
@ -3222,14 +3436,86 @@ pub enum Length {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Length {
|
impl Debug for Length {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Length {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
|
Length::Definite(definite_length) => write!(f, "{}", definite_length),
|
||||||
Length::Auto => write!(f, "auto"),
|
Length::Auto => write!(f, "auto"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EXPECTED_LENGTH: &str = "expected 'auto' or number with 'px', 'rem', or '%' suffix";
|
||||||
|
|
||||||
|
impl TryFrom<&'_ str> for Length {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||||
|
if value == "auto" {
|
||||||
|
Ok(Length::Auto)
|
||||||
|
} else if let Ok(definite_length) = value.try_into() {
|
||||||
|
Ok(Length::Definite(definite_length))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"invalid Length '{value}', expected {EXPECTED_LENGTH}"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for Length {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
"Length".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
|
||||||
|
use schemars::schema::{InstanceType, SchemaObject, StringValidation};
|
||||||
|
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
string: Some(Box::new(StringValidation {
|
||||||
|
pattern: Some(r"^(auto|-?\d+(\.\d+)?(px|rem|%))$".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Length {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
struct StringVisitor;
|
||||||
|
|
||||||
|
impl de::Visitor<'_> for StringVisitor {
|
||||||
|
type Value = Length;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{EXPECTED_LENGTH}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||||
|
Length::try_from(value).map_err(E::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_str(StringVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Length {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{self}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a `DefiniteLength` representing a relative fraction of a parent size.
|
/// Constructs a `DefiniteLength` representing a relative fraction of a parent size.
|
||||||
///
|
///
|
||||||
/// This function creates a `DefiniteLength` that is a specified fraction of a parent's dimension.
|
/// This function creates a `DefiniteLength` that is a specified fraction of a parent's dimension.
|
||||||
|
|
|
@ -81,6 +81,7 @@ mod executor;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
mod global;
|
mod global;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod inspector;
|
||||||
mod interactive;
|
mod interactive;
|
||||||
mod key_dispatch;
|
mod key_dispatch;
|
||||||
mod keymap;
|
mod keymap;
|
||||||
|
@ -135,6 +136,7 @@ pub use global::*;
|
||||||
pub use gpui_macros::{AppContext, IntoElement, Render, VisualContext, register_action, test};
|
pub use gpui_macros::{AppContext, IntoElement, Render, VisualContext, register_action, test};
|
||||||
pub use http_client;
|
pub use http_client;
|
||||||
pub use input::*;
|
pub use input::*;
|
||||||
|
pub use inspector::*;
|
||||||
pub use interactive::*;
|
pub use interactive::*;
|
||||||
use key_dispatch::*;
|
use key_dispatch::*;
|
||||||
pub use keymap::*;
|
pub use keymap::*;
|
||||||
|
|
223
crates/gpui/src/inspector.rs
Normal file
223
crates/gpui/src/inspector.rs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
/// A unique identifier for an element that can be inspected.
|
||||||
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||||
|
pub struct InspectorElementId {
|
||||||
|
/// Stable part of the ID.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub path: std::rc::Rc<InspectorElementPath>,
|
||||||
|
/// Disambiguates elements that have the same path.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub instance_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<InspectorElementId> for &InspectorElementId {
|
||||||
|
fn into(self) -> InspectorElementId {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub use conditional::*;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
mod conditional {
|
||||||
|
use super::*;
|
||||||
|
use crate::{AnyElement, App, Context, Empty, IntoElement, Render, Window};
|
||||||
|
use collections::FxHashMap;
|
||||||
|
use std::any::{Any, TypeId};
|
||||||
|
|
||||||
|
/// `GlobalElementId` qualified by source location of element construction.
|
||||||
|
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub struct InspectorElementPath {
|
||||||
|
/// The path to the nearest ancestor element that has an `ElementId`.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub global_id: crate::GlobalElementId,
|
||||||
|
/// Source location where this element was constructed.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub source_location: &'static std::panic::Location<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for InspectorElementPath {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
global_id: crate::GlobalElementId(self.global_id.0.clone()),
|
||||||
|
source_location: self.source_location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<InspectorElementPath> for &InspectorElementPath {
|
||||||
|
fn into(self) -> InspectorElementPath {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function set on `App` to render the inspector UI.
|
||||||
|
pub type InspectorRenderer =
|
||||||
|
Box<dyn Fn(&mut Inspector, &mut Window, &mut Context<Inspector>) -> AnyElement>;
|
||||||
|
|
||||||
|
/// Manages inspector state - which element is currently selected and whether the inspector is
|
||||||
|
/// in picking mode.
|
||||||
|
pub struct Inspector {
|
||||||
|
active_element: Option<InspectedElement>,
|
||||||
|
pub(crate) pick_depth: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InspectedElement {
|
||||||
|
id: InspectorElementId,
|
||||||
|
states: FxHashMap<TypeId, Box<dyn Any>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InspectedElement {
|
||||||
|
fn new(id: InspectorElementId) -> Self {
|
||||||
|
InspectedElement {
|
||||||
|
id,
|
||||||
|
states: FxHashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inspector {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
active_element: None,
|
||||||
|
pick_depth: Some(0.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn select(&mut self, id: InspectorElementId, window: &mut Window) {
|
||||||
|
self.set_active_element_id(id, window);
|
||||||
|
self.pick_depth = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hover(&mut self, id: InspectorElementId, window: &mut Window) {
|
||||||
|
if self.is_picking() {
|
||||||
|
let changed = self.set_active_element_id(id, window);
|
||||||
|
if changed {
|
||||||
|
self.pick_depth = Some(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_active_element_id(
|
||||||
|
&mut self,
|
||||||
|
id: InspectorElementId,
|
||||||
|
window: &mut Window,
|
||||||
|
) -> bool {
|
||||||
|
let changed = Some(&id) != self.active_element_id();
|
||||||
|
if changed {
|
||||||
|
self.active_element = Some(InspectedElement::new(id));
|
||||||
|
window.refresh();
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ID of the currently hovered or selected element.
|
||||||
|
pub fn active_element_id(&self) -> Option<&InspectorElementId> {
|
||||||
|
self.active_element.as_ref().map(|e| &e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_active_element_state<T: 'static, R>(
|
||||||
|
&mut self,
|
||||||
|
window: &mut Window,
|
||||||
|
f: impl FnOnce(&mut Option<T>, &mut Window) -> R,
|
||||||
|
) -> R {
|
||||||
|
let Some(active_element) = &mut self.active_element else {
|
||||||
|
return f(&mut None, window);
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_id = TypeId::of::<T>();
|
||||||
|
let mut inspector_state = active_element
|
||||||
|
.states
|
||||||
|
.remove(&type_id)
|
||||||
|
.map(|state| *state.downcast().unwrap());
|
||||||
|
|
||||||
|
let result = f(&mut inspector_state, window);
|
||||||
|
|
||||||
|
if let Some(inspector_state) = inspector_state {
|
||||||
|
active_element
|
||||||
|
.states
|
||||||
|
.insert(type_id, Box::new(inspector_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts element picking mode, allowing the user to select elements by clicking.
|
||||||
|
pub fn start_picking(&mut self) {
|
||||||
|
self.pick_depth = Some(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the inspector is currently in picking mode.
|
||||||
|
pub fn is_picking(&self) -> bool {
|
||||||
|
self.pick_depth.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders elements for all registered inspector states of the active inspector element.
|
||||||
|
pub fn render_inspector_states(
|
||||||
|
&mut self,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Vec<AnyElement> {
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
if let Some(active_element) = self.active_element.take() {
|
||||||
|
for (type_id, state) in &active_element.states {
|
||||||
|
if let Some(render_inspector) = cx
|
||||||
|
.inspector_element_registry
|
||||||
|
.renderers_by_type_id
|
||||||
|
.remove(&type_id)
|
||||||
|
{
|
||||||
|
let mut element = (render_inspector)(
|
||||||
|
active_element.id.clone(),
|
||||||
|
state.as_ref(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
elements.push(element);
|
||||||
|
cx.inspector_element_registry
|
||||||
|
.renderers_by_type_id
|
||||||
|
.insert(*type_id, render_inspector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.active_element = Some(active_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Inspector {
|
||||||
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
if let Some(inspector_renderer) = cx.inspector_renderer.take() {
|
||||||
|
let result = inspector_renderer(self, window, cx);
|
||||||
|
cx.inspector_renderer = Some(inspector_renderer);
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
Empty.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct InspectorElementRegistry {
|
||||||
|
renderers_by_type_id: FxHashMap<
|
||||||
|
TypeId,
|
||||||
|
Box<dyn Fn(InspectorElementId, &dyn Any, &mut Window, &mut App) -> AnyElement>,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InspectorElementRegistry {
|
||||||
|
pub fn register<T: 'static, R: IntoElement>(
|
||||||
|
&mut self,
|
||||||
|
f: impl 'static + Fn(InspectorElementId, &T, &mut Window, &mut App) -> R,
|
||||||
|
) {
|
||||||
|
self.renderers_by_type_id.insert(
|
||||||
|
TypeId::of::<T>(),
|
||||||
|
Box::new(move |id, value, window, cx| {
|
||||||
|
let value = value.downcast_ref().unwrap();
|
||||||
|
f(id, value, window, cx).into_any_element()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ use image::codecs::gif::GifDecoder;
|
||||||
use image::{AnimationDecoder as _, Frame};
|
use image::{AnimationDecoder as _, Frame};
|
||||||
use parking::Unparker;
|
use parking::Unparker;
|
||||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use seahash::SeaHasher;
|
use seahash::SeaHasher;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -1244,7 +1245,7 @@ pub enum PromptLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The style of the cursor (pointer)
|
/// The style of the cursor (pointer)
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum CursorStyle {
|
pub enum CursorStyle {
|
||||||
/// The default cursor
|
/// The default cursor
|
||||||
Arrow,
|
Arrow,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// todo("windows"): remove
|
// todo("windows"): remove
|
||||||
#![cfg_attr(windows, allow(dead_code))]
|
#![cfg_attr(windows, allow(dead_code))]
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
|
AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
|
||||||
Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
|
Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
|
||||||
|
@ -506,7 +509,7 @@ impl From<Shadow> for Primitive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The style of a border.
|
/// The style of a border.
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum BorderStyle {
|
pub enum BorderStyle {
|
||||||
/// A solid border.
|
/// A solid border.
|
||||||
|
|
|
@ -13,11 +13,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use smallvec::SmallVec;
|
use schemars::JsonSchema;
|
||||||
pub use taffy::style::{
|
use serde::{Deserialize, Serialize};
|
||||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
|
||||||
Overflow, Position,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Use this struct for interfacing with the 'debug_below' styling from your own elements.
|
/// Use this struct for interfacing with the 'debug_below' styling from your own elements.
|
||||||
/// If a parent element has this style set on it, then this struct will be set as a global in
|
/// If a parent element has this style set on it, then this struct will be set as a global in
|
||||||
|
@ -143,7 +140,7 @@ impl ObjectFit {
|
||||||
|
|
||||||
/// The CSS styling that can be applied to an element via the `Styled` trait
|
/// The CSS styling that can be applied to an element via the `Styled` trait
|
||||||
#[derive(Clone, Refineable, Debug)]
|
#[derive(Clone, Refineable, Debug)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// What layout strategy should be used?
|
/// What layout strategy should be used?
|
||||||
pub display: Display,
|
pub display: Display,
|
||||||
|
@ -252,7 +249,7 @@ pub struct Style {
|
||||||
pub corner_radii: Corners<AbsoluteLength>,
|
pub corner_radii: Corners<AbsoluteLength>,
|
||||||
|
|
||||||
/// Box shadow of the element
|
/// Box shadow of the element
|
||||||
pub box_shadow: SmallVec<[BoxShadow; 2]>,
|
pub box_shadow: Vec<BoxShadow>,
|
||||||
|
|
||||||
/// The text style of this element
|
/// The text style of this element
|
||||||
pub text: TextStyleRefinement,
|
pub text: TextStyleRefinement,
|
||||||
|
@ -279,7 +276,7 @@ impl Styled for StyleRefinement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The value of the visibility property, similar to the CSS property `visibility`
|
/// The value of the visibility property, similar to the CSS property `visibility`
|
||||||
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum Visibility {
|
pub enum Visibility {
|
||||||
/// The element should be drawn as normal.
|
/// The element should be drawn as normal.
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -289,7 +286,7 @@ pub enum Visibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The possible values of the box-shadow property
|
/// The possible values of the box-shadow property
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct BoxShadow {
|
pub struct BoxShadow {
|
||||||
/// What color should the shadow have?
|
/// What color should the shadow have?
|
||||||
pub color: Hsla,
|
pub color: Hsla,
|
||||||
|
@ -302,7 +299,7 @@ pub struct BoxShadow {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to handle whitespace in text
|
/// How to handle whitespace in text
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum WhiteSpace {
|
pub enum WhiteSpace {
|
||||||
/// Normal line wrapping when text overflows the width of the element
|
/// Normal line wrapping when text overflows the width of the element
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -312,14 +309,15 @@ pub enum WhiteSpace {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to truncate text that overflows the width of the element
|
/// How to truncate text that overflows the width of the element
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum TextOverflow {
|
pub enum TextOverflow {
|
||||||
/// Truncate the text with an ellipsis, same as: `text-overflow: ellipsis;` in CSS
|
/// Truncate the text when it doesn't fit, and represent this truncation by displaying the
|
||||||
Ellipsis(&'static str),
|
/// provided string.
|
||||||
|
Truncate(SharedString),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to align text within the element
|
/// How to align text within the element
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum TextAlign {
|
pub enum TextAlign {
|
||||||
/// Align the text to the left of the element
|
/// Align the text to the left of the element
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -334,7 +332,7 @@ pub enum TextAlign {
|
||||||
|
|
||||||
/// The properties that can be used to style text in GPUI
|
/// The properties that can be used to style text in GPUI
|
||||||
#[derive(Refineable, Clone, Debug, PartialEq)]
|
#[derive(Refineable, Clone, Debug, PartialEq)]
|
||||||
#[refineable(Debug)]
|
#[refineable(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
/// The color of the text
|
/// The color of the text
|
||||||
pub color: Hsla,
|
pub color: Hsla,
|
||||||
|
@ -769,8 +767,9 @@ impl Default for Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The properties that can be applied to an underline.
|
/// The properties that can be applied to an underline.
|
||||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
#[derive(
|
||||||
#[refineable(Debug)]
|
Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema,
|
||||||
|
)]
|
||||||
pub struct UnderlineStyle {
|
pub struct UnderlineStyle {
|
||||||
/// The thickness of the underline.
|
/// The thickness of the underline.
|
||||||
pub thickness: Pixels,
|
pub thickness: Pixels,
|
||||||
|
@ -783,8 +782,9 @@ pub struct UnderlineStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The properties that can be applied to a strikethrough.
|
/// The properties that can be applied to a strikethrough.
|
||||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
|
#[derive(
|
||||||
#[refineable(Debug)]
|
Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema,
|
||||||
|
)]
|
||||||
pub struct StrikethroughStyle {
|
pub struct StrikethroughStyle {
|
||||||
/// The thickness of the strikethrough.
|
/// The thickness of the strikethrough.
|
||||||
pub thickness: Pixels,
|
pub thickness: Pixels,
|
||||||
|
@ -794,7 +794,7 @@ pub struct StrikethroughStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The kinds of fill that can be applied to a shape.
|
/// The kinds of fill that can be applied to a shape.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum Fill {
|
pub enum Fill {
|
||||||
/// A solid color fill.
|
/// A solid color fill.
|
||||||
Color(Background),
|
Color(Background),
|
||||||
|
@ -984,6 +984,305 @@ pub fn combine_highlights(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to control how child nodes are aligned.
|
||||||
|
/// For Flexbox it controls alignment in the cross axis
|
||||||
|
/// For Grid it controls alignment in the block axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum AlignItems {
|
||||||
|
/// Items are packed toward the start of the axis
|
||||||
|
Start,
|
||||||
|
/// Items are packed toward the end of the axis
|
||||||
|
End,
|
||||||
|
/// Items are packed towards the flex-relative start of the axis.
|
||||||
|
///
|
||||||
|
/// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent
|
||||||
|
/// to End. In all other cases it is equivalent to Start.
|
||||||
|
FlexStart,
|
||||||
|
/// Items are packed towards the flex-relative end of the axis.
|
||||||
|
///
|
||||||
|
/// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent
|
||||||
|
/// to Start. In all other cases it is equivalent to End.
|
||||||
|
FlexEnd,
|
||||||
|
/// Items are packed along the center of the cross axis
|
||||||
|
Center,
|
||||||
|
/// Items are aligned such as their baselines align
|
||||||
|
Baseline,
|
||||||
|
/// Stretch to fill the container
|
||||||
|
Stretch,
|
||||||
|
}
|
||||||
|
/// Used to control how child nodes are aligned.
|
||||||
|
/// Does not apply to Flexbox, and will be ignored if specified on a flex container
|
||||||
|
/// For Grid it controls alignment in the inline axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items)
|
||||||
|
pub type JustifyItems = AlignItems;
|
||||||
|
/// Used to control how the specified nodes is aligned.
|
||||||
|
/// Overrides the parent Node's `AlignItems` property.
|
||||||
|
/// For Flexbox it controls alignment in the cross axis
|
||||||
|
/// For Grid it controls alignment in the block axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-self)
|
||||||
|
pub type AlignSelf = AlignItems;
|
||||||
|
/// Used to control how the specified nodes is aligned.
|
||||||
|
/// Overrides the parent Node's `JustifyItems` property.
|
||||||
|
/// Does not apply to Flexbox, and will be ignored if specified on a flex child
|
||||||
|
/// For Grid it controls alignment in the inline axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self)
|
||||||
|
pub type JustifySelf = AlignItems;
|
||||||
|
|
||||||
|
/// Sets the distribution of space between and around content items
|
||||||
|
/// For Flexbox it controls alignment in the cross axis
|
||||||
|
/// For Grid it controls alignment in the block axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-content)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum AlignContent {
|
||||||
|
/// Items are packed toward the start of the axis
|
||||||
|
Start,
|
||||||
|
/// Items are packed toward the end of the axis
|
||||||
|
End,
|
||||||
|
/// Items are packed towards the flex-relative start of the axis.
|
||||||
|
///
|
||||||
|
/// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent
|
||||||
|
/// to End. In all other cases it is equivalent to Start.
|
||||||
|
FlexStart,
|
||||||
|
/// Items are packed towards the flex-relative end of the axis.
|
||||||
|
///
|
||||||
|
/// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent
|
||||||
|
/// to Start. In all other cases it is equivalent to End.
|
||||||
|
FlexEnd,
|
||||||
|
/// Items are centered around the middle of the axis
|
||||||
|
Center,
|
||||||
|
/// Items are stretched to fill the container
|
||||||
|
Stretch,
|
||||||
|
/// The first and last items are aligned flush with the edges of the container (no gap)
|
||||||
|
/// The gap between items is distributed evenly.
|
||||||
|
SpaceBetween,
|
||||||
|
/// The gap between the first and last items is exactly THE SAME as the gap between items.
|
||||||
|
/// The gaps are distributed evenly
|
||||||
|
SpaceEvenly,
|
||||||
|
/// The gap between the first and last items is exactly HALF the gap between items.
|
||||||
|
/// The gaps are distributed evenly in proportion to these ratios.
|
||||||
|
SpaceAround,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the distribution of space between and around content items
|
||||||
|
/// For Flexbox it controls alignment in the main axis
|
||||||
|
/// For Grid it controls alignment in the inline axis
|
||||||
|
///
|
||||||
|
/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content)
|
||||||
|
pub type JustifyContent = AlignContent;
|
||||||
|
|
||||||
|
/// Sets the layout used for the children of this node
|
||||||
|
///
|
||||||
|
/// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum Display {
|
||||||
|
/// The children will follow the block layout algorithm
|
||||||
|
Block,
|
||||||
|
/// The children will follow the flexbox layout algorithm
|
||||||
|
#[default]
|
||||||
|
Flex,
|
||||||
|
/// The children will follow the CSS Grid layout algorithm
|
||||||
|
Grid,
|
||||||
|
/// The children will not be laid out, and will follow absolute positioning
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Controls whether flex items are forced onto one line or can wrap onto multiple lines.
|
||||||
|
///
|
||||||
|
/// Defaults to [`FlexWrap::NoWrap`]
|
||||||
|
///
|
||||||
|
/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum FlexWrap {
|
||||||
|
/// Items will not wrap and stay on a single line
|
||||||
|
#[default]
|
||||||
|
NoWrap,
|
||||||
|
/// Items will wrap according to this item's [`FlexDirection`]
|
||||||
|
Wrap,
|
||||||
|
/// Items will wrap in the opposite direction to this item's [`FlexDirection`]
|
||||||
|
WrapReverse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The direction of the flexbox layout main axis.
|
||||||
|
///
|
||||||
|
/// There are always two perpendicular layout axes: main (or primary) and cross (or secondary).
|
||||||
|
/// Adding items will cause them to be positioned adjacent to each other along the main axis.
|
||||||
|
/// By varying this value throughout your tree, you can create complex axis-aligned layouts.
|
||||||
|
///
|
||||||
|
/// Items are always aligned relative to the cross axis, and justified relative to the main axis.
|
||||||
|
///
|
||||||
|
/// The default behavior is [`FlexDirection::Row`].
|
||||||
|
///
|
||||||
|
/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum FlexDirection {
|
||||||
|
/// Defines +x as the main axis
|
||||||
|
///
|
||||||
|
/// Items will be added from left to right in a row.
|
||||||
|
#[default]
|
||||||
|
Row,
|
||||||
|
/// Defines +y as the main axis
|
||||||
|
///
|
||||||
|
/// Items will be added from top to bottom in a column.
|
||||||
|
Column,
|
||||||
|
/// Defines -x as the main axis
|
||||||
|
///
|
||||||
|
/// Items will be added from right to left in a row.
|
||||||
|
RowReverse,
|
||||||
|
/// Defines -y as the main axis
|
||||||
|
///
|
||||||
|
/// Items will be added from bottom to top in a column.
|
||||||
|
ColumnReverse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How children overflowing their container should affect layout
|
||||||
|
///
|
||||||
|
/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
|
||||||
|
/// be displayed anyway, be clipped, or trigger the container to become a scroll container. However it also has secondary effects on layout,
|
||||||
|
/// the main ones being:
|
||||||
|
///
|
||||||
|
/// - The automatic minimum size Flexbox/CSS Grid items with non-`Visible` overflow is `0` rather than being content based
|
||||||
|
/// - `Overflow::Scroll` nodes have space in the layout reserved for a scrollbar (width controlled by the `scrollbar_width` property)
|
||||||
|
///
|
||||||
|
/// In Taffy, we only implement the layout related secondary effects as we are not concerned with drawing/painting. The amount of space reserved for
|
||||||
|
/// a scrollbar is controlled by the `scrollbar_width` property. If this is `0` then `Scroll` behaves identically to `Hidden`.
|
||||||
|
///
|
||||||
|
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum Overflow {
|
||||||
|
/// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content.
|
||||||
|
/// Content that overflows this node *should* contribute to the scroll region of its parent.
|
||||||
|
#[default]
|
||||||
|
Visible,
|
||||||
|
/// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content.
|
||||||
|
/// Content that overflows this node should *not* contribute to the scroll region of its parent.
|
||||||
|
Clip,
|
||||||
|
/// The automatic minimum size of this node as a flexbox/grid item should be `0`.
|
||||||
|
/// Content that overflows this node should *not* contribute to the scroll region of its parent.
|
||||||
|
Hidden,
|
||||||
|
/// The automatic minimum size of this node as a flexbox/grid item should be `0`. Additionally, space should be reserved
|
||||||
|
/// for a scrollbar. The amount of space reserved is controlled by the `scrollbar_width` property.
|
||||||
|
/// Content that overflows this node should *not* contribute to the scroll region of its parent.
|
||||||
|
Scroll,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The positioning strategy for this item.
|
||||||
|
///
|
||||||
|
/// This controls both how the origin is determined for the [`Style::position`] field,
|
||||||
|
/// and whether or not the item will be controlled by flexbox's layout algorithm.
|
||||||
|
///
|
||||||
|
/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
|
||||||
|
/// which can be unintuitive.
|
||||||
|
///
|
||||||
|
/// [`Position::Relative`] is the default value, in contrast to the default behavior in CSS.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
// Copy of taffy::style type of the same name, to derive JsonSchema.
|
||||||
|
pub enum Position {
|
||||||
|
/// The offset is computed relative to the final position given by the layout algorithm.
|
||||||
|
/// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
|
||||||
|
#[default]
|
||||||
|
Relative,
|
||||||
|
/// The offset is computed relative to this item's closest positioned ancestor, if any.
|
||||||
|
/// Otherwise, it is placed relative to the origin.
|
||||||
|
/// No space is created for the item in the page layout, and its size will not be altered.
|
||||||
|
///
|
||||||
|
/// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
|
||||||
|
Absolute,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AlignItems> for taffy::style::AlignItems {
|
||||||
|
fn from(value: AlignItems) -> Self {
|
||||||
|
match value {
|
||||||
|
AlignItems::Start => Self::Start,
|
||||||
|
AlignItems::End => Self::End,
|
||||||
|
AlignItems::FlexStart => Self::FlexStart,
|
||||||
|
AlignItems::FlexEnd => Self::FlexEnd,
|
||||||
|
AlignItems::Center => Self::Center,
|
||||||
|
AlignItems::Baseline => Self::Baseline,
|
||||||
|
AlignItems::Stretch => Self::Stretch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AlignContent> for taffy::style::AlignContent {
|
||||||
|
fn from(value: AlignContent) -> Self {
|
||||||
|
match value {
|
||||||
|
AlignContent::Start => Self::Start,
|
||||||
|
AlignContent::End => Self::End,
|
||||||
|
AlignContent::FlexStart => Self::FlexStart,
|
||||||
|
AlignContent::FlexEnd => Self::FlexEnd,
|
||||||
|
AlignContent::Center => Self::Center,
|
||||||
|
AlignContent::Stretch => Self::Stretch,
|
||||||
|
AlignContent::SpaceBetween => Self::SpaceBetween,
|
||||||
|
AlignContent::SpaceEvenly => Self::SpaceEvenly,
|
||||||
|
AlignContent::SpaceAround => Self::SpaceAround,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Display> for taffy::style::Display {
|
||||||
|
fn from(value: Display) -> Self {
|
||||||
|
match value {
|
||||||
|
Display::Block => Self::Block,
|
||||||
|
Display::Flex => Self::Flex,
|
||||||
|
Display::Grid => Self::Grid,
|
||||||
|
Display::None => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FlexWrap> for taffy::style::FlexWrap {
|
||||||
|
fn from(value: FlexWrap) -> Self {
|
||||||
|
match value {
|
||||||
|
FlexWrap::NoWrap => Self::NoWrap,
|
||||||
|
FlexWrap::Wrap => Self::Wrap,
|
||||||
|
FlexWrap::WrapReverse => Self::WrapReverse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FlexDirection> for taffy::style::FlexDirection {
|
||||||
|
fn from(value: FlexDirection) -> Self {
|
||||||
|
match value {
|
||||||
|
FlexDirection::Row => Self::Row,
|
||||||
|
FlexDirection::Column => Self::Column,
|
||||||
|
FlexDirection::RowReverse => Self::RowReverse,
|
||||||
|
FlexDirection::ColumnReverse => Self::ColumnReverse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Overflow> for taffy::style::Overflow {
|
||||||
|
fn from(value: Overflow) -> Self {
|
||||||
|
match value {
|
||||||
|
Overflow::Visible => Self::Visible,
|
||||||
|
Overflow::Clip => Self::Clip,
|
||||||
|
Overflow::Hidden => Self::Hidden,
|
||||||
|
Overflow::Scroll => Self::Scroll,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Position> for taffy::style::Position {
|
||||||
|
fn from(value: Position) -> Self {
|
||||||
|
match value {
|
||||||
|
Position::Relative => Self::Relative,
|
||||||
|
Position::Absolute => Self::Absolute,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{blue, green, red, yellow};
|
use crate::{blue, green, red, yellow};
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
self as gpui, AbsoluteLength, AlignItems, BorderStyle, CursorStyle, DefiniteLength, Fill,
|
self as gpui, AbsoluteLength, AlignContent, AlignItems, BorderStyle, CursorStyle,
|
||||||
FlexDirection, FlexWrap, Font, FontStyle, FontWeight, Hsla, JustifyContent, Length,
|
DefiniteLength, Display, Fill, FlexDirection, FlexWrap, Font, FontStyle, FontWeight, Hsla,
|
||||||
SharedString, StrikethroughStyle, StyleRefinement, TextOverflow, UnderlineStyle, WhiteSpace,
|
JustifyContent, Length, SharedString, StrikethroughStyle, StyleRefinement, TextAlign,
|
||||||
px, relative, rems,
|
TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
||||||
};
|
};
|
||||||
use crate::{TextAlign, TextStyleRefinement};
|
|
||||||
pub use gpui_macros::{
|
pub use gpui_macros::{
|
||||||
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
||||||
overflow_style_methods, padding_style_methods, position_style_methods,
|
overflow_style_methods, padding_style_methods, position_style_methods,
|
||||||
visibility_style_methods,
|
visibility_style_methods,
|
||||||
};
|
};
|
||||||
use taffy::style::{AlignContent, Display};
|
|
||||||
|
|
||||||
const ELLIPSIS: &str = "…";
|
const ELLIPSIS: SharedString = SharedString::new_static("…");
|
||||||
|
|
||||||
/// A trait for elements that can be styled.
|
/// A trait for elements that can be styled.
|
||||||
/// Use this to opt-in to a utility CSS-like styling API.
|
/// Use this to opt-in to a utility CSS-like styling API.
|
||||||
|
@ -67,7 +65,7 @@ pub trait Styled: Sized {
|
||||||
fn text_ellipsis(mut self) -> Self {
|
fn text_ellipsis(mut self) -> Self {
|
||||||
self.text_style()
|
self.text_style()
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.text_overflow = Some(TextOverflow::Ellipsis(ELLIPSIS));
|
.text_overflow = Some(TextOverflow::Truncate(ELLIPSIS));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,10 +250,10 @@ trait ToTaffy<Output> {
|
||||||
impl ToTaffy<taffy::style::Style> for Style {
|
impl ToTaffy<taffy::style::Style> for Style {
|
||||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style {
|
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style {
|
||||||
taffy::style::Style {
|
taffy::style::Style {
|
||||||
display: self.display,
|
display: self.display.into(),
|
||||||
overflow: self.overflow.into(),
|
overflow: self.overflow.into(),
|
||||||
scrollbar_width: self.scrollbar_width,
|
scrollbar_width: self.scrollbar_width,
|
||||||
position: self.position,
|
position: self.position.into(),
|
||||||
inset: self.inset.to_taffy(rem_size),
|
inset: self.inset.to_taffy(rem_size),
|
||||||
size: self.size.to_taffy(rem_size),
|
size: self.size.to_taffy(rem_size),
|
||||||
min_size: self.min_size.to_taffy(rem_size),
|
min_size: self.min_size.to_taffy(rem_size),
|
||||||
|
@ -262,13 +262,13 @@ impl ToTaffy<taffy::style::Style> for Style {
|
||||||
margin: self.margin.to_taffy(rem_size),
|
margin: self.margin.to_taffy(rem_size),
|
||||||
padding: self.padding.to_taffy(rem_size),
|
padding: self.padding.to_taffy(rem_size),
|
||||||
border: self.border_widths.to_taffy(rem_size),
|
border: self.border_widths.to_taffy(rem_size),
|
||||||
align_items: self.align_items,
|
align_items: self.align_items.map(|x| x.into()),
|
||||||
align_self: self.align_self,
|
align_self: self.align_self.map(|x| x.into()),
|
||||||
align_content: self.align_content,
|
align_content: self.align_content.map(|x| x.into()),
|
||||||
justify_content: self.justify_content,
|
justify_content: self.justify_content.map(|x| x.into()),
|
||||||
gap: self.gap.to_taffy(rem_size),
|
gap: self.gap.to_taffy(rem_size),
|
||||||
flex_direction: self.flex_direction,
|
flex_direction: self.flex_direction.into(),
|
||||||
flex_wrap: self.flex_wrap,
|
flex_wrap: self.flex_wrap.into(),
|
||||||
flex_basis: self.flex_basis.to_taffy(rem_size),
|
flex_basis: self.flex_basis.to_taffy(rem_size),
|
||||||
flex_grow: self.flex_grow,
|
flex_grow: self.flex_grow,
|
||||||
flex_shrink: self.flex_shrink,
|
flex_shrink: self.flex_shrink,
|
||||||
|
|
|
@ -583,7 +583,7 @@ impl DerefMut for LineWrapperHandle {
|
||||||
|
|
||||||
/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
|
/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
|
||||||
/// with 400.0 as normal.
|
/// with 400.0 as normal.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Deserialize, Serialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct FontWeight(pub f32);
|
pub struct FontWeight(pub f32);
|
||||||
|
|
||||||
impl Default for FontWeight {
|
impl Default for FontWeight {
|
||||||
|
@ -636,7 +636,7 @@ impl FontWeight {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows italic or oblique faces to be selected.
|
/// Allows italic or oblique faces to be selected.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum FontStyle {
|
pub enum FontStyle {
|
||||||
/// A face that is neither italic not obliqued.
|
/// A face that is neither italic not obliqued.
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -133,21 +133,18 @@ impl LineWrapper {
|
||||||
&mut self,
|
&mut self,
|
||||||
line: SharedString,
|
line: SharedString,
|
||||||
truncate_width: Pixels,
|
truncate_width: Pixels,
|
||||||
ellipsis: Option<&str>,
|
truncation_suffix: &str,
|
||||||
runs: &mut Vec<TextRun>,
|
runs: &mut Vec<TextRun>,
|
||||||
) -> SharedString {
|
) -> SharedString {
|
||||||
let mut width = px(0.);
|
let mut width = px(0.);
|
||||||
let mut ellipsis_width = px(0.);
|
let mut suffix_width = truncation_suffix
|
||||||
if let Some(ellipsis) = ellipsis {
|
.chars()
|
||||||
for c in ellipsis.chars() {
|
.map(|c| self.width_for_char(c))
|
||||||
ellipsis_width += self.width_for_char(c);
|
.fold(px(0.0), |a, x| a + x);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut char_indices = line.char_indices();
|
let mut char_indices = line.char_indices();
|
||||||
let mut truncate_ix = 0;
|
let mut truncate_ix = 0;
|
||||||
for (ix, c) in char_indices {
|
for (ix, c) in char_indices {
|
||||||
if width + ellipsis_width < truncate_width {
|
if width + suffix_width < truncate_width {
|
||||||
truncate_ix = ix;
|
truncate_ix = ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +152,9 @@ impl LineWrapper {
|
||||||
width += char_width;
|
width += char_width;
|
||||||
|
|
||||||
if width.floor() > truncate_width {
|
if width.floor() > truncate_width {
|
||||||
let ellipsis = ellipsis.unwrap_or("");
|
let result =
|
||||||
let result = SharedString::from(format!("{}{}", &line[..truncate_ix], ellipsis));
|
SharedString::from(format!("{}{}", &line[..truncate_ix], truncation_suffix));
|
||||||
update_runs_after_truncation(&result, ellipsis, runs);
|
update_runs_after_truncation(&result, truncation_suffix, runs);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +497,7 @@ mod tests {
|
||||||
wrapper: &mut LineWrapper,
|
wrapper: &mut LineWrapper,
|
||||||
text: &'static str,
|
text: &'static str,
|
||||||
result: &'static str,
|
result: &'static str,
|
||||||
ellipsis: Option<&str>,
|
ellipsis: &str,
|
||||||
) {
|
) {
|
||||||
let dummy_run_lens = vec![text.len()];
|
let dummy_run_lens = vec![text.len()];
|
||||||
let mut dummy_runs = generate_test_runs(&dummy_run_lens);
|
let mut dummy_runs = generate_test_runs(&dummy_run_lens);
|
||||||
|
@ -515,19 +512,19 @@ mod tests {
|
||||||
&mut wrapper,
|
&mut wrapper,
|
||||||
"aa bbb cccc ddddd eeee ffff gggg",
|
"aa bbb cccc ddddd eeee ffff gggg",
|
||||||
"aa bbb cccc ddddd eeee",
|
"aa bbb cccc ddddd eeee",
|
||||||
None,
|
"",
|
||||||
);
|
);
|
||||||
perform_test(
|
perform_test(
|
||||||
&mut wrapper,
|
&mut wrapper,
|
||||||
"aa bbb cccc ddddd eeee ffff gggg",
|
"aa bbb cccc ddddd eeee ffff gggg",
|
||||||
"aa bbb cccc ddddd eee…",
|
"aa bbb cccc ddddd eee…",
|
||||||
Some("…"),
|
"…",
|
||||||
);
|
);
|
||||||
perform_test(
|
perform_test(
|
||||||
&mut wrapper,
|
&mut wrapper,
|
||||||
"aa bbb cccc ddddd eeee ffff gggg",
|
"aa bbb cccc ddddd eeee ffff gggg",
|
||||||
"aa bbb cccc dddd......",
|
"aa bbb cccc dddd......",
|
||||||
Some("......"),
|
"......",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,7 +542,7 @@ mod tests {
|
||||||
) {
|
) {
|
||||||
let mut dummy_runs = generate_test_runs(run_lens);
|
let mut dummy_runs = generate_test_runs(run_lens);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
wrapper.truncate_line(text.into(), line_width, Some("…"), &mut dummy_runs),
|
wrapper.truncate_line(text.into(), line_width, "…", &mut dummy_runs),
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
for (run, result_len) in dummy_runs.iter().zip(result_run_len) {
|
for (run, result_len) in dummy_runs.iter().zip(result_run_len) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId,
|
AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId,
|
||||||
Entity, EntityId, GlobalElementId, IntoElement, LayoutId, PaintIndex, Pixels,
|
Entity, EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, PaintIndex,
|
||||||
PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
|
Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
|
||||||
};
|
};
|
||||||
use crate::{Empty, Window};
|
use crate::{Empty, Window};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -33,9 +33,14 @@ impl<V: Render> Element for Entity<V> {
|
||||||
Some(ElementId::View(self.entity_id()))
|
Some(ElementId::View(self.entity_id()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -49,6 +54,7 @@ impl<V: Render> Element for Entity<V> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -61,6 +67,7 @@ impl<V: Render> Element for Entity<V> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_: Bounds<Pixels>,
|
_: Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
_: &mut Self::PrepaintState,
|
_: &mut Self::PrepaintState,
|
||||||
|
@ -146,22 +153,32 @@ impl Element for AnyView {
|
||||||
Some(ElementId::View(self.entity_id()))
|
Some(ElementId::View(self.entity_id()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
window.with_rendered_view(self.entity_id(), |window| {
|
window.with_rendered_view(self.entity_id(), |window| {
|
||||||
if let Some(style) = self.cached_style.as_ref() {
|
// Disable caching when inspecting so that mouse_hit_test has all hitboxes.
|
||||||
let mut root_style = Style::default();
|
let caching_disabled = window.is_inspector_picking(cx);
|
||||||
root_style.refine(style);
|
match self.cached_style.as_ref() {
|
||||||
let layout_id = window.request_layout(root_style, None, cx);
|
Some(style) if !caching_disabled => {
|
||||||
(layout_id, None)
|
let mut root_style = Style::default();
|
||||||
} else {
|
root_style.refine(style);
|
||||||
let mut element = (self.render)(self, window, cx);
|
let layout_id = window.request_layout(root_style, None, cx);
|
||||||
let layout_id = element.request_layout(window, cx);
|
(layout_id, None)
|
||||||
(layout_id, Some(element))
|
}
|
||||||
|
_ => {
|
||||||
|
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(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
element: &mut Self::RequestLayoutState,
|
element: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -176,70 +194,69 @@ impl Element for AnyView {
|
||||||
) -> Option<AnyElement> {
|
) -> Option<AnyElement> {
|
||||||
window.set_view_id(self.entity_id());
|
window.set_view_id(self.entity_id());
|
||||||
window.with_rendered_view(self.entity_id(), |window| {
|
window.with_rendered_view(self.entity_id(), |window| {
|
||||||
if self.cached_style.is_some() {
|
if let Some(mut element) = element.take() {
|
||||||
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();
|
|
||||||
element.prepaint(window, cx);
|
element.prepaint(window, cx);
|
||||||
|
return Some(element);
|
||||||
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(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
element: &mut Self::PrepaintState,
|
element: &mut Self::PrepaintState,
|
||||||
|
@ -247,7 +264,8 @@ impl Element for AnyView {
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
window.with_rendered_view(self.entity_id(), |window| {
|
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, _>(
|
window.with_element_state::<AnyViewState, _>(
|
||||||
global_id.unwrap(),
|
global_id.unwrap(),
|
||||||
|element_state, window| {
|
|element_state, window| {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
use crate::Inspector;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
|
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
|
||||||
AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Context,
|
AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Context,
|
||||||
|
@ -13,7 +15,7 @@ use crate::{
|
||||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
||||||
TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance,
|
TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance,
|
||||||
WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
|
WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
|
||||||
point, prelude::*, px, size, transparent_black,
|
point, prelude::*, px, rems, size, transparent_black,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use collections::{FxHashMap, FxHashSet};
|
use collections::{FxHashMap, FxHashSet};
|
||||||
|
@ -412,7 +414,7 @@ pub(crate) struct CursorStyleRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier for a [Hitbox].
|
/// An identifier for a [Hitbox].
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
||||||
pub struct HitboxId(usize);
|
pub struct HitboxId(usize);
|
||||||
|
|
||||||
impl HitboxId {
|
impl HitboxId {
|
||||||
|
@ -502,6 +504,10 @@ pub(crate) struct Frame {
|
||||||
pub(crate) cursor_styles: Vec<CursorStyleRequest>,
|
pub(crate) cursor_styles: Vec<CursorStyleRequest>,
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub(crate) next_inspector_instance_ids: FxHashMap<Rc<crate::InspectorElementPath>, usize>,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub(crate) inspector_hitboxes: FxHashMap<HitboxId, crate::InspectorElementId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -542,6 +548,12 @@ impl Frame {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
debug_bounds: FxHashMap::default(),
|
debug_bounds: FxHashMap::default(),
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
next_inspector_instance_ids: FxHashMap::default(),
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
inspector_hitboxes: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +569,12 @@ impl Frame {
|
||||||
self.hitboxes.clear();
|
self.hitboxes.clear();
|
||||||
self.deferred_draws.clear();
|
self.deferred_draws.clear();
|
||||||
self.focus = None;
|
self.focus = None;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
{
|
||||||
|
self.next_inspector_instance_ids.clear();
|
||||||
|
self.inspector_hitboxes.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
|
||||||
|
@ -648,6 +666,8 @@ pub struct Window {
|
||||||
pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>,
|
pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>,
|
||||||
prompt: Option<RenderablePromptHandle>,
|
prompt: Option<RenderablePromptHandle>,
|
||||||
pub(crate) client_inset: Option<Pixels>,
|
pub(crate) client_inset: Option<Pixels>,
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
inspector: Option<Entity<Inspector>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -935,6 +955,8 @@ impl Window {
|
||||||
prompt: None,
|
prompt: None,
|
||||||
client_inset: None,
|
client_inset: None,
|
||||||
image_cache_stack: Vec::new(),
|
image_cache_stack: Vec::new(),
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
inspector: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1658,9 +1680,30 @@ impl Window {
|
||||||
self.invalidator.set_phase(DrawPhase::Prepaint);
|
self.invalidator.set_phase(DrawPhase::Prepaint);
|
||||||
self.tooltip_bounds.take();
|
self.tooltip_bounds.take();
|
||||||
|
|
||||||
|
let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size());
|
||||||
|
let root_size = {
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
{
|
||||||
|
if self.inspector.is_some() {
|
||||||
|
let mut size = self.viewport_size;
|
||||||
|
size.width = (size.width - _inspector_width).max(px(0.0));
|
||||||
|
size
|
||||||
|
} else {
|
||||||
|
self.viewport_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(any(feature = "inspector", debug_assertions)))]
|
||||||
|
{
|
||||||
|
self.viewport_size
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Layout all root elements.
|
// Layout all root elements.
|
||||||
let mut root_element = self.root.as_ref().unwrap().clone().into_any();
|
let mut root_element = self.root.as_ref().unwrap().clone().into_any();
|
||||||
root_element.prepaint_as_root(Point::default(), self.viewport_size.into(), self, cx);
|
root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
let inspector_element = self.prepaint_inspector(_inspector_width, cx);
|
||||||
|
|
||||||
let mut sorted_deferred_draws =
|
let mut sorted_deferred_draws =
|
||||||
(0..self.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
(0..self.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
||||||
|
@ -1672,7 +1715,7 @@ impl Window {
|
||||||
let mut tooltip_element = None;
|
let mut tooltip_element = None;
|
||||||
if let Some(prompt) = self.prompt.take() {
|
if let Some(prompt) = self.prompt.take() {
|
||||||
let mut element = prompt.view.any_view().into_any();
|
let mut element = prompt.view.any_view().into_any();
|
||||||
element.prepaint_as_root(Point::default(), self.viewport_size.into(), self, cx);
|
element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
|
||||||
prompt_element = Some(element);
|
prompt_element = Some(element);
|
||||||
self.prompt = Some(prompt);
|
self.prompt = Some(prompt);
|
||||||
} else if let Some(active_drag) = cx.active_drag.take() {
|
} else if let Some(active_drag) = cx.active_drag.take() {
|
||||||
|
@ -1691,6 +1734,9 @@ impl Window {
|
||||||
self.invalidator.set_phase(DrawPhase::Paint);
|
self.invalidator.set_phase(DrawPhase::Paint);
|
||||||
root_element.paint(self, cx);
|
root_element.paint(self, cx);
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
self.paint_inspector(inspector_element, cx);
|
||||||
|
|
||||||
self.paint_deferred_draws(&sorted_deferred_draws, cx);
|
self.paint_deferred_draws(&sorted_deferred_draws, cx);
|
||||||
|
|
||||||
if let Some(mut prompt_element) = prompt_element {
|
if let Some(mut prompt_element) = prompt_element {
|
||||||
|
@ -1700,6 +1746,9 @@ impl Window {
|
||||||
} else if let Some(mut tooltip_element) = tooltip_element {
|
} else if let Some(mut tooltip_element) = tooltip_element {
|
||||||
tooltip_element.paint(self, cx);
|
tooltip_element.paint(self, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
self.paint_inspector_hitbox(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
|
fn prepaint_tooltip(&mut self, cx: &mut App) -> Option<AnyElement> {
|
||||||
|
@ -3200,6 +3249,13 @@ impl Window {
|
||||||
self.reset_cursor_style(cx);
|
self.reset_cursor_style(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
if self.is_inspector_picking(cx) {
|
||||||
|
self.handle_inspector_mouse_event(event, cx);
|
||||||
|
// When inspector is picking, all other mouse handling is skipped.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners);
|
let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners);
|
||||||
|
|
||||||
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
||||||
|
@ -3830,6 +3886,197 @@ impl Window {
|
||||||
pub fn gpu_specs(&self) -> Option<GpuSpecs> {
|
pub fn gpu_specs(&self) -> Option<GpuSpecs> {
|
||||||
self.platform_window.gpu_specs()
|
self.platform_window.gpu_specs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Toggles the inspector mode on this window.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub fn toggle_inspector(&mut self, cx: &mut App) {
|
||||||
|
self.inspector = match self.inspector {
|
||||||
|
None => Some(cx.new(|_| Inspector::new())),
|
||||||
|
Some(_) => None,
|
||||||
|
};
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the window is in inspector mode.
|
||||||
|
pub fn is_inspector_picking(&self, _cx: &App) -> bool {
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
{
|
||||||
|
if let Some(inspector) = &self.inspector {
|
||||||
|
return inspector.read(_cx).is_picking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the provided function with mutable access to an inspector state.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub fn with_inspector_state<T: 'static, R>(
|
||||||
|
&mut self,
|
||||||
|
_inspector_id: Option<&crate::InspectorElementId>,
|
||||||
|
cx: &mut App,
|
||||||
|
f: impl FnOnce(&mut Option<T>, &mut Self) -> R,
|
||||||
|
) -> R {
|
||||||
|
if let Some(inspector_id) = _inspector_id {
|
||||||
|
if let Some(inspector) = &self.inspector {
|
||||||
|
let inspector = inspector.clone();
|
||||||
|
let active_element_id = inspector.read(cx).active_element_id();
|
||||||
|
if Some(inspector_id) == active_element_id {
|
||||||
|
return inspector.update(cx, |inspector, _cx| {
|
||||||
|
inspector.with_active_element_state(self, f)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(&mut None, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub(crate) fn build_inspector_element_id(
|
||||||
|
&mut self,
|
||||||
|
path: crate::InspectorElementPath,
|
||||||
|
) -> crate::InspectorElementId {
|
||||||
|
self.invalidator.debug_assert_paint_or_prepaint();
|
||||||
|
let path = Rc::new(path);
|
||||||
|
let next_instance_id = self
|
||||||
|
.next_frame
|
||||||
|
.next_inspector_instance_ids
|
||||||
|
.entry(path.clone())
|
||||||
|
.or_insert(0);
|
||||||
|
let instance_id = *next_instance_id;
|
||||||
|
*next_instance_id += 1;
|
||||||
|
crate::InspectorElementId { path, instance_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option<AnyElement> {
|
||||||
|
if let Some(inspector) = self.inspector.take() {
|
||||||
|
let mut inspector_element = AnyView::from(inspector.clone()).into_any_element();
|
||||||
|
inspector_element.prepaint_as_root(
|
||||||
|
point(self.viewport_size.width - inspector_width, px(0.0)),
|
||||||
|
size(inspector_width, self.viewport_size.height).into(),
|
||||||
|
self,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
self.inspector = Some(inspector);
|
||||||
|
Some(inspector_element)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
fn paint_inspector(&mut self, mut inspector_element: Option<AnyElement>, cx: &mut App) {
|
||||||
|
if let Some(mut inspector_element) = inspector_element {
|
||||||
|
inspector_element.paint(self, cx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a hitbox that can be used for inspector picking mode, allowing users to select and
|
||||||
|
/// inspect UI elements by clicking on them.
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
pub fn insert_inspector_hitbox(
|
||||||
|
&mut self,
|
||||||
|
hitbox_id: HitboxId,
|
||||||
|
inspector_id: Option<&crate::InspectorElementId>,
|
||||||
|
cx: &App,
|
||||||
|
) {
|
||||||
|
self.invalidator.debug_assert_paint_or_prepaint();
|
||||||
|
if !self.is_inspector_picking(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(inspector_id) = inspector_id {
|
||||||
|
self.next_frame
|
||||||
|
.inspector_hitboxes
|
||||||
|
.insert(hitbox_id, inspector_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
fn paint_inspector_hitbox(&mut self, cx: &App) {
|
||||||
|
if let Some(inspector) = self.inspector.as_ref() {
|
||||||
|
let inspector = inspector.read(cx);
|
||||||
|
if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame)
|
||||||
|
{
|
||||||
|
if let Some(hitbox) = self
|
||||||
|
.next_frame
|
||||||
|
.hitboxes
|
||||||
|
.iter()
|
||||||
|
.find(|hitbox| hitbox.id == hitbox_id)
|
||||||
|
{
|
||||||
|
self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) {
|
||||||
|
let Some(inspector) = self.inspector.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if event.downcast_ref::<MouseMoveEvent>().is_some() {
|
||||||
|
inspector.update(cx, |inspector, _cx| {
|
||||||
|
if let Some((_, inspector_id)) =
|
||||||
|
self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
|
||||||
|
{
|
||||||
|
inspector.hover(inspector_id, self);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if event.downcast_ref::<crate::MouseDownEvent>().is_some() {
|
||||||
|
inspector.update(cx, |inspector, _cx| {
|
||||||
|
if let Some((_, inspector_id)) =
|
||||||
|
self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
|
||||||
|
{
|
||||||
|
inspector.select(inspector_id, self);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if let Some(event) = event.downcast_ref::<crate::ScrollWheelEvent>() {
|
||||||
|
// This should be kept in sync with SCROLL_LINES in x11 platform.
|
||||||
|
const SCROLL_LINES: f32 = 3.0;
|
||||||
|
const SCROLL_PIXELS_PER_LAYER: f32 = 36.0;
|
||||||
|
let delta_y = event
|
||||||
|
.delta
|
||||||
|
.pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES))
|
||||||
|
.y;
|
||||||
|
if let Some(inspector) = self.inspector.clone() {
|
||||||
|
inspector.update(cx, |inspector, _cx| {
|
||||||
|
if let Some(depth) = inspector.pick_depth.as_mut() {
|
||||||
|
*depth += delta_y.0 / SCROLL_PIXELS_PER_LAYER;
|
||||||
|
let max_depth = self.mouse_hit_test.0.len() as f32 - 0.5;
|
||||||
|
if *depth < 0.0 {
|
||||||
|
*depth = 0.0;
|
||||||
|
} else if *depth > max_depth {
|
||||||
|
*depth = max_depth;
|
||||||
|
}
|
||||||
|
if let Some((_, inspector_id)) =
|
||||||
|
self.hovered_inspector_hitbox(inspector, &self.rendered_frame)
|
||||||
|
{
|
||||||
|
inspector.set_active_element_id(inspector_id.clone(), self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "inspector", debug_assertions))]
|
||||||
|
fn hovered_inspector_hitbox(
|
||||||
|
&self,
|
||||||
|
inspector: &Inspector,
|
||||||
|
frame: &Frame,
|
||||||
|
) -> Option<(HitboxId, crate::InspectorElementId)> {
|
||||||
|
if let Some(pick_depth) = inspector.pick_depth {
|
||||||
|
let depth = (pick_depth as i64).try_into().unwrap_or(0);
|
||||||
|
let max_skipped = self.mouse_hit_test.0.len().saturating_sub(1);
|
||||||
|
let skip_count = (depth as usize).min(max_skipped);
|
||||||
|
for hitbox_id in self.mouse_hit_test.0.iter().skip(skip_count) {
|
||||||
|
if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) {
|
||||||
|
return Some((*hitbox_id, inspector_id.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
// #[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
@ -4069,7 +4316,7 @@ pub enum ElementId {
|
||||||
FocusHandle(FocusId),
|
FocusHandle(FocusId),
|
||||||
/// A combination of a name and an integer.
|
/// A combination of a name and an integer.
|
||||||
NamedInteger(SharedString, u64),
|
NamedInteger(SharedString, u64),
|
||||||
/// A path
|
/// A path.
|
||||||
Path(Arc<std::path::Path>),
|
Path(Arc<std::path::Path>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
||||||
{
|
{
|
||||||
type Element = gpui::Component<Self>;
|
type Element = gpui::Component<Self>;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn into_element(self) -> Self::Element {
|
fn into_element(self) -> Self::Element {
|
||||||
gpui::Component::new(self)
|
gpui::Component::new(self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,7 +393,7 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
/// Sets the box shadow of the element.
|
/// Sets the box shadow of the element.
|
||||||
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow(mut self, shadows: smallvec::SmallVec<[gpui::BoxShadow; 2]>) -> Self {
|
#visibility fn shadow(mut self, shadows: std::vec::Vec<gpui::BoxShadow>) -> Self {
|
||||||
self.style().box_shadow = Some(shadows);
|
self.style().box_shadow = Some(shadows);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -409,9 +409,9 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
||||||
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow_sm(mut self) -> Self {
|
#visibility fn shadow_sm(mut self) -> Self {
|
||||||
use gpui::{BoxShadow, hsla, point, px};
|
use gpui::{BoxShadow, hsla, point, px};
|
||||||
use smallvec::smallvec;
|
use std::vec;
|
||||||
|
|
||||||
self.style().box_shadow = Some(smallvec
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow_md(mut self) -> Self {
|
#visibility fn shadow_md(mut self) -> Self {
|
||||||
use gpui::{BoxShadow, hsla, point, px};
|
use gpui::{BoxShadow, hsla, point, px};
|
||||||
use smallvec::smallvec;
|
use std::vec;
|
||||||
|
|
||||||
self.style().box_shadow = Some(smallvec
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow_lg(mut self) -> Self {
|
#visibility fn shadow_lg(mut self) -> Self {
|
||||||
use gpui::{BoxShadow, hsla, point, px};
|
use gpui::{BoxShadow, hsla, point, px};
|
||||||
use smallvec::smallvec;
|
use std::vec;
|
||||||
|
|
||||||
self.style().box_shadow = Some(smallvec
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow_xl(mut self) -> Self {
|
#visibility fn shadow_xl(mut self) -> Self {
|
||||||
use gpui::{BoxShadow, hsla, point, px};
|
use gpui::{BoxShadow, hsla, point, px};
|
||||||
use smallvec::smallvec;
|
use std::vec;
|
||||||
|
|
||||||
self.style().box_shadow = Some(smallvec
|
/// [Docs](https://tailwindcss.com/docs/box-shadow)
|
||||||
#visibility fn shadow_2xl(mut self) -> Self {
|
#visibility fn shadow_2xl(mut self) -> Self {
|
||||||
use gpui::{BoxShadow, hsla, point, px};
|
use gpui::{BoxShadow, hsla, point, px};
|
||||||
use smallvec::smallvec;
|
use std::vec;
|
||||||
|
|
||||||
self.style().box_shadow = Some(smallvec![BoxShadow {
|
self.style().box_shadow = Some(vec![BoxShadow {
|
||||||
color: hsla(0., 0., 0., 0.25),
|
color: hsla(0., 0., 0., 0.25),
|
||||||
offset: point(px(0.), px(25.)),
|
offset: point(px(0.), px(25.)),
|
||||||
blur_radius: px(50.),
|
blur_radius: px(50.),
|
||||||
|
|
28
crates/inspector_ui/Cargo.toml
Normal file
28
crates/inspector_ui/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "inspector_ui"
|
||||||
|
version = "0.1.0"
|
||||||
|
publish.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/inspector_ui.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
command_palette_hooks.workspace = true
|
||||||
|
editor.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
project.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
serde_json_lenient.workspace = true
|
||||||
|
theme.workspace = true
|
||||||
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
workspace-hack.workspace = true
|
||||||
|
zed_actions.workspace = true
|
1
crates/inspector_ui/LICENSE-GPL
Symbolic link
1
crates/inspector_ui/LICENSE-GPL
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-GPL
|
84
crates/inspector_ui/README.md
Normal file
84
crates/inspector_ui/README.md
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# Inspector
|
||||||
|
|
||||||
|
This is a tool for inspecting and manipulating rendered elements in Zed. It is
|
||||||
|
only available in debug builds. Use the `dev::ToggleInspector` action to toggle
|
||||||
|
inspector mode and click on UI elements to inspect them.
|
||||||
|
|
||||||
|
# Current features
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
# Known bugs
|
||||||
|
|
||||||
|
* The style inspector buffer will leak memory over time due to building up
|
||||||
|
history on each change of inspected element. Instead of using `Project` to
|
||||||
|
create it, should just directly build the `Buffer` and `File` each time the inspected element changes.
|
||||||
|
|
||||||
|
# Future features
|
||||||
|
|
||||||
|
* Info and manipulation of element types other than `Div`.
|
||||||
|
|
||||||
|
* Ability to highlight current element after it's been picked.
|
||||||
|
|
||||||
|
* Indicate when the picked element has disappeared.
|
||||||
|
|
||||||
|
* Hierarchy view?
|
||||||
|
|
||||||
|
## Better manipulation than JSON
|
||||||
|
|
||||||
|
The current approach is not easy to move back to the code. Possibilities:
|
||||||
|
|
||||||
|
* Editable list of style attributes to apply.
|
||||||
|
|
||||||
|
* Rust buffer of code that does a very lenient parse to get the style attributes. Some options:
|
||||||
|
|
||||||
|
- Take all the identifier-like tokens and use them if they are the name of an attribute. A custom completion provider in a buffer could be used.
|
||||||
|
|
||||||
|
- Use TreeSitter to parse out the fluent style method chain. With this approach the buffer could even be the actual code file. Tricky part of this is LSP - ideally the LSP already being used by the developer's Zed would be used.
|
||||||
|
|
||||||
|
## Source locations
|
||||||
|
|
||||||
|
* Mode to navigate to source code on every element change while picking.
|
||||||
|
|
||||||
|
* Tracking of more source locations - currently the source location is often in a ui compoenent. Ideally this would have a way for the components to indicate that they are probably not the source location the user is looking for.
|
||||||
|
|
||||||
|
## Persistent modification
|
||||||
|
|
||||||
|
Currently, element modifications disappear when picker mode is started. Handling this well is tricky. Potential features:
|
||||||
|
|
||||||
|
* Support modifying multiple elements at once. This requires a way to specify which elements are modified - possibly wildcards in a match of the `InspectorElementId` path. This might default to ignoring all numeric parts and just matching on the names.
|
||||||
|
|
||||||
|
* Show a list of active modifications in the UI.
|
||||||
|
|
||||||
|
* Support for modifications being partial overrides instead of snapshots. A trickiness here is that multiple modifications may apply to the same element.
|
||||||
|
|
||||||
|
* The code should probably distinguish the data that is provided by the element and the modifications from the inspector. Currently these are conflated in element states.
|
||||||
|
|
||||||
|
# Code cleanups
|
||||||
|
|
||||||
|
## Remove special side pane rendering
|
||||||
|
|
||||||
|
Currently the inspector has special rendering in the UI, but maybe it could just be a workspace item.
|
||||||
|
|
||||||
|
## Pull more inspector logic out of GPUI
|
||||||
|
|
||||||
|
Currently `crates/gpui/inspector.rs` and `crates/inspector_ui/inspector.rs` are quite entangled. It seems cleaner to pull as much logic a possible out of GPUI.
|
||||||
|
|
||||||
|
## Cleaner lifecycle for inspector state viewers / editors
|
||||||
|
|
||||||
|
Currently element state inspectors are just called on render. Ideally instead they would be implementors of some trait like:
|
||||||
|
|
||||||
|
```
|
||||||
|
trait StateInspector: Render {
|
||||||
|
fn new(cx: &mut App) -> Task<Self>;
|
||||||
|
fn element_changed(inspector_id: &InspectorElementId, window: &mut Window, cx: &mut App);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See `div_inspector.rs` - it needs to initialize itself, keep track of its own loading state, and keep track of the last inspected ID in its render function.
|
20
crates/inspector_ui/build.rs
Normal file
20
crates/inspector_ui/build.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
fn main() {
|
||||||
|
let cargo_manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let mut path = std::path::PathBuf::from(&cargo_manifest_dir);
|
||||||
|
|
||||||
|
if path.file_name().as_ref().and_then(|name| name.to_str()) != Some("inspector_ui") {
|
||||||
|
panic!(
|
||||||
|
"expected CARGO_MANIFEST_DIR to end with crates/inspector_ui, but got {cargo_manifest_dir}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
|
||||||
|
if path.file_name().as_ref().and_then(|name| name.to_str()) != Some("crates") {
|
||||||
|
panic!(
|
||||||
|
"expected CARGO_MANIFEST_DIR to end with crates/inspector_ui, but got {cargo_manifest_dir}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=ZED_REPO_DIR={}", path.display());
|
||||||
|
}
|
223
crates/inspector_ui/src/div_inspector.rs
Normal file
223
crates/inspector_ui/src/div_inspector.rs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use editor::{Editor, EditorEvent, EditorMode, MultiBuffer};
|
||||||
|
use gpui::{
|
||||||
|
AsyncWindowContext, DivInspectorState, Entity, InspectorElementId, IntoElement, WeakEntity,
|
||||||
|
Window,
|
||||||
|
};
|
||||||
|
use language::Buffer;
|
||||||
|
use language::language_settings::SoftWrap;
|
||||||
|
use project::{Project, ProjectPath};
|
||||||
|
use std::path::Path;
|
||||||
|
use ui::{Label, LabelSize, Tooltip, prelude::*, v_flex};
|
||||||
|
|
||||||
|
/// Path used for unsaved buffer that contains style json. To support the json language server, this
|
||||||
|
/// matches the name used in the generated schemas.
|
||||||
|
const ZED_INSPECTOR_STYLE_PATH: &str = "/zed-inspector-style.json";
|
||||||
|
|
||||||
|
pub(crate) struct DivInspector {
|
||||||
|
project: Entity<Project>,
|
||||||
|
inspector_id: Option<InspectorElementId>,
|
||||||
|
state: Option<DivInspectorState>,
|
||||||
|
style_buffer: Option<Entity<Buffer>>,
|
||||||
|
style_editor: Option<Entity<Editor>>,
|
||||||
|
last_error: Option<SharedString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivInspector {
|
||||||
|
pub fn new(
|
||||||
|
project: Entity<Project>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> DivInspector {
|
||||||
|
// Open the buffer once, so it can then be used for each editor.
|
||||||
|
cx.spawn_in(window, {
|
||||||
|
let project = project.clone();
|
||||||
|
async move |this, cx| Self::open_style_buffer(project, this, cx).await
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
DivInspector {
|
||||||
|
project,
|
||||||
|
inspector_id: None,
|
||||||
|
state: None,
|
||||||
|
style_buffer: None,
|
||||||
|
style_editor: None,
|
||||||
|
last_error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_style_buffer(
|
||||||
|
project: Entity<Project>,
|
||||||
|
this: WeakEntity<DivInspector>,
|
||||||
|
cx: &mut AsyncWindowContext,
|
||||||
|
) -> Result<()> {
|
||||||
|
let worktree = project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.create_worktree(ZED_INSPECTOR_STYLE_PATH, false, cx)
|
||||||
|
})?
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let project_path = worktree.read_with(cx, |worktree, _cx| ProjectPath {
|
||||||
|
worktree_id: worktree.id(),
|
||||||
|
path: Path::new("").into(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let style_buffer = project
|
||||||
|
.update(cx, |project, cx| project.open_path(project_path, cx))?
|
||||||
|
.await?
|
||||||
|
.1;
|
||||||
|
|
||||||
|
project.update(cx, |project, cx| {
|
||||||
|
project.register_buffer_with_language_servers(&style_buffer, cx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
this.update_in(cx, |this, window, cx| {
|
||||||
|
this.style_buffer = Some(style_buffer);
|
||||||
|
if let Some(id) = this.inspector_id.clone() {
|
||||||
|
let state =
|
||||||
|
window.with_inspector_state(Some(&id), cx, |state, _window| state.clone());
|
||||||
|
if let Some(state) = state {
|
||||||
|
this.update_inspected_element(&id, state, window, cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_inspected_element(
|
||||||
|
&mut self,
|
||||||
|
id: &InspectorElementId,
|
||||||
|
state: DivInspectorState,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
let base_style_json = serde_json::to_string_pretty(&state.base_style);
|
||||||
|
self.state = Some(state);
|
||||||
|
|
||||||
|
if self.inspector_id.as_ref() == Some(id) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.inspector_id = Some(id.clone());
|
||||||
|
}
|
||||||
|
let Some(style_buffer) = self.style_buffer.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_style_json = match base_style_json {
|
||||||
|
Ok(base_style_json) => base_style_json,
|
||||||
|
Err(err) => {
|
||||||
|
self.style_editor = None;
|
||||||
|
self.last_error =
|
||||||
|
Some(format!("Failed to convert base_style to JSON: {err}").into());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.last_error = None;
|
||||||
|
|
||||||
|
style_buffer.update(cx, |style_buffer, cx| {
|
||||||
|
style_buffer.set_text(base_style_json, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
let style_editor = cx.new(|cx| {
|
||||||
|
let multi_buffer = cx.new(|cx| MultiBuffer::singleton(style_buffer, cx));
|
||||||
|
let mut editor = Editor::new(
|
||||||
|
EditorMode::full(),
|
||||||
|
multi_buffer,
|
||||||
|
Some(self.project.clone()),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||||
|
editor.set_show_line_numbers(false, cx);
|
||||||
|
editor.set_show_code_actions(false, cx);
|
||||||
|
editor.set_show_breakpoints(false, cx);
|
||||||
|
editor.set_show_git_diff_gutter(false, cx);
|
||||||
|
editor.set_show_runnables(false, cx);
|
||||||
|
editor.set_show_edit_predictions(Some(false), window, cx);
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.subscribe_in(&style_editor, window, {
|
||||||
|
let id = id.clone();
|
||||||
|
move |this, editor, event: &EditorEvent, window, cx| match event {
|
||||||
|
EditorEvent::BufferEdited => {
|
||||||
|
let base_style_json = editor.read(cx).text(cx);
|
||||||
|
match serde_json_lenient::from_str(&base_style_json) {
|
||||||
|
Ok(new_base_style) => {
|
||||||
|
window.with_inspector_state::<DivInspectorState, _>(
|
||||||
|
Some(&id),
|
||||||
|
cx,
|
||||||
|
|state, _window| {
|
||||||
|
if let Some(state) = state.as_mut() {
|
||||||
|
*state.base_style = new_base_style;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
window.refresh();
|
||||||
|
this.last_error = None;
|
||||||
|
}
|
||||||
|
Err(err) => this.last_error = Some(err.to_string().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
self.style_editor = Some(style_editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for DivInspector {
|
||||||
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
v_flex()
|
||||||
|
.size_full()
|
||||||
|
.gap_2()
|
||||||
|
.when_some(self.state.as_ref(), |this, state| {
|
||||||
|
this.child(
|
||||||
|
v_flex()
|
||||||
|
.child(Label::new("Layout").size(LabelSize::Large))
|
||||||
|
.child(render_layout_state(state, cx)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when_some(self.style_editor.as_ref(), |this, style_editor| {
|
||||||
|
this.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(Label::new("Style").size(LabelSize::Large))
|
||||||
|
.child(div().h_128().child(style_editor.clone()))
|
||||||
|
.when_some(self.last_error.as_ref(), |this, last_error| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.border_1()
|
||||||
|
.border_color(Color::Error.color(cx))
|
||||||
|
.child(Label::new(last_error)),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when_none(&self.style_editor, |this| {
|
||||||
|
this.child(Label::new("Loading..."))
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_layout_state(state: &DivInspectorState, cx: &App) -> Div {
|
||||||
|
v_flex()
|
||||||
|
.child(div().text_ui(cx).child(format!("Bounds: {}", state.bounds)))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id("content-size")
|
||||||
|
.text_ui(cx)
|
||||||
|
.tooltip(Tooltip::text("Size of the element's children"))
|
||||||
|
.child(if state.content_size != state.bounds.size {
|
||||||
|
format!("Content size: {}", state.content_size)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
168
crates/inspector_ui/src/inspector.rs
Normal file
168
crates/inspector_ui/src/inspector.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use anyhow::{Context as _, anyhow};
|
||||||
|
use gpui::{App, DivInspectorState, Inspector, InspectorElementId, IntoElement, Window};
|
||||||
|
use std::{cell::OnceCell, path::Path, sync::Arc};
|
||||||
|
use ui::{Label, Tooltip, prelude::*};
|
||||||
|
use util::{ResultExt as _, command::new_smol_command};
|
||||||
|
use workspace::AppState;
|
||||||
|
|
||||||
|
use crate::div_inspector::DivInspector;
|
||||||
|
|
||||||
|
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
|
||||||
|
cx.on_action(|_: &zed_actions::dev::ToggleInspector, cx| {
|
||||||
|
let Some(active_window) = cx
|
||||||
|
.active_window()
|
||||||
|
.context("no active window to toggle inspector")
|
||||||
|
.log_err()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// This is deferred to avoid double lease due to window already being updated.
|
||||||
|
cx.defer(move |cx| {
|
||||||
|
active_window
|
||||||
|
.update(cx, |_, window, cx| window.toggle_inspector(cx))
|
||||||
|
.log_err();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Project used for editor buffers + LSP support
|
||||||
|
let project = project::Project::local(
|
||||||
|
app_state.client.clone(),
|
||||||
|
app_state.node_runtime.clone(),
|
||||||
|
app_state.user_store.clone(),
|
||||||
|
app_state.languages.clone(),
|
||||||
|
app_state.fs.clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
let div_inspector = OnceCell::new();
|
||||||
|
cx.register_inspector_element(move |id, state: &DivInspectorState, window, cx| {
|
||||||
|
let div_inspector = div_inspector
|
||||||
|
.get_or_init(|| cx.new(|cx| DivInspector::new(project.clone(), window, cx)));
|
||||||
|
div_inspector.update(cx, |div_inspector, cx| {
|
||||||
|
div_inspector.update_inspected_element(&id, state.clone(), window, cx);
|
||||||
|
div_inspector.render(window, cx).into_any_element()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.set_inspector_renderer(Box::new(render_inspector));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_inspector(
|
||||||
|
inspector: &mut Inspector,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Inspector>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let ui_font = theme::setup_ui_font(window, cx);
|
||||||
|
let colors = cx.theme().colors();
|
||||||
|
let inspector_id = inspector.active_element_id();
|
||||||
|
v_flex()
|
||||||
|
.id("gpui-inspector")
|
||||||
|
.size_full()
|
||||||
|
.bg(colors.panel_background)
|
||||||
|
.text_color(colors.text)
|
||||||
|
.font(ui_font)
|
||||||
|
.border_l_1()
|
||||||
|
.border_color(colors.border)
|
||||||
|
.overflow_y_scroll()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.p_2()
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(colors.border_variant)
|
||||||
|
.child(
|
||||||
|
IconButton::new("pick-mode", IconName::MagnifyingGlass)
|
||||||
|
.tooltip(Tooltip::text("Start inspector pick mode"))
|
||||||
|
.selected_icon_color(Color::Selected)
|
||||||
|
.toggle_state(inspector.is_picking())
|
||||||
|
.on_click(cx.listener(|inspector, _, window, _cx| {
|
||||||
|
inspector.start_picking();
|
||||||
|
window.refresh();
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.justify_end()
|
||||||
|
.child(Label::new("GPUI Inspector").size(LabelSize::Large)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.p_2()
|
||||||
|
.gap_2()
|
||||||
|
.when_some(inspector_id, |this, inspector_id| {
|
||||||
|
this.child(render_inspector_id(inspector_id, cx))
|
||||||
|
})
|
||||||
|
.children(inspector.render_inspector_states(window, cx)),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_inspector_id(inspector_id: &InspectorElementId, cx: &App) -> Div {
|
||||||
|
let source_location = inspector_id.path.source_location;
|
||||||
|
v_flex()
|
||||||
|
.child(Label::new("Element ID").size(LabelSize::Large))
|
||||||
|
.when(inspector_id.instance_id != 0, |this| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.id("instance-id")
|
||||||
|
.text_ui(cx)
|
||||||
|
.tooltip(Tooltip::text(
|
||||||
|
"Disambiguates elements from the same source location",
|
||||||
|
))
|
||||||
|
.child(format!("Instance {}", inspector_id.instance_id)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id("source-location")
|
||||||
|
.text_ui(cx)
|
||||||
|
.bg(cx.theme().colors().editor_foreground.opacity(0.025))
|
||||||
|
.underline()
|
||||||
|
.child(format!("{}", source_location))
|
||||||
|
.tooltip(Tooltip::text("Click to open by running zed cli"))
|
||||||
|
.on_click(move |_, _window, cx| {
|
||||||
|
cx.background_spawn(open_zed_source_location(source_location))
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id("global-id")
|
||||||
|
.text_ui(cx)
|
||||||
|
.min_h_12()
|
||||||
|
.tooltip(Tooltip::text(
|
||||||
|
"GlobalElementId of the nearest ancestor with an ID",
|
||||||
|
))
|
||||||
|
.child(inspector_id.path.global_id.to_string()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_zed_source_location(
|
||||||
|
location: &'static std::panic::Location<'static>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut path = Path::new(env!("ZED_REPO_DIR")).to_path_buf();
|
||||||
|
path.push(Path::new(location.file()));
|
||||||
|
let path_arg = format!(
|
||||||
|
"{}:{}:{}",
|
||||||
|
path.display(),
|
||||||
|
location.line(),
|
||||||
|
location.column()
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = new_smol_command("zed")
|
||||||
|
.arg(&path_arg)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("running zed to open {path_arg} failed"))?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
Err(anyhow!(
|
||||||
|
"running zed to open {path_arg} failed with stderr: {}",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
24
crates/inspector_ui/src/inspector_ui.rs
Normal file
24
crates/inspector_ui/src/inspector_ui.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mod div_inspector;
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mod inspector;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub use inspector::init;
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub fn init(_app_state: std::sync::Arc<workspace::AppState>, cx: &mut gpui::App) {
|
||||||
|
use std::any::TypeId;
|
||||||
|
use workspace::notifications::NotifyResultExt as _;
|
||||||
|
|
||||||
|
cx.on_action(|_: &zed_actions::dev::ToggleInspector, cx| {
|
||||||
|
Err::<(), anyhow::Error>(anyhow::anyhow!(
|
||||||
|
"dev::ToggleInspector is only available in debug builds"
|
||||||
|
))
|
||||||
|
.notify_app_err(cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||||
|
filter.hide_action_types(&[TypeId::of::<zed_actions::dev::ToggleInspector>()]);
|
||||||
|
});
|
||||||
|
}
|
|
@ -59,8 +59,10 @@ project.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
rope.workspace = true
|
rope.workspace = true
|
||||||
rust-embed.workspace = true
|
rust-embed.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
serde_json_lenient.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
snippet_provider.workspace = true
|
snippet_provider.workspace = true
|
||||||
|
|
|
@ -97,6 +97,65 @@ impl JsonLspAdapter {
|
||||||
let tsconfig_schema = serde_json::Value::from_str(TSCONFIG_SCHEMA).unwrap();
|
let tsconfig_schema = serde_json::Value::from_str(TSCONFIG_SCHEMA).unwrap();
|
||||||
let package_json_schema = serde_json::Value::from_str(PACKAGE_JSON_SCHEMA).unwrap();
|
let package_json_schema = serde_json::Value::from_str(PACKAGE_JSON_SCHEMA).unwrap();
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut schemas = serde_json::json!([
|
||||||
|
{
|
||||||
|
"fileMatch": ["tsconfig.json"],
|
||||||
|
"schema":tsconfig_schema
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": ["package.json"],
|
||||||
|
"schema":package_json_schema
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
schema_file_match(paths::settings_file()),
|
||||||
|
paths::local_settings_file_relative_path()
|
||||||
|
],
|
||||||
|
"schema": settings_schema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [schema_file_match(paths::keymap_file())],
|
||||||
|
"schema": keymap_schema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
schema_file_match(paths::tasks_file()),
|
||||||
|
paths::local_tasks_file_relative_path()
|
||||||
|
],
|
||||||
|
"schema": tasks_schema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
schema_file_match(
|
||||||
|
paths::snippets_dir()
|
||||||
|
.join("*.json")
|
||||||
|
.as_path()
|
||||||
|
)
|
||||||
|
],
|
||||||
|
"schema": snippets_schema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
schema_file_match(paths::debug_scenarios_file()),
|
||||||
|
paths::local_debug_file_relative_path()
|
||||||
|
],
|
||||||
|
"schema": debug_schema,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
schemas.as_array_mut().unwrap().push(serde_json::json!(
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
"zed-inspector-style.json"
|
||||||
|
],
|
||||||
|
"schema": generate_inspector_style_schema(),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// This can be viewed via `dev: open language server logs` -> `json-language-server` ->
|
// This can be viewed via `dev: open language server logs` -> `json-language-server` ->
|
||||||
// `Server Info`
|
// `Server Info`
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
|
@ -108,52 +167,7 @@ impl JsonLspAdapter {
|
||||||
{
|
{
|
||||||
"enable": true,
|
"enable": true,
|
||||||
},
|
},
|
||||||
"schemas": [
|
"schemas": schemas
|
||||||
{
|
|
||||||
"fileMatch": ["tsconfig.json"],
|
|
||||||
"schema":tsconfig_schema
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": ["package.json"],
|
|
||||||
"schema":package_json_schema
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
schema_file_match(paths::settings_file()),
|
|
||||||
paths::local_settings_file_relative_path()
|
|
||||||
],
|
|
||||||
"schema": settings_schema,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [schema_file_match(paths::keymap_file())],
|
|
||||||
"schema": keymap_schema,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
schema_file_match(paths::tasks_file()),
|
|
||||||
paths::local_tasks_file_relative_path()
|
|
||||||
],
|
|
||||||
"schema": tasks_schema,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
schema_file_match(
|
|
||||||
paths::snippets_dir()
|
|
||||||
.join("*.json")
|
|
||||||
.as_path()
|
|
||||||
)
|
|
||||||
],
|
|
||||||
"schema": snippets_schema,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": [
|
|
||||||
schema_file_match(paths::debug_scenarios_file()),
|
|
||||||
paths::local_debug_file_relative_path()
|
|
||||||
],
|
|
||||||
"schema": debug_schema,
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -180,6 +194,16 @@ impl JsonLspAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
fn generate_inspector_style_schema() -> serde_json_lenient::Value {
|
||||||
|
let schema = schemars::r#gen::SchemaSettings::draft07()
|
||||||
|
.with(|settings| settings.option_add_null_type = false)
|
||||||
|
.into_generator()
|
||||||
|
.into_root_schema_for::<gpui::StyleRefinement>();
|
||||||
|
|
||||||
|
serde_json_lenient::to_value(schema).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl LspAdapter for JsonLspAdapter {
|
impl LspAdapter for JsonLspAdapter {
|
||||||
fn name(&self) -> LanguageServerName {
|
fn name(&self) -> LanguageServerName {
|
||||||
|
|
|
@ -715,9 +715,14 @@ impl Element for MarkdownElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -1189,6 +1194,7 @@ impl Element for MarkdownElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
rendered_markdown: &mut Self::RequestLayoutState,
|
rendered_markdown: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -1206,6 +1212,7 @@ impl Element for MarkdownElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
rendered_markdown: &mut Self::RequestLayoutState,
|
rendered_markdown: &mut Self::RequestLayoutState,
|
||||||
hitbox: &mut Self::PrepaintState,
|
hitbox: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -66,8 +66,8 @@ use image_store::{ImageItemEvent, ImageStoreEvent};
|
||||||
|
|
||||||
use ::git::{blame::Blame, status::FileStatus};
|
use ::git::{blame::Blame, status::FileStatus};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyEntity, App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla,
|
App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString,
|
||||||
SharedString, Task, WeakEntity, Window,
|
Task, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -2322,7 +2322,7 @@ impl Project {
|
||||||
&mut self,
|
&mut self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<(Option<ProjectEntryId>, AnyEntity)>> {
|
) -> Task<Result<(Option<ProjectEntryId>, Entity<Buffer>)>> {
|
||||||
let task = self.open_buffer(path.clone(), cx);
|
let task = self.open_buffer(path.clone(), cx);
|
||||||
cx.spawn(async move |_project, cx| {
|
cx.spawn(async move |_project, cx| {
|
||||||
let buffer = task.await?;
|
let buffer = task.await?;
|
||||||
|
@ -2330,8 +2330,7 @@ impl Project {
|
||||||
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
|
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let buffer: &AnyEntity = &buffer;
|
Ok((project_entry_id, buffer))
|
||||||
Ok((project_entry_id, buffer.clone()))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
let refineable_attr = attrs.iter().find(|attr| attr.path().is_ident("refineable"));
|
let refineable_attr = attrs.iter().find(|attr| attr.path().is_ident("refineable"));
|
||||||
|
|
||||||
let mut impl_debug_on_refinement = false;
|
let mut impl_debug_on_refinement = false;
|
||||||
|
let mut derives_serialize = false;
|
||||||
let mut refinement_traits_to_derive = vec![];
|
let mut refinement_traits_to_derive = vec![];
|
||||||
|
|
||||||
if let Some(refineable_attr) = refineable_attr {
|
if let Some(refineable_attr) = refineable_attr {
|
||||||
|
@ -26,6 +27,9 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
if meta.path.is_ident("Debug") {
|
if meta.path.is_ident("Debug") {
|
||||||
impl_debug_on_refinement = true;
|
impl_debug_on_refinement = true;
|
||||||
} else {
|
} else {
|
||||||
|
if meta.path.is_ident("Serialize") {
|
||||||
|
derives_serialize = true;
|
||||||
|
}
|
||||||
refinement_traits_to_derive.push(meta.path);
|
refinement_traits_to_derive.push(meta.path);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -47,6 +51,21 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
let field_visibilities: Vec<_> = fields.iter().map(|f| &f.vis).collect();
|
let field_visibilities: Vec<_> = fields.iter().map(|f| &f.vis).collect();
|
||||||
let wrapped_types: Vec<_> = fields.iter().map(|f| get_wrapper_type(f, &f.ty)).collect();
|
let wrapped_types: Vec<_> = fields.iter().map(|f| get_wrapper_type(f, &f.ty)).collect();
|
||||||
|
|
||||||
|
let field_attributes: Vec<TokenStream2> = fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
if derives_serialize {
|
||||||
|
if is_refineable_field(f) {
|
||||||
|
quote! { #[serde(default, skip_serializing_if = "::refineable::IsEmpty::is_empty")] }
|
||||||
|
} else {
|
||||||
|
quote! { #[serde(skip_serializing_if = "::std::option::Option::is_none")] }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Create trait bound that each wrapped type must implement Clone // & Default
|
// Create trait bound that each wrapped type must implement Clone // & Default
|
||||||
let type_param_bounds: Vec<_> = wrapped_types
|
let type_param_bounds: Vec<_> = wrapped_types
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -234,6 +253,26 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
quote! {}
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let refinement_is_empty_conditions: Vec<TokenStream2> = fields
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, field)| {
|
||||||
|
let name = &field.ident;
|
||||||
|
|
||||||
|
let condition = if is_refineable_field(field) {
|
||||||
|
quote! { self.#name.is_empty() }
|
||||||
|
} else {
|
||||||
|
quote! { self.#name.is_none() }
|
||||||
|
};
|
||||||
|
|
||||||
|
if i < fields.len() - 1 {
|
||||||
|
quote! { #condition && }
|
||||||
|
} else {
|
||||||
|
condition
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut derive_stream = quote! {};
|
let mut derive_stream = quote! {};
|
||||||
for trait_to_derive in refinement_traits_to_derive {
|
for trait_to_derive in refinement_traits_to_derive {
|
||||||
derive_stream.extend(quote! { #[derive(#trait_to_derive)] })
|
derive_stream.extend(quote! { #[derive(#trait_to_derive)] })
|
||||||
|
@ -246,6 +285,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
pub struct #refinement_ident #impl_generics {
|
pub struct #refinement_ident #impl_generics {
|
||||||
#(
|
#(
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
#field_attributes
|
||||||
#field_visibilities #field_names: #wrapped_types
|
#field_visibilities #field_names: #wrapped_types
|
||||||
),*
|
),*
|
||||||
}
|
}
|
||||||
|
@ -280,6 +320,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl #impl_generics ::refineable::IsEmpty for #refinement_ident #ty_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
#( #refinement_is_empty_conditions )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl #impl_generics From<#refinement_ident #ty_generics> for #ident #ty_generics
|
impl #impl_generics From<#refinement_ident #ty_generics> for #ident #ty_generics
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
pub use derive_refineable::Refineable;
|
pub use derive_refineable::Refineable;
|
||||||
|
|
||||||
pub trait Refineable: Clone {
|
pub trait Refineable: Clone {
|
||||||
type Refinement: Refineable<Refinement = Self::Refinement> + Default;
|
type Refinement: Refineable<Refinement = Self::Refinement> + IsEmpty + Default;
|
||||||
|
|
||||||
fn refine(&mut self, refinement: &Self::Refinement);
|
fn refine(&mut self, refinement: &Self::Refinement);
|
||||||
fn refined(self, refinement: Self::Refinement) -> Self;
|
fn refined(self, refinement: Self::Refinement) -> Self;
|
||||||
|
@ -13,6 +13,11 @@ pub trait Refineable: Clone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait IsEmpty {
|
||||||
|
/// When `true`, indicates that use applying this refinement does nothing.
|
||||||
|
fn is_empty(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Cascade<S: Refineable>(Vec<Option<S::Refinement>>);
|
pub struct Cascade<S: Refineable>(Vec<Option<S::Refinement>>);
|
||||||
|
|
||||||
impl<S: Refineable + Default> Default for Cascade<S> {
|
impl<S: Refineable + Default> Default for Cascade<S> {
|
||||||
|
|
|
@ -581,9 +581,14 @@ impl Element for TerminalElement {
|
||||||
self.interactivity.element_id.clone()
|
self.interactivity.element_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -597,21 +602,26 @@ impl Element for TerminalElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout_id =
|
let layout_id = self.interactivity.request_layout(
|
||||||
self.interactivity
|
global_id,
|
||||||
.request_layout(global_id, window, cx, |mut style, window, cx| {
|
inspector_id,
|
||||||
style.size.width = relative(1.).into();
|
window,
|
||||||
style.size.height = relative(1.).into();
|
cx,
|
||||||
// style.overflow = point(Overflow::Hidden, Overflow::Hidden);
|
|mut style, window, cx| {
|
||||||
|
style.size.width = relative(1.).into();
|
||||||
|
style.size.height = relative(1.).into();
|
||||||
|
// style.overflow = point(Overflow::Hidden, Overflow::Hidden);
|
||||||
|
|
||||||
window.request_layout(style, None, cx)
|
window.request_layout(style, None, cx)
|
||||||
});
|
},
|
||||||
|
);
|
||||||
(layout_id, ())
|
(layout_id, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -620,6 +630,7 @@ impl Element for TerminalElement {
|
||||||
let rem_size = self.rem_size(cx);
|
let rem_size = self.rem_size(cx);
|
||||||
self.interactivity.prepaint(
|
self.interactivity.prepaint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
bounds.size,
|
bounds.size,
|
||||||
window,
|
window,
|
||||||
|
@ -904,6 +915,7 @@ impl Element for TerminalElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::PrepaintState,
|
layout: &mut Self::PrepaintState,
|
||||||
|
@ -947,6 +959,7 @@ impl Element for TerminalElement {
|
||||||
let block_below_cursor_element = layout.block_below_cursor_element.take();
|
let block_below_cursor_element = layout.block_below_cursor_element.take();
|
||||||
self.interactivity.paint(
|
self.interactivity.paint(
|
||||||
global_id,
|
global_id,
|
||||||
|
inspector_id,
|
||||||
bounds,
|
bounds,
|
||||||
Some(&layout.hitbox),
|
Some(&layout.hitbox),
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl RenderOnce for SplitButton {
|
||||||
)
|
)
|
||||||
.child(self.right)
|
.child(self.right)
|
||||||
.bg(ElevationIndex::Surface.on_elevation_bg(cx))
|
.bg(ElevationIndex::Surface.on_elevation_bg(cx))
|
||||||
.shadow(smallvec::smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: hsla(0.0, 0.0, 0.0, 0.16),
|
color: hsla(0.0, 0.0, 0.0, 0.16),
|
||||||
offset: point(px(0.), px(1.)),
|
offset: point(px(0.), px(1.)),
|
||||||
blur_radius: px(0.),
|
blur_radius: px(0.),
|
||||||
|
|
|
@ -227,9 +227,14 @@ mod uniform_list {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -239,6 +244,7 @@ mod uniform_list {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -264,6 +270,7 @@ mod uniform_list {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::KeyBinding;
|
use crate::KeyBinding;
|
||||||
use crate::{h_flex, prelude::*};
|
use crate::{h_flex, prelude::*};
|
||||||
use gpui::{AnyElement, App, BoxShadow, FontStyle, Hsla, IntoElement, Window, point};
|
use gpui::{AnyElement, App, BoxShadow, FontStyle, Hsla, IntoElement, Window, point};
|
||||||
use smallvec::smallvec;
|
|
||||||
use theme::Appearance;
|
use theme::Appearance;
|
||||||
|
|
||||||
/// Represents a hint for a keybinding, optionally with a prefix and suffix.
|
/// Represents a hint for a keybinding, optionally with a prefix and suffix.
|
||||||
|
@ -193,7 +192,7 @@ impl RenderOnce for KeybindingHint {
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.bg(bg_color)
|
.bg(bg_color)
|
||||||
.shadow(smallvec![BoxShadow {
|
.shadow(vec![BoxShadow {
|
||||||
color: shadow_color,
|
color: shadow_color,
|
||||||
offset: point(px(0.), px(1.)),
|
offset: point(px(0.), px(1.)),
|
||||||
blur_radius: px(0.),
|
blur_radius: px(0.),
|
||||||
|
|
|
@ -316,9 +316,14 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
Some(self.id.clone())
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -394,6 +399,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: Bounds<Pixels>,
|
_bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -422,6 +428,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_: Bounds<gpui::Pixels>,
|
_: Bounds<gpui::Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
child_hitbox: &mut Option<HitboxId>,
|
child_hitbox: &mut Option<HitboxId>,
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl RenderOnce for ProgressBar {
|
||||||
.py(px(2.0))
|
.py(px(2.0))
|
||||||
.px(px(4.0))
|
.px(px(4.0))
|
||||||
.bg(self.bg_color)
|
.bg(self.bg_color)
|
||||||
.shadow(smallvec::smallvec![gpui::BoxShadow {
|
.shadow(vec![gpui::BoxShadow {
|
||||||
color: gpui::black().opacity(0.08),
|
color: gpui::black().opacity(0.08),
|
||||||
offset: point(px(0.), px(1.)),
|
offset: point(px(0.), px(1.)),
|
||||||
blur_radius: px(0.),
|
blur_radius: px(0.),
|
||||||
|
|
|
@ -116,9 +116,14 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
Some(self.id.clone())
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -174,6 +179,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -200,6 +206,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: Bounds<gpui::Pixels>,
|
_bounds: Bounds<gpui::Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint_state: &mut Self::PrepaintState,
|
prepaint_state: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -162,16 +162,20 @@ impl Scrollbar {
|
||||||
|
|
||||||
impl Element for Scrollbar {
|
impl Element for Scrollbar {
|
||||||
type RequestLayoutState = ();
|
type RequestLayoutState = ();
|
||||||
|
|
||||||
type PrepaintState = Hitbox;
|
type PrepaintState = Hitbox;
|
||||||
|
|
||||||
fn id(&self) -> Option<ElementId> {
|
fn id(&self) -> Option<ElementId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -193,6 +197,7 @@ impl Element for Scrollbar {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -206,6 +211,7 @@ impl Element for Scrollbar {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use gpui::{App, BoxShadow, Hsla, hsla, point, px};
|
use gpui::{App, BoxShadow, Hsla, hsla, point, px};
|
||||||
use smallvec::{SmallVec, smallvec};
|
|
||||||
use theme::{ActiveTheme, Appearance};
|
use theme::{ActiveTheme, Appearance};
|
||||||
|
|
||||||
/// Today, elevation is primarily used to add shadows to elements, and set the correct background for elements like buttons.
|
/// 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 {
|
impl ElevationIndex {
|
||||||
/// Returns an appropriate shadow for the given elevation index.
|
/// 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;
|
let is_light = cx.theme().appearance() == Appearance::Light;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
ElevationIndex::Surface => smallvec![],
|
ElevationIndex::Surface => vec![],
|
||||||
ElevationIndex::EditorSurface => smallvec![],
|
ElevationIndex::EditorSurface => vec![],
|
||||||
|
|
||||||
ElevationIndex::ElevatedSurface => smallvec![
|
ElevationIndex::ElevatedSurface => vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
color: hsla(0., 0., 0., 0.12),
|
color: hsla(0., 0., 0., 0.12),
|
||||||
offset: point(px(0.), px(2.)),
|
offset: point(px(0.), px(2.)),
|
||||||
|
@ -59,10 +58,10 @@ impl ElevationIndex {
|
||||||
offset: point(px(1.), px(1.)),
|
offset: point(px(1.), px(1.)),
|
||||||
blur_radius: px(0.),
|
blur_radius: px(0.),
|
||||||
spread_radius: px(0.),
|
spread_radius: px(0.),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
ElevationIndex::ModalSurface => smallvec![
|
ElevationIndex::ModalSurface => vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.12 }),
|
color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.12 }),
|
||||||
offset: point(px(0.), px(2.)),
|
offset: point(px(0.), px(2.)),
|
||||||
|
@ -89,7 +88,7 @@ impl ElevationIndex {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
_ => smallvec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,33 +50,41 @@ impl Element for WithRemSize {
|
||||||
Element::id(&self.div)
|
Element::id(&self.div)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
Element::source_location(&self.div)
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (LayoutId, Self::RequestLayoutState) {
|
) -> (LayoutId, Self::RequestLayoutState) {
|
||||||
window.with_rem_size(Some(self.rem_size), |window| {
|
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(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
window.with_rem_size(Some(self.rem_size), |window| {
|
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(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: Option<&GlobalElementId>,
|
id: Option<&GlobalElementId>,
|
||||||
|
inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
request_layout: &mut Self::RequestLayoutState,
|
request_layout: &mut Self::RequestLayoutState,
|
||||||
prepaint: &mut Self::PrepaintState,
|
prepaint: &mut Self::PrepaintState,
|
||||||
|
@ -84,8 +92,15 @@ impl Element for WithRemSize {
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
window.with_rem_size(Some(self.rem_size), |window| {
|
window.with_rem_size(Some(self.rem_size), |window| {
|
||||||
self.div
|
self.div.paint(
|
||||||
.paint(id, bounds, request_layout, prepaint, window, cx)
|
id,
|
||||||
|
inspector_id,
|
||||||
|
bounds,
|
||||||
|
request_layout,
|
||||||
|
prepaint,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1113,9 +1113,14 @@ mod element {
|
||||||
Some(self.basis.into())
|
Some(self.basis.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_global_id: Option<&GlobalElementId>,
|
_global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -1132,6 +1137,7 @@ mod element {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_id: Option<&GlobalElementId>,
|
global_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
_state: &mut Self::RequestLayoutState,
|
_state: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -1224,6 +1230,7 @@ mod element {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&GlobalElementId>,
|
_id: Option<&GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||||
_: &mut Self::RequestLayoutState,
|
_: &mut Self::RequestLayoutState,
|
||||||
layout: &mut Self::PrepaintState,
|
layout: &mut Self::PrepaintState,
|
||||||
|
|
|
@ -7277,7 +7277,7 @@ pub fn client_side_decorations(
|
||||||
.when(!tiling.left, |div| div.border_l(BORDER_SIZE))
|
.when(!tiling.left, |div| div.border_l(BORDER_SIZE))
|
||||||
.when(!tiling.right, |div| div.border_r(BORDER_SIZE))
|
.when(!tiling.right, |div| div.border_r(BORDER_SIZE))
|
||||||
.when(!tiling.is_tiled(), |div| {
|
.when(!tiling.is_tiled(), |div| {
|
||||||
div.shadow(smallvec::smallvec![gpui::BoxShadow {
|
div.shadow(vec![gpui::BoxShadow {
|
||||||
color: Hsla {
|
color: Hsla {
|
||||||
h: 0.,
|
h: 0.,
|
||||||
s: 0.,
|
s: 0.,
|
||||||
|
|
|
@ -67,6 +67,7 @@ http_client.workspace = true
|
||||||
image_viewer.workspace = true
|
image_viewer.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
inline_completion_button.workspace = true
|
inline_completion_button.workspace = true
|
||||||
|
inspector_ui.workspace = true
|
||||||
install_cli.workspace = true
|
install_cli.workspace = true
|
||||||
jj_ui.workspace = true
|
jj_ui.workspace = true
|
||||||
journal.workspace = true
|
journal.workspace = true
|
||||||
|
|
|
@ -574,6 +574,7 @@ fn main() {
|
||||||
settings_ui::init(cx);
|
settings_ui::init(cx);
|
||||||
extensions_ui::init(cx);
|
extensions_ui::init(cx);
|
||||||
zeta::init(cx);
|
zeta::init(cx);
|
||||||
|
inspector_ui::init(app_state.clone(), cx);
|
||||||
|
|
||||||
cx.observe_global::<SettingsStore>({
|
cx.observe_global::<SettingsStore>({
|
||||||
let fs = fs.clone();
|
let fs = fs.clone();
|
||||||
|
|
|
@ -111,6 +111,12 @@ impl_actions!(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub mod dev {
|
||||||
|
use gpui::actions;
|
||||||
|
|
||||||
|
actions!(dev, [ToggleInspector]);
|
||||||
|
}
|
||||||
|
|
||||||
pub mod workspace {
|
pub mod workspace {
|
||||||
use gpui::action_with_deprecated_aliases;
|
use gpui::action_with_deprecated_aliases;
|
||||||
|
|
||||||
|
|
|
@ -105,9 +105,14 @@ impl Element for CompletionDiffElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||||
|
@ -117,6 +122,7 @@ impl Element for CompletionDiffElement {
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: gpui::Bounds<Pixels>,
|
_bounds: gpui::Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -128,6 +134,7 @@ impl Element for CompletionDiffElement {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_id: Option<&gpui::GlobalElementId>,
|
_id: Option<&gpui::GlobalElementId>,
|
||||||
|
_inspector_id: Option<&gpui::InspectorElementId>,
|
||||||
_bounds: gpui::Bounds<Pixels>,
|
_bounds: gpui::Bounds<Pixels>,
|
||||||
_request_layout: &mut Self::RequestLayoutState,
|
_request_layout: &mut Self::RequestLayoutState,
|
||||||
_prepaint: &mut Self::PrepaintState,
|
_prepaint: &mut Self::PrepaintState,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue