Detailed changes
@@ -2085,6 +2085,15 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "derive_refineable"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "dhat"
version = "0.3.2"
@@ -3067,6 +3076,7 @@ dependencies = [
"png",
"postage",
"rand 0.8.5",
+ "refineable",
"resvg",
"schemars",
"seahash",
@@ -3078,6 +3088,7 @@ dependencies = [
"smol",
"sqlez",
"sum_tree",
+ "taffy",
"time 0.3.24",
"tiny-skia",
"usvg",
@@ -3090,11 +3101,18 @@ dependencies = [
name = "gpui_macros"
version = "0.1.0"
dependencies = [
+ "lazy_static",
"proc-macro2",
"quote",
"syn 1.0.109",
]
+[[package]]
+name = "grid"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
+
[[package]]
name = "h2"
version = "0.3.20"
@@ -5087,6 +5105,33 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+[[package]]
+name = "playground"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "gpui",
+ "log",
+ "parking_lot 0.11.2",
+ "playground_macros",
+ "refineable",
+ "serde",
+ "simplelog",
+ "smallvec",
+ "taffy",
+ "util",
+]
+
+[[package]]
+name = "playground_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "plist"
version = "1.5.0"
@@ -5764,6 +5809,16 @@ dependencies = [
"thiserror",
]
+[[package]]
+name = "refineable"
+version = "0.1.0"
+dependencies = [
+ "derive_refineable",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "regalloc2"
version = "0.2.3"
@@ -6929,6 +6984,15 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
[[package]]
name = "sluice"
version = "0.5.5"
@@ -7368,6 +7432,17 @@ dependencies = [
"winx",
]
+[[package]]
+name = "taffy"
+version = "0.3.11"
+source = "git+https://github.com/DioxusLabs/taffy?rev=dab541d6104d58e2e10ce90c4a1dad0b703160cd#dab541d6104d58e2e10ce90c4a1dad0b703160cd"
+dependencies = [
+ "arrayvec 0.7.4",
+ "grid",
+ "num-traits",
+ "slotmap",
+]
+
[[package]]
name = "take-until"
version = "0.2.0"
@@ -17,6 +17,8 @@ members = [
"crates/copilot",
"crates/copilot_button",
"crates/db",
+ "crates/refineable",
+ "crates/refineable/derive_refineable",
"crates/diagnostics",
"crates/drag_and_drop",
"crates/editor",
@@ -28,6 +30,8 @@ members = [
"crates/git",
"crates/go_to_line",
"crates/gpui",
+ "crates/gpui/playground",
+ "crates/gpui/playground_macros",
"crates/gpui_macros",
"crates/install_cli",
"crates/journal",
@@ -92,6 +96,7 @@ ordered-float = { version = "2.1.1" }
parking_lot = { version = "0.11.1" }
postage = { version = "0.5", features = ["futures-traits"] }
rand = { version = "0.8.5" }
+refineable = { path = "./crates/refineable" }
regex = { version = "1.5" }
rust-embed = { version = "6.3", features = ["include-exclude"] }
schemars = { version = "0.8" }
@@ -1096,7 +1096,7 @@ impl CollabTitlebarItem {
style
}
- fn render_face<V: View>(
+ fn render_face<V: 'static>(
avatar: Arc<ImageData>,
avatar_style: AvatarStyle,
background_color: Color,
@@ -2,14 +2,14 @@ use client::User;
use gpui::{
elements::*,
platform::{CursorStyle, MouseButton},
- AnyElement, Element, View, ViewContext,
+ AnyElement, Element, ViewContext,
};
use std::sync::Arc;
enum Dismiss {}
enum Button {}
-pub fn render_user_notification<F, V>(
+pub fn render_user_notification<F, V: 'static>(
user: Arc<User>,
title: &'static str,
body: Option<&'static str>,
@@ -19,7 +19,6 @@ pub fn render_user_notification<F, V>(
) -> AnyElement<V>
where
F: 'static + Fn(&mut V, &mut ViewContext<V>),
- V: View,
{
let theme = theme::current(cx).clone();
let theme = &theme.contact_notification;
@@ -538,7 +538,7 @@ impl ProjectDiagnosticsEditor {
}
impl Item for ProjectDiagnosticsEditor {
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
_detail: Option<usize>,
style: &theme::Tab,
@@ -735,7 +735,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
})
}
-pub(crate) fn render_summary<T: View>(
+pub(crate) fn render_summary<T: 'static>(
summary: &DiagnosticSummary,
text_style: &TextStyle,
theme: &theme::ProjectDiagnostics,
@@ -11,7 +11,7 @@ use gpui::{
const DEAD_ZONE: f32 = 4.;
-enum State<V: View> {
+enum State<V> {
Down {
region_offset: Vector2F,
region: RectF,
@@ -31,7 +31,7 @@ enum State<V: View> {
Canceled,
}
-impl<V: View> Clone for State<V> {
+impl<V> Clone for State<V> {
fn clone(&self) -> Self {
match self {
&State::Down {
@@ -68,12 +68,12 @@ impl<V: View> Clone for State<V> {
}
}
-pub struct DragAndDrop<V: View> {
+pub struct DragAndDrop<V> {
containers: HashSet<WeakViewHandle<V>>,
currently_dragged: Option<State<V>>,
}
-impl<V: View> Default for DragAndDrop<V> {
+impl<V> Default for DragAndDrop<V> {
fn default() -> Self {
Self {
containers: Default::default(),
@@ -82,7 +82,7 @@ impl<V: View> Default for DragAndDrop<V> {
}
}
-impl<V: View> DragAndDrop<V> {
+impl<V: 'static> DragAndDrop<V> {
pub fn register_container(&mut self, handle: WeakViewHandle<V>) {
self.containers.insert(handle);
}
@@ -291,7 +291,7 @@ impl<V: View> DragAndDrop<V> {
}
}
-pub trait Draggable<V: View> {
+pub trait Draggable<V> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
@@ -301,7 +301,7 @@ pub trait Draggable<V: View> {
Self: Sized;
}
-impl<V: View> Draggable<V> for MouseEventHandler<V> {
+impl<V: 'static> Draggable<V> for MouseEventHandler<V> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
@@ -8563,6 +8563,7 @@ fn build_style(
font_size,
font_properties,
underline: Default::default(),
+ soft_wrap: false,
},
placeholder_text: None,
line_height_scalar,
@@ -605,7 +605,7 @@ impl EditorElement {
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut ViewContext<Editor>,
+ cx: &mut PaintContext<Editor>,
) {
let line_height = layout.position_map.line_height;
@@ -760,7 +760,7 @@ impl EditorElement {
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut ViewContext<Editor>,
+ cx: &mut PaintContext<Editor>,
) {
let style = &self.style;
let local_replica_id = editor.replica_id(cx);
@@ -1337,7 +1337,7 @@ impl EditorElement {
visible_bounds: RectF,
layout: &mut LayoutState,
editor: &mut Editor,
- cx: &mut ViewContext<Editor>,
+ cx: &mut PaintContext<Editor>,
) {
let scroll_position = layout.position_map.snapshot.scroll_position();
let scroll_left = scroll_position.x() * layout.position_map.em_width;
@@ -561,7 +561,7 @@ impl Item for Editor {
}
}
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
detail: Option<usize>,
style: &theme::Tab,
@@ -268,7 +268,7 @@ impl Item for FeedbackEditor {
Some("Send Feedback".into())
}
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
@@ -39,6 +39,7 @@ pathfinder_color = "0.5"
pathfinder_geometry = "0.5"
postage.workspace = true
rand.workspace = true
+refineable.workspace = true
resvg = "0.14"
schemars = "0.8"
seahash = "4.1"
@@ -47,6 +48,7 @@ serde_derive.workspace = true
serde_json.workspace = true
smallvec.workspace = true
smol.workspace = true
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
time.workspace = true
tiny-skia = "0.5"
usvg = { version = "0.14", features = [] }
@@ -58,6 +58,7 @@ impl gpui::View for TextView {
font_family_id: family,
underline: Default::default(),
font_properties: Default::default(),
+ soft_wrap: false,
},
)
.with_highlights(vec![(17..26, underline), (34..40, underline)])
@@ -0,0 +1,2919 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "adler32"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "async-channel"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
+dependencies = [
+ "async-lock",
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-fs"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
+dependencies = [
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-io"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
+dependencies = [
+ "async-lock",
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-lite",
+ "log",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "socket2",
+ "waker-fn",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-net"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f"
+dependencies = [
+ "async-io",
+ "autocfg",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+ "signal-hook",
+ "windows-sys",
+]
+
+[[package]]
+name = "async-task"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
+
+[[package]]
+name = "atomic"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide 0.7.1",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "bindgen"
+version = "0.65.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "peeking_take_while",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.25",
+ "which",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "blocking"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
+dependencies = [
+ "async-channel",
+ "async-lock",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "log",
+]
+
+[[package]]
+name = "bstr"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "bytemuck"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "castaway"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cocoa"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "collections"
+version = "0.1.0"
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "concurrent-queue"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "const-cstr"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "uuid 0.5.1",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "core-graphics"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "core-text"
+version = "19.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
+dependencies = [
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.63+curl-8.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "winapi",
+]
+
+[[package]]
+name = "data-url"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
+dependencies = [
+ "matches",
+]
+
+[[package]]
+name = "deflate"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
+dependencies = [
+ "adler32",
+ "byteorder",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading 0.8.0",
+]
+
+[[package]]
+name = "dwrote"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "winapi",
+ "wio",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "erased-serde"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f94c0e13118e7d7533271f754a168ae8400e6a1cc043f2bfd53cc7290f1a1de3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "etagere"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcf22f748754352918e082e0039335ee92454a5d62bcaf69b5e8daf5907d9644"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "euclid"
+version = "0.22.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide 0.7.1",
+]
+
+[[package]]
+name = "float-cmp"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e"
+
+[[package]]
+name = "float-ord"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "font-kit"
+version = "0.11.0"
+source = "git+https://github.com/zed-industries/font-kit?rev=b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18#b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "core-foundation",
+ "core-graphics",
+ "core-text",
+ "dirs-next",
+ "dwrote",
+ "float-ord",
+ "freetype",
+ "lazy_static",
+ "libc",
+ "log",
+ "pathfinder_geometry",
+ "pathfinder_simd",
+ "walkdir",
+ "winapi",
+ "yeslogic-fontconfig-sys",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58903f4f8d5b58c7d300908e4ebe5289c1bfdf5587964330f12023b8ff17fd1"
+dependencies = [
+ "log",
+ "memmap2",
+ "ttf-parser 0.12.3",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "freetype"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6"
+dependencies = [
+ "freetype-sys",
+ "libc",
+]
+
+[[package]]
+name = "freetype-sys"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a"
+dependencies = [
+ "cmake",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-lite"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gif"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "globset"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "gpui"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-task",
+ "bindgen",
+ "block",
+ "cc",
+ "cocoa",
+ "collections",
+ "core-foundation",
+ "core-graphics",
+ "core-text",
+ "ctor",
+ "etagere",
+ "font-kit",
+ "foreign-types",
+ "futures",
+ "gpui_macros",
+ "image",
+ "itertools",
+ "lazy_static",
+ "log",
+ "media",
+ "metal",
+ "num_cpus",
+ "objc",
+ "ordered-float",
+ "parking",
+ "parking_lot 0.11.2",
+ "pathfinder_color",
+ "pathfinder_geometry",
+ "postage",
+ "rand",
+ "resvg",
+ "schemars",
+ "seahash",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smallvec",
+ "smol",
+ "sqlez",
+ "sum_tree",
+ "time",
+ "tiny-skia",
+ "usvg",
+ "util",
+ "uuid 1.4.0",
+ "waker-fn",
+]
+
+[[package]]
+name = "gpui_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "idna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "image"
+version = "0.23.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "gif",
+ "jpeg-decoder",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "indoc"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "isahc"
+version = "1.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
+dependencies = [
+ "async-channel",
+ "castaway",
+ "crossbeam-utils",
+ "curl",
+ "curl-sys",
+ "encoding_rs",
+ "event-listener",
+ "futures-lite",
+ "http",
+ "log",
+ "mime",
+ "once_cell",
+ "polling",
+ "slab",
+ "sluice",
+ "tracing",
+ "tracing-futures",
+ "url",
+ "waker-fn",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "kurbo"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
+dependencies = [
+ "arrayvec 0.7.4",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
+dependencies = [
+ "cfg-if",
+ "windows-sys",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "lock_api"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+dependencies = [
+ "serde",
+ "value-bag",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "media"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bindgen",
+ "block",
+ "bytes",
+ "core-foundation",
+ "foreign-types",
+ "metal",
+ "objc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memmap2"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+dependencies = [
+ "adler32",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "object"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "ordered-float"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "parking"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.8",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.3.5",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "pathfinder_color"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62"
+dependencies = [
+ "pathfinder_simd",
+]
+
+[[package]]
+name = "pathfinder_geometry"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
+dependencies = [
+ "log",
+ "pathfinder_simd",
+]
+
+[[package]]
+name = "pathfinder_simd"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pest"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pico-args"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
+
+[[package]]
+name = "pin-project"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "playground"
+version = "0.1.0"
+dependencies = [
+ "gpui",
+]
+
+[[package]]
+name = "png"
+version = "0.16.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "deflate",
+ "miniz_oxide 0.3.7",
+]
+
+[[package]]
+name = "polling"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "concurrent-queue",
+ "libc",
+ "log",
+ "pin-project-lite",
+ "windows-sys",
+]
+
+[[package]]
+name = "pollster"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
+
+[[package]]
+name = "postage"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1"
+dependencies = [
+ "atomic",
+ "crossbeam-queue",
+ "futures",
+ "log",
+ "parking_lot 0.12.1",
+ "pin-project",
+ "pollster",
+ "static_assertions",
+ "thiserror",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "rctree"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8"
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall 0.2.16",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+
+[[package]]
+name = "resvg"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09697862c5c3f940cbaffef91969c62188b5c8ed385b0aef43a5ff01ddc8000f"
+dependencies = [
+ "jpeg-decoder",
+ "log",
+ "pico-args",
+ "png",
+ "rgb",
+ "svgfilters",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
+dependencies = [
+ "xmlparser",
+]
+
+[[package]]
+name = "rust-embed"
+version = "6.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661"
+dependencies = [
+ "rust-embed-impl",
+ "rust-embed-utils",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-impl"
+version = "6.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rust-embed-utils",
+ "syn 2.0.25",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-utils"
+version = "7.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74"
+dependencies = [
+ "globset",
+ "sha2",
+ "walkdir",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_version"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustybuzz"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "smallvec",
+ "ttf-parser 0.9.0",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-general-category",
+ "unicode-script",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
+
+[[package]]
+name = "safe_arch"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "schemars"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "seahash"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
+
+[[package]]
+name = "semver"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+dependencies = [
+ "pest",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "serde_fmt"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed"
+dependencies = [
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simplecss"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "sluice"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
+dependencies = [
+ "async-channel",
+ "futures-core",
+ "futures-io",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
+[[package]]
+name = "smol"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-fs",
+ "async-io",
+ "async-lock",
+ "async-net",
+ "async-process",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "sqlez"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "futures",
+ "indoc",
+ "lazy_static",
+ "libsqlite3-sys",
+ "parking_lot 0.11.2",
+ "smol",
+ "thread_local",
+ "uuid 1.4.0",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "sum_tree"
+version = "0.1.0"
+dependencies = [
+ "arrayvec 0.7.4",
+ "log",
+]
+
+[[package]]
+name = "sval"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b031320a434d3e9477ccf9b5756d57d4272937b8d22cb88af80b7633a1b78b1"
+
+[[package]]
+name = "sval_buffer"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf7e9412af26b342f3f2cc5cc4122b0105e9d16eb76046cd14ed10106cf6028"
+dependencies = [
+ "sval",
+ "sval_ref",
+]
+
+[[package]]
+name = "sval_dynamic"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0ef628e8a77a46ed3338db8d1b08af77495123cc229453084e47cd716d403cf"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_fmt"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_json"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_ref"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703ca1942a984bd0d9b5a4c0a65ab8b4b794038d080af4eb303c71bc6bf22d7c"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_serde"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830926cd0581f7c3e5d51efae4d35c6b6fc4db583842652891ba2f1bed8db046"
+dependencies = [
+ "serde",
+ "sval",
+ "sval_buffer",
+ "sval_fmt",
+]
+
+[[package]]
+name = "svg_fmt"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
+
+[[package]]
+name = "svgfilters"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e"
+dependencies = [
+ "float-cmp",
+ "rgb",
+]
+
+[[package]]
+name = "svgtypes"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff"
+dependencies = [
+ "float-cmp",
+ "siphasher",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "take-until"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
+
+[[package]]
+name = "thiserror"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
+dependencies = [
+ "jpeg-decoder",
+ "miniz_oxide 0.4.4",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
+dependencies = [
+ "itoa",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+
+[[package]]
+name = "time-macros"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe"
+dependencies = [
+ "arrayref",
+ "arrayvec 0.5.2",
+ "bytemuck",
+ "cfg-if",
+ "png",
+ "safe_arch",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca"
+
+[[package]]
+name = "ttf-parser"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6"
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
+
+[[package]]
+name = "unicode-general-category"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-script"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc"
+
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
+[[package]]
+name = "url"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "usvg"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5"
+dependencies = [
+ "base64",
+ "data-url",
+ "flate2",
+ "fontdb",
+ "kurbo",
+ "log",
+ "memmap2",
+ "pico-args",
+ "rctree",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "svgtypes",
+ "ttf-parser 0.12.3",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
+[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "backtrace",
+ "dirs",
+ "futures",
+ "isahc",
+ "lazy_static",
+ "log",
+ "rand",
+ "rust-embed",
+ "serde",
+ "serde_json",
+ "smol",
+ "take-until",
+ "url",
+]
+
+[[package]]
+name = "uuid"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
+
+[[package]]
+name = "uuid"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "value-bag"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3"
+dependencies = [
+ "value-bag-serde1",
+ "value-bag-sval2",
+]
+
+[[package]]
+name = "value-bag-serde1"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394"
+dependencies = [
+ "erased-serde",
+ "serde",
+ "serde_fmt",
+]
+
+[[package]]
+name = "value-bag-sval2"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d"
+dependencies = [
+ "sval",
+ "sval_buffer",
+ "sval_dynamic",
+ "sval_fmt",
+ "sval_json",
+ "sval_ref",
+ "sval_serde",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "weezl"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "wio"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "xmlparser"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
+
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
+[[package]]
+name = "yeslogic-fontconfig-sys"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386"
+dependencies = [
+ "const-cstr",
+ "dlib",
+ "once_cell",
+ "pkg-config",
+]
@@ -0,0 +1,26 @@
+[package]
+name = "playground"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[[bin]]
+name = "playground"
+path = "src/playground.rs"
+
+[dependencies]
+anyhow.workspace = true
+derive_more.workspace = true
+gpui = { path = ".." }
+log.workspace = true
+playground_macros = { path = "../playground_macros" }
+parking_lot.workspace = true
+refineable.workspace = true
+serde.workspace = true
+simplelog = "0.9"
+smallvec.workspace = true
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
+util = { path = "../../util" }
+
+[dev-dependencies]
+gpui = { path = "..", features = ["test-support"] }
@@ -0,0 +1,72 @@
+Much of element styling is now handled by an external engine.
+
+
+How do I make an element hover.
+
+There's a hover style.
+
+Hoverable needs to wrap another element. That element can be styled.
+
+```rs
+struct Hoverable<E: Element> {
+
+}
+
+impl<V> Element<V> for Hoverable {
+
+}
+
+```
+
+
+
+```rs
+#[derive(Styled, Interactive)]
+pub struct Div {
+ declared_style: StyleRefinement,
+ interactions: Interactions
+}
+
+pub trait Styled {
+ fn declared_style(&mut self) -> &mut StyleRefinement;
+ fn compute_style(&mut self) -> Style {
+ Style::default().refine(self.declared_style())
+ }
+
+ // All the tailwind classes, modifying self.declared_style()
+}
+
+impl Style {
+ pub fn paint_background<V>(layout: Layout, cx: &mut PaintContext<V>);
+ pub fn paint_foreground<V>(layout: Layout, cx: &mut PaintContext<V>);
+}
+
+pub trait Interactive<V> {
+ fn interactions(&mut self) -> &mut Interactions<V>;
+
+ fn on_click(self, )
+}
+
+struct Interactions<V> {
+ click: SmallVec<[<Rc<dyn Fn(&mut V, &dyn Any, )>; 1]>,
+}
+
+
+```
+
+
+```rs
+
+
+trait Stylable {
+ type Style;
+
+ fn with_style(self, style: Self::Style) -> Self;
+}
+
+
+
+
+
+
+```
@@ -0,0 +1,78 @@
+use crate::{layout_context::LayoutContext, paint_context::PaintContext};
+use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
+use util::ResultExt;
+
+/// Makes a new, playground-style element into a legacy element.
+pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
+
+impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
+ type LayoutState = Option<(LayoutEngine, LayoutId)>;
+ type PaintState = ();
+
+ fn layout(
+ &mut self,
+ constraint: gpui::SizeConstraint,
+ view: &mut V,
+ cx: &mut gpui::LayoutContext<V>,
+ ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+ cx.push_layout_engine(LayoutEngine::new());
+
+ let size = constraint.max;
+ let mut cx = LayoutContext::new(cx);
+ let layout_id = self.0.layout(view, &mut cx).log_err();
+ if let Some(layout_id) = layout_id {
+ cx.layout_engine()
+ .unwrap()
+ .compute_layout(layout_id, constraint.max)
+ .log_err();
+ }
+
+ let layout_engine = cx.pop_layout_engine();
+ debug_assert!(layout_engine.is_some(),
+ "unexpected layout stack state. is there an unmatched pop_layout_engine in the called code?"
+ );
+
+ (constraint.max, layout_engine.zip(layout_id))
+ }
+
+ fn paint(
+ &mut self,
+ scene: &mut gpui::SceneBuilder,
+ bounds: RectF,
+ visible_bounds: RectF,
+ layout_data: &mut Option<(LayoutEngine, LayoutId)>,
+ view: &mut V,
+ legacy_cx: &mut gpui::PaintContext<V>,
+ ) -> Self::PaintState {
+ let (layout_engine, layout_id) = layout_data.take().unwrap();
+ legacy_cx.push_layout_engine(layout_engine);
+ let mut cx = PaintContext::new(legacy_cx, scene);
+ self.0.paint(view, &mut cx);
+ *layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
+ debug_assert!(layout_data.is_some());
+ }
+
+ fn rect_for_text_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ bounds: RectF,
+ visible_bounds: RectF,
+ layout: &Self::LayoutState,
+ paint: &Self::PaintState,
+ view: &V,
+ cx: &gpui::ViewContext<V>,
+ ) -> Option<RectF> {
+ todo!("implement before merging to main")
+ }
+
+ fn debug(
+ &self,
+ bounds: RectF,
+ layout: &Self::LayoutState,
+ paint: &Self::PaintState,
+ view: &V,
+ cx: &gpui::ViewContext<V>,
+ ) -> gpui::serde_json::Value {
+ todo!("implement before merging to main")
+ }
+}
@@ -0,0 +1,276 @@
+#![allow(dead_code)]
+
+use std::{num::ParseIntError, ops::Range};
+
+use smallvec::SmallVec;
+
+pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+ let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
+ let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
+ let b = (hex & 0xFF) as f32 / 255.0;
+ Rgba { r, g, b, a: 1.0 }.into()
+}
+
+#[derive(Clone, Copy, Default, Debug)]
+pub struct Rgba {
+ pub r: f32,
+ pub g: f32,
+ pub b: f32,
+ pub a: f32,
+}
+
+pub trait Lerp {
+ fn lerp(&self, level: f32) -> Hsla;
+}
+
+impl Lerp for Range<Hsla> {
+ fn lerp(&self, level: f32) -> Hsla {
+ let level = level.clamp(0., 1.);
+ Hsla {
+ h: self.start.h + (level * (self.end.h - self.start.h)),
+ s: self.start.s + (level * (self.end.s - self.start.s)),
+ l: self.start.l + (level * (self.end.l - self.start.l)),
+ a: self.start.a + (level * (self.end.a - self.start.a)),
+ }
+ }
+}
+
+impl From<gpui::color::Color> for Rgba {
+ fn from(value: gpui::color::Color) -> Self {
+ Self {
+ r: value.0.r as f32 / 255.0,
+ g: value.0.g as f32 / 255.0,
+ b: value.0.b as f32 / 255.0,
+ a: value.0.a as f32 / 255.0,
+ }
+ }
+}
+
+impl From<Hsla> for Rgba {
+ fn from(color: Hsla) -> Self {
+ let h = color.h;
+ let s = color.s;
+ let l = color.l;
+
+ let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+ let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
+ let m = l - c / 2.0;
+ let cm = c + m;
+ let xm = x + m;
+
+ let (r, g, b) = match (h * 6.0).floor() as i32 {
+ 0 | 6 => (cm, xm, m),
+ 1 => (xm, cm, m),
+ 2 => (m, cm, xm),
+ 3 => (m, xm, cm),
+ 4 => (xm, m, cm),
+ _ => (cm, m, xm),
+ };
+
+ Rgba {
+ r,
+ g,
+ b,
+ a: color.a,
+ }
+ }
+}
+
+impl TryFrom<&'_ str> for Rgba {
+ type Error = ParseIntError;
+
+ fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
+ let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
+ let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
+ let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
+ let a = if value.len() > 7 {
+ u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
+ } else {
+ 1.0
+ };
+
+ Ok(Rgba { r, g, b, a })
+ }
+}
+
+impl Into<gpui::color::Color> for Rgba {
+ fn into(self) -> gpui::color::Color {
+ gpui::color::rgba(self.r, self.g, self.b, self.a)
+ }
+}
+
+#[derive(Default, Copy, Clone, Debug, PartialEq)]
+pub struct Hsla {
+ pub h: f32,
+ pub s: f32,
+ pub l: f32,
+ pub a: f32,
+}
+
+pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
+ Hsla {
+ h: h.clamp(0., 1.),
+ s: s.clamp(0., 1.),
+ l: l.clamp(0., 1.),
+ a: a.clamp(0., 1.),
+ }
+}
+
+pub fn black() -> Hsla {
+ Hsla {
+ h: 0.,
+ s: 0.,
+ l: 0.,
+ a: 1.,
+ }
+}
+
+impl From<Rgba> for Hsla {
+ fn from(color: Rgba) -> Self {
+ let r = color.r;
+ let g = color.g;
+ let b = color.b;
+
+ let max = r.max(g.max(b));
+ let min = r.min(g.min(b));
+ let delta = max - min;
+
+ let l = (max + min) / 2.0;
+ let s = if l == 0.0 || l == 1.0 {
+ 0.0
+ } else if l < 0.5 {
+ delta / (2.0 * l)
+ } else {
+ delta / (2.0 - 2.0 * l)
+ };
+
+ let h = if delta == 0.0 {
+ 0.0
+ } else if max == r {
+ ((g - b) / delta).rem_euclid(6.0) / 6.0
+ } else if max == g {
+ ((b - r) / delta + 2.0) / 6.0
+ } else {
+ ((r - g) / delta + 4.0) / 6.0
+ };
+
+ Hsla {
+ h,
+ s,
+ l,
+ a: color.a,
+ }
+ }
+}
+
+impl Hsla {
+ /// Scales the saturation and lightness by the given values, clamping at 1.0.
+ pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
+ self.s = (self.s * s).clamp(0., 1.);
+ self.l = (self.l * l).clamp(0., 1.);
+ self
+ }
+
+ /// Increases the saturation of the color by a certain amount, with a max
+ /// value of 1.0.
+ pub fn saturate(mut self, amount: f32) -> Self {
+ self.s += amount;
+ self.s = self.s.clamp(0.0, 1.0);
+ self
+ }
+
+ /// Decreases the saturation of the color by a certain amount, with a min
+ /// value of 0.0.
+ pub fn desaturate(mut self, amount: f32) -> Self {
+ self.s -= amount;
+ self.s = self.s.max(0.0);
+ if self.s < 0.0 {
+ self.s = 0.0;
+ }
+ self
+ }
+
+ /// Brightens the color by increasing the lightness by a certain amount,
+ /// with a max value of 1.0.
+ pub fn brighten(mut self, amount: f32) -> Self {
+ self.l += amount;
+ self.l = self.l.clamp(0.0, 1.0);
+ self
+ }
+
+ /// Darkens the color by decreasing the lightness by a certain amount,
+ /// with a max value of 0.0.
+ pub fn darken(mut self, amount: f32) -> Self {
+ self.l -= amount;
+ self.l = self.l.clamp(0.0, 1.0);
+ self
+ }
+}
+
+impl From<gpui::color::Color> for Hsla {
+ fn from(value: gpui::color::Color) -> Self {
+ Rgba::from(value).into()
+ }
+}
+
+impl Into<gpui::color::Color> for Hsla {
+ fn into(self) -> gpui::color::Color {
+ Rgba::from(self).into()
+ }
+}
+
+pub struct ColorScale {
+ colors: SmallVec<[Hsla; 2]>,
+ positions: SmallVec<[f32; 2]>,
+}
+
+pub fn scale<I, C>(colors: I) -> ColorScale
+where
+ I: IntoIterator<Item = C>,
+ C: Into<Hsla>,
+{
+ let mut scale = ColorScale {
+ colors: colors.into_iter().map(Into::into).collect(),
+ positions: SmallVec::new(),
+ };
+ let num_colors: f32 = scale.colors.len() as f32 - 1.0;
+ scale.positions = (0..scale.colors.len())
+ .map(|i| i as f32 / num_colors)
+ .collect();
+ scale
+}
+
+impl ColorScale {
+ fn at(&self, t: f32) -> Hsla {
+ // Ensure that the input is within [0.0, 1.0]
+ debug_assert!(
+ 0.0 <= t && t <= 1.0,
+ "t value {} is out of range. Expected value in range 0.0 to 1.0",
+ t
+ );
+
+ let position = match self
+ .positions
+ .binary_search_by(|a| a.partial_cmp(&t).unwrap())
+ {
+ Ok(index) | Err(index) => index,
+ };
+ let lower_bound = position.saturating_sub(1);
+ let upper_bound = position.min(self.colors.len() - 1);
+ let lower_color = &self.colors[lower_bound];
+ let upper_color = &self.colors[upper_bound];
+
+ match upper_bound.checked_sub(lower_bound) {
+ Some(0) | None => *lower_color,
+ Some(_) => {
+ let interval_t = (t - self.positions[lower_bound])
+ / (self.positions[upper_bound] - self.positions[lower_bound]);
+ let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
+ let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
+ let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
+ let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
+ Hsla { h, s, l, a }
+ }
+ }
+ }
+}
@@ -0,0 +1,100 @@
+use crate::{
+ div::div,
+ element::{Element, ParentElement},
+ style::StyleHelpers,
+ text::ArcCow,
+ themes::rose_pine,
+};
+use gpui::ViewContext;
+use playground_macros::Element;
+use std::{marker::PhantomData, rc::Rc};
+
+struct ButtonHandlers<V, D> {
+ click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+}
+
+impl<V, D> Default for ButtonHandlers<V, D> {
+ fn default() -> Self {
+ Self { click: None }
+ }
+}
+
+use crate as playground;
+#[derive(Element)]
+pub struct Button<V: 'static, D: 'static> {
+ handlers: ButtonHandlers<V, D>,
+ label: Option<ArcCow<'static, str>>,
+ icon: Option<ArcCow<'static, str>>,
+ data: Rc<D>,
+ view_type: PhantomData<V>,
+}
+
+// Impl block for buttons without data.
+// See below for an impl block for any button.
+impl<V: 'static> Button<V, ()> {
+ fn new() -> Self {
+ Self {
+ handlers: ButtonHandlers::default(),
+ label: None,
+ icon: None,
+ data: Rc::new(()),
+ view_type: PhantomData,
+ }
+ }
+
+ pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+ Button {
+ handlers: ButtonHandlers::default(),
+ label: self.label,
+ icon: self.icon,
+ data: Rc::new(data),
+ view_type: PhantomData,
+ }
+ }
+}
+
+// Impl block for *any* button.
+impl<V: 'static, D: 'static> Button<V, D> {
+ pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+ self.label = Some(label.into());
+ self
+ }
+
+ pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+ self.icon = Some(icon.into());
+ self
+ }
+
+ // pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
+ // let data = self.data.clone();
+ // Self::click(self, MouseButton::Left, move |view, _, cx| {
+ // handler(view, data.as_ref(), cx);
+ // })
+ // }
+}
+
+pub fn button<V>() -> Button<V, ()> {
+ Button::new()
+}
+
+impl<V: 'static, D: 'static> Button<V, D> {
+ fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ // TODO: Drive theme from the context
+ let button = div()
+ .fill(rose_pine::dawn().error(0.5))
+ .h_4()
+ .children(self.label.clone());
+
+ button
+
+ // TODO: Event handling
+ // if let Some(handler) = self.handlers.click.clone() {
+ // let data = self.data.clone();
+ // // button.mouse_down(MouseButton::Left, move |view, event, cx| {
+ // // handler(view, data.as_ref(), cx)
+ // // })
+ // } else {
+ // button
+ // }
+ }
+}
@@ -0,0 +1,108 @@
+use crate::{
+ element::{AnyElement, Element, Layout, ParentElement},
+ interactive::{InteractionHandlers, Interactive},
+ layout_context::LayoutContext,
+ paint_context::PaintContext,
+ style::{Style, StyleHelpers, StyleRefinement, Styleable},
+};
+use anyhow::Result;
+use gpui::LayoutId;
+use smallvec::SmallVec;
+
+pub struct Div<V: 'static> {
+ style: StyleRefinement,
+ handlers: InteractionHandlers<V>,
+ children: SmallVec<[AnyElement<V>; 2]>,
+}
+
+pub fn div<V>() -> Div<V> {
+ Div {
+ style: Default::default(),
+ handlers: Default::default(),
+ children: Default::default(),
+ }
+}
+
+impl<V: 'static> Element<V> for Div<V> {
+ type Layout = ();
+
+ fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
+ where
+ Self: Sized,
+ {
+ let children = self
+ .children
+ .iter_mut()
+ .map(|child| child.layout(view, cx))
+ .collect::<Result<Vec<LayoutId>>>()?;
+
+ cx.add_layout_node(self.style(), (), children)
+ }
+
+ fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
+ where
+ Self: Sized,
+ {
+ let style = self.style();
+
+ style.paint_background::<V, Self>(layout, cx);
+ for child in &mut self.children {
+ child.paint(view, cx);
+ }
+ }
+}
+
+impl<V> Styleable for Div<V> {
+ type Style = Style;
+
+ fn declared_style(&mut self) -> &mut StyleRefinement {
+ &mut self.style
+ }
+}
+
+impl<V> StyleHelpers for Div<V> {}
+
+impl<V> Interactive<V> for Div<V> {
+ fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
+ &mut self.handlers
+ }
+}
+
+impl<V: 'static> ParentElement<V> for Div<V> {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+ &mut self.children
+ }
+}
+
+#[test]
+fn test() {
+ // let elt = div().w_auto();
+}
+
+// trait Element<V: 'static> {
+// type Style;
+
+// fn layout()
+// }
+
+// trait Stylable<V: 'static>: Element<V> {
+// type Style;
+
+// fn with_style(self, style: Self::Style) -> Self;
+// }
+
+// pub struct HoverStyle<S> {
+// default: S,
+// hovered: S,
+// }
+
+// struct Hover<V: 'static, C: Stylable<V>> {
+// child: C,
+// style: HoverStyle<C::Style>,
+// }
+
+// impl<V: 'static, C: Stylable<V>> Hover<V, C> {
+// fn new(child: C, style: HoverStyle<C::Style>) -> Self {
+// Self { child, style }
+// }
+// }
@@ -0,0 +1,158 @@
+use anyhow::Result;
+use derive_more::{Deref, DerefMut};
+use gpui::{geometry::rect::RectF, EngineLayout};
+use smallvec::SmallVec;
+use std::marker::PhantomData;
+use util::ResultExt;
+
+pub use crate::layout_context::LayoutContext;
+pub use crate::paint_context::PaintContext;
+
+type LayoutId = gpui::LayoutId;
+
+pub trait Element<V: 'static>: 'static {
+ type Layout;
+
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<Layout<V, Self::Layout>>
+ where
+ Self: Sized;
+
+ fn paint(
+ &mut self,
+ view: &mut V,
+ layout: &mut Layout<V, Self::Layout>,
+ cx: &mut PaintContext<V>,
+ ) where
+ Self: Sized;
+
+ fn into_any(self) -> AnyElement<V>
+ where
+ Self: 'static + Sized,
+ {
+ AnyElement(Box::new(ElementState {
+ element: self,
+ layout: None,
+ }))
+ }
+}
+
+/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
+trait ElementStateObject<V> {
+ fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
+ fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
+}
+
+/// A wrapper around an element that stores its layout state.
+struct ElementState<V: 'static, E: Element<V>> {
+ element: E,
+ layout: Option<Layout<V, E::Layout>>,
+}
+
+/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
+impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
+ fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+ let layout = self.element.layout(view, cx)?;
+ let layout_id = layout.id;
+ self.layout = Some(layout);
+ Ok(layout_id)
+ }
+
+ fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ let layout = self.layout.as_mut().expect("paint called before layout");
+ if layout.engine_layout.is_none() {
+ layout.engine_layout = cx.computed_layout(layout.id).log_err()
+ }
+ self.element.paint(view, layout, cx)
+ }
+}
+
+/// A dynamic element.
+pub struct AnyElement<V>(Box<dyn ElementStateObject<V>>);
+
+impl<V> AnyElement<V> {
+ pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+ self.0.layout(view, cx)
+ }
+
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ self.0.paint(view, cx)
+ }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct Layout<V, D> {
+ id: LayoutId,
+ engine_layout: Option<EngineLayout>,
+ #[deref]
+ #[deref_mut]
+ element_data: D,
+ view_type: PhantomData<V>,
+}
+
+impl<V: 'static, D> Layout<V, D> {
+ pub fn new(id: LayoutId, element_data: D) -> Self {
+ Self {
+ id,
+ engine_layout: None,
+ element_data: element_data,
+ view_type: PhantomData,
+ }
+ }
+
+ pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
+ self.engine_layout(cx).bounds
+ }
+
+ pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
+ self.engine_layout(cx).order
+ }
+
+ fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
+ self.engine_layout
+ .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
+ }
+}
+
+impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ let mut element = self.element_data.take().unwrap();
+ element.paint(view, cx);
+ self.element_data = Some(element);
+ }
+}
+
+pub trait ParentElement<V: 'static> {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
+
+ fn child(mut self, child: impl IntoElement<V>) -> Self
+ where
+ Self: Sized,
+ {
+ self.children_mut().push(child.into_element().into_any());
+ self
+ }
+
+ fn children<I, E>(mut self, children: I) -> Self
+ where
+ I: IntoIterator<Item = E>,
+ E: IntoElement<V>,
+ Self: Sized,
+ {
+ self.children_mut().extend(
+ children
+ .into_iter()
+ .map(|child| child.into_element().into_any()),
+ );
+ self
+ }
+}
+
+pub trait IntoElement<V: 'static> {
+ type Element: Element<V>;
+
+ fn into_element(self) -> Self::Element;
+}
@@ -0,0 +1,76 @@
+use crate::{
+ element::{Element, Layout},
+ layout_context::LayoutContext,
+ paint_context::PaintContext,
+ style::{StyleRefinement, Styleable},
+};
+use anyhow::Result;
+use gpui::platform::MouseMovedEvent;
+use refineable::Refineable;
+use std::{cell::Cell, marker::PhantomData};
+
+pub struct Hoverable<V: 'static, E: Element<V> + Styleable> {
+ hovered: Cell<bool>,
+ child_style: StyleRefinement,
+ hovered_style: StyleRefinement,
+ child: E,
+ view_type: PhantomData<V>,
+}
+
+pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
+ Hoverable {
+ hovered: Cell::new(false),
+ child_style: child.declared_style().clone(),
+ hovered_style: Default::default(),
+ child,
+ view_type: PhantomData,
+ }
+}
+
+impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
+ type Style = E::Style;
+
+ fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+ self.child.declared_style()
+ }
+}
+
+impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
+ type Layout = E::Layout;
+
+ fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+ where
+ Self: Sized,
+ {
+ self.child.layout(view, cx)
+ }
+
+ fn paint(
+ &mut self,
+ view: &mut V,
+ layout: &mut Layout<V, Self::Layout>,
+ cx: &mut PaintContext<V>,
+ ) where
+ Self: Sized,
+ {
+ if self.hovered.get() {
+ // If hovered, refine the child's style with this element's style.
+ self.child.declared_style().refine(&self.hovered_style);
+ } else {
+ // Otherwise, set the child's style back to its original style.
+ *self.child.declared_style() = self.child_style.clone();
+ }
+
+ let bounds = layout.bounds(cx);
+ let order = layout.order(cx);
+ self.hovered.set(bounds.contains_point(cx.mouse_position()));
+ let was_hovered = self.hovered.clone();
+ cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
+ let is_hovered = bounds.contains_point(event.position);
+ if is_hovered != was_hovered.get() {
+ was_hovered.set(is_hovered);
+ cx.repaint();
+ }
+ });
+ }
+}
@@ -0,0 +1,34 @@
+use gpui::{platform::MouseMovedEvent, EventContext};
+use smallvec::SmallVec;
+use std::rc::Rc;
+
+pub trait Interactive<V: 'static> {
+ fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
+
+ fn on_mouse_move<H>(mut self, handler: H) -> Self
+ where
+ H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>),
+ Self: Sized,
+ {
+ self.interaction_handlers()
+ .mouse_moved
+ .push(Rc::new(move |view, event, hit_test, cx| {
+ handler(view, event, hit_test, cx);
+ cx.bubble
+ }));
+ self
+ }
+}
+
+pub struct InteractionHandlers<V: 'static> {
+ mouse_moved:
+ SmallVec<[Rc<dyn Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>) -> bool>; 2]>,
+}
+
+impl<V> Default for InteractionHandlers<V> {
+ fn default() -> Self {
+ Self {
+ mouse_moved: Default::default(),
+ }
+ }
+}
@@ -0,0 +1,54 @@
+use anyhow::{anyhow, Result};
+use derive_more::{Deref, DerefMut};
+pub use gpui::LayoutContext as LegacyLayoutContext;
+use gpui::{RenderContext, ViewContext};
+pub use taffy::tree::NodeId;
+
+use crate::{element::Layout, style::Style};
+
+#[derive(Deref, DerefMut)]
+pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
+ #[deref]
+ #[deref_mut]
+ pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
+}
+
+impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
+ fn text_style(&self) -> gpui::fonts::TextStyle {
+ self.legacy_cx.text_style()
+ }
+
+ fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+ self.legacy_cx.push_text_style(style)
+ }
+
+ fn pop_text_style(&mut self) {
+ self.legacy_cx.pop_text_style()
+ }
+
+ fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+ &mut self.view_context
+ }
+}
+
+impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
+ pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
+ Self { legacy_cx }
+ }
+
+ pub fn add_layout_node<D>(
+ &mut self,
+ style: Style,
+ element_data: D,
+ children: impl IntoIterator<Item = NodeId>,
+ ) -> Result<Layout<V, D>> {
+ let rem_size = self.rem_pixels();
+ let id = self
+ .legacy_cx
+ .layout_engine()
+ .ok_or_else(|| anyhow!("no layout engine"))?
+ .add_node(style.to_taffy(rem_size), children)?;
+
+ Ok(Layout::new(id, element_data))
+ }
+}
@@ -0,0 +1,71 @@
+use anyhow::{anyhow, Result};
+use derive_more::{Deref, DerefMut};
+use gpui::{scene::EventHandler, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext};
+pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
+use std::{any::TypeId, rc::Rc};
+pub use taffy::tree::NodeId;
+
+#[derive(Deref, DerefMut)]
+pub struct PaintContext<'a, 'b, 'c, 'd, V> {
+ #[deref]
+ #[deref_mut]
+ pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+ pub(crate) scene: &'d mut gpui::SceneBuilder,
+}
+
+impl<'a, 'b, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, '_, '_, V> {
+ fn text_style(&self) -> gpui::fonts::TextStyle {
+ self.legacy_cx.text_style()
+ }
+
+ fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+ self.legacy_cx.push_text_style(style)
+ }
+
+ fn pop_text_style(&mut self) {
+ self.legacy_cx.pop_text_style()
+ }
+
+ fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+ &mut self.view_context
+ }
+}
+
+impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
+ pub fn new(
+ legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+ scene: &'d mut gpui::SceneBuilder,
+ ) -> Self {
+ Self { legacy_cx, scene }
+ }
+
+ pub fn on_event<E: 'static>(
+ &mut self,
+ order: u32,
+ handler: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
+ ) {
+ let view = self.weak_handle();
+
+ self.scene.event_handlers.push(EventHandler {
+ order,
+ handler: Rc::new(move |event, window_cx| {
+ if let Some(view) = view.upgrade(window_cx) {
+ view.update(window_cx, |view, view_cx| {
+ let mut event_cx = EventContext::new(view_cx);
+ handler(view, event.downcast_ref().unwrap(), &mut event_cx);
+ event_cx.bubble
+ })
+ } else {
+ true
+ }
+ }),
+ event_type: TypeId::of::<E>(),
+ })
+ }
+
+ pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
+ self.layout_engine()
+ .ok_or_else(|| anyhow!("no layout engine present"))?
+ .computed_layout(layout_id)
+ }
+}
@@ -0,0 +1,83 @@
+#![allow(dead_code, unused_variables)]
+use crate::{color::black, style::StyleHelpers};
+use element::Element;
+use gpui::{
+ geometry::{rect::RectF, vector::vec2f},
+ platform::WindowOptions,
+};
+use log::LevelFilter;
+use simplelog::SimpleLogger;
+use themes::{rose_pine, ThemeColors};
+use view::view;
+
+mod adapter;
+mod color;
+mod components;
+mod div;
+mod element;
+mod hoverable;
+mod interactive;
+mod layout_context;
+mod paint_context;
+mod style;
+mod text;
+mod themes;
+mod view;
+
+fn main() {
+ SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
+
+ gpui::App::new(()).unwrap().run(|cx| {
+ cx.add_window(
+ WindowOptions {
+ bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
+ vec2f(0., 0.),
+ vec2f(400., 300.),
+ )),
+ center: true,
+ ..Default::default()
+ },
+ |_| view(|_| playground(&rose_pine::moon())),
+ );
+ cx.platform().activate(true);
+ });
+}
+
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+ use div::div;
+
+ div()
+ .text_color(black())
+ .h_full()
+ .w_1_2()
+ .fill(theme.success(0.5))
+ // .hover()
+ // .fill(theme.error(0.5))
+ // .child(button().label("Hello").click(|_, _, _| println!("click!")))
+}
+
+// todo!()
+// // column()
+// // .size(auto())
+// // .fill(theme.base(0.5))
+// // .text_color(theme.text(0.5))
+// // .child(title_bar(theme))
+// // .child(stage(theme))
+// // .child(status_bar(theme))
+// }
+
+// fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+// row()
+// .fill(theme.base(0.2))
+// .justify(0.)
+// .width(auto())
+// .child(text("Zed Playground"))
+// }
+
+// fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+// row().fill(theme.surface(0.9))
+// }
+
+// fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+// row().fill(theme.surface(0.1))
+// }
@@ -0,0 +1,286 @@
+use crate::{
+ color::Hsla,
+ element::{Element, Layout},
+ paint_context::PaintContext,
+};
+use gpui::{
+ fonts::TextStyleRefinement,
+ geometry::{
+ AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
+ Size, SizeRefinement,
+ },
+};
+use playground_macros::styleable_helpers;
+use refineable::Refineable;
+pub use taffy::style::{
+ AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
+ Overflow, Position,
+};
+
+#[derive(Clone, Refineable)]
+pub struct Style {
+ /// What layout strategy should be used?
+ pub display: Display,
+
+ // Overflow properties
+ /// How children overflowing their container should affect layout
+ #[refineable]
+ pub overflow: Point<Overflow>,
+ /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
+ pub scrollbar_width: f32,
+
+ // Position properties
+ /// What should the `position` value of this struct use as a base offset?
+ pub position: Position,
+ /// How should the position of this element be tweaked relative to the layout defined?
+ #[refineable]
+ pub inset: Edges<Length>,
+
+ // Size properies
+ /// Sets the initial size of the item
+ #[refineable]
+ pub size: Size<Length>,
+ /// Controls the minimum size of the item
+ #[refineable]
+ pub min_size: Size<Length>,
+ /// Controls the maximum size of the item
+ #[refineable]
+ pub max_size: Size<Length>,
+ /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
+ pub aspect_ratio: Option<f32>,
+
+ // Spacing Properties
+ /// How large should the margin be on each side?
+ #[refineable]
+ pub margin: Edges<Length>,
+ /// How large should the padding be on each side?
+ #[refineable]
+ pub padding: Edges<DefiniteLength>,
+ /// How large should the border be on each side?
+ #[refineable]
+ pub border: Edges<DefiniteLength>,
+
+ // Alignment properties
+ /// How this node's children aligned in the cross/block axis?
+ pub align_items: Option<AlignItems>,
+ /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
+ pub align_self: Option<AlignSelf>,
+ /// How should content contained within this item be aligned in the cross/block axis
+ pub align_content: Option<AlignContent>,
+ /// How should contained within this item be aligned in the main/inline axis
+ pub justify_content: Option<JustifyContent>,
+ /// How large should the gaps between items in a flex container be?
+ #[refineable]
+ pub gap: Size<DefiniteLength>,
+
+ // Flexbox properies
+ /// Which direction does the main axis flow in?
+ pub flex_direction: FlexDirection,
+ /// Should elements wrap, or stay in a single line?
+ pub flex_wrap: FlexWrap,
+ /// Sets the initial main axis size of the item
+ pub flex_basis: Length,
+ /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
+ pub flex_grow: f32,
+ /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
+ pub flex_shrink: f32,
+
+ /// The fill color of this element
+ pub fill: Option<Fill>,
+ /// The radius of the corners of this element
+ #[refineable]
+ pub corner_radii: CornerRadii,
+ /// The color of text within this element. Cascades to children unless overridden.
+ pub text_color: Option<Hsla>,
+}
+
+impl Style {
+ pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
+ taffy::style::Style {
+ display: self.display,
+ overflow: self.overflow.clone().into(),
+ scrollbar_width: self.scrollbar_width,
+ position: self.position,
+ inset: self.inset.to_taffy(rem_size),
+ size: self.size.to_taffy(rem_size),
+ min_size: self.min_size.to_taffy(rem_size),
+ max_size: self.max_size.to_taffy(rem_size),
+ aspect_ratio: self.aspect_ratio,
+ margin: self.margin.to_taffy(rem_size),
+ padding: self.padding.to_taffy(rem_size),
+ border: self.border.to_taffy(rem_size),
+ align_items: self.align_items,
+ align_self: self.align_self,
+ align_content: self.align_content,
+ justify_content: self.justify_content,
+ gap: self.gap.to_taffy(rem_size),
+ flex_direction: self.flex_direction,
+ flex_wrap: self.flex_wrap,
+ flex_basis: self.flex_basis.to_taffy(rem_size).into(),
+ flex_grow: self.flex_grow,
+ flex_shrink: self.flex_shrink,
+ ..Default::default() // Ignore grid properties for now
+ }
+ }
+
+ /// Paints the background of an element styled with this style.
+ /// Return the bounds in which to paint the content.
+ pub fn paint_background<V: 'static, E: Element<V>>(
+ &self,
+ layout: &mut Layout<V, E::Layout>,
+ cx: &mut PaintContext<V>,
+ ) {
+ let bounds = layout.bounds(cx);
+ let rem_size = cx.rem_pixels();
+ if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
+ cx.scene.push_quad(gpui::Quad {
+ bounds,
+ background: Some(color.into()),
+ corner_radii: self.corner_radii.to_gpui(rem_size),
+ border: Default::default(),
+ });
+ }
+ }
+}
+
+impl Default for Style {
+ fn default() -> Self {
+ Style {
+ display: Display::DEFAULT,
+ overflow: Point {
+ x: Overflow::Visible,
+ y: Overflow::Visible,
+ },
+ scrollbar_width: 0.0,
+ position: Position::Relative,
+ inset: Edges::auto(),
+ margin: Edges::<Length>::zero(),
+ padding: Edges::<DefiniteLength>::zero(),
+ border: Edges::<DefiniteLength>::zero(),
+ size: Size::auto(),
+ min_size: Size::auto(),
+ max_size: Size::auto(),
+ aspect_ratio: None,
+ gap: Size::zero(),
+ // Aligment
+ align_items: None,
+ align_self: None,
+ align_content: None,
+ justify_content: None,
+ // Flexbox
+ flex_direction: FlexDirection::Row,
+ flex_wrap: FlexWrap::NoWrap,
+ flex_grow: 0.0,
+ flex_shrink: 1.0,
+ flex_basis: Length::Auto,
+ fill: None,
+ text_color: None,
+ corner_radii: CornerRadii::default(),
+ }
+ }
+}
+
+impl StyleRefinement {
+ pub fn text_style(&self) -> Option<TextStyleRefinement> {
+ self.text_color.map(|color| TextStyleRefinement {
+ color: Some(color.into()),
+ ..Default::default()
+ })
+ }
+}
+
+pub struct OptionalTextStyle {
+ color: Option<Hsla>,
+}
+
+impl OptionalTextStyle {
+ pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
+ if let Some(color) = self.color {
+ style.color = color.into();
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum Fill {
+ Color(Hsla),
+}
+
+impl Fill {
+ pub fn color(&self) -> Option<Hsla> {
+ match self {
+ Fill::Color(color) => Some(*color),
+ }
+ }
+}
+
+impl Default for Fill {
+ fn default() -> Self {
+ Self::Color(Hsla::default())
+ }
+}
+
+impl From<Hsla> for Fill {
+ fn from(color: Hsla) -> Self {
+ Self::Color(color)
+ }
+}
+
+#[derive(Clone, Refineable, Default)]
+pub struct CornerRadii {
+ top_left: AbsoluteLength,
+ top_right: AbsoluteLength,
+ bottom_left: AbsoluteLength,
+ bottom_right: AbsoluteLength,
+}
+
+impl CornerRadii {
+ pub fn to_gpui(&self, rem_size: f32) -> gpui::scene::CornerRadii {
+ gpui::scene::CornerRadii {
+ top_left: self.top_left.to_pixels(rem_size),
+ top_right: self.top_right.to_pixels(rem_size),
+ bottom_left: self.bottom_left.to_pixels(rem_size),
+ bottom_right: self.bottom_right.to_pixels(rem_size),
+ }
+ }
+}
+
+pub trait Styleable {
+ type Style: refineable::Refineable;
+
+ fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
+
+ fn style(&mut self) -> playground::style::Style {
+ let mut style = playground::style::Style::default();
+ style.refine(self.declared_style());
+ style
+ }
+}
+
+// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
+//
+// Example:
+// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
+// fn p_2(mut self) -> Self where Self: Sized;
+use crate as playground; // Macro invocation references this crate as playground.
+pub trait StyleHelpers: Styleable<Style = Style> {
+ styleable_helpers!();
+
+ fn fill<F>(mut self, fill: F) -> Self
+ where
+ F: Into<Fill>,
+ Self: Sized,
+ {
+ self.declared_style().fill = Some(fill.into());
+ self
+ }
+
+ fn text_color<C>(mut self, color: C) -> Self
+ where
+ C: Into<Hsla>,
+ Self: Sized,
+ {
+ self.declared_style().text_color = Some(color.into());
+ self
+ }
+}
@@ -0,0 +1,151 @@
+use crate::{
+ element::{Element, IntoElement, Layout},
+ layout_context::LayoutContext,
+ paint_context::PaintContext,
+};
+use anyhow::Result;
+use gpui::text_layout::LineLayout;
+use parking_lot::Mutex;
+use std::sync::Arc;
+
+impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
+ type Element = Text;
+
+ fn into_element(self) -> Self::Element {
+ Text { text: self.into() }
+ }
+}
+
+pub struct Text {
+ text: ArcCow<'static, str>,
+}
+
+impl<V: 'static> Element<V> for Text {
+ type Layout = Arc<Mutex<Option<TextLayout>>>;
+
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<Layout<V, Self::Layout>> {
+ // let rem_size = cx.rem_pixels();
+ // let fonts = cx.platform().fonts();
+ // let text_style = cx.text_style();
+ // let line_height = cx.font_cache().line_height(text_style.font_size);
+ // let layout_engine = cx.layout_engine().expect("no layout engine present");
+ // let text = self.text.clone();
+ // let layout = Arc::new(Mutex::new(None));
+
+ // let style: Style = Style::default().refined(&self.metadata.style);
+ // let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
+ // let layout = layout.clone();
+ // move |params| {
+ // let line_layout = fonts.layout_line(
+ // text.as_ref(),
+ // text_style.font_size,
+ // &[(text.len(), text_style.to_run())],
+ // );
+
+ // let size = Size {
+ // width: line_layout.width,
+ // height: line_height,
+ // };
+
+ // layout.lock().replace(TextLayout {
+ // line_layout: Arc::new(line_layout),
+ // line_height,
+ // });
+
+ // size
+ // }
+ // })?;
+
+ // Ok((node_id, layout))
+ todo!()
+ }
+
+ fn paint<'a>(
+ &mut self,
+ view: &mut V,
+ layout: &mut Layout<V, Self::Layout>,
+ cx: &mut PaintContext<V>,
+ ) {
+ // ) {
+ // let element_layout_lock = layout.from_element.lock();
+ // let element_layout = element_layout_lock
+ // .as_ref()
+ // .expect("layout has not been performed");
+ // let line_layout = element_layout.line_layout.clone();
+ // let line_height = element_layout.line_height;
+ // drop(element_layout_lock);
+
+ // let text_style = cx.text_style();
+ // let line =
+ // gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
+ // line.paint(
+ // cx.scene,
+ // layout.from_engine.bounds.origin(),
+ // layout.from_engine.bounds,
+ // line_height,
+ // cx.legacy_cx,
+ // );
+ todo!()
+ }
+}
+
+pub struct TextLayout {
+ line_layout: Arc<LineLayout>,
+ line_height: f32,
+}
+
+pub enum ArcCow<'a, T: ?Sized> {
+ Borrowed(&'a T),
+ Owned(Arc<T>),
+}
+
+impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
+ fn clone(&self) -> Self {
+ match self {
+ Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
+ Self::Owned(owned) => Self::Owned(owned.clone()),
+ }
+ }
+}
+
+impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
+ fn from(s: &'a T) -> Self {
+ Self::Borrowed(s)
+ }
+}
+
+impl<T> From<Arc<T>> for ArcCow<'_, T> {
+ fn from(s: Arc<T>) -> Self {
+ Self::Owned(s)
+ }
+}
+
+impl From<String> for ArcCow<'_, str> {
+ fn from(value: String) -> Self {
+ Self::Owned(value.into())
+ }
+}
+
+impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ ArcCow::Borrowed(s) => s,
+ ArcCow::Owned(s) => s.as_ref(),
+ }
+ }
+}
+
+impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
+ fn as_ref(&self) -> &T {
+ match self {
+ ArcCow::Borrowed(borrowed) => borrowed,
+ ArcCow::Owned(owned) => owned.as_ref(),
+ }
+ }
+}
@@ -0,0 +1,84 @@
+use crate::color::{Hsla, Lerp};
+use std::ops::Range;
+
+pub mod rose_pine;
+
+pub struct ThemeColors {
+ pub base: Range<Hsla>,
+ pub surface: Range<Hsla>,
+ pub overlay: Range<Hsla>,
+ pub muted: Range<Hsla>,
+ pub subtle: Range<Hsla>,
+ pub text: Range<Hsla>,
+ pub highlight_low: Range<Hsla>,
+ pub highlight_med: Range<Hsla>,
+ pub highlight_high: Range<Hsla>,
+ pub success: Range<Hsla>,
+ pub warning: Range<Hsla>,
+ pub error: Range<Hsla>,
+ pub inserted: Range<Hsla>,
+ pub deleted: Range<Hsla>,
+ pub modified: Range<Hsla>,
+}
+
+impl ThemeColors {
+ pub fn base(&self, level: f32) -> Hsla {
+ self.base.lerp(level)
+ }
+
+ pub fn surface(&self, level: f32) -> Hsla {
+ self.surface.lerp(level)
+ }
+
+ pub fn overlay(&self, level: f32) -> Hsla {
+ self.overlay.lerp(level)
+ }
+
+ pub fn muted(&self, level: f32) -> Hsla {
+ self.muted.lerp(level)
+ }
+
+ pub fn subtle(&self, level: f32) -> Hsla {
+ self.subtle.lerp(level)
+ }
+
+ pub fn text(&self, level: f32) -> Hsla {
+ self.text.lerp(level)
+ }
+
+ pub fn highlight_low(&self, level: f32) -> Hsla {
+ self.highlight_low.lerp(level)
+ }
+
+ pub fn highlight_med(&self, level: f32) -> Hsla {
+ self.highlight_med.lerp(level)
+ }
+
+ pub fn highlight_high(&self, level: f32) -> Hsla {
+ self.highlight_high.lerp(level)
+ }
+
+ pub fn success(&self, level: f32) -> Hsla {
+ self.success.lerp(level)
+ }
+
+ pub fn warning(&self, level: f32) -> Hsla {
+ self.warning.lerp(level)
+ }
+
+ pub fn error(&self, level: f32) -> Hsla {
+ self.error.lerp(level)
+ }
+
+ pub fn inserted(&self, level: f32) -> Hsla {
+ self.inserted.lerp(level)
+ }
+
+ pub fn deleted(&self, level: f32) -> Hsla {
+ self.deleted.lerp(level)
+ }
+
+ pub fn modified(&self, level: f32) -> Hsla {
+ self.modified.lerp(level)
+ }
+}
@@ -0,0 +1,133 @@
+use std::ops::Range;
+
+use crate::{
+ color::{hsla, rgb, Hsla},
+ ThemeColors,
+};
+
+pub struct RosePineThemes {
+ pub default: RosePinePalette,
+ pub dawn: RosePinePalette,
+ pub moon: RosePinePalette,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct RosePinePalette {
+ pub base: Hsla,
+ pub surface: Hsla,
+ pub overlay: Hsla,
+ pub muted: Hsla,
+ pub subtle: Hsla,
+ pub text: Hsla,
+ pub love: Hsla,
+ pub gold: Hsla,
+ pub rose: Hsla,
+ pub pine: Hsla,
+ pub foam: Hsla,
+ pub iris: Hsla,
+ pub highlight_low: Hsla,
+ pub highlight_med: Hsla,
+ pub highlight_high: Hsla,
+}
+
+impl RosePinePalette {
+ pub fn default() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0x191724),
+ surface: rgb(0x1f1d2e),
+ overlay: rgb(0x26233a),
+ muted: rgb(0x6e6a86),
+ subtle: rgb(0x908caa),
+ text: rgb(0xe0def4),
+ love: rgb(0xeb6f92),
+ gold: rgb(0xf6c177),
+ rose: rgb(0xebbcba),
+ pine: rgb(0x31748f),
+ foam: rgb(0x9ccfd8),
+ iris: rgb(0xc4a7e7),
+ highlight_low: rgb(0x21202e),
+ highlight_med: rgb(0x403d52),
+ highlight_high: rgb(0x524f67),
+ }
+ }
+
+ pub fn moon() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0x232136),
+ surface: rgb(0x2a273f),
+ overlay: rgb(0x393552),
+ muted: rgb(0x6e6a86),
+ subtle: rgb(0x908caa),
+ text: rgb(0xe0def4),
+ love: rgb(0xeb6f92),
+ gold: rgb(0xf6c177),
+ rose: rgb(0xea9a97),
+ pine: rgb(0x3e8fb0),
+ foam: rgb(0x9ccfd8),
+ iris: rgb(0xc4a7e7),
+ highlight_low: rgb(0x2a283e),
+ highlight_med: rgb(0x44415a),
+ highlight_high: rgb(0x56526e),
+ }
+ }
+
+ pub fn dawn() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0xfaf4ed),
+ surface: rgb(0xfffaf3),
+ overlay: rgb(0xf2e9e1),
+ muted: rgb(0x9893a5),
+ subtle: rgb(0x797593),
+ text: rgb(0x575279),
+ love: rgb(0xb4637a),
+ gold: rgb(0xea9d34),
+ rose: rgb(0xd7827e),
+ pine: rgb(0x286983),
+ foam: rgb(0x56949f),
+ iris: rgb(0x907aa9),
+ highlight_low: rgb(0xf4ede8),
+ highlight_med: rgb(0xdfdad9),
+ highlight_high: rgb(0xcecacd),
+ }
+ }
+}
+
+pub fn default() -> ThemeColors {
+ theme_colors(&RosePinePalette::default())
+}
+
+pub fn moon() -> ThemeColors {
+ theme_colors(&RosePinePalette::moon())
+}
+
+pub fn dawn() -> ThemeColors {
+ theme_colors(&RosePinePalette::dawn())
+}
+
+fn theme_colors(p: &RosePinePalette) -> ThemeColors {
+ ThemeColors {
+ base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
+ surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
+ overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
+ muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
+ subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
+ text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
+ highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
+ highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
+ highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
+ success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+ warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
+ error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+ inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+ deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+ modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
+ }
+}
+
+/// Produces a range by multiplying the saturation and lightness of the base color by the given
+/// start and end factors.
+fn scale_sl(base: Hsla, (start_s, start_l): (f32, f32), (end_s, end_l): (f32, f32)) -> Range<Hsla> {
+ let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
+ let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
+ Range { start, end }
+}
@@ -0,0 +1,26 @@
+use crate::{
+ adapter::AdapterElement,
+ element::{AnyElement, Element},
+};
+use gpui::ViewContext;
+
+pub fn view<F, E>(mut render: F) -> ViewFn
+where
+ F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
+ E: Element<ViewFn>,
+{
+ ViewFn(Box::new(move |cx| (render)(cx).into_any()))
+}
+
+pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
+
+impl gpui::Entity for ViewFn {
+ type Event = ();
+}
+
+impl gpui::View for ViewFn {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
+ use gpui::Element as _;
+ AdapterElement((self.0)(cx)).into_any()
+ }
+}
@@ -0,0 +1,14 @@
+[package]
+name = "playground_macros"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/playground_macros.rs"
+proc-macro = true
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"
@@ -0,0 +1,91 @@
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
+
+use crate::derive_into_element::impl_into_element;
+
+pub fn derive_element(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let type_name = ast.ident;
+ let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
+
+ let (impl_generics, type_generics, where_clause, view_type_name, lifetimes) =
+ if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
+ if let GenericParam::Type(type_param) = param {
+ Some(type_param.ident.clone())
+ } else {
+ None
+ }
+ }) {
+ let mut lifetimes = vec![];
+ for param in ast.generics.params.iter() {
+ if let GenericParam::Lifetime(lifetime_def) = param {
+ lifetimes.push(lifetime_def.lifetime.clone());
+ }
+ }
+ let generics = ast.generics.split_for_impl();
+ (
+ generics.0,
+ Some(generics.1),
+ generics.2,
+ first_type_param,
+ lifetimes,
+ )
+ } else {
+ let generics = placeholder_view_generics.split_for_impl();
+ let placeholder_view_type_name: Ident = parse_quote! { V };
+ (
+ generics.0,
+ None,
+ generics.2,
+ placeholder_view_type_name,
+ vec![],
+ )
+ };
+
+ let lifetimes = if !lifetimes.is_empty() {
+ quote! { <#(#lifetimes),*> }
+ } else {
+ quote! {}
+ };
+
+ let impl_into_element = impl_into_element(
+ &impl_generics,
+ &view_type_name,
+ &type_name,
+ &type_generics,
+ &where_clause,
+ );
+
+ let gen = quote! {
+ impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
+ #where_clause
+ {
+ type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
+
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut playground::element::LayoutContext<V>,
+ ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
+ let mut element = self.render(view, cx).into_any();
+ let layout_id = element.layout(view, cx)?;
+ Ok(playground::element::Layout::new(layout_id, Some(element)))
+ }
+
+ fn paint(
+ &mut self,
+ view: &mut V,
+ layout: &mut playground::element::Layout<V, Self::Layout>,
+ cx: &mut playground::element::PaintContext<V>,
+ ) {
+ layout.paint(view, cx);
+ }
+ }
+
+ #impl_into_element
+ };
+
+ gen.into()
+}
@@ -0,0 +1,69 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{
+ parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, WhereClause,
+};
+
+pub fn derive_into_element(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let type_name = ast.ident;
+
+ let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
+ let placeholder_view_type_name: Ident = parse_quote! { V };
+ let view_type_name: Ident;
+ let impl_generics: syn::ImplGenerics<'_>;
+ let type_generics: Option<syn::TypeGenerics<'_>>;
+ let where_clause: Option<&'_ WhereClause>;
+
+ match ast.generics.params.iter().find_map(|param| {
+ if let GenericParam::Type(type_param) = param {
+ Some(type_param.ident.clone())
+ } else {
+ None
+ }
+ }) {
+ Some(type_name) => {
+ view_type_name = type_name;
+ let generics = ast.generics.split_for_impl();
+ impl_generics = generics.0;
+ type_generics = Some(generics.1);
+ where_clause = generics.2;
+ }
+ _ => {
+ view_type_name = placeholder_view_type_name;
+ let generics = placeholder_view_generics.split_for_impl();
+ impl_generics = generics.0;
+ type_generics = None;
+ where_clause = generics.2;
+ }
+ }
+
+ impl_into_element(
+ &impl_generics,
+ &view_type_name,
+ &type_name,
+ &type_generics,
+ &where_clause,
+ )
+ .into()
+}
+
+pub fn impl_into_element(
+ impl_generics: &syn::ImplGenerics<'_>,
+ view_type_name: &Ident,
+ type_name: &Ident,
+ type_generics: &Option<syn::TypeGenerics<'_>>,
+ where_clause: &Option<&WhereClause>,
+) -> proc_macro2::TokenStream {
+ quote! {
+ impl #impl_generics playground::element::IntoElement<#view_type_name> for #type_name #type_generics
+ #where_clause
+ {
+ type Element = Self;
+
+ fn into_element(self) -> Self {
+ self
+ }
+ }
+ }
+}
@@ -0,0 +1,26 @@
+use proc_macro::TokenStream;
+
+mod derive_element;
+mod derive_into_element;
+mod styleable_helpers;
+mod tailwind_lengths;
+
+#[proc_macro]
+pub fn styleable_helpers(args: TokenStream) -> TokenStream {
+ styleable_helpers::styleable_helpers(args)
+}
+
+#[proc_macro_derive(Element, attributes(element_crate))]
+pub fn derive_element(input: TokenStream) -> TokenStream {
+ derive_element::derive_element(input)
+}
+
+#[proc_macro_derive(IntoElement, attributes(element_crate))]
+pub fn derive_into_element(input: TokenStream) -> TokenStream {
+ derive_into_element::derive_into_element(input)
+}
+
+#[proc_macro_attribute]
+pub fn tailwind_lengths(attr: TokenStream, item: TokenStream) -> TokenStream {
+ tailwind_lengths::tailwind_lengths(attr, item)
+}
@@ -0,0 +1,147 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+ parse::{Parse, ParseStream, Result},
+ parse_macro_input,
+};
+
+struct StyleableMacroInput;
+
+impl Parse for StyleableMacroInput {
+ fn parse(_input: ParseStream) -> Result<Self> {
+ Ok(StyleableMacroInput)
+ }
+}
+
+pub fn styleable_helpers(input: TokenStream) -> TokenStream {
+ let _ = parse_macro_input!(input as StyleableMacroInput);
+ let methods = generate_methods();
+ let output = quote! {
+ #(#methods)*
+ };
+ output.into()
+}
+
+fn generate_methods() -> Vec<TokenStream2> {
+ let mut methods = Vec::new();
+
+ for (prefix, auto_allowed, fields) in tailwind_prefixes() {
+ for (suffix, length_tokens) in tailwind_lengths() {
+ if !auto_allowed && suffix == "auto" {
+ // Conditional to skip "auto"
+ continue;
+ }
+
+ let method_name = format_ident!("{}_{}", prefix, suffix);
+ let field_assignments = fields
+ .iter()
+ .map(|field_tokens| {
+ quote! {
+ style.#field_tokens = Some(gpui::geometry::#length_tokens);
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let method = quote! {
+ fn #method_name(mut self) -> Self where Self: std::marker::Sized {
+ let mut style = self.declared_style();
+ #(#field_assignments)*
+ self
+ }
+ };
+
+ methods.push(method);
+ }
+ }
+
+ methods
+}
+
+fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> {
+ vec![
+ ("0", quote! { pixels(0.) }),
+ ("1", quote! { rems(0.25) }),
+ ("2", quote! { rems(0.5) }),
+ ("3", quote! { rems(0.75) }),
+ ("4", quote! { rems(1.) }),
+ ("5", quote! { rems(1.25) }),
+ ("6", quote! { rems(1.5) }),
+ ("8", quote! { rems(2.0) }),
+ ("10", quote! { rems(2.5) }),
+ ("12", quote! { rems(3.) }),
+ ("16", quote! { rems(4.) }),
+ ("20", quote! { rems(5.) }),
+ ("24", quote! { rems(6.) }),
+ ("32", quote! { rems(8.) }),
+ ("40", quote! { rems(10.) }),
+ ("48", quote! { rems(12.) }),
+ ("56", quote! { rems(14.) }),
+ ("64", quote! { rems(16.) }),
+ ("72", quote! { rems(18.) }),
+ ("80", quote! { rems(20.) }),
+ ("96", quote! { rems(24.) }),
+ ("auto", quote! { auto() }),
+ ("px", quote! { pixels(1.) }),
+ ("full", quote! { relative(1.) }),
+ ("1_2", quote! { relative(0.5) }),
+ ("1_3", quote! { relative(1./3.) }),
+ ("2_3", quote! { relative(2./3.) }),
+ ("1_4", quote! { relative(0.25) }),
+ ("2_4", quote! { relative(0.5) }),
+ ("3_4", quote! { relative(0.75) }),
+ ("1_5", quote! { relative(0.2) }),
+ ("2_5", quote! { relative(0.4) }),
+ ("3_5", quote! { relative(0.6) }),
+ ("4_5", quote! { relative(0.8) }),
+ ("1_6", quote! { relative(1./6.) }),
+ ("5_6", quote! { relative(5./6.) }),
+ ("1_12", quote! { relative(1./12.) }),
+ // ("screen_50", quote! { DefiniteLength::Vh(50.0) }),
+ // ("screen_75", quote! { DefiniteLength::Vh(75.0) }),
+ // ("screen", quote! { DefiniteLength::Vh(100.0) }),
+ ]
+}
+
+fn tailwind_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
+ vec![
+ ("w", true, vec![quote! { size.width }]),
+ ("h", true, vec![quote! { size.height }]),
+ ("min_w", false, vec![quote! { min_size.width }]),
+ ("min_h", false, vec![quote! { min_size.height }]),
+ ("max_w", false, vec![quote! { max_size.width }]),
+ ("max_h", false, vec![quote! { max_size.height }]),
+ (
+ "m",
+ true,
+ vec![quote! { margin.top }, quote! { margin.bottom }],
+ ),
+ ("mt", true, vec![quote! { margin.top }]),
+ ("mb", true, vec![quote! { margin.bottom }]),
+ (
+ "mx",
+ true,
+ vec![quote! { margin.left }, quote! { margin.right }],
+ ),
+ ("ml", true, vec![quote! { margin.left }]),
+ ("mr", true, vec![quote! { margin.right }]),
+ (
+ "p",
+ false,
+ vec![quote! { padding.top }, quote! { padding.bottom }],
+ ),
+ ("pt", false, vec![quote! { padding.top }]),
+ ("pb", false, vec![quote! { padding.bottom }]),
+ (
+ "px",
+ false,
+ vec![quote! { padding.left }, quote! { padding.right }],
+ ),
+ ("pl", false, vec![quote! { padding.left }]),
+ ("pr", false, vec![quote! { padding.right }]),
+ ("top", true, vec![quote! { inset.top }]),
+ ("bottom", true, vec![quote! { inset.bottom }]),
+ ("left", true, vec![quote! { inset.left }]),
+ ("right", true, vec![quote! { inset.right }]),
+ ]
+}
@@ -0,0 +1,99 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{parse_macro_input, FnArg, ItemFn, PatType};
+
+pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let input_function = parse_macro_input!(item as ItemFn);
+
+ let visibility = &input_function.vis;
+ let function_signature = input_function.sig.clone();
+ let function_body = input_function.block;
+ let where_clause = &function_signature.generics.where_clause;
+
+ let argument_name = match function_signature.inputs.iter().nth(1) {
+ Some(FnArg::Typed(PatType { pat, .. })) => pat,
+ _ => panic!("Couldn't find the second argument in the function signature"),
+ };
+
+ let mut output_functions = TokenStream2::new();
+
+ for (length, value) in fixed_lengths() {
+ let function_name = format_ident!("{}{}", function_signature.ident, length);
+ output_functions.extend(quote! {
+ #visibility fn #function_name(mut self) -> Self #where_clause {
+ let #argument_name = #value.into();
+ #function_body
+ }
+ });
+ }
+
+ output_functions.into()
+}
+
+fn fixed_lengths() -> Vec<(&'static str, TokenStream2)> {
+ vec![
+ ("0", quote! { DefinedLength::Pixels(0.) }),
+ ("px", quote! { DefinedLength::Pixels(1.) }),
+ ("0_5", quote! { DefinedLength::Rems(0.125) }),
+ ("1", quote! { DefinedLength::Rems(0.25) }),
+ ("1_5", quote! { DefinedLength::Rems(0.375) }),
+ ("2", quote! { DefinedLength::Rems(0.5) }),
+ ("2_5", quote! { DefinedLength::Rems(0.625) }),
+ ("3", quote! { DefinedLength::Rems(0.75) }),
+ ("3_5", quote! { DefinedLength::Rems(0.875) }),
+ ("4", quote! { DefinedLength::Rems(1.) }),
+ ("5", quote! { DefinedLength::Rems(1.25) }),
+ ("6", quote! { DefinedLength::Rems(1.5) }),
+ ("7", quote! { DefinedLength::Rems(1.75) }),
+ ("8", quote! { DefinedLength::Rems(2.) }),
+ ("9", quote! { DefinedLength::Rems(2.25) }),
+ ("10", quote! { DefinedLength::Rems(2.5) }),
+ ("11", quote! { DefinedLength::Rems(2.75) }),
+ ("12", quote! { DefinedLength::Rems(3.) }),
+ ("14", quote! { DefinedLength::Rems(3.5) }),
+ ("16", quote! { DefinedLength::Rems(4.) }),
+ ("20", quote! { DefinedLength::Rems(5.) }),
+ ("24", quote! { DefinedLength::Rems(6.) }),
+ ("28", quote! { DefinedLength::Rems(7.) }),
+ ("32", quote! { DefinedLength::Rems(8.) }),
+ ("36", quote! { DefinedLength::Rems(9.) }),
+ ("40", quote! { DefinedLength::Rems(10.) }),
+ ("44", quote! { DefinedLength::Rems(11.) }),
+ ("48", quote! { DefinedLength::Rems(12.) }),
+ ("52", quote! { DefinedLength::Rems(13.) }),
+ ("56", quote! { DefinedLength::Rems(14.) }),
+ ("60", quote! { DefinedLength::Rems(15.) }),
+ ("64", quote! { DefinedLength::Rems(16.) }),
+ ("72", quote! { DefinedLength::Rems(18.) }),
+ ("80", quote! { DefinedLength::Rems(20.) }),
+ ("96", quote! { DefinedLength::Rems(24.) }),
+ ("half", quote! { DefinedLength::Percent(50.) }),
+ ("1_3rd", quote! { DefinedLength::Percent(33.333333) }),
+ ("2_3rd", quote! { DefinedLength::Percent(66.666667) }),
+ ("1_4th", quote! { DefinedLength::Percent(25.) }),
+ ("2_4th", quote! { DefinedLength::Percent(50.) }),
+ ("3_4th", quote! { DefinedLength::Percent(75.) }),
+ ("1_5th", quote! { DefinedLength::Percent(20.) }),
+ ("2_5th", quote! { DefinedLength::Percent(40.) }),
+ ("3_5th", quote! { DefinedLength::Percent(60.) }),
+ ("4_5th", quote! { DefinedLength::Percent(80.) }),
+ ("1_6th", quote! { DefinedLength::Percent(16.666667) }),
+ ("2_6th", quote! { DefinedLength::Percent(33.333333) }),
+ ("3_6th", quote! { DefinedLength::Percent(50.) }),
+ ("4_6th", quote! { DefinedLength::Percent(66.666667) }),
+ ("5_6th", quote! { DefinedLength::Percent(83.333333) }),
+ ("1_12th", quote! { DefinedLength::Percent(8.333333) }),
+ ("2_12th", quote! { DefinedLength::Percent(16.666667) }),
+ ("3_12th", quote! { DefinedLength::Percent(25.) }),
+ ("4_12th", quote! { DefinedLength::Percent(33.333333) }),
+ ("5_12th", quote! { DefinedLength::Percent(41.666667) }),
+ ("6_12th", quote! { DefinedLength::Percent(50.) }),
+ ("7_12th", quote! { DefinedLength::Percent(58.333333) }),
+ ("8_12th", quote! { DefinedLength::Percent(66.666667) }),
+ ("9_12th", quote! { DefinedLength::Percent(75.) }),
+ ("10_12th", quote! { DefinedLength::Percent(83.333333) }),
+ ("11_12th", quote! { DefinedLength::Percent(91.666667) }),
+ ("full", quote! { DefinedLength::Percent(100.) }),
+ ]
+}
@@ -7,6 +7,34 @@ pub mod test_app_context;
pub(crate) mod window;
mod window_input_handler;
+use crate::{
+ elements::{AnyElement, AnyRootElement, RootElement},
+ executor::{self, Task},
+ fonts::TextStyle,
+ json,
+ keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
+ platform::{
+ self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
+ PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
+ },
+ util::post_inc,
+ window::{Window, WindowContext},
+ AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
+};
+pub use action::*;
+use anyhow::{anyhow, Context, Result};
+use callback_collection::CallbackCollection;
+use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
+use derive_more::Deref;
+pub use menu::*;
+use parking_lot::Mutex;
+use platform::Event;
+use postage::oneshot;
+#[cfg(any(test, feature = "test-support"))]
+use ref_counts::LeakDetector;
+use ref_counts::RefCounts;
+use smallvec::SmallVec;
+use smol::prelude::*;
use std::{
any::{type_name, Any, TypeId},
cell::RefCell,
@@ -21,45 +49,12 @@ use std::{
sync::{Arc, Weak},
time::Duration,
};
-
-use anyhow::{anyhow, Context, Result};
-
-use derive_more::Deref;
-use parking_lot::Mutex;
-use postage::oneshot;
-use smallvec::SmallVec;
-use smol::prelude::*;
-use util::ResultExt;
-use uuid::Uuid;
-
-pub use action::*;
-use callback_collection::CallbackCollection;
-use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
-pub use menu::*;
-use platform::Event;
-#[cfg(any(test, feature = "test-support"))]
-use ref_counts::LeakDetector;
#[cfg(any(test, feature = "test-support"))]
pub use test_app_context::{ContextHandle, TestAppContext};
+use util::ResultExt;
+use uuid::Uuid;
use window_input_handler::WindowInputHandler;
-use crate::{
- elements::{AnyElement, AnyRootElement, RootElement},
- executor::{self, Task},
- fonts::TextStyle,
- json,
- keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
- platform::{
- self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
- PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
- },
- util::post_inc,
- window::{Window, WindowContext},
- AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
-};
-
-use self::ref_counts::RefCounts;
-
pub trait Entity: 'static {
type Event;
@@ -73,10 +68,12 @@ pub trait Entity: 'static {
}
pub trait View: Entity + Sized {
- fn ui_name() -> &'static str;
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self>;
fn focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
+ fn ui_name() -> &'static str {
+ type_name::<Self>()
+ }
fn key_down(&mut self, _: &KeyDownEvent, _: &mut ViewContext<Self>) -> bool {
false
}
@@ -640,7 +637,7 @@ impl AppContext {
pub fn add_action<A, V, F, R>(&mut self, handler: F)
where
A: Action,
- V: View,
+ V: 'static,
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
{
self.add_action_internal(handler, false)
@@ -649,7 +646,7 @@ impl AppContext {
pub fn capture_action<A, V, F>(&mut self, handler: F)
where
A: Action,
- V: View,
+ V: 'static,
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
{
self.add_action_internal(handler, true)
@@ -658,7 +655,7 @@ impl AppContext {
fn add_action_internal<A, V, F, R>(&mut self, mut handler: F, capture: bool)
where
A: Action,
- V: View,
+ V: 'static,
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
{
let handler = Box::new(
@@ -699,7 +696,7 @@ impl AppContext {
pub fn add_async_action<A, V, F>(&mut self, mut handler: F)
where
A: Action,
- V: View,
+ V: 'static,
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> Option<Task<Result<()>>>,
{
self.add_action(move |view, action, cx| {
@@ -898,8 +895,8 @@ impl AppContext {
fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
where
+ V: 'static,
F: 'static + FnMut(ViewHandle<V>, bool, &mut WindowContext) -> bool,
- V: View,
{
let subscription_id = post_inc(&mut self.next_subscription_id);
let observed = handle.downgrade();
@@ -1382,15 +1379,15 @@ impl AppContext {
self.windows.keys().copied()
}
- pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
+ pub fn read_view<V: 'static>(&self, handle: &ViewHandle<V>) -> &V {
if let Some(view) = self.views.get(&(handle.window, handle.view_id)) {
view.as_any().downcast_ref().expect("downcast is type safe")
} else {
- panic!("circular view reference for type {}", type_name::<T>());
+ panic!("circular view reference for type {}", type_name::<V>());
}
}
- fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
+ fn upgrade_view_handle<V: 'static>(&self, handle: &WeakViewHandle<V>) -> Option<ViewHandle<V>> {
if self.ref_counts.lock().is_entity_alive(handle.view_id) {
Some(ViewHandle::new(
handle.window,
@@ -1659,6 +1656,9 @@ impl AppContext {
subscription_id,
callback,
),
+ Effect::RepaintWindow { window } => {
+ self.handle_repaint_window_effect(window)
+ }
}
self.pending_notifications.clear();
} else {
@@ -1896,6 +1896,15 @@ impl AppContext {
});
}
+ fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
+ self.update_window(window, |cx| {
+ cx.layout(false).log_err();
+ if let Some(scene) = cx.paint().log_err() {
+ cx.window.platform_window.present_scene(scene);
+ }
+ });
+ }
+
fn handle_window_activation_effect(&mut self, window: AnyWindowHandle, active: bool) -> bool {
self.update_window(window, |cx| {
if cx.window.is_active == active {
@@ -2151,7 +2160,7 @@ struct ViewMetadata {
keymap_context: KeymapContext,
}
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Debug)]
pub struct WindowInvalidation {
pub updated: HashSet<usize>,
pub removed: Vec<usize>,
@@ -2255,6 +2264,9 @@ pub enum Effect {
window: AnyWindowHandle,
is_active: bool,
},
+ RepaintWindow {
+ window: AnyWindowHandle,
+ },
WindowActivationObservation {
window: AnyWindowHandle,
subscription_id: usize,
@@ -2448,6 +2460,10 @@ impl Debug for Effect {
.debug_struct("Effect::ActiveLabeledTasksObservation")
.field("subscription_id", subscription_id)
.finish(),
+ Effect::RepaintWindow { window } => f
+ .debug_struct("Effect::RepaintWindow")
+ .field("window_id", &window.id())
+ .finish(),
}
}
}
@@ -2543,10 +2559,7 @@ pub trait AnyView {
}
}
-impl<V> AnyView for V
-where
- V: View,
-{
+impl<V: View> AnyView for V {
fn as_any(&self) -> &dyn Any {
self
}
@@ -2878,7 +2891,7 @@ pub struct ViewContext<'a, 'b, T: ?Sized> {
view_type: PhantomData<T>,
}
-impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
+impl<'a, 'b, V> Deref for ViewContext<'a, 'b, V> {
type Target = WindowContext<'a>;
fn deref(&self) -> &Self::Target {
@@ -2886,14 +2899,14 @@ impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
}
}
-impl<T: View> DerefMut for ViewContext<'_, '_, T> {
+impl<'a, 'b, V> DerefMut for ViewContext<'a, 'b, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.window_context
}
}
-impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
- pub(crate) fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
+impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
+ pub fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
Self {
window_context: Reference::Mutable(window_context),
view_id,
@@ -2901,7 +2914,7 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
}
}
- pub(crate) fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
+ pub fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
Self {
window_context: Reference::Immutable(window_context),
view_id,
@@ -2913,6 +2926,12 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
&mut self.window_context
}
+ pub fn notify(&mut self) {
+ let window = self.window_handle;
+ let view_id = self.view_id;
+ self.window_context.notify_view(window, view_id);
+ }
+
pub fn handle(&self) -> ViewHandle<V> {
ViewHandle::new(
self.window_handle,
@@ -3226,21 +3245,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
})
}
- pub fn emit(&mut self, payload: V::Event) {
- self.window_context
- .pending_effects
- .push_back(Effect::Event {
- entity_id: self.view_id,
- payload: Box::new(payload),
- });
- }
-
- pub fn notify(&mut self) {
- let window = self.window_handle;
- let view_id = self.view_id;
- self.window_context.notify_view(window, view_id);
- }
-
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
let handle = self.handle();
self.window_context
@@ -3341,6 +3345,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
self.element_state::<Tag, T>(element_id, T::default())
}
+ pub fn rem_pixels(&self) -> f32 {
+ 16.
+ }
+
pub fn default_element_state_dynamic<T: 'static + Default>(
&mut self,
tag: TypeTag,
@@ -3350,6 +3358,17 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
}
}
+impl<V: View> ViewContext<'_, '_, V> {
+ pub fn emit(&mut self, event: V::Event) {
+ self.window_context
+ .pending_effects
+ .push_back(Effect::Event {
+ entity_id: self.view_id,
+ payload: Box::new(event),
+ });
+ }
+}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TypeTag {
tag: TypeId,
@@ -3428,15 +3447,27 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
}
}
-pub struct LayoutContext<'a, 'b, 'c, V: View> {
- view_context: &'c mut ViewContext<'a, 'b, V>,
+/// Methods shared by both LayoutContext and PaintContext
+///
+/// It's that PaintContext should be implemented in terms of layout context and
+/// deref to it, in which case we wouldn't need this.
+pub trait RenderContext<'a, 'b, V> {
+ fn text_style(&self) -> TextStyle;
+ fn push_text_style(&mut self, style: TextStyle);
+ fn pop_text_style(&mut self);
+ fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V>;
+}
+
+pub struct LayoutContext<'a, 'b, 'c, V> {
+ // Nathan: Making this is public while I work on playground.
+ pub view_context: &'c mut ViewContext<'a, 'b, V>,
new_parents: &'c mut HashMap<usize, usize>,
views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
- text_style_stack: Vec<Arc<TextStyle>>,
+ text_style_stack: Vec<TextStyle>,
pub refreshing: bool,
}
-impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
pub fn new(
view_context: &'c mut ViewContext<'a, 'b, V>,
new_parents: &'c mut HashMap<usize, usize>,
@@ -3500,26 +3531,39 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
.push(self_view_id);
}
- pub fn text_style(&self) -> Arc<TextStyle> {
+ pub fn with_text_style<F, T>(&mut self, style: TextStyle, f: F) -> T
+ where
+ F: FnOnce(&mut Self) -> T,
+ {
+ self.push_text_style(style);
+ let result = f(self);
+ self.pop_text_style();
+ result
+ }
+}
+
+impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, 'c, V> {
+ fn text_style(&self) -> TextStyle {
self.text_style_stack
.last()
.cloned()
- .unwrap_or(Default::default())
+ .unwrap_or(TextStyle::default(&self.font_cache))
}
- pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
- where
- S: Into<Arc<TextStyle>>,
- F: FnOnce(&mut Self) -> T,
- {
- self.text_style_stack.push(style.into());
- let result = f(self);
+ fn push_text_style(&mut self, style: TextStyle) {
+ self.text_style_stack.push(style);
+ }
+
+ fn pop_text_style(&mut self) {
self.text_style_stack.pop();
- result
+ }
+
+ fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+ &mut self.view_context
}
}
-impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {
type Target = ViewContext<'a, 'b, V>;
fn deref(&self) -> &Self::Target {
@@ -3527,13 +3571,13 @@ impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
}
}
-impl<V: View> DerefMut for LayoutContext<'_, '_, '_, V> {
+impl<V> DerefMut for LayoutContext<'_, '_, '_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_context
}
}
-impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
BorrowAppContext::read_with(&*self.view_context, f)
}
@@ -3543,7 +3587,7 @@ impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
}
}
-impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
type Result<T> = T;
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
@@ -3573,39 +3617,42 @@ impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
}
}
-pub struct PaintContext<'a, 'b, 'c, V: View> {
- view_context: &'c mut ViewContext<'a, 'b, V>,
- text_style_stack: Vec<Arc<TextStyle>>,
+pub struct PaintContext<'a, 'b, 'c, V> {
+ pub view_context: &'c mut ViewContext<'a, 'b, V>,
+ text_style_stack: Vec<TextStyle>,
}
-impl<'a, 'b, 'c, V: View> PaintContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
Self {
view_context,
text_style_stack: Vec::new(),
}
}
+}
- pub fn text_style(&self) -> Arc<TextStyle> {
+impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, 'c, V> {
+ fn text_style(&self) -> TextStyle {
self.text_style_stack
.last()
.cloned()
- .unwrap_or(Default::default())
+ .unwrap_or(TextStyle::default(&self.font_cache))
}
- pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
- where
- S: Into<Arc<TextStyle>>,
- F: FnOnce(&mut Self) -> T,
- {
- self.text_style_stack.push(style.into());
- let result = f(self);
+ fn push_text_style(&mut self, style: TextStyle) {
+ self.text_style_stack.push(style);
+ }
+
+ fn pop_text_style(&mut self) {
self.text_style_stack.pop();
- result
+ }
+
+ fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+ &mut self.view_context
}
}
-impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for PaintContext<'a, 'b, 'c, V> {
type Target = ViewContext<'a, 'b, V>;
fn deref(&self) -> &Self::Target {
@@ -3613,13 +3660,13 @@ impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
}
}
-impl<V: View> DerefMut for PaintContext<'_, '_, '_, V> {
+impl<V> DerefMut for PaintContext<'_, '_, '_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_context
}
}
-impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for PaintContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
BorrowAppContext::read_with(&*self.view_context, f)
}
@@ -3629,7 +3676,7 @@ impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
}
}
-impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
type Result<T> = T;
fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
@@ -3661,25 +3708,37 @@ impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
}
}
-pub struct EventContext<'a, 'b, 'c, V: View> {
+pub struct EventContext<'a, 'b, 'c, V> {
view_context: &'c mut ViewContext<'a, 'b, V>,
pub(crate) handled: bool,
+ // I would like to replace handled with this.
+ // Being additive for now.
+ pub bubble: bool,
}
-impl<'a, 'b, 'c, V: View> EventContext<'a, 'b, 'c, V> {
- pub(crate) fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
+impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
+ pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
EventContext {
view_context,
handled: true,
+ bubble: false,
}
}
pub fn propagate_event(&mut self) {
self.handled = false;
}
+
+ pub fn bubble_event(&mut self) {
+ self.bubble = true;
+ }
+
+ pub fn event_bubbled(&self) -> bool {
+ self.bubble
+ }
}
-impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {
type Target = ViewContext<'a, 'b, V>;
fn deref(&self) -> &Self::Target {
@@ -3687,13 +3746,13 @@ impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
}
}
-impl<V: View> DerefMut for EventContext<'_, '_, '_, V> {
+impl<V> DerefMut for EventContext<'_, '_, '_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_context
}
}
-impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for EventContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
BorrowAppContext::read_with(&*self.view_context, f)
}
@@ -3703,7 +3762,7 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
}
}
-impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for EventContext<'_, '_, '_, V> {
type Result<T> = T;
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
@@ -4031,7 +4090,7 @@ impl<V> Clone for WindowHandle<V> {
impl<V> Copy for WindowHandle<V> {}
-impl<V: View> WindowHandle<V> {
+impl<V: 'static> WindowHandle<V> {
fn new(window_id: usize) -> Self {
WindowHandle {
any_handle: AnyWindowHandle::new(window_id, TypeId::of::<V>()),
@@ -4069,7 +4128,9 @@ impl<V: View> WindowHandle<V> {
.update(cx, update)
})
}
+}
+impl<V: View> WindowHandle<V> {
pub fn replace_root<C, F>(&self, cx: &mut C, build_root: F) -> C::Result<ViewHandle<V>>
where
C: BorrowWindowContext,
@@ -4149,7 +4210,7 @@ impl AnyWindowHandle {
self.update(cx, |cx| cx.add_view(build_view))
}
- pub fn downcast<V: View>(self) -> Option<WindowHandle<V>> {
+ pub fn downcast<V: 'static>(self) -> Option<WindowHandle<V>> {
if self.root_view_type == TypeId::of::<V>() {
Some(WindowHandle {
any_handle: self,
@@ -4160,7 +4221,7 @@ impl AnyWindowHandle {
}
}
- pub fn root_is<V: View>(&self) -> bool {
+ pub fn root_is<V: 'static>(&self) -> bool {
self.root_view_type == TypeId::of::<V>()
}
@@ -4238,9 +4299,9 @@ impl AnyWindowHandle {
}
#[repr(transparent)]
-pub struct ViewHandle<T> {
+pub struct ViewHandle<V> {
any_handle: AnyViewHandle,
- view_type: PhantomData<T>,
+ view_type: PhantomData<V>,
}
impl<T> Deref for ViewHandle<T> {
@@ -4251,15 +4312,15 @@ impl<T> Deref for ViewHandle<T> {
}
}
-impl<T: View> ViewHandle<T> {
+impl<V: 'static> ViewHandle<V> {
fn new(window: AnyWindowHandle, view_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
Self {
- any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<T>(), ref_counts.clone()),
+ any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<V>(), ref_counts.clone()),
view_type: PhantomData,
}
}
- pub fn downgrade(&self) -> WeakViewHandle<T> {
+ pub fn downgrade(&self) -> WeakViewHandle<V> {
WeakViewHandle::new(self.window, self.view_id)
}
@@ -4275,14 +4336,14 @@ impl<T: View> ViewHandle<T> {
self.view_id
}
- pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
+ pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
cx.read_view(self)
}
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Result<S>
where
C: BorrowWindowContext,
- F: FnOnce(&T, &ViewContext<T>) -> S,
+ F: FnOnce(&V, &ViewContext<V>) -> S,
{
cx.read_window(self.window, |cx| {
let cx = ViewContext::immutable(cx, self.view_id);
@@ -4293,7 +4354,7 @@ impl<T: View> ViewHandle<T> {
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Result<S>
where
C: BorrowWindowContext,
- F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
+ F: FnOnce(&mut V, &mut ViewContext<V>) -> S,
{
let mut update = Some(update);
@@ -4429,8 +4490,8 @@ impl AnyViewHandle {
TypeId::of::<T>() == self.view_type
}
- pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
- if self.is::<T>() {
+ pub fn downcast<V: 'static>(self) -> Option<ViewHandle<V>> {
+ if self.is::<V>() {
Some(ViewHandle {
any_handle: self,
view_type: PhantomData,
@@ -4440,8 +4501,8 @@ impl AnyViewHandle {
}
}
- pub fn downcast_ref<T: View>(&self) -> Option<&ViewHandle<T>> {
- if self.is::<T>() {
+ pub fn downcast_ref<V: 'static>(&self) -> Option<&ViewHandle<V>> {
+ if self.is::<V>() {
Some(unsafe { mem::transmute(self) })
} else {
None
@@ -4640,7 +4701,7 @@ impl<T> WeakHandle for WeakViewHandle<T> {
}
}
-impl<V: View> WeakViewHandle<V> {
+impl<V: 'static> WeakViewHandle<V> {
fn new(window: AnyWindowHandle, view_id: usize) -> Self {
Self {
any_handle: AnyWeakViewHandle {
@@ -4680,28 +4741,47 @@ impl<V: View> WeakViewHandle<V> {
cx.read(|cx| {
let handle = cx
.upgrade_view_handle(self)
- .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
+ .ok_or_else(|| anyhow!("view was dropped"))?;
cx.read_window(self.window, |cx| handle.read_with(cx, read))
.ok_or_else(|| anyhow!("window was removed"))
})
}
- pub fn update<T>(
+ pub fn update<T, B>(
&self,
- cx: &mut AsyncAppContext,
+ cx: &mut B,
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
- ) -> Result<T> {
- cx.update(|cx| {
- let handle = cx
- .upgrade_view_handle(self)
- .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
- cx.update_window(self.window, |cx| handle.update(cx, update))
- .ok_or_else(|| anyhow!("window was removed"))
+ ) -> Result<T>
+ where
+ B: BorrowWindowContext,
+ B::Result<Option<T>>: Flatten<T>,
+ {
+ cx.update_window(self.window(), |cx| {
+ cx.upgrade_view_handle(self)
+ .map(|handle| handle.update(cx, update))
})
+ .flatten()
+ .ok_or_else(|| anyhow!("window was removed"))
+ }
+}
+
+pub trait Flatten<T> {
+ fn flatten(self) -> Option<T>;
+}
+
+impl<T> Flatten<T> for Option<Option<T>> {
+ fn flatten(self) -> Option<T> {
+ self.flatten()
+ }
+}
+
+impl<T> Flatten<T> for Option<T> {
+ fn flatten(self) -> Option<T> {
+ self
}
}
-impl<T> Deref for WeakViewHandle<T> {
+impl<V> Deref for WeakViewHandle<V> {
type Target = AnyWeakViewHandle;
fn deref(&self) -> &Self::Target {
@@ -4709,7 +4789,7 @@ impl<T> Deref for WeakViewHandle<T> {
}
}
-impl<T> Clone for WeakViewHandle<T> {
+impl<V> Clone for WeakViewHandle<V> {
fn clone(&self) -> Self {
Self {
any_handle: self.any_handle.clone(),
@@ -5263,6 +5343,7 @@ mod tests {
button: MouseButton::Left,
modifiers: Default::default(),
click_count: 1,
+ is_down: true,
}),
false,
);
@@ -1,6 +1,6 @@
use crate::{
elements::AnyRootElement,
- geometry::rect::RectF,
+ geometry::{rect::RectF, Size},
json::ToJson,
keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
platform::{
@@ -8,8 +8,9 @@ use crate::{
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
},
scene::{
- CursorRegion, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag, MouseEvent,
- MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
+ CursorRegion, EventHandler, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
+ MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
+ Scene,
},
text_layout::TextLayoutCache,
util::post_inc,
@@ -31,7 +32,11 @@ use sqlez::{
use std::{
any::TypeId,
mem,
- ops::{Deref, DerefMut, Range},
+ ops::{Deref, DerefMut, Range, Sub},
+};
+use taffy::{
+ tree::{Measurable, MeasureFunc},
+ Taffy,
};
use util::ResultExt;
use uuid::Uuid;
@@ -39,6 +44,7 @@ use uuid::Uuid;
use super::{Reference, ViewMetadata};
pub struct Window {
+ layout_engines: Vec<LayoutEngine>,
pub(crate) root_view: Option<AnyViewHandle>,
pub(crate) focused_view_id: Option<usize>,
pub(crate) parents: HashMap<usize, usize>,
@@ -51,6 +57,7 @@ pub struct Window {
appearance: Appearance,
cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>,
+ event_handlers: Vec<EventHandler>,
last_mouse_moved_event: Option<Event>,
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
@@ -67,12 +74,13 @@ impl Window {
build_view: F,
) -> Self
where
- F: FnOnce(&mut ViewContext<V>) -> V,
V: View,
+ F: FnOnce(&mut ViewContext<V>) -> V,
{
let titlebar_height = platform_window.titlebar_height();
let appearance = platform_window.appearance();
let mut window = Self {
+ layout_engines: Vec::new(),
root_view: None,
focused_view_id: None,
parents: Default::default(),
@@ -83,6 +91,7 @@ impl Window {
rendered_views: Default::default(),
cursor_regions: Default::default(),
mouse_regions: Default::default(),
+ event_handlers: Default::default(),
text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
last_mouse_moved_event: None,
hovered_region_ids: Default::default(),
@@ -109,6 +118,10 @@ impl Window {
.as_ref()
.expect("root_view called during window construction")
}
+
+ pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+ mem::take(&mut self.event_handlers)
+ }
}
pub struct WindowContext<'a> {
@@ -207,6 +220,24 @@ impl<'a> WindowContext<'a> {
}
}
+ pub fn repaint(&mut self) {
+ let window = self.window();
+ self.pending_effects
+ .push_back(Effect::RepaintWindow { window });
+ }
+
+ pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
+ self.window.layout_engines.last_mut()
+ }
+
+ pub fn push_layout_engine(&mut self, engine: LayoutEngine) {
+ self.window.layout_engines.push(engine);
+ }
+
+ pub fn pop_layout_engine(&mut self) -> Option<LayoutEngine> {
+ self.window.layout_engines.pop()
+ }
+
pub fn remove_window(&mut self) {
self.removed = true;
}
@@ -227,6 +258,10 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.content_size()
}
+ pub fn mouse_position(&self) -> Vector2F {
+ self.window.mouse_position
+ }
+
pub fn text_layout_cache(&self) -> &TextLayoutCache {
&self.window.text_layout_cache
}
@@ -242,14 +277,11 @@ impl<'a> WindowContext<'a> {
Some(result)
}
- pub(crate) fn update_view<T, S>(
+ pub(crate) fn update_view<V: 'static, S>(
&mut self,
- handle: &ViewHandle<T>,
- update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
- ) -> S
- where
- T: View,
- {
+ handle: &ViewHandle<V>,
+ update: &mut dyn FnMut(&mut V, &mut ViewContext<V>) -> S,
+ ) -> S {
self.update_any_view(handle.view_id, |view, cx| {
let mut cx = ViewContext::mutable(cx, handle.view_id);
update(
@@ -475,6 +507,8 @@ impl<'a> WindowContext<'a> {
}
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
+ self.dispatch_to_new_event_handlers(&event);
+
let mut mouse_events = SmallVec::<[_; 2]>::new();
let mut notified_views: HashSet<usize> = Default::default();
let handle = self.window_handle;
@@ -852,6 +886,18 @@ impl<'a> WindowContext<'a> {
any_event_handled
}
+ fn dispatch_to_new_event_handlers(&mut self, event: &Event) {
+ if let Some(mouse_event) = event.mouse_event() {
+ let event_handlers = self.window.take_event_handlers();
+ for event_handler in event_handlers.iter().rev() {
+ if event_handler.event_type == mouse_event.type_id() {
+ (event_handler.handler)(mouse_event, self);
+ }
+ }
+ self.window.event_handlers = event_handlers;
+ }
+ }
+
pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
let handle = self.window_handle;
if let Some(focused_view_id) = self.window.focused_view_id {
@@ -942,14 +988,16 @@ impl<'a> WindowContext<'a> {
Ok(element)
}
- pub(crate) fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
+ pub fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
let window_size = self.window.platform_window.content_size();
let root_view_id = self.window.root_view().id();
+
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
+
let mut new_parents = HashMap::default();
let mut views_to_notify_if_ancestors_change = HashMap::default();
rendered_root.layout(
- SizeConstraint::strict(window_size),
+ SizeConstraint::new(window_size, window_size),
&mut new_parents,
&mut views_to_notify_if_ancestors_change,
refreshing,
@@ -982,7 +1030,7 @@ impl<'a> WindowContext<'a> {
Ok(old_parents)
}
- pub(crate) fn paint(&mut self) -> Result<Scene> {
+ pub fn paint(&mut self) -> Result<Scene> {
let window_size = self.window.platform_window.content_size();
let scale_factor = self.window.platform_window.scale_factor();
@@ -1001,9 +1049,10 @@ impl<'a> WindowContext<'a> {
.insert(root_view_id, rendered_root);
self.window.text_layout_cache.finish_frame();
- let scene = scene_builder.build();
+ let mut scene = scene_builder.build();
self.window.cursor_regions = scene.cursor_regions();
self.window.mouse_regions = scene.mouse_regions();
+ self.window.event_handlers = scene.take_event_handlers();
if self.window_is_active() {
if let Some(event) = self.window.last_mouse_moved_event.clone() {
@@ -1014,6 +1063,11 @@ impl<'a> WindowContext<'a> {
Ok(scene)
}
+ pub fn root_element(&self) -> &Box<dyn AnyRootElement> {
+ let view_id = self.window.root_view().id();
+ self.window.rendered_views.get(&view_id).unwrap()
+ }
+
pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
let focused_view_id = self.window.focused_view_id?;
self.window
@@ -1216,6 +1270,119 @@ impl<'a> WindowContext<'a> {
}
}
+#[derive(Default)]
+pub struct LayoutEngine(Taffy);
+pub use taffy::style::Style as LayoutStyle;
+
+impl LayoutEngine {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutId>
+ where
+ C: IntoIterator<Item = LayoutId>,
+ {
+ Ok(self
+ .0
+ .new_with_children(style, &children.into_iter().collect::<Vec<_>>())?)
+ }
+
+ pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
+ where
+ F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
+ {
+ Ok(self
+ .0
+ .new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?)
+ }
+
+ pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> {
+ self.0.compute_layout(
+ root,
+ taffy::geometry::Size {
+ width: available_space.x().into(),
+ height: available_space.y().into(),
+ },
+ )?;
+ Ok(())
+ }
+
+ pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
+ Ok(self.0.layout(node)?.into())
+ }
+}
+
+pub struct MeasureFn<F>(F);
+
+impl<F: Send + Sync> Measurable for MeasureFn<F>
+where
+ F: Fn(MeasureParams) -> Size<f32>,
+{
+ fn measure(
+ &self,
+ known_dimensions: taffy::prelude::Size<Option<f32>>,
+ available_space: taffy::prelude::Size<taffy::style::AvailableSpace>,
+ ) -> taffy::prelude::Size<f32> {
+ (self.0)(MeasureParams {
+ known_dimensions: known_dimensions.into(),
+ available_space: available_space.into(),
+ })
+ .into()
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct EngineLayout {
+ pub bounds: RectF,
+ pub order: u32,
+}
+
+pub struct MeasureParams {
+ pub known_dimensions: Size<Option<f32>>,
+ pub available_space: Size<AvailableSpace>,
+}
+
+#[derive(Clone)]
+pub enum AvailableSpace {
+ /// The amount of space available is the specified number of pixels
+ Pixels(f32),
+ /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
+ MinContent,
+ /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
+ MaxContent,
+}
+
+impl Default for AvailableSpace {
+ fn default() -> Self {
+ Self::Pixels(0.)
+ }
+}
+
+impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
+ fn from(value: taffy::prelude::AvailableSpace) -> Self {
+ match value {
+ taffy::prelude::AvailableSpace::Definite(pixels) => Self::Pixels(pixels),
+ taffy::prelude::AvailableSpace::MinContent => Self::MinContent,
+ taffy::prelude::AvailableSpace::MaxContent => Self::MaxContent,
+ }
+ }
+}
+
+impl From<&taffy::tree::Layout> for EngineLayout {
+ fn from(value: &taffy::tree::Layout) -> Self {
+ Self {
+ bounds: RectF::new(
+ vec2f(value.location.x, value.location.y),
+ vec2f(value.size.width, value.size.height),
+ ),
+ order: value.order,
+ }
+ }
+}
+
+pub type LayoutId = taffy::prelude::NodeId;
+
pub struct RenderParams {
pub view_id: usize,
pub titlebar_height: f32,
@@ -1324,6 +1491,12 @@ impl SizeConstraint {
max: size,
}
}
+ pub fn loose(max: Vector2F) -> Self {
+ Self {
+ min: Vector2F::zero(),
+ max,
+ }
+ }
pub fn strict_along(axis: Axis, max: f32) -> Self {
match axis {
@@ -1360,6 +1533,17 @@ impl SizeConstraint {
}
}
+impl Sub<Vector2F> for SizeConstraint {
+ type Output = SizeConstraint;
+
+ fn sub(self, rhs: Vector2F) -> SizeConstraint {
+ SizeConstraint {
+ min: self.min - rhs,
+ max: self.max - rhs,
+ }
+ }
+}
+
impl Default for SizeConstraint {
fn default() -> Self {
SizeConstraint {
@@ -1378,6 +1562,7 @@ impl ToJson for SizeConstraint {
}
}
+#[derive(Clone)]
pub struct ChildView {
view_id: usize,
view_name: &'static str,
@@ -1393,7 +1578,7 @@ impl ChildView {
}
}
-impl<V: View> Element<V> for ChildView {
+impl<V: 'static> Element<V> for ChildView {
type LayoutState = ();
type PaintState = ();
@@ -15,35 +15,75 @@ use serde_json::json;
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
#[repr(transparent)]
-pub struct Color(#[schemars(with = "String")] ColorU);
+pub struct Color(#[schemars(with = "String")] pub ColorU);
+
+pub fn color(rgba: u32) -> Color {
+ Color::from_u32(rgba)
+}
+
+pub fn rgb(r: f32, g: f32, b: f32) -> Color {
+ Color(ColorF::new(r, g, b, 1.).to_u8())
+}
+
+pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
+ Color(ColorF::new(r, g, b, a).to_u8())
+}
+
+pub fn transparent_black() -> Color {
+ Color(ColorU::transparent_black())
+}
+
+pub fn black() -> Color {
+ Color(ColorU::black())
+}
+
+pub fn white() -> Color {
+ Color(ColorU::white())
+}
+
+pub fn red() -> Color {
+ color(0xff0000ff)
+}
+
+pub fn green() -> Color {
+ color(0x00ff00ff)
+}
+
+pub fn blue() -> Color {
+ color(0x0000ffff)
+}
+
+pub fn yellow() -> Color {
+ color(0xffff00ff)
+}
impl Color {
pub fn transparent_black() -> Self {
- Self(ColorU::transparent_black())
+ transparent_black()
}
pub fn black() -> Self {
- Self(ColorU::black())
+ black()
}
pub fn white() -> Self {
- Self(ColorU::white())
+ white()
}
pub fn red() -> Self {
- Self(ColorU::from_u32(0xff0000ff))
+ Color::from_u32(0xff0000ff)
}
pub fn green() -> Self {
- Self(ColorU::from_u32(0x00ff00ff))
+ Color::from_u32(0x00ff00ff)
}
pub fn blue() -> Self {
- Self(ColorU::from_u32(0x0000ffff))
+ Color::from_u32(0x0000ffff)
}
pub fn yellow() -> Self {
- Self(ColorU::from_u32(0xffff00ff))
+ Color::from_u32(0xffff00ff)
}
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
@@ -101,6 +141,12 @@ impl<'de> Deserialize<'de> for Color {
}
}
+impl From<u32> for Color {
+ fn from(value: u32) -> Self {
+ Self(ColorU::from_u32(value))
+ }
+}
+
impl ToJson for Color {
fn to_json(&self) -> serde_json::Value {
json!(format!(
@@ -34,7 +34,7 @@ use crate::{
rect::RectF,
vector::{vec2f, Vector2F},
},
- json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
+ json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
ViewContext, WeakViewHandle, WindowContext,
};
use anyhow::{anyhow, Result};
@@ -42,14 +42,19 @@ use collections::HashMap;
use core::panic;
use json::ToJson;
use smallvec::SmallVec;
-use std::{any::Any, borrow::Cow, mem, ops::Range};
+use std::{
+ any::{type_name, Any},
+ borrow::Cow,
+ mem,
+ ops::Range,
+};
-pub trait Element<V: View>: 'static {
+pub trait Element<V: 'static>: 'static {
type LayoutState;
type PaintState;
fn view_name(&self) -> &'static str {
- V::ui_name()
+ type_name::<V>()
}
fn layout(
@@ -231,11 +236,7 @@ pub trait Element<V: View>: 'static {
}
}
-pub trait RenderElement {
- fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
-}
-
-trait AnyElementState<V: View> {
+trait AnyElementState<V> {
fn layout(
&mut self,
constraint: SizeConstraint,
@@ -249,7 +250,7 @@ trait AnyElementState<V: View> {
origin: Vector2F,
visible_bounds: RectF,
view: &mut V,
- cx: &mut ViewContext<V>,
+ cx: &mut PaintContext<V>,
);
fn rect_for_text_range(
@@ -266,7 +267,7 @@ trait AnyElementState<V: View> {
fn metadata(&self) -> Option<&dyn Any>;
}
-enum ElementState<V: View, E: Element<V>> {
+enum ElementState<V: 'static, E: Element<V>> {
Empty,
Init {
element: E,
@@ -287,7 +288,7 @@ enum ElementState<V: View, E: Element<V>> {
},
}
-impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
+impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
fn layout(
&mut self,
constraint: SizeConstraint,
@@ -330,7 +331,7 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
origin: Vector2F,
visible_bounds: RectF,
view: &mut V,
- cx: &mut ViewContext<V>,
+ cx: &mut PaintContext<V>,
) {
*self = match mem::take(self) {
ElementState::PostLayout {
@@ -469,18 +470,18 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
}
}
-impl<V: View, E: Element<V>> Default for ElementState<V, E> {
+impl<V, E: Element<V>> Default for ElementState<V, E> {
fn default() -> Self {
Self::Empty
}
}
-pub struct AnyElement<V: View> {
+pub struct AnyElement<V> {
state: Box<dyn AnyElementState<V>>,
name: Option<Cow<'static, str>>,
}
-impl<V: View> AnyElement<V> {
+impl<V> AnyElement<V> {
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
@@ -506,7 +507,7 @@ impl<V: View> AnyElement<V> {
origin: Vector2F,
visible_bounds: RectF,
view: &mut V,
- cx: &mut ViewContext<V>,
+ cx: &mut PaintContext<V>,
) {
self.state.paint(scene, origin, visible_bounds, view, cx);
}
@@ -548,7 +549,7 @@ impl<V: View> AnyElement<V> {
}
}
-impl<V: View> Element<V> for AnyElement<V> {
+impl<V: 'static> Element<V> for AnyElement<V> {
type LayoutState = ();
type PaintState = ();
@@ -606,12 +607,18 @@ impl<V: View> Element<V> for AnyElement<V> {
}
}
-pub struct RootElement<V: View> {
+impl Entity for AnyElement<()> {
+ type Event = ();
+}
+
+// impl View for AnyElement<()> {}
+
+pub struct RootElement<V> {
element: AnyElement<V>,
view: WeakViewHandle<V>,
}
-impl<V: View> RootElement<V> {
+impl<V> RootElement<V> {
pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
Self { element, view }
}
@@ -679,7 +686,9 @@ impl<V: View> AnyRootElement for RootElement<V> {
.ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
view.update(cx, |view, cx| {
- self.element.paint(scene, origin, visible_bounds, view, cx);
+ let mut cx = PaintContext::new(cx);
+ self.element
+ .paint(scene, origin, visible_bounds, view, &mut cx);
Ok(())
})
}
@@ -719,7 +728,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
}
}
-pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
+pub trait ParentElement<'a, V: 'static>: Extend<AnyElement<V>> + Sized {
fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
self.extend(children.into_iter().map(|child| child.into_any()));
}
@@ -739,7 +748,12 @@ pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
}
}
-impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
+impl<'a, V, T> ParentElement<'a, V> for T
+where
+ V: 'static,
+ T: Extend<AnyElement<V>>,
+{
+}
pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
if max_size.x().is_infinite() && max_size.y().is_infinite() {
@@ -1,18 +1,18 @@
use crate::{
geometry::{rect::RectF, vector::Vector2F},
- json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+ json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
ViewContext,
};
use json::ToJson;
use serde_json::json;
-pub struct Align<V: View> {
+pub struct Align<V> {
child: AnyElement<V>,
alignment: Vector2F,
}
-impl<V: View> Align<V> {
+impl<V> Align<V> {
pub fn new(child: AnyElement<V>) -> Self {
Self {
child,
@@ -41,7 +41,7 @@ impl<V: View> Align<V> {
}
}
-impl<V: View> Element<V> for Align<V> {
+impl<V: 'static> Element<V> for Align<V> {
type LayoutState = ();
type PaintState = ();
@@ -3,7 +3,7 @@ use std::marker::PhantomData;
use super::Element;
use crate::{
json::{self, json},
- PaintContext, SceneBuilder, View, ViewContext,
+ PaintContext, SceneBuilder, ViewContext,
};
use json::ToJson;
use pathfinder_geometry::{
@@ -15,7 +15,6 @@ pub struct Canvas<V, F>(F, PhantomData<V>);
impl<V, F> Canvas<V, F>
where
- V: View,
F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
{
pub fn new(f: F) -> Self {
@@ -23,7 +22,7 @@ where
}
}
-impl<V: View, F> Element<V> for Canvas<V, F>
+impl<V: 'static, F> Element<V> for Canvas<V, F>
where
F: 'static + FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
{
@@ -4,21 +4,21 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use serde_json::json;
use crate::{
- json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+ json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
ViewContext,
};
-pub struct Clipped<V: View> {
+pub struct Clipped<V> {
child: AnyElement<V>,
}
-impl<V: View> Clipped<V> {
+impl<V> Clipped<V> {
pub fn new(child: AnyElement<V>) -> Self {
Self { child }
}
}
-impl<V: View> Element<V> for Clipped<V> {
+impl<V: 'static> Element<V> for Clipped<V> {
type LayoutState = ();
type PaintState = ();
@@ -5,21 +5,21 @@ use serde_json::json;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
- json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+ json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
ViewContext,
};
-pub struct ConstrainedBox<V: View> {
+pub struct ConstrainedBox<V> {
child: AnyElement<V>,
constraint: Constraint<V>,
}
-pub enum Constraint<V: View> {
+pub enum Constraint<V> {
Static(SizeConstraint),
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>),
}
-impl<V: View> ToJson for Constraint<V> {
+impl<V> ToJson for Constraint<V> {
fn to_json(&self) -> serde_json::Value {
match self {
Constraint::Static(constraint) => constraint.to_json(),
@@ -28,7 +28,7 @@ impl<V: View> ToJson for Constraint<V> {
}
}
-impl<V: View> ConstrainedBox<V> {
+impl<V: 'static> ConstrainedBox<V> {
pub fn new(child: impl Element<V>) -> Self {
Self {
child: child.into_any(),
@@ -132,7 +132,7 @@ impl<V: View> ConstrainedBox<V> {
}
}
-impl<V: View> Element<V> for ConstrainedBox<V> {
+impl<V: 'static> Element<V> for ConstrainedBox<V> {
type LayoutState = ();
type PaintState = ();
@@ -10,8 +10,7 @@ use crate::{
json::ToJson,
platform::CursorStyle,
scene::{self, Border, CornerRadii, CursorRegion, Quad},
- AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
- ViewContext,
+ AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
};
use schemars::JsonSchema;
use serde::Deserialize;
@@ -47,12 +46,12 @@ impl ContainerStyle {
}
}
-pub struct Container<V: View> {
+pub struct Container<V> {
child: AnyElement<V>,
style: ContainerStyle,
}
-impl<V: View> Container<V> {
+impl<V> Container<V> {
pub fn new(child: AnyElement<V>) -> Self {
Self {
child,
@@ -199,7 +198,7 @@ impl<V: View> Container<V> {
}
}
-impl<V: View> Element<V> for Container<V> {
+impl<V: 'static> Element<V> for Container<V> {
type LayoutState = ();
type PaintState = ();
@@ -350,8 +349,8 @@ impl ToJson for ContainerStyle {
#[derive(Clone, Copy, Debug, Default, JsonSchema)]
pub struct Margin {
pub top: f32,
- pub left: f32,
pub bottom: f32,
+ pub left: f32,
pub right: f32,
}
@@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
- LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
+ LayoutContext, PaintContext, SceneBuilder, ViewContext,
};
use crate::{Element, SizeConstraint};
@@ -26,7 +26,7 @@ impl Empty {
}
}
-impl<V: View> Element<V> for Empty {
+impl<V: 'static> Element<V> for Empty {
type LayoutState = ();
type PaintState = ();
@@ -2,18 +2,18 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
- json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+ json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
ViewContext,
};
use serde_json::json;
-pub struct Expanded<V: View> {
+pub struct Expanded<V> {
child: AnyElement<V>,
full_width: bool,
full_height: bool,
}
-impl<V: View> Expanded<V> {
+impl<V: 'static> Expanded<V> {
pub fn new(child: impl Element<V>) -> Self {
Self {
child: child.into_any(),
@@ -35,7 +35,7 @@ impl<V: View> Expanded<V> {
}
}
-impl<V: View> Element<V> for Expanded<V> {
+impl<V: 'static> Element<V> for Expanded<V> {
type LayoutState = ();
type PaintState = ();
@@ -3,7 +3,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
use crate::{
json::{self, ToJson, Value},
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
- SizeConstraint, Vector2FExt, View, ViewContext,
+ SizeConstraint, Vector2FExt, ViewContext,
};
use pathfinder_geometry::{
rect::RectF,
@@ -17,14 +17,14 @@ struct ScrollState {
scroll_position: Cell<f32>,
}
-pub struct Flex<V: View> {
+pub struct Flex<V> {
axis: Axis,
children: Vec<AnyElement<V>>,
scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>,
child_alignment: f32,
}
-impl<V: View> Flex<V> {
+impl<V: 'static> Flex<V> {
pub fn new(axis: Axis) -> Self {
Self {
axis,
@@ -115,13 +115,13 @@ impl<V: View> Flex<V> {
}
}
-impl<V: View> Extend<AnyElement<V>> for Flex<V> {
+impl<V> Extend<AnyElement<V>> for Flex<V> {
fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
self.children.extend(children);
}
}
-impl<V: View> Element<V> for Flex<V> {
+impl<V: 'static> Element<V> for Flex<V> {
type LayoutState = f32;
type PaintState = ();
@@ -401,12 +401,12 @@ struct FlexParentData {
float: bool,
}
-pub struct FlexItem<V: View> {
+pub struct FlexItem<V> {
metadata: FlexParentData,
child: AnyElement<V>,
}
-impl<V: View> FlexItem<V> {
+impl<V: 'static> FlexItem<V> {
pub fn new(child: impl Element<V>) -> Self {
FlexItem {
metadata: FlexParentData {
@@ -428,7 +428,7 @@ impl<V: View> FlexItem<V> {
}
}
-impl<V: View> Element<V> for FlexItem<V> {
+impl<V: 'static> Element<V> for FlexItem<V> {
type LayoutState = ();
type PaintState = ();
@@ -3,16 +3,15 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::json,
- AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
- ViewContext,
+ AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
};
-pub struct Hook<V: View> {
+pub struct Hook<V> {
child: AnyElement<V>,
after_layout: Option<Box<dyn FnMut(Vector2F, &mut ViewContext<V>)>>,
}
-impl<V: View> Hook<V> {
+impl<V: 'static> Hook<V> {
pub fn new(child: impl Element<V>) -> Self {
Self {
child: child.into_any(),
@@ -29,7 +28,7 @@ impl<V: View> Hook<V> {
}
}
-impl<V: View> Element<V> for Hook<V> {
+impl<V: 'static> Element<V> for Hook<V> {
type LayoutState = ();
type PaintState = ();
@@ -6,7 +6,7 @@ use crate::{
},
json::{json, ToJson},
scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
- View, ViewContext,
+ ViewContext,
};
use schemars::JsonSchema;
use serde::Deserialize;
@@ -57,7 +57,7 @@ impl Image {
}
}
-impl<V: View> Element<V> for Image {
+impl<V: 'static> Element<V> for Image {
type LayoutState = Option<Arc<ImageData>>;
type PaintState = ();
@@ -31,7 +31,7 @@ impl KeystrokeLabel {
}
}
-impl<V: View> Element<V> for KeystrokeLabel {
+impl<V: 'static> Element<V> for KeystrokeLabel {
type LayoutState = AnyElement<V>;
type PaintState = ();
@@ -8,7 +8,7 @@ use crate::{
},
json::{ToJson, Value},
text_layout::{Line, RunStyle},
- Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
+ Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
};
use schemars::JsonSchema;
use serde::Deserialize;
@@ -128,7 +128,7 @@ impl Label {
}
}
-impl<V: View> Element<V> for Label {
+impl<V: 'static> Element<V> for Label {
type LayoutState = Line;
type PaintState = ();
@@ -5,16 +5,16 @@ use crate::{
},
json::json,
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
- View, ViewContext,
+ ViewContext,
};
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree};
-pub struct List<V: View> {
+pub struct List<V> {
state: ListState<V>,
}
-pub struct ListState<V: View>(Rc<RefCell<StateInner<V>>>);
+pub struct ListState<V>(Rc<RefCell<StateInner<V>>>);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation {
@@ -22,7 +22,7 @@ pub enum Orientation {
Bottom,
}
-struct StateInner<V: View> {
+struct StateInner<V> {
last_layout_width: Option<f32>,
render_item: Box<dyn FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement<V>>,
rendered_range: Range<usize>,
@@ -40,13 +40,13 @@ pub struct ListOffset {
pub offset_in_item: f32,
}
-enum ListItem<V: View> {
+enum ListItem<V> {
Unrendered,
Rendered(Rc<RefCell<AnyElement<V>>>),
Removed(f32),
}
-impl<V: View> Clone for ListItem<V> {
+impl<V> Clone for ListItem<V> {
fn clone(&self) -> Self {
match self {
Self::Unrendered => Self::Unrendered,
@@ -56,7 +56,7 @@ impl<V: View> Clone for ListItem<V> {
}
}
-impl<V: View> Debug for ListItem<V> {
+impl<V> Debug for ListItem<V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unrendered => write!(f, "Unrendered"),
@@ -86,13 +86,13 @@ struct UnrenderedCount(usize);
#[derive(Clone, Debug, Default)]
struct Height(f32);
-impl<V: View> List<V> {
+impl<V> List<V> {
pub fn new(state: ListState<V>) -> Self {
Self { state }
}
}
-impl<V: View> Element<V> for List<V> {
+impl<V: 'static> Element<V> for List<V> {
type LayoutState = ListOffset;
type PaintState = ();
@@ -347,7 +347,7 @@ impl<V: View> Element<V> for List<V> {
}
}
-impl<V: View> ListState<V> {
+impl<V: 'static> ListState<V> {
pub fn new<D, F>(
element_count: usize,
orientation: Orientation,
@@ -440,13 +440,13 @@ impl<V: View> ListState<V> {
}
}
-impl<V: View> Clone for ListState<V> {
+impl<V> Clone for ListState<V> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
-impl<V: View> StateInner<V> {
+impl<V: 'static> StateInner<V> {
fn render_item(
&mut self,
ix: usize,
@@ -560,7 +560,7 @@ impl<V: View> StateInner<V> {
}
}
-impl<V: View> ListItem<V> {
+impl<V> ListItem<V> {
fn remove(&self) -> Self {
match self {
ListItem::Unrendered => ListItem::Unrendered,
@@ -570,7 +570,7 @@ impl<V: View> ListItem<V> {
}
}
-impl<V: View> sum_tree::Item for ListItem<V> {
+impl<V> sum_tree::Item for ListItem<V> {
type Summary = ListItemSummary;
fn summary(&self) -> Self::Summary {
@@ -944,7 +944,7 @@ mod tests {
type Event = ();
}
- impl View for TestView {
+ impl crate::View for TestView {
fn ui_name() -> &'static str {
"TestView"
}
@@ -968,7 +968,7 @@ mod tests {
}
}
- impl<V: View> Element<V> for TestElement {
+ impl<V: 'static> Element<V> for TestElement {
type LayoutState = ();
type PaintState = ();
@@ -11,12 +11,12 @@ use crate::{
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
},
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
- SceneBuilder, SizeConstraint, TypeTag, View, ViewContext,
+ SceneBuilder, SizeConstraint, TypeTag, ViewContext,
};
use serde_json::json;
use std::ops::Range;
-pub struct MouseEventHandler<V: View> {
+pub struct MouseEventHandler<V: 'static> {
child: AnyElement<V>,
region_id: usize,
cursor_style: Option<CursorStyle>,
@@ -31,7 +31,7 @@ pub struct MouseEventHandler<V: View> {
/// Element which provides a render_child callback with a MouseState and paints a mouse
/// region under (or above) it for easy mouse event handling.
-impl<V: View> MouseEventHandler<V> {
+impl<V: 'static> MouseEventHandler<V> {
pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
Self {
child: child.into_any(),
@@ -267,7 +267,7 @@ impl<V: View> MouseEventHandler<V> {
}
}
-impl<V: View> Element<V> for MouseEventHandler<V> {
+impl<V: 'static> Element<V> for MouseEventHandler<V> {
type LayoutState = ();
type PaintState = ();
@@ -4,11 +4,11 @@ use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
- SizeConstraint, View, ViewContext,
+ SizeConstraint, ViewContext,
};
use serde_json::json;
-pub struct Overlay<V: View> {
+pub struct Overlay<V> {
child: AnyElement<V>,
anchor_position: Option<Vector2F>,
anchor_corner: AnchorCorner,
@@ -73,7 +73,7 @@ impl AnchorCorner {
}
}
-impl<V: View> Overlay<V> {
+impl<V: 'static> Overlay<V> {
pub fn new(child: impl Element<V>) -> Self {
Self {
child: child.into_any(),
@@ -117,7 +117,7 @@ impl<V: View> Overlay<V> {
}
}
-impl<V: View> Element<V> for Overlay<V> {
+impl<V: 'static> Element<V> for Overlay<V> {
type LayoutState = Vector2F;
type PaintState = ();
@@ -59,7 +59,7 @@ where
.and_then(|map| map.0.get(&tag))
}
-pub struct Resizable<V: View> {
+pub struct Resizable<V: 'static> {
child: AnyElement<V>,
tag: TypeTag,
handle_side: HandleSide,
@@ -69,7 +69,7 @@ pub struct Resizable<V: View> {
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
-impl<V: View> Resizable<V> {
+impl<V: 'static> Resizable<V> {
pub fn new<Tag: 'static>(
child: AnyElement<V>,
handle_side: HandleSide,
@@ -97,7 +97,7 @@ impl<V: View> Resizable<V> {
}
}
-impl<V: View> Element<V> for Resizable<V> {
+impl<V: 'static> Element<V> for Resizable<V> {
type LayoutState = SizeConstraint;
type PaintState = ();
@@ -219,12 +219,12 @@ impl<V: View> Element<V> for Resizable<V> {
#[derive(Debug, Default)]
struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
-pub struct BoundsProvider<V: View, P> {
+pub struct BoundsProvider<V: 'static, P> {
child: AnyElement<V>,
phantom: std::marker::PhantomData<P>,
}
-impl<V: View, P: 'static> BoundsProvider<V, P> {
+impl<V: 'static, P: 'static> BoundsProvider<V, P> {
pub fn new(child: AnyElement<V>) -> Self {
Self {
child,
@@ -3,17 +3,16 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::{self, json, ToJson},
- AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
- ViewContext,
+ AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
};
/// Element which renders it's children in a stack on top of each other.
/// The first child determines the size of the others.
-pub struct Stack<V: View> {
+pub struct Stack<V> {
children: Vec<AnyElement<V>>,
}
-impl<V: View> Default for Stack<V> {
+impl<V> Default for Stack<V> {
fn default() -> Self {
Self {
children: Vec::new(),
@@ -21,13 +20,13 @@ impl<V: View> Default for Stack<V> {
}
}
-impl<V: View> Stack<V> {
+impl<V> Stack<V> {
pub fn new() -> Self {
Self::default()
}
}
-impl<V: View> Element<V> for Stack<V> {
+impl<V: 'static> Element<V> for Stack<V> {
type LayoutState = ();
type PaintState = ();
@@ -99,7 +98,7 @@ impl<V: View> Element<V> for Stack<V> {
}
}
-impl<V: View> Extend<AnyElement<V>> for Stack<V> {
+impl<V> Extend<AnyElement<V>> for Stack<V> {
fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
self.children.extend(children)
}
@@ -7,7 +7,7 @@ use crate::{
rect::RectF,
vector::{vec2f, Vector2F},
},
- scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
+ scene, Element, LayoutContext, SceneBuilder, SizeConstraint, ViewContext,
};
use schemars::JsonSchema;
use serde_derive::Deserialize;
@@ -27,7 +27,7 @@ impl Svg {
}
}
- pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
+ pub fn for_style<V: 'static>(style: SvgStyle) -> impl Element<V> {
Self::new(style.asset)
.with_color(style.color)
.constrained()
@@ -41,7 +41,7 @@ impl Svg {
}
}
-impl<V: View> Element<V> for Svg {
+impl<V: 'static> Element<V> for Svg {
type LayoutState = Option<usvg::Tree>;
type PaintState = ();
@@ -8,7 +8,7 @@ use crate::{
json::{ToJson, Value},
text_layout::{Line, RunStyle, ShapedBoundary},
AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
- TextLayoutCache, View, ViewContext,
+ TextLayoutCache, ViewContext,
};
use log::warn;
use serde_json::json;
@@ -70,7 +70,7 @@ impl Text {
}
}
-impl<V: View> Element<V> for Text {
+impl<V: 'static> Element<V> for Text {
type LayoutState = LayoutState;
type PaintState = ();
@@ -338,7 +338,7 @@ impl<V: View> Element<V> for Text {
}
/// Perform text layout on a series of highlighted chunks of text.
-fn layout_highlighted_chunks<'a>(
+pub fn layout_highlighted_chunks<'a>(
chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
text_style: &TextStyle,
text_layout_cache: &TextLayoutCache,
@@ -7,7 +7,7 @@ use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::json,
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
- Task, TypeTag, View, ViewContext,
+ Task, TypeTag, ViewContext,
};
use schemars::JsonSchema;
use serde::Deserialize;
@@ -22,7 +22,7 @@ use util::ResultExt;
const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
-pub struct Tooltip<V: View> {
+pub struct Tooltip<V> {
child: AnyElement<V>,
tooltip: Option<AnyElement<V>>,
_state: ElementStateHandle<Rc<TooltipState>>,
@@ -52,7 +52,7 @@ pub struct KeystrokeStyle {
text: TextStyle,
}
-impl<V: View> Tooltip<V> {
+impl<V: 'static> Tooltip<V> {
pub fn new<Tag: 'static>(
id: usize,
text: impl Into<Cow<'static, str>>,
@@ -181,7 +181,7 @@ impl<V: View> Tooltip<V> {
}
}
-impl<V: View> Element<V> for Tooltip<V> {
+impl<V: 'static> Element<V> for Tooltip<V> {
type LayoutState = ();
type PaintState = ();
@@ -6,7 +6,7 @@ use crate::{
},
json::{self, json},
platform::ScrollWheelEvent,
- AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext,
+ AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, ViewContext,
};
use json::ToJson;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -36,13 +36,13 @@ struct StateInner {
scroll_to: Option<ScrollTarget>,
}
-pub struct UniformListLayoutState<V: View> {
+pub struct UniformListLayoutState<V> {
scroll_max: f32,
item_height: f32,
items: Vec<AnyElement<V>>,
}
-pub struct UniformList<V: View> {
+pub struct UniformList<V> {
state: UniformListState,
item_count: usize,
#[allow(clippy::type_complexity)]
@@ -53,7 +53,7 @@ pub struct UniformList<V: View> {
view_id: usize,
}
-impl<V: View> UniformList<V> {
+impl<V: 'static> UniformList<V> {
pub fn new<F>(
state: UniformListState,
item_count: usize,
@@ -61,7 +61,6 @@ impl<V: View> UniformList<V> {
append_items: F,
) -> Self
where
- V: View,
F: 'static + Fn(&mut V, Range<usize>, &mut Vec<AnyElement<V>>, &mut ViewContext<V>),
{
Self {
@@ -151,7 +150,7 @@ impl<V: View> UniformList<V> {
}
}
-impl<V: View> Element<V> for UniformList<V> {
+impl<V: 'static> Element<V> for UniformList<V> {
type LayoutState = UniformListLayoutState<V>;
type PaintState = ();
@@ -11,6 +11,7 @@ pub use font_kit::{
properties::{Properties, Stretch, Style, Weight},
};
use ordered_float::OrderedFloat;
+use refineable::Refineable;
use schemars::JsonSchema;
use serde::{de, Deserialize, Serialize};
use serde_json::Value;
@@ -59,7 +60,7 @@ pub struct Features {
pub zero: Option<bool>,
}
-#[derive(Clone, Debug, JsonSchema)]
+#[derive(Clone, Debug, JsonSchema, Refineable)]
pub struct TextStyle {
pub color: Color,
pub font_family_name: Arc<str>,
@@ -69,6 +70,7 @@ pub struct TextStyle {
#[schemars(with = "PropertiesDef")]
pub font_properties: Properties,
pub underline: Underline,
+ pub soft_wrap: bool,
}
impl TextStyle {
@@ -90,20 +92,11 @@ impl TextStyle {
font_size: refinement.font_size.unwrap_or(self.font_size),
font_properties: refinement.font_properties.unwrap_or(self.font_properties),
underline: refinement.underline.unwrap_or(self.underline),
+ soft_wrap: refinement.soft_wrap.unwrap_or(self.soft_wrap),
}
}
}
-pub struct TextStyleRefinement {
- pub color: Option<Color>,
- pub font_family_name: Option<Arc<str>>,
- pub font_family_id: Option<FamilyId>,
- pub font_id: Option<FontId>,
- pub font_size: Option<f32>,
- pub font_properties: Option<Properties>,
- pub underline: Option<Underline>,
-}
-
#[derive(JsonSchema)]
#[serde(remote = "Properties")]
pub struct PropertiesDef {
@@ -222,9 +215,31 @@ impl TextStyle {
font_size,
font_properties,
underline,
+ soft_wrap: false,
})
}
+ pub fn default(font_cache: &FontCache) -> Self {
+ let font_family_id = font_cache.known_existing_family();
+ let font_id = font_cache
+ .select_font(font_family_id, &Default::default())
+ .expect("did not have any font in system-provided family");
+ let font_family_name = font_cache
+ .family_name(font_family_id)
+ .expect("we loaded this family from the font cache, so this should work");
+
+ Self {
+ color: Color::default(),
+ font_family_name,
+ font_family_id,
+ font_id,
+ font_size: 14.,
+ font_properties: Default::default(),
+ underline: Default::default(),
+ soft_wrap: true,
+ }
+ }
+
pub fn with_font_size(mut self, font_size: f32) -> Self {
self.font_size = font_size;
self
@@ -352,24 +367,7 @@ impl Default for TextStyle {
let font_cache = font_cache
.as_ref()
.expect("TextStyle::default can only be called within a call to with_font_cache");
-
- let font_family_id = font_cache.known_existing_family();
- let font_id = font_cache
- .select_font(font_family_id, &Default::default())
- .expect("did not have any font in system-provided family");
- let font_family_name = font_cache
- .family_name(font_family_id)
- .expect("we loaded this family from the font cache, so this should work");
-
- Self {
- color: Default::default(),
- font_family_name,
- font_family_id,
- font_id,
- font_size: 14.,
- font_properties: Default::default(),
- underline: Default::default(),
- }
+ Self::default(font_cache)
})
}
}
@@ -2,6 +2,7 @@ use super::scene::{Path, PathVertex};
use crate::{color::Color, json::ToJson};
pub use pathfinder_geometry::*;
use rect::RectF;
+use refineable::Refineable;
use serde::{Deserialize, Deserializer};
use serde_json::json;
use vector::{vec2f, Vector2F};
@@ -131,3 +132,258 @@ impl ToJson for RectF {
json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
}
}
+
+#[derive(Refineable)]
+pub struct Point<T: Clone + Default> {
+ pub x: T,
+ pub y: T,
+}
+
+impl<T: Clone + Default> Clone for Point<T> {
+ fn clone(&self) -> Self {
+ Self {
+ x: self.x.clone(),
+ y: self.y.clone(),
+ }
+ }
+}
+
+impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
+ fn into(self) -> taffy::geometry::Point<T> {
+ taffy::geometry::Point {
+ x: self.x,
+ y: self.y,
+ }
+ }
+}
+
+#[derive(Clone, Refineable)]
+pub struct Size<T: Clone + Default> {
+ pub width: T,
+ pub height: T,
+}
+
+impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
+where
+ S: Into<T>,
+{
+ fn from(value: taffy::geometry::Size<S>) -> Self {
+ Self {
+ width: value.width.into(),
+ height: value.height.into(),
+ }
+ }
+}
+
+impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
+where
+ T: Into<S>,
+{
+ fn into(self) -> taffy::geometry::Size<S> {
+ taffy::geometry::Size {
+ width: self.width.into(),
+ height: self.height.into(),
+ }
+ }
+}
+
+impl Size<DefiniteLength> {
+ pub fn zero() -> Self {
+ Self {
+ width: pixels(0.),
+ height: pixels(0.),
+ }
+ }
+
+ pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
+ taffy::geometry::Size {
+ width: self.width.to_taffy(rem_size),
+ height: self.height.to_taffy(rem_size),
+ }
+ }
+}
+
+impl Size<Length> {
+ pub fn auto() -> Self {
+ Self {
+ width: Length::Auto,
+ height: Length::Auto,
+ }
+ }
+
+ pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
+ &self,
+ rem_size: f32,
+ ) -> taffy::geometry::Size<T> {
+ taffy::geometry::Size {
+ width: self.width.to_taffy(rem_size).into(),
+ height: self.height.to_taffy(rem_size).into(),
+ }
+ }
+}
+
+#[derive(Clone, Default, Refineable)]
+pub struct Edges<T: Clone + Default> {
+ pub top: T,
+ pub right: T,
+ pub bottom: T,
+ pub left: T,
+}
+
+impl Edges<DefiniteLength> {
+ pub fn zero() -> Self {
+ Self {
+ top: pixels(0.),
+ right: pixels(0.),
+ bottom: pixels(0.),
+ left: pixels(0.),
+ }
+ }
+
+ pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
+ taffy::geometry::Rect {
+ top: self.top.to_taffy(rem_size),
+ right: self.right.to_taffy(rem_size),
+ bottom: self.bottom.to_taffy(rem_size),
+ left: self.left.to_taffy(rem_size),
+ }
+ }
+}
+
+impl Edges<Length> {
+ pub fn auto() -> Self {
+ Self {
+ top: Length::Auto,
+ right: Length::Auto,
+ bottom: Length::Auto,
+ left: Length::Auto,
+ }
+ }
+
+ pub fn zero() -> Self {
+ Self {
+ top: pixels(0.),
+ right: pixels(0.),
+ bottom: pixels(0.),
+ left: pixels(0.),
+ }
+ }
+
+ pub fn to_taffy(
+ &self,
+ rem_size: f32,
+ ) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
+ taffy::geometry::Rect {
+ top: self.top.to_taffy(rem_size),
+ right: self.right.to_taffy(rem_size),
+ bottom: self.bottom.to_taffy(rem_size),
+ left: self.left.to_taffy(rem_size),
+ }
+ }
+}
+
+#[derive(Clone, Copy)]
+pub enum AbsoluteLength {
+ Pixels(f32),
+ Rems(f32),
+}
+
+impl AbsoluteLength {
+ pub fn to_pixels(&self, rem_size: f32) -> f32 {
+ match self {
+ AbsoluteLength::Pixels(pixels) => *pixels,
+ AbsoluteLength::Rems(rems) => rems * rem_size,
+ }
+ }
+}
+
+impl Default for AbsoluteLength {
+ fn default() -> Self {
+ Self::Pixels(0.0)
+ }
+}
+
+/// A non-auto length that can be defined in pixels, rems, or percent of parent.
+#[derive(Clone, Copy)]
+pub enum DefiniteLength {
+ Absolute(AbsoluteLength),
+ Relative(f32), // Percent, from 0 to 100.
+}
+
+impl DefiniteLength {
+ fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
+ match self {
+ DefiniteLength::Absolute(length) => match length {
+ AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
+ AbsoluteLength::Rems(rems) => {
+ taffy::style::LengthPercentage::Length(rems * rem_size)
+ }
+ },
+ DefiniteLength::Relative(fraction) => {
+ taffy::style::LengthPercentage::Percent(*fraction)
+ }
+ }
+ }
+}
+
+impl From<AbsoluteLength> for DefiniteLength {
+ fn from(length: AbsoluteLength) -> Self {
+ Self::Absolute(length)
+ }
+}
+
+impl Default for DefiniteLength {
+ fn default() -> Self {
+ Self::Absolute(AbsoluteLength::default())
+ }
+}
+
+/// A length that can be defined in pixels, rems, percent of parent, or auto.
+#[derive(Clone, Copy)]
+pub enum Length {
+ Definite(DefiniteLength),
+ Auto,
+}
+
+pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
+ DefiniteLength::Relative(fraction).into()
+}
+
+pub fn rems<T: From<AbsoluteLength>>(rems: f32) -> T {
+ AbsoluteLength::Rems(rems).into()
+}
+
+pub fn pixels<T: From<AbsoluteLength>>(pixels: f32) -> T {
+ AbsoluteLength::Pixels(pixels).into()
+}
+
+pub fn auto() -> Length {
+ Length::Auto
+}
+
+impl Length {
+ pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
+ match self {
+ Length::Definite(length) => length.to_taffy(rem_size).into(),
+ Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
+ }
+ }
+}
+
+impl From<DefiniteLength> for Length {
+ fn from(length: DefiniteLength) -> Self {
+ Self::Definite(length)
+ }
+}
+
+impl From<AbsoluteLength> for Length {
+ fn from(length: AbsoluteLength) -> Self {
+ Self::Definite(length.into())
+ }
+}
+
+impl Default for Length {
+ fn default() -> Self {
+ Self::Definite(DefiniteLength::default())
+ }
+}
@@ -27,7 +27,10 @@ pub mod json;
pub mod keymap_matcher;
pub mod platform;
pub use gpui_macros::{test, Element};
-pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext};
+pub use window::{
+ Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
+ WindowContext,
+};
pub use anyhow;
pub use serde_json;
@@ -192,7 +192,7 @@ impl<'a> WindowOptions<'a> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub struct TitlebarOptions<'a> {
pub title: Option<&'a str>,
pub appears_transparent: bool,
@@ -1,4 +1,4 @@
-use std::ops::Deref;
+use std::{any::Any, ops::Deref};
use pathfinder_geometry::vector::vec2f;
@@ -142,6 +142,7 @@ pub struct MouseButtonEvent {
pub position: Vector2F,
pub modifiers: Modifiers,
pub click_count: usize,
+ pub is_down: bool,
}
impl Deref for MouseButtonEvent {
@@ -174,6 +175,7 @@ impl MouseMovedEvent {
button: self.pressed_button.unwrap_or(button),
modifiers: self.modifiers,
click_count: 0,
+ is_down: self.pressed_button.is_some(),
}
}
}
@@ -211,10 +213,24 @@ impl Event {
Event::KeyDown { .. } => None,
Event::KeyUp { .. } => None,
Event::ModifiersChanged { .. } => None,
- Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position),
+ Event::MouseDown(event) => Some(event.position),
+ Event::MouseUp(event) => Some(event.position),
Event::MouseMoved(event) => Some(event.position),
Event::MouseExited(event) => Some(event.position),
Event::ScrollWheel(event) => Some(event.position),
}
}
+
+ pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
+ match self {
+ Event::KeyDown { .. } => None,
+ Event::KeyUp { .. } => None,
+ Event::ModifiersChanged { .. } => None,
+ Event::MouseDown(event) => Some(event),
+ Event::MouseUp(event) => Some(event),
+ Event::MouseMoved(event) => Some(event),
+ Event::MouseExited(event) => Some(event),
+ Event::ScrollWheel(event) => Some(event),
+ }
+ }
}
@@ -132,6 +132,7 @@ impl Event {
),
modifiers: read_modifiers(native_event),
click_count: native_event.clickCount() as usize,
+ is_down: true,
})
})
}
@@ -158,6 +159,7 @@ impl Event {
),
modifiers: read_modifiers(native_event),
click_count: native_event.clickCount() as usize,
+ is_down: false,
})
})
}
@@ -1,5 +1,6 @@
mod mouse_event;
mod mouse_region;
+mod region;
#[cfg(debug_assertions)]
use collections::HashSet;
@@ -8,7 +9,12 @@ use schemars::JsonSchema;
use serde::Deserialize;
use serde_derive::Serialize;
use serde_json::json;
-use std::{borrow::Cow, sync::Arc};
+use std::{
+ any::{Any, TypeId},
+ borrow::Cow,
+ rc::Rc,
+ sync::Arc,
+};
use crate::{
color::Color,
@@ -16,7 +22,7 @@ use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
platform::{current::Surface, CursorStyle},
- ImageData,
+ ImageData, WindowContext,
};
pub use mouse_event::*;
pub use mouse_region::*;
@@ -25,6 +31,8 @@ pub struct SceneBuilder {
scale_factor: f32,
stacking_contexts: Vec<StackingContext>,
active_stacking_context_stack: Vec<usize>,
+ /// Used by the playground crate.
+ pub event_handlers: Vec<EventHandler>,
#[cfg(debug_assertions)]
mouse_region_ids: HashSet<MouseRegionId>,
}
@@ -32,6 +40,7 @@ pub struct SceneBuilder {
pub struct Scene {
scale_factor: f32,
stacking_contexts: Vec<StackingContext>,
+ event_handlers: Vec<EventHandler>,
}
struct StackingContext {
@@ -272,6 +281,12 @@ impl Scene {
})
.collect()
}
+
+ pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+ self.event_handlers
+ .sort_by(|a, b| a.order.cmp(&b.order).reverse());
+ std::mem::take(&mut self.event_handlers)
+ }
}
impl SceneBuilder {
@@ -283,6 +298,7 @@ impl SceneBuilder {
active_stacking_context_stack: vec![0],
#[cfg(debug_assertions)]
mouse_region_ids: Default::default(),
+ event_handlers: Vec::new(),
}
}
@@ -292,6 +308,7 @@ impl SceneBuilder {
Scene {
scale_factor: self.scale_factor,
stacking_contexts: self.stacking_contexts,
+ event_handlers: self.event_handlers,
}
}
@@ -688,6 +705,13 @@ impl MouseRegion {
}
}
+pub struct EventHandler {
+ pub order: u32,
+ // The &dyn Any parameter below expects an event.
+ pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
+ pub event_type: TypeId,
+}
+
fn can_draw(bounds: RectF) -> bool {
let size = bounds.size();
size.x() > 0. && size.y() > 0.
@@ -1,6 +1,4 @@
-use crate::{
- platform::MouseButton, window::WindowContext, EventContext, TypeTag, View, ViewContext,
-};
+use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
use collections::HashMap;
use pathfinder_geometry::rect::RectF;
use smallvec::SmallVec;
@@ -72,7 +70,7 @@ impl MouseRegion {
pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_down(button, handler);
@@ -81,7 +79,7 @@ impl MouseRegion {
pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_up(button, handler);
@@ -90,7 +88,7 @@ impl MouseRegion {
pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_click(button, handler);
@@ -99,7 +97,7 @@ impl MouseRegion {
pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_click_out(button, handler);
@@ -108,7 +106,7 @@ impl MouseRegion {
pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_down_out(button, handler);
@@ -117,7 +115,7 @@ impl MouseRegion {
pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_up_out(button, handler);
@@ -126,7 +124,7 @@ impl MouseRegion {
pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_drag(button, handler);
@@ -135,7 +133,7 @@ impl MouseRegion {
pub fn on_hover<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_hover(handler);
@@ -144,7 +142,7 @@ impl MouseRegion {
pub fn on_move<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_move(handler);
@@ -153,7 +151,7 @@ impl MouseRegion {
pub fn on_move_out<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_move_out(handler);
@@ -162,7 +160,7 @@ impl MouseRegion {
pub fn on_scroll<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
{
self.handlers = self.handlers.on_scroll(handler);
@@ -314,7 +312,7 @@ impl HandlerSet {
pub fn on_move<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::move_disc(), None,
@@ -336,7 +334,7 @@ impl HandlerSet {
pub fn on_move_out<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::move_out_disc(), None,
@@ -358,7 +356,7 @@ impl HandlerSet {
pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::down_disc(), Some(button),
@@ -380,7 +378,7 @@ impl HandlerSet {
pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::up_disc(), Some(button),
@@ -402,7 +400,7 @@ impl HandlerSet {
pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::click_disc(), Some(button),
@@ -424,7 +422,7 @@ impl HandlerSet {
pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::click_out_disc(), Some(button),
@@ -446,7 +444,7 @@ impl HandlerSet {
pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::down_out_disc(), Some(button),
@@ -468,7 +466,7 @@ impl HandlerSet {
pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::up_out_disc(), Some(button),
@@ -490,7 +488,7 @@ impl HandlerSet {
pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::drag_disc(), Some(button),
@@ -512,7 +510,7 @@ impl HandlerSet {
pub fn on_hover<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::hover_disc(), None,
@@ -534,7 +532,7 @@ impl HandlerSet {
pub fn on_scroll<V, F>(mut self, handler: F) -> Self
where
- V: View,
+ V: 'static,
F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
{
self.insert(MouseEvent::scroll_wheel_disc(), None,
@@ -0,0 +1,7 @@
+// use crate::geometry::rect::RectF;
+// use crate::WindowContext;
+
+// struct Region {
+// pub bounds: RectF,
+// pub click_handler: Option<Rc<dyn Fn(&dyn Any, MouseEvent, &mut WindowContext)>>,
+// }
@@ -212,7 +212,7 @@ pub struct Glyph {
}
impl Line {
- fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
+ pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
let mut style_runs = SmallVec::new();
for (len, style) in runs {
style_runs.push(StyleRun {
@@ -1,14 +1,14 @@
-use gpui::{elements::RenderElement, View, ViewContext};
-use gpui_macros::Element;
+use gpui::{elements::Empty, Element, ViewContext};
+// use gpui_macros::Element;
#[test]
fn test_derive_render_element() {
#[derive(Element)]
struct TestElement {}
- impl RenderElement for TestElement {
- fn render<V: View>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> gpui::AnyElement<V> {
- unimplemented!()
+ impl TestElement {
+ fn render<V: 'static>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> impl Element<V> {
+ Empty::new()
}
}
}
@@ -10,6 +10,7 @@ proc-macro = true
doctest = false
[dependencies]
+lazy_static.workspace = true
+proc-macro2 = "1.0"
syn = "1.0"
quote = "1.0"
-proc-macro2 = "1.0"
@@ -4,7 +4,7 @@ use quote::{format_ident, quote};
use std::mem;
use syn::{
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
- ItemFn, Lit, Meta, NestedMeta, Type,
+ GenericParam, Generics, ItemFn, Lit, Meta, NestedMeta, Type, WhereClause,
};
#[proc_macro_attribute]
@@ -278,18 +278,44 @@ fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
#[proc_macro_derive(Element)]
pub fn element_derive(input: TokenStream) -> TokenStream {
- // Parse the input tokens into a syntax tree
- let input = parse_macro_input!(input as DeriveInput);
+ let ast = parse_macro_input!(input as DeriveInput);
+ let type_name = ast.ident;
- // The name of the struct/enum
- let name = input.ident;
- let must_implement = format_ident!("{}MustImplementRenderElement", name);
+ let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
+ let placeholder_view_type_name: Ident = parse_quote! { V };
+ let view_type_name: Ident;
+ let impl_generics: syn::ImplGenerics<'_>;
+ let type_generics: Option<syn::TypeGenerics<'_>>;
+ let where_clause: Option<&'_ WhereClause>;
- let expanded = quote! {
- trait #must_implement : gpui::elements::RenderElement {}
- impl #must_implement for #name {}
+ match ast.generics.params.iter().find_map(|param| {
+ if let GenericParam::Type(type_param) = param {
+ Some(type_param.ident.clone())
+ } else {
+ None
+ }
+ }) {
+ Some(type_name) => {
+ view_type_name = type_name;
+ let generics = ast.generics.split_for_impl();
+ impl_generics = generics.0;
+ type_generics = Some(generics.1);
+ where_clause = generics.2;
+ }
+ _ => {
+ view_type_name = placeholder_view_type_name;
+ let generics = placeholder_view_generics.split_for_impl();
+ impl_generics = generics.0;
+ type_generics = None;
+ where_clause = generics.2;
+ }
+ }
+
+ let gen = quote! {
+ impl #impl_generics Element<#view_type_name> for #type_name #type_generics
+ #where_clause
+ {
- impl<V: gpui::View> gpui::elements::Element<V> for #name {
type LayoutState = gpui::elements::AnyElement<V>;
type PaintState = ();
@@ -299,7 +325,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
view: &mut V,
cx: &mut gpui::LayoutContext<V>,
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
- let mut element = self.render(view, cx);
+ let mut element = self.render(view, cx).into_any();
let size = element.layout(constraint, view, cx);
(size, element)
}
@@ -336,11 +362,11 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
_: &(),
view: &V,
cx: &gpui::ViewContext<V>,
- ) -> gpui::serde_json::Value {
+ ) -> gpui::json::Value {
element.debug(view, cx)
}
}
};
- // Return generated code
- TokenStream::from(expanded)
+
+ gen.into()
}
@@ -450,7 +450,7 @@ impl View for LspLogView {
}
impl Item for LspLogView {
- fn tab_content<V: View>(
+ fn tab_content<V: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
@@ -373,6 +373,7 @@ impl View for SyntaxTreeView {
font_size,
font_properties: Default::default(),
underline: Default::default(),
+ soft_wrap: false,
};
let line_height = cx.font_cache().line_height(font_size);
@@ -451,7 +452,7 @@ impl View for SyntaxTreeView {
}
impl Item for SyntaxTreeView {
- fn tab_content<V: View>(
+ fn tab_content<V: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
@@ -1320,7 +1320,7 @@ impl ProjectPanel {
}
}
- fn render_entry_visual_element<V: View>(
+ fn render_entry_visual_element<V: 'static>(
details: &EntryDetails,
editor: Option<&ViewHandle<Editor>>,
padding: f32,
@@ -3,7 +3,7 @@ use std::path::Path;
use fuzzy::StringMatch;
use gpui::{
elements::{Label, LabelStyle},
- AnyElement, Element, View,
+ AnyElement, Element,
};
use util::paths::PathExt;
use workspace::WorkspaceLocation;
@@ -43,7 +43,7 @@ impl HighlightedText {
}
}
- pub fn render<V: View>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
+ pub fn render<V: 'static>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
Label::new(self.text, style)
.with_highlights(self.highlight_positions)
.into_any()
@@ -0,0 +1,15 @@
+[package]
+name = "refineable"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/refineable.rs"
+doctest = false
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"
+derive_refineable = { path = "./derive_refineable" }
@@ -0,0 +1,15 @@
+[package]
+name = "derive_refineable"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/derive_refineable.rs"
+proc-macro = true
+doctest = false
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"
@@ -0,0 +1,188 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+ parse_macro_input, parse_quote, DeriveInput, Field, FieldsNamed, PredicateType, TraitBound,
+ Type, TypeParamBound, WhereClause, WherePredicate,
+};
+
+#[proc_macro_derive(Refineable, attributes(refineable))]
+pub fn derive_refineable(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ generics,
+ ..
+ } = parse_macro_input!(input);
+
+ let refinement_ident = format_ident!("{}Refinement", ident);
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let fields = match data {
+ syn::Data::Struct(syn::DataStruct {
+ fields: syn::Fields::Named(FieldsNamed { named, .. }),
+ ..
+ }) => named.into_iter().collect::<Vec<Field>>(),
+ _ => panic!("This derive macro only supports structs with named fields"),
+ };
+
+ let field_names: Vec<_> = fields.iter().map(|f| f.ident.as_ref().unwrap()).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();
+
+ // Create trait bound that each wrapped type must implement Clone & Default
+ let type_param_bounds: Vec<_> = wrapped_types
+ .iter()
+ .map(|ty| {
+ WherePredicate::Type(PredicateType {
+ lifetimes: None,
+ bounded_ty: ty.clone(),
+ colon_token: Default::default(),
+ bounds: {
+ let mut punctuated = syn::punctuated::Punctuated::new();
+ punctuated.push_value(TypeParamBound::Trait(TraitBound {
+ paren_token: None,
+ modifier: syn::TraitBoundModifier::None,
+ lifetimes: None,
+ path: parse_quote!(Clone),
+ }));
+ punctuated.push_punct(syn::token::Add::default());
+ punctuated.push_value(TypeParamBound::Trait(TraitBound {
+ paren_token: None,
+ modifier: syn::TraitBoundModifier::None,
+ lifetimes: None,
+ path: parse_quote!(Default),
+ }));
+ punctuated
+ },
+ })
+ })
+ .collect();
+
+ // Append to where_clause or create a new one if it doesn't exist
+ let where_clause = match where_clause.cloned() {
+ Some(mut where_clause) => {
+ where_clause
+ .predicates
+ .extend(type_param_bounds.into_iter());
+ where_clause.clone()
+ }
+ None => WhereClause {
+ where_token: Default::default(),
+ predicates: type_param_bounds.into_iter().collect(),
+ },
+ };
+
+ let field_assignments: Vec<TokenStream2> = fields
+ .iter()
+ .map(|field| {
+ let name = &field.ident;
+ let is_refineable = is_refineable_field(field);
+ let is_optional = is_optional_field(field);
+
+ if is_refineable {
+ quote! {
+ self.#name.refine(&refinement.#name);
+ }
+ } else if is_optional {
+ quote! {
+ if let Some(ref value) = &refinement.#name {
+ self.#name = Some(value.clone());
+ }
+ }
+ } else {
+ quote! {
+ if let Some(ref value) = &refinement.#name {
+ self.#name = value.clone();
+ }
+ }
+ }
+ })
+ .collect();
+
+ let refinement_field_assignments: Vec<TokenStream2> = fields
+ .iter()
+ .map(|field| {
+ let name = &field.ident;
+ let is_refineable = is_refineable_field(field);
+
+ if is_refineable {
+ quote! {
+ self.#name.refine(&refinement.#name);
+ }
+ } else {
+ quote! {
+ if let Some(ref value) = &refinement.#name {
+ self.#name = Some(value.clone());
+ }
+ }
+ }
+ })
+ .collect();
+
+ let gen = quote! {
+ #[derive(Default, Clone)]
+ pub struct #refinement_ident #impl_generics {
+ #( #field_visibilities #field_names: #wrapped_types ),*
+ }
+
+ impl #impl_generics Refineable for #ident #ty_generics
+ #where_clause
+ {
+ type Refinement = #refinement_ident #ty_generics;
+
+ fn refine(&mut self, refinement: &Self::Refinement) {
+ #( #field_assignments )*
+ }
+ }
+
+ impl #impl_generics Refineable for #refinement_ident #ty_generics
+ #where_clause
+ {
+ type Refinement = #refinement_ident #ty_generics;
+
+ fn refine(&mut self, refinement: &Self::Refinement) {
+ #( #refinement_field_assignments )*
+ }
+ }
+ };
+
+ gen.into()
+}
+
+fn is_refineable_field(f: &Field) -> bool {
+ f.attrs.iter().any(|attr| attr.path.is_ident("refineable"))
+}
+
+fn is_optional_field(f: &Field) -> bool {
+ if let Type::Path(typepath) = &f.ty {
+ if typepath.qself.is_none() {
+ let segments = &typepath.path.segments;
+ if segments.len() == 1 && segments.iter().any(|s| s.ident == "Option") {
+ return true;
+ }
+ }
+ }
+ false
+}
+
+fn get_wrapper_type(field: &Field, ty: &Type) -> syn::Type {
+ if is_refineable_field(field) {
+ let struct_name = if let Type::Path(tp) = ty {
+ tp.path.segments.last().unwrap().ident.clone()
+ } else {
+ panic!("Expected struct type for a refineable field");
+ };
+ let refinement_struct_name = format_ident!("{}Refinement", struct_name);
+ let generics = if let Type::Path(tp) = ty {
+ &tp.path.segments.last().unwrap().arguments
+ } else {
+ &syn::PathArguments::None
+ };
+ parse_quote!(#refinement_struct_name #generics)
+ } else if is_optional_field(field) {
+ ty.clone()
+ } else {
+ parse_quote!(Option<#ty>)
+ }
+}
@@ -0,0 +1,14 @@
+pub use derive_refineable::Refineable;
+
+pub trait Refineable {
+ type Refinement: Default;
+
+ fn refine(&mut self, refinement: &Self::Refinement);
+ fn refined(mut self, refinement: &Self::Refinement) -> Self
+ where
+ Self: Sized,
+ {
+ self.refine(refinement);
+ self
+ }
+}
@@ -482,7 +482,7 @@ impl Item for ProjectSearchView {
.update(cx, |editor, cx| editor.deactivated(cx));
}
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
_detail: Option<usize>,
tab_theme: &theme::Tab,
@@ -567,6 +567,7 @@ impl Element<TerminalView> for TerminalElement {
font_size,
font_properties: Default::default(),
underline: Default::default(),
+ soft_wrap: false,
};
let selection_color = settings.theme.editor.selection.selection;
let match_color = settings.theme.search.match_background;
@@ -661,7 +661,7 @@ impl Item for TerminalView {
Some(self.terminal().read(cx).title().into())
}
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
_detail: Option<usize>,
tab_theme: &theme::Tab,
@@ -10,7 +10,7 @@ use gpui::{
platform,
platform::MouseButton,
scene::MouseClick,
- Action, Element, EventContext, MouseState, View, ViewContext,
+ Action, Element, EventContext, MouseState, ViewContext,
};
use schemars::JsonSchema;
use serde::Deserialize;
@@ -37,7 +37,7 @@ pub fn checkbox<Tag, V, F>(
) -> MouseEventHandler<V>
where
Tag: 'static,
- V: View,
+ V: 'static,
F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
{
let label = Label::new(label, style.label.text.clone())
@@ -57,7 +57,7 @@ pub fn checkbox_with_label<Tag, D, V, F>(
where
Tag: 'static,
D: Element<V>,
- V: View,
+ V: 'static,
F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
{
MouseEventHandler::new::<Tag, _>(id, cx, |state, _| {
@@ -93,7 +93,7 @@ where
.with_cursor_style(platform::CursorStyle::PointingHand)
}
-pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
+pub fn svg<V: 'static>(style: &SvgStyle) -> ConstrainedBox<V> {
Svg::new(style.asset.clone())
.with_color(style.color)
.constrained()
@@ -117,11 +117,11 @@ impl IconStyle {
}
}
-pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
+pub fn icon<V: 'static>(style: &IconStyle) -> Container<V> {
svg(&style.icon).contained().with_style(style.container)
}
-pub fn keystroke_label<V: View>(
+pub fn keystroke_label<V: 'static>(
label_text: &'static str,
label_style: &ContainedText,
keystroke_style: &ContainedText,
@@ -157,7 +157,7 @@ pub fn cta_button<Tag, L, V, F>(
where
Tag: 'static,
L: Into<Cow<'static, str>>,
- V: View,
+ V: 'static,
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
{
MouseEventHandler::new::<Tag, _>(0, cx, |state, _| {
@@ -196,9 +196,9 @@ pub fn modal<Tag, V, I, D, F>(
) -> impl Element<V>
where
Tag: 'static,
- V: View,
I: Into<Cow<'static, str>>,
D: Element<V>,
+ V: 'static,
F: FnOnce(&mut gpui::ViewContext<V>) -> D,
{
const TITLEBAR_HEIGHT: f32 = 28.;
@@ -232,7 +232,7 @@ impl Item for WelcomePage {
Some("Welcome to Zed!".into())
}
- fn tab_content<T: View>(
+ fn tab_content<T: 'static>(
&self,
_detail: Option<usize>,
style: &theme::Tab,
@@ -101,7 +101,7 @@ pub trait Item: View {
fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
None
}
- fn tab_content<V: View>(
+ fn tab_content<V: 'static>(
&self,
detail: Option<usize>,
style: &theme::Tab,
@@ -943,7 +943,7 @@ pub mod test {
})
}
- fn tab_content<V: View>(
+ fn tab_content<V: 'static>(
&self,
detail: Option<usize>,
_: &theme::Tab,
@@ -1976,12 +1976,12 @@ impl NavHistoryState {
}
}
-pub struct PaneBackdrop<V: View> {
+pub struct PaneBackdrop<V> {
child_view: usize,
child: AnyElement<V>,
}
-impl<V: View> PaneBackdrop<V> {
+impl<V> PaneBackdrop<V> {
pub fn new(pane_item_view: usize, child: AnyElement<V>) -> Self {
PaneBackdrop {
child,
@@ -1990,7 +1990,7 @@ impl<V: View> PaneBackdrop<V> {
}
}
-impl<V: View> Element<V> for PaneBackdrop<V> {
+impl<V: 'static> Element<V> for PaneBackdrop<V> {
type LayoutState = ();
type PaintState = ();
@@ -7,7 +7,7 @@ use gpui::{
geometry::{rect::RectF, vector::Vector2F},
platform::MouseButton,
scene::MouseUp,
- AppContext, Element, EventContext, MouseState, Quad, View, ViewContext, WeakViewHandle,
+ AppContext, Element, EventContext, MouseState, Quad, ViewContext, WeakViewHandle,
};
use project::ProjectEntryId;
@@ -107,7 +107,7 @@ where
handler
}
-pub fn handle_dropped_item<V: View>(
+pub fn handle_dropped_item<V: 'static>(
event: MouseUp,
workspace: WeakViewHandle<Workspace>,
pane: &WeakViewHandle<Pane>,
@@ -104,7 +104,7 @@ impl Item for SharedScreen {
}
}
- fn tab_content<V: View>(
+ fn tab_content<V: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
@@ -93,7 +93,7 @@ postage.workspace = true
rand.workspace = true
regex.workspace = true
rsa = "0.4"
-rust-embed = { version = "6.3", features = ["include-exclude"] }
+rust-embed = { version = "6.8.1" }
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
@@ -0,0 +1,5670 @@
+#![feature(prelude_import)]
+#![allow(dead_code, unused_variables)]
+#[prelude_import]
+use std::prelude::rust_2021::*;
+#[macro_use]
+extern crate std;
+use color::black;
+use components::button;
+use element::Element;
+use frame::frame;
+use gpui::{
+ geometry::{rect::RectF, vector::vec2f},
+ platform::WindowOptions,
+};
+use log::LevelFilter;
+use simplelog::SimpleLogger;
+use themes::{rose_pine, ThemeColors};
+use view::view;
+mod adapter {
+ use crate::element::{LayoutContext, PaintContext};
+ use gpui::{geometry::rect::RectF, LayoutEngine};
+ use util::ResultExt;
+ use crate::element::AnyElement;
+ pub struct Adapter<V>(pub(crate) AnyElement<V>);
+ impl<V: 'static> gpui::Element<V> for Adapter<V> {
+ type LayoutState = Option<LayoutEngine>;
+ type PaintState = ();
+ fn layout(
+ &mut self,
+ constraint: gpui::SizeConstraint,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+ cx.push_layout_engine(LayoutEngine::new());
+ let node = self.0.layout(view, cx).log_err();
+ if let Some(node) = node {
+ let layout_engine = cx.layout_engine().unwrap();
+ layout_engine.compute_layout(node, constraint.max).log_err();
+ }
+ let layout_engine = cx.pop_layout_engine();
+ if true {
+ if !layout_engine.is_some() {
+ ::core::panicking::panic("assertion failed: layout_engine.is_some()")
+ }
+ }
+ (constraint.max, layout_engine)
+ }
+ fn paint(
+ &mut self,
+ scene: &mut gpui::SceneBuilder,
+ bounds: RectF,
+ visible_bounds: RectF,
+ layout_engine: &mut Option<LayoutEngine>,
+ view: &mut V,
+ legacy_cx: &mut gpui::PaintContext<V>,
+ ) -> Self::PaintState {
+ legacy_cx.push_layout_engine(layout_engine.take().unwrap());
+ let mut cx = PaintContext::new(legacy_cx, scene);
+ self.0.paint(view, &mut cx).log_err();
+ *layout_engine = legacy_cx.pop_layout_engine();
+ if true {
+ if !layout_engine.is_some() {
+ ::core::panicking::panic("assertion failed: layout_engine.is_some()")
+ }
+ }
+ }
+ fn rect_for_text_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ bounds: RectF,
+ visible_bounds: RectF,
+ layout: &Self::LayoutState,
+ paint: &Self::PaintState,
+ view: &V,
+ cx: &gpui::ViewContext<V>,
+ ) -> Option<RectF> {
+ ::core::panicking::panic("not yet implemented")
+ }
+ fn debug(
+ &self,
+ bounds: RectF,
+ layout: &Self::LayoutState,
+ paint: &Self::PaintState,
+ view: &V,
+ cx: &gpui::ViewContext<V>,
+ ) -> gpui::serde_json::Value {
+ ::core::panicking::panic("not yet implemented")
+ }
+ }
+}
+mod color {
+ #![allow(dead_code)]
+ use std::{num::ParseIntError, ops::Range};
+ use smallvec::SmallVec;
+ pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+ let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
+ let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
+ let b = (hex & 0xFF) as f32 / 255.0;
+ Rgba { r, g, b, a: 1.0 }.into()
+ }
+ pub struct Rgba {
+ pub r: f32,
+ pub g: f32,
+ pub b: f32,
+ pub a: f32,
+ }
+ #[automatically_derived]
+ impl ::core::clone::Clone for Rgba {
+ #[inline]
+ fn clone(&self) -> Rgba {
+ let _: ::core::clone::AssertParamIsClone<f32>;
+ *self
+ }
+ }
+ #[automatically_derived]
+ impl ::core::marker::Copy for Rgba {}
+ #[automatically_derived]
+ impl ::core::default::Default for Rgba {
+ #[inline]
+ fn default() -> Rgba {
+ Rgba {
+ r: ::core::default::Default::default(),
+ g: ::core::default::Default::default(),
+ b: ::core::default::Default::default(),
+ a: ::core::default::Default::default(),
+ }
+ }
+ }
+ #[automatically_derived]
+ impl ::core::fmt::Debug for Rgba {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ ::core::fmt::Formatter::debug_struct_field4_finish(
+ f,
+ "Rgba",
+ "r",
+ &self.r,
+ "g",
+ &self.g,
+ "b",
+ &self.b,
+ "a",
+ &&self.a,
+ )
+ }
+ }
+ pub trait Lerp {
+ fn lerp(&self, level: f32) -> Hsla;
+ }
+ impl Lerp for Range<Hsla> {
+ fn lerp(&self, level: f32) -> Hsla {
+ let level = level.clamp(0., 1.);
+ Hsla {
+ h: self.start.h + (level * (self.end.h - self.start.h)),
+ s: self.start.s + (level * (self.end.s - self.start.s)),
+ l: self.start.l + (level * (self.end.l - self.start.l)),
+ a: self.start.a + (level * (self.end.a - self.start.a)),
+ }
+ }
+ }
+ impl From<gpui::color::Color> for Rgba {
+ fn from(value: gpui::color::Color) -> Self {
+ Self {
+ r: value.0.r as f32 / 255.0,
+ g: value.0.g as f32 / 255.0,
+ b: value.0.b as f32 / 255.0,
+ a: value.0.a as f32 / 255.0,
+ }
+ }
+ }
+ impl From<Hsla> for Rgba {
+ fn from(color: Hsla) -> Self {
+ let h = color.h;
+ let s = color.s;
+ let l = color.l;
+ let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+ let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
+ let m = l - c / 2.0;
+ let cm = c + m;
+ let xm = x + m;
+ let (r, g, b) = match (h * 6.0).floor() as i32 {
+ 0 | 6 => (cm, xm, m),
+ 1 => (xm, cm, m),
+ 2 => (m, cm, xm),
+ 3 => (m, xm, cm),
+ 4 => (xm, m, cm),
+ _ => (cm, m, xm),
+ };
+ Rgba { r, g, b, a: color.a }
+ }
+ }
+ impl TryFrom<&'_ str> for Rgba {
+ type Error = ParseIntError;
+ fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
+ let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
+ let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
+ let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
+ let a = if value.len() > 7 {
+ u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
+ } else {
+ 1.0
+ };
+ Ok(Rgba { r, g, b, a })
+ }
+ }
+ impl Into<gpui::color::Color> for Rgba {
+ fn into(self) -> gpui::color::Color {
+ gpui::color::rgba(self.r, self.g, self.b, self.a)
+ }
+ }
+ pub struct Hsla {
+ pub h: f32,
+ pub s: f32,
+ pub l: f32,
+ pub a: f32,
+ }
+ #[automatically_derived]
+ impl ::core::default::Default for Hsla {
+ #[inline]
+ fn default() -> Hsla {
+ Hsla {
+ h: ::core::default::Default::default(),
+ s: ::core::default::Default::default(),
+ l: ::core::default::Default::default(),
+ a: ::core::default::Default::default(),
+ }
+ }
+ }
+ #[automatically_derived]
+ impl ::core::marker::Copy for Hsla {}
+ #[automatically_derived]
+ impl ::core::clone::Clone for Hsla {
+ #[inline]
+ fn clone(&self) -> Hsla {
+ let _: ::core::clone::AssertParamIsClone<f32>;
+ *self
+ }
+ }
+ #[automatically_derived]
+ impl ::core::fmt::Debug for Hsla {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ ::core::fmt::Formatter::debug_struct_field4_finish(
+ f,
+ "Hsla",
+ "h",
+ &self.h,
+ "s",
+ &self.s,
+ "l",
+ &self.l,
+ "a",
+ &&self.a,
+ )
+ }
+ }
+ #[automatically_derived]
+ impl ::core::marker::StructuralPartialEq for Hsla {}
+ #[automatically_derived]
+ impl ::core::cmp::PartialEq for Hsla {
+ #[inline]
+ fn eq(&self, other: &Hsla) -> bool {
+ self.h == other.h && self.s == other.s && self.l == other.l
+ && self.a == other.a
+ }
+ }
+ pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
+ Hsla {
+ h: h.clamp(0., 1.),
+ s: s.clamp(0., 1.),
+ l: l.clamp(0., 1.),
+ a: a.clamp(0., 1.),
+ }
+ }
+ pub fn black() -> Hsla {
+ Hsla { h: 0., s: 0., l: 0., a: 1. }
+ }
+ impl From<Rgba> for Hsla {
+ fn from(color: Rgba) -> Self {
+ let r = color.r;
+ let g = color.g;
+ let b = color.b;
+ let max = r.max(g.max(b));
+ let min = r.min(g.min(b));
+ let delta = max - min;
+ let l = (max + min) / 2.0;
+ let s = if l == 0.0 || l == 1.0 {
+ 0.0
+ } else if l < 0.5 {
+ delta / (2.0 * l)
+ } else {
+ delta / (2.0 - 2.0 * l)
+ };
+ let h = if delta == 0.0 {
+ 0.0
+ } else if max == r {
+ ((g - b) / delta).rem_euclid(6.0) / 6.0
+ } else if max == g {
+ ((b - r) / delta + 2.0) / 6.0
+ } else {
+ ((r - g) / delta + 4.0) / 6.0
+ };
+ Hsla { h, s, l, a: color.a }
+ }
+ }
+ impl Hsla {
+ /// Scales the saturation and lightness by the given values, clamping at 1.0.
+ pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
+ self.s = (self.s * s).clamp(0., 1.);
+ self.l = (self.l * l).clamp(0., 1.);
+ self
+ }
+ /// Increases the saturation of the color by a certain amount, with a max
+ /// value of 1.0.
+ pub fn saturate(mut self, amount: f32) -> Self {
+ self.s += amount;
+ self.s = self.s.clamp(0.0, 1.0);
+ self
+ }
+ /// Decreases the saturation of the color by a certain amount, with a min
+ /// value of 0.0.
+ pub fn desaturate(mut self, amount: f32) -> Self {
+ self.s -= amount;
+ self.s = self.s.max(0.0);
+ if self.s < 0.0 {
+ self.s = 0.0;
+ }
+ self
+ }
+ /// Brightens the color by increasing the lightness by a certain amount,
+ /// with a max value of 1.0.
+ pub fn brighten(mut self, amount: f32) -> Self {
+ self.l += amount;
+ self.l = self.l.clamp(0.0, 1.0);
+ self
+ }
+ /// Darkens the color by decreasing the lightness by a certain amount,
+ /// with a max value of 0.0.
+ pub fn darken(mut self, amount: f32) -> Self {
+ self.l -= amount;
+ self.l = self.l.clamp(0.0, 1.0);
+ self
+ }
+ }
+ impl From<gpui::color::Color> for Hsla {
+ fn from(value: gpui::color::Color) -> Self {
+ Rgba::from(value).into()
+ }
+ }
+ impl Into<gpui::color::Color> for Hsla {
+ fn into(self) -> gpui::color::Color {
+ Rgba::from(self).into()
+ }
+ }
+ pub struct ColorScale {
+ colors: SmallVec<[Hsla; 2]>,
+ positions: SmallVec<[f32; 2]>,
+ }
+ pub fn scale<I, C>(colors: I) -> ColorScale
+ where
+ I: IntoIterator<Item = C>,
+ C: Into<Hsla>,
+ {
+ let mut scale = ColorScale {
+ colors: colors.into_iter().map(Into::into).collect(),
+ positions: SmallVec::new(),
+ };
+ let num_colors: f32 = scale.colors.len() as f32 - 1.0;
+ scale
+ .positions = (0..scale.colors.len())
+ .map(|i| i as f32 / num_colors)
+ .collect();
+ scale
+ }
+ impl ColorScale {
+ fn at(&self, t: f32) -> Hsla {
+ if true {
+ if !(0.0 <= t && t <= 1.0) {
+ {
+ ::core::panicking::panic_fmt(
+ format_args!(
+ "t value {0} is out of range. Expected value in range 0.0 to 1.0",
+ t,
+ ),
+ );
+ }
+ }
+ }
+ let position = match self
+ .positions
+ .binary_search_by(|a| a.partial_cmp(&t).unwrap())
+ {
+ Ok(index) | Err(index) => index,
+ };
+ let lower_bound = position.saturating_sub(1);
+ let upper_bound = position.min(self.colors.len() - 1);
+ let lower_color = &self.colors[lower_bound];
+ let upper_color = &self.colors[upper_bound];
+ match upper_bound.checked_sub(lower_bound) {
+ Some(0) | None => *lower_color,
+ Some(_) => {
+ let interval_t = (t - self.positions[lower_bound])
+ / (self.positions[upper_bound] - self.positions[lower_bound]);
+ let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
+ let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
+ let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
+ let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
+ Hsla { h, s, l, a }
+ }
+ }
+ }
+ }
+}
+mod components {
+ use crate::{
+ element::{Element, ElementMetadata},
+ frame, text::ArcCow, themes::rose_pine,
+ };
+ use gpui::{platform::MouseButton, ViewContext};
+ use playground_macros::Element;
+ use std::{marker::PhantomData, rc::Rc};
+ struct ButtonHandlers<V, D> {
+ click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+ }
+ impl<V, D> Default for ButtonHandlers<V, D> {
+ fn default() -> Self {
+ Self { click: None }
+ }
+ }
+ #[element_crate = "crate"]
+ pub struct Button<V: 'static, D: 'static> {
+ metadata: ElementMetadata<V>,
+ handlers: ButtonHandlers<V, D>,
+ label: Option<ArcCow<'static, str>>,
+ icon: Option<ArcCow<'static, str>>,
+ data: Rc<D>,
+ view_type: PhantomData<V>,
+ }
+ impl<V: 'static, D: 'static> crate::element::Element<V> for Button<V, D> {
+ type Layout = crate::element::AnyElement<V>;
+ fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
+ &mut self.metadata.style
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
+ &mut self.metadata.handlers
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut crate::element::LayoutContext<V>,
+ ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+ let mut element = self.render(view, cx).into_any();
+ let node_id = element.layout(view, cx)?;
+ Ok((node_id, element))
+ }
+ fn paint<'a>(
+ &mut self,
+ layout: crate::element::Layout<'a, Self::Layout>,
+ view: &mut V,
+ cx: &mut crate::element::PaintContext<V>,
+ ) -> anyhow::Result<()> {
+ layout.from_element.paint(view, cx)?;
+ Ok(())
+ }
+ }
+ impl<V: 'static, D: 'static> crate::element::IntoElement<V> for Button<V, D> {
+ type Element = Self;
+ fn into_element(self) -> Self {
+ self
+ }
+ }
+ impl<V: 'static> Button<V, ()> {
+ fn new() -> Self {
+ Self {
+ metadata: Default::default(),
+ handlers: ButtonHandlers::default(),
+ label: None,
+ icon: None,
+ data: Rc::new(()),
+ view_type: PhantomData,
+ }
+ }
+ pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+ Button {
+ metadata: Default::default(),
+ handlers: ButtonHandlers::default(),
+ label: self.label,
+ icon: self.icon,
+ data: Rc::new(data),
+ view_type: PhantomData,
+ }
+ }
+ }
+ impl<V: 'static, D: 'static> Button<V, D> {
+ pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+ self.label = Some(label.into());
+ self
+ }
+ pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+ self.icon = Some(icon.into());
+ self
+ }
+ pub fn click(
+ self,
+ handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static,
+ ) -> Self {
+ let data = self.data.clone();
+ Element::click(
+ self,
+ MouseButton::Left,
+ move |view, _, cx| {
+ handler(view, data.as_ref(), cx);
+ },
+ )
+ }
+ }
+ pub fn button<V>() -> Button<V, ()> {
+ Button::new()
+ }
+ impl<V: 'static, D: 'static> Button<V, D> {
+ fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ let button = frame()
+ .fill(rose_pine::dawn().error(0.5))
+ .h_4()
+ .children(self.label.clone());
+ if let Some(handler) = self.handlers.click.clone() {
+ let data = self.data.clone();
+ button
+ .mouse_down(
+ MouseButton::Left,
+ move |view, event, cx| { handler(view, data.as_ref(), cx) },
+ )
+ } else {
+ button
+ }
+ }
+ }
+}
+mod element {
+ use crate::{
+ adapter::Adapter, color::Hsla, hoverable::Hoverable,
+ style::{Display, Fill, OptionalStyle, Overflow, Position},
+ };
+ use anyhow::Result;
+ pub use gpui::LayoutContext;
+ use gpui::{
+ geometry::{DefinedLength, Length, OptionalPoint},
+ platform::{MouseButton, MouseButtonEvent},
+ EngineLayout, EventContext, RenderContext, ViewContext,
+ };
+ use playground_macros::tailwind_lengths;
+ use std::{
+ any::{Any, TypeId},
+ cell::Cell, rc::Rc,
+ };
+ pub use crate::paint_context::PaintContext;
+ pub use taffy::tree::NodeId;
+ pub struct Layout<'a, E: ?Sized> {
+ pub from_engine: EngineLayout,
+ pub from_element: &'a mut E,
+ }
+ pub struct ElementMetadata<V> {
+ pub style: OptionalStyle,
+ pub handlers: Vec<EventHandler<V>>,
+ }
+ pub struct EventHandler<V> {
+ handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
+ event_type: TypeId,
+ outside_bounds: bool,
+ }
+ impl<V> Clone for EventHandler<V> {
+ fn clone(&self) -> Self {
+ Self {
+ handler: self.handler.clone(),
+ event_type: self.event_type,
+ outside_bounds: self.outside_bounds,
+ }
+ }
+ }
+ impl<V> Default for ElementMetadata<V> {
+ fn default() -> Self {
+ Self {
+ style: OptionalStyle::default(),
+ handlers: Vec::new(),
+ }
+ }
+ }
+ pub trait Element<V: 'static>: 'static {
+ type Layout: 'static;
+ fn declared_style(&mut self) -> &mut OptionalStyle;
+ fn computed_style(&mut self) -> &OptionalStyle {
+ self.declared_style()
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(NodeId, Self::Layout)>;
+ fn paint<'a>(
+ &mut self,
+ layout: Layout<Self::Layout>,
+ view: &mut V,
+ cx: &mut PaintContext<V>,
+ ) -> Result<()>;
+ /// Convert to a dynamically-typed element suitable for layout and paint.
+ fn into_any(self) -> AnyElement<V>
+ where
+ Self: 'static + Sized,
+ {
+ AnyElement {
+ element: Box::new(self) as Box<dyn ElementObject<V>>,
+ layout: None,
+ }
+ }
+ fn adapt(self) -> Adapter<V>
+ where
+ Self: Sized,
+ Self: Element<V>,
+ {
+ Adapter(self.into_any())
+ }
+ fn click(
+ self,
+ button: MouseButton,
+ handler: impl Fn(&mut V, &MouseButtonEvent, &mut ViewContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ let pressed: Rc<Cell<bool>> = Default::default();
+ self.mouse_down(
+ button,
+ {
+ let pressed = pressed.clone();
+ move |_, _, _| {
+ pressed.set(true);
+ }
+ },
+ )
+ .mouse_up_outside(
+ button,
+ {
+ let pressed = pressed.clone();
+ move |_, _, _| {
+ pressed.set(false);
+ }
+ },
+ )
+ .mouse_up(
+ button,
+ move |view, event, event_cx| {
+ if pressed.get() {
+ pressed.set(false);
+ handler(view, event, event_cx);
+ }
+ },
+ )
+ }
+ fn mouse_down(
+ mut self,
+ button: MouseButton,
+ handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.handlers_mut()
+ .push(EventHandler {
+ handler: Rc::new(move |view, event, event_cx| {
+ let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+ if event.button == button && event.is_down {
+ handler(view, event, event_cx);
+ }
+ }),
+ event_type: TypeId::of::<MouseButtonEvent>(),
+ outside_bounds: false,
+ });
+ self
+ }
+ fn mouse_down_outside(
+ mut self,
+ button: MouseButton,
+ handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.handlers_mut()
+ .push(EventHandler {
+ handler: Rc::new(move |view, event, event_cx| {
+ let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+ if event.button == button && event.is_down {
+ handler(view, event, event_cx);
+ }
+ }),
+ event_type: TypeId::of::<MouseButtonEvent>(),
+ outside_bounds: true,
+ });
+ self
+ }
+ fn mouse_up(
+ mut self,
+ button: MouseButton,
+ handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.handlers_mut()
+ .push(EventHandler {
+ handler: Rc::new(move |view, event, event_cx| {
+ let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+ if event.button == button && !event.is_down {
+ handler(view, event, event_cx);
+ }
+ }),
+ event_type: TypeId::of::<MouseButtonEvent>(),
+ outside_bounds: false,
+ });
+ self
+ }
+ fn mouse_up_outside(
+ mut self,
+ button: MouseButton,
+ handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.handlers_mut()
+ .push(EventHandler {
+ handler: Rc::new(move |view, event, event_cx| {
+ let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+ if event.button == button && !event.is_down {
+ handler(view, event, event_cx);
+ }
+ }),
+ event_type: TypeId::of::<MouseButtonEvent>(),
+ outside_bounds: true,
+ });
+ self
+ }
+ fn block(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().display = Some(Display::Block);
+ self
+ }
+ fn flex(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().display = Some(Display::Flex);
+ self
+ }
+ fn grid(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().display = Some(Display::Grid);
+ self
+ }
+ fn overflow_visible(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self
+ .declared_style()
+ .overflow = OptionalPoint {
+ x: Some(Overflow::Visible),
+ y: Some(Overflow::Visible),
+ };
+ self
+ }
+ fn overflow_hidden(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self
+ .declared_style()
+ .overflow = OptionalPoint {
+ x: Some(Overflow::Hidden),
+ y: Some(Overflow::Hidden),
+ };
+ self
+ }
+ fn overflow_scroll(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self
+ .declared_style()
+ .overflow = OptionalPoint {
+ x: Some(Overflow::Scroll),
+ y: Some(Overflow::Scroll),
+ };
+ self
+ }
+ fn overflow_x_visible(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.x = Some(Overflow::Visible);
+ self
+ }
+ fn overflow_x_hidden(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.x = Some(Overflow::Hidden);
+ self
+ }
+ fn overflow_x_scroll(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.x = Some(Overflow::Scroll);
+ self
+ }
+ fn overflow_y_visible(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.y = Some(Overflow::Visible);
+ self
+ }
+ fn overflow_y_hidden(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.y = Some(Overflow::Hidden);
+ self
+ }
+ fn overflow_y_scroll(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().overflow.y = Some(Overflow::Scroll);
+ self
+ }
+ fn relative(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().position = Some(Position::Relative);
+ self
+ }
+ fn absolute(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().position = Some(Position::Absolute);
+ self
+ }
+ fn inset_0(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(0.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_px(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(1.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_0_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.125).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.25).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.375).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.5).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.625).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.75).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.875).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_4(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.25).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_6(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.5).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_7(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.75).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_8(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_9(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.25).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_10(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.5).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_11(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.75).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_12(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_14(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.5).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_16(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(4.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_20(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(5.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_24(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(6.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_28(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(7.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_32(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(8.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_36(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(9.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_40(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(10.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_44(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(11.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_48(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(12.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_52(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(13.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_56(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(14.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_60(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(15.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_64(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(16.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_72(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(18.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_80(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(20.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_96(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(24.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_half(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(20.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(40.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(60.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_4_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(80.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_4_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_5_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_1_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(8.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_2_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_3_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_4_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_5_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(41.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_6_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_7_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(58.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_8_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_9_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_10_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_11_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(91.666667).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn inset_full(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(100.).into();
+ {
+ let inset = self
+ .computed_style()
+ .inset
+ .get_or_insert_with(Default::default);
+ inset.top = length;
+ inset.right = length;
+ inset.bottom = length;
+ inset.left = length;
+ self
+ }
+ }
+ fn w(mut self, width: impl Into<Length>) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().size.width = Some(width.into());
+ self
+ }
+ fn w_auto(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().size.width = Some(Length::Auto);
+ self
+ }
+ fn w_0(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(0.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_px(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(1.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_0_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.125).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.25).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.375).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.5).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.625).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.75).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.875).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_4(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.25).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_6(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.5).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_7(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.75).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_8(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_9(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.25).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_10(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.5).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_11(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.75).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_12(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_14(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.5).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_16(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(4.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_20(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(5.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_24(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(6.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_28(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(7.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_32(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(8.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_36(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(9.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_40(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(10.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_44(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(11.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_48(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(12.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_52(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(13.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_56(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(14.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_60(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(15.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_64(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(16.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_72(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(18.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_80(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(20.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_96(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(24.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_half(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(20.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(40.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(60.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_4_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(80.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_4_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_5_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_1_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(8.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_2_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_3_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_4_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_5_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(41.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_6_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_7_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(58.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_8_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_9_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_10_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_11_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(91.666667).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn w_full(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(100.).into();
+ {
+ self.declared_style().size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_0(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(0.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_px(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(1.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_0_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.125).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.25).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.375).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.5).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.625).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.75).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.875).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_4(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.25).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_6(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.5).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_7(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.75).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_8(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_9(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.25).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_10(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.5).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_11(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.75).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_12(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_14(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.5).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_16(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(4.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_20(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(5.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_24(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(6.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_28(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(7.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_32(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(8.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_36(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(9.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_40(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(10.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_44(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(11.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_48(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(12.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_52(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(13.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_56(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(14.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_60(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(15.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_64(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(16.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_72(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(18.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_80(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(20.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_96(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(24.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_half(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(20.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(40.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(60.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_4_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(80.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_4_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_5_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_1_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(8.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_2_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_3_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_4_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_5_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(41.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_6_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_7_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(58.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_8_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_9_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_10_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_11_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(91.666667).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn min_w_full(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(100.).into();
+ {
+ self.declared_style().min_size.width = Some(length);
+ self
+ }
+ }
+ fn h(mut self, height: impl Into<Length>) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().size.height = Some(height.into());
+ self
+ }
+ fn h_auto(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().size.height = Some(Length::Auto);
+ self
+ }
+ fn h_0(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Pixels(0.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_px(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Pixels(1.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_0_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.125).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.25).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.375).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.5).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.625).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.75).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(0.875).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_4(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(1.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(1.25).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_6(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(1.5).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_7(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(1.75).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_8(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(2.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_9(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(2.25).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_10(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(2.5).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_11(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(2.75).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_12(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(3.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_14(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(3.5).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_16(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(4.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_20(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(5.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_24(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(6.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_28(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(7.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_32(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(8.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_36(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(9.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_40(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(10.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_44(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(11.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_48(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(12.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_52(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(13.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_56(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(14.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_60(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(15.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_64(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(16.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_72(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(18.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_80(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(20.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_96(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Rems(24.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_half(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(20.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(40.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(60.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_4_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(80.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_4_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_5_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_1_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(8.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_2_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_3_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_4_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_5_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(41.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_6_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_7_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(58.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_8_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_9_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_10_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_11_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(91.666667).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn h_full(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let height = DefinedLength::Percent(100.).into();
+ {
+ self.declared_style().size.height = Some(height);
+ self
+ }
+ }
+ fn min_h_0(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(0.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_px(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Pixels(1.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_0_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.125).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.25).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.375).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.5).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.625).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.75).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(0.875).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_4(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_5(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.25).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_6(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.5).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_7(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(1.75).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_8(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_9(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.25).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_10(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.5).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_11(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(2.75).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_12(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_14(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(3.5).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_16(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(4.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_20(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(5.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_24(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(6.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_28(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(7.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_32(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(8.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_36(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(9.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_40(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(10.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_44(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(11.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_48(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(12.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_52(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(13.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_56(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(14.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_60(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(15.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_64(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(16.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_72(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(18.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_80(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(20.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_96(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Rems(24.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_half(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_3rd(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3_4th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(20.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(40.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(60.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_4_5th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(80.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_4_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_5_6th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_1_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(8.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_2_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(16.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_3_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(25.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_4_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(33.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_5_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(41.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_6_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(50.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_7_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(58.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_8_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(66.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_9_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(75.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_10_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(83.333333).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_11_12th(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(91.666667).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn min_h_full(mut self) -> Self
+ where
+ Self: Sized,
+ {
+ let length = DefinedLength::Percent(100.).into();
+ {
+ self.declared_style().min_size.height = Some(length);
+ self
+ }
+ }
+ fn hoverable(self) -> Hoverable<V, Self>
+ where
+ Self: Sized,
+ {
+ Hoverable::new(self)
+ }
+ fn fill(mut self, fill: impl Into<Fill>) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().fill = Some(Some(fill.into()));
+ self
+ }
+ fn text_color(mut self, color: impl Into<Hsla>) -> Self
+ where
+ Self: Sized,
+ {
+ self.declared_style().text_color = Some(Some(color.into()));
+ self
+ }
+ }
+ trait ElementObject<V> {
+ fn style(&mut self) -> &mut OptionalStyle;
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(NodeId, Box<dyn Any>)>;
+ fn paint(
+ &mut self,
+ layout: Layout<dyn Any>,
+ view: &mut V,
+ cx: &mut PaintContext<V>,
+ ) -> Result<()>;
+ }
+ impl<V: 'static, E: Element<V>> ElementObject<V> for E {
+ fn style(&mut self) -> &mut OptionalStyle {
+ Element::declared_style(self)
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+ Element::handlers_mut(self)
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(NodeId, Box<dyn Any>)> {
+ let (node_id, layout) = self.layout(view, cx)?;
+ let layout = Box::new(layout) as Box<dyn Any>;
+ Ok((node_id, layout))
+ }
+ fn paint(
+ &mut self,
+ layout: Layout<dyn Any>,
+ view: &mut V,
+ cx: &mut PaintContext<V>,
+ ) -> Result<()> {
+ let layout = Layout {
+ from_engine: layout.from_engine,
+ from_element: layout.from_element.downcast_mut::<E::Layout>().unwrap(),
+ };
+ self.paint(layout, view, cx)
+ }
+ }
+ /// A dynamically typed element.
+ pub struct AnyElement<V> {
+ element: Box<dyn ElementObject<V>>,
+ layout: Option<(NodeId, Box<dyn Any>)>,
+ }
+ impl<V: 'static> AnyElement<V> {
+ pub fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<NodeId> {
+ let pushed_text_style = self.push_text_style(cx);
+ let (node_id, layout) = self.element.layout(view, cx)?;
+ self.layout = Some((node_id, layout));
+ if pushed_text_style {
+ cx.pop_text_style();
+ }
+ Ok(node_id)
+ }
+ pub fn push_text_style(&mut self, cx: &mut impl RenderContext) -> bool {
+ let text_style = self.element.style().text_style();
+ if let Some(text_style) = text_style {
+ let mut current_text_style = cx.text_style();
+ text_style.apply(&mut current_text_style);
+ cx.push_text_style(current_text_style);
+ true
+ } else {
+ false
+ }
+ }
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
+ let pushed_text_style = self.push_text_style(cx);
+ let (layout_node_id, element_layout) = self
+ .layout
+ .as_mut()
+ .expect("paint called before layout");
+ let layout = Layout {
+ from_engine: cx
+ .layout_engine()
+ .unwrap()
+ .computed_layout(*layout_node_id)
+ .expect(
+ "you can currently only use playground elements within an adapter",
+ ),
+ from_element: element_layout.as_mut(),
+ };
+ let style = self.element.style();
+ let fill_color = style.fill.flatten().and_then(|fill| fill.color());
+ if let Some(fill_color) = fill_color {
+ cx.scene
+ .push_quad(gpui::scene::Quad {
+ bounds: layout.from_engine.bounds,
+ background: Some(fill_color.into()),
+ border: Default::default(),
+ corner_radii: Default::default(),
+ });
+ }
+ for event_handler in self.element.handlers_mut().iter().cloned() {
+ let EngineLayout { order, bounds } = layout.from_engine;
+ let view_id = cx.view_id();
+ let view_event_handler = event_handler.handler.clone();
+ cx.scene
+ .interactive_regions
+ .push(gpui::scene::InteractiveRegion {
+ order,
+ bounds,
+ outside_bounds: event_handler.outside_bounds,
+ event_handler: Rc::new(move |view, event, window_cx, view_id| {
+ let mut view_context = ViewContext::mutable(
+ window_cx,
+ view_id,
+ );
+ let mut event_context = EventContext::new(&mut view_context);
+ view_event_handler(
+ view.downcast_mut().unwrap(),
+ event,
+ &mut event_context,
+ );
+ }),
+ event_type: event_handler.event_type,
+ view_id,
+ });
+ }
+ self.element.paint(layout, view, cx)?;
+ if pushed_text_style {
+ cx.pop_text_style();
+ }
+ Ok(())
+ }
+ }
+ impl<V: 'static> Element<V> for AnyElement<V> {
+ type Layout = ();
+ fn declared_style(&mut self) -> &mut OptionalStyle {
+ self.element.style()
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+ self.element.handlers_mut()
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(NodeId, Self::Layout)> {
+ Ok((self.layout(view, cx)?, ()))
+ }
+ fn paint(
+ &mut self,
+ layout: Layout<()>,
+ view: &mut V,
+ cx: &mut PaintContext<V>,
+ ) -> Result<()> {
+ self.paint(view, cx)
+ }
+ }
+ pub trait IntoElement<V: 'static> {
+ type Element: Element<V>;
+ fn into_element(self) -> Self::Element;
+ fn into_any_element(self) -> AnyElement<V>
+ where
+ Self: Sized,
+ {
+ self.into_element().into_any()
+ }
+ }
+}
+mod frame {
+ use crate::{
+ element::{
+ AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext,
+ NodeId, PaintContext,
+ },
+ style::{OptionalStyle, Style},
+ };
+ use anyhow::{anyhow, Result};
+ use gpui::LayoutNodeId;
+ use playground_macros::IntoElement;
+ #[element_crate = "crate"]
+ pub struct Frame<V: 'static> {
+ style: OptionalStyle,
+ handlers: Vec<EventHandler<V>>,
+ children: Vec<AnyElement<V>>,
+ }
+ impl<V: 'static> crate::element::IntoElement<V> for Frame<V> {
+ type Element = Self;
+ fn into_element(self) -> Self {
+ self
+ }
+ }
+ pub fn frame<V>() -> Frame<V> {
+ Frame {
+ style: OptionalStyle::default(),
+ handlers: Vec::new(),
+ children: Vec::new(),
+ }
+ }
+ impl<V: 'static> Element<V> for Frame<V> {
+ type Layout = ();
+ fn declared_style(&mut self) -> &mut OptionalStyle {
+ &mut self.style
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+ &mut self.handlers
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(NodeId, Self::Layout)> {
+ let child_layout_node_ids = self
+ .children
+ .iter_mut()
+ .map(|child| child.layout(view, cx))
+ .collect::<Result<Vec<LayoutNodeId>>>()?;
+ let rem_size = cx.rem_pixels();
+ let style: Style = self.style.into();
+ let node_id = cx
+ .layout_engine()
+ .ok_or_else(|| ::anyhow::__private::must_use({
+ let error = ::anyhow::__private::format_err(
+ format_args!("no layout engine"),
+ );
+ error
+ }))?
+ .add_node(style.to_taffy(rem_size), child_layout_node_ids)?;
+ Ok((node_id, ()))
+ }
+ fn paint(
+ &mut self,
+ layout: Layout<()>,
+ view: &mut V,
+ cx: &mut PaintContext<V>,
+ ) -> Result<()> {
+ for child in &mut self.children {
+ child.paint(view, cx)?;
+ }
+ Ok(())
+ }
+ }
+ impl<V: 'static> Frame<V> {
+ pub fn child(mut self, child: impl IntoElement<V>) -> Self {
+ self.children.push(child.into_any_element());
+ self
+ }
+ pub fn children<I, E>(mut self, children: I) -> Self
+ where
+ I: IntoIterator<Item = E>,
+ E: IntoElement<V>,
+ {
+ self.children.extend(children.into_iter().map(|e| e.into_any_element()));
+ self
+ }
+ }
+}
+mod hoverable {
+ use std::{cell::Cell, marker::PhantomData, rc::Rc};
+ use gpui::{
+ geometry::{rect::RectF, vector::Vector2F},
+ scene::MouseMove, EngineLayout,
+ };
+ use crate::{element::Element, style::{OptionalStyle, Style}};
+ pub struct Hoverable<V, E> {
+ hover_style: OptionalStyle,
+ computed_style: Option<Style>,
+ view_type: PhantomData<V>,
+ child: E,
+ }
+ impl<V, E> Hoverable<V, E> {
+ pub fn new(child: E) -> Self {
+ Self {
+ hover_style: OptionalStyle::default(),
+ computed_style: None,
+ view_type: PhantomData,
+ child,
+ }
+ }
+ }
+ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
+ type Layout = E::Layout;
+ fn declared_style(&mut self) -> &mut OptionalStyle {
+ &mut self.hover_style
+ }
+ fn computed_style(&mut self) -> &OptionalStyle {
+ ::core::panicking::panic("not yet implemented")
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
+ self.child.handlers_mut()
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut gpui::LayoutContext<V>,
+ ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+ self.child.layout(view, cx)
+ }
+ fn paint<'a>(
+ &mut self,
+ layout: crate::element::Layout<Self::Layout>,
+ view: &mut V,
+ cx: &mut crate::element::PaintContext<V>,
+ ) -> anyhow::Result<()> {
+ let EngineLayout { bounds, order } = layout.from_engine;
+ let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
+ let was_hovered = Rc::new(Cell::new(false));
+ self.child.paint(layout, view, cx)?;
+ cx.draw_interactive_region(
+ order,
+ window_bounds,
+ false,
+ move |view, event: &MouseMove, cx| {
+ let is_hovered = bounds.contains_point(cx.mouse_position());
+ if is_hovered != was_hovered.get() {
+ was_hovered.set(is_hovered);
+ cx.repaint();
+ }
+ },
+ );
+ Ok(())
+ }
+ }
+}
+mod paint_context {
+ use std::{any::TypeId, rc::Rc};
+ use derive_more::{Deref, DerefMut};
+ use gpui::{geometry::rect::RectF, EventContext, RenderContext, ViewContext};
+ pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
+ pub use taffy::tree::NodeId;
+ pub struct PaintContext<'a, 'b, 'c, 'd, V> {
+ #[deref]
+ #[deref_mut]
+ pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+ pub(crate) scene: &'d mut gpui::SceneBuilder,
+ }
+ impl<'a, 'b, 'c, 'd, V> ::core::ops::Deref for PaintContext<'a, 'b, 'c, 'd, V> {
+ type Target = &'d mut LegacyPaintContext<'a, 'b, 'c, V>;
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.legacy_cx
+ }
+ }
+ impl<'a, 'b, 'c, 'd, V> ::core::ops::DerefMut for PaintContext<'a, 'b, 'c, 'd, V> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.legacy_cx
+ }
+ }
+ impl<V> RenderContext for PaintContext<'_, '_, '_, '_, V> {
+ fn text_style(&self) -> gpui::fonts::TextStyle {
+ self.legacy_cx.text_style()
+ }
+ fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+ self.legacy_cx.push_text_style(style)
+ }
+ fn pop_text_style(&mut self) {
+ self.legacy_cx.pop_text_style()
+ }
+ }
+ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
+ pub fn new(
+ legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+ scene: &'d mut gpui::SceneBuilder,
+ ) -> Self {
+ Self { legacy_cx, scene }
+ }
+ pub fn draw_interactive_region<E: 'static>(
+ &mut self,
+ order: u32,
+ bounds: RectF,
+ outside_bounds: bool,
+ event_handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
+ ) {
+ self.scene
+ .interactive_regions
+ .push(gpui::scene::InteractiveRegion {
+ order,
+ bounds,
+ outside_bounds,
+ event_handler: Rc::new(move |view, event, window_cx, view_id| {
+ let mut view_context = ViewContext::mutable(window_cx, view_id);
+ let mut event_context = EventContext::new(&mut view_context);
+ event_handler(
+ view.downcast_mut().unwrap(),
+ event.downcast_ref().unwrap(),
+ &mut event_context,
+ );
+ }),
+ event_type: TypeId::of::<E>(),
+ view_id: self.view_id(),
+ });
+ }
+ }
+}
+mod style {
+ use crate::color::Hsla;
+ use gpui::geometry::{
+ DefinedLength, Edges, Length, OptionalEdges, OptionalPoint, OptionalSize, Point,
+ Size,
+ };
+ use optional::Optional;
+ pub use taffy::style::{
+ AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap,
+ JustifyContent, Overflow, Position,
+ };
+ pub struct Style {
+ /// What layout strategy should be used?
+ pub display: Display,
+ /// How children overflowing their container should affect layout
+ #[optional]
+ pub overflow: Point<Overflow>,
+ /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
+ pub scrollbar_width: f32,
+ /// What should the `position` value of this struct use as a base offset?
+ pub position: Position,
+ /// How should the position of this element be tweaked relative to the layout defined?
+ pub inset: Edges<Length>,
+ /// Sets the initial size of the item
+ #[optional]
+ pub size: Size<Length>,
+ /// Controls the minimum size of the item
+ #[optional]
+ pub min_size: Size<Length>,
+ /// Controls the maximum size of the item
+ #[optional]
+ pub max_size: Size<Length>,
+ /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
+ pub aspect_ratio: Option<f32>,
+ /// How large should the margin be on each side?
+ #[optional]
+ pub margin: Edges<Length>,
+ /// How large should the padding be on each side?
+ pub padding: Edges<DefinedLength>,
+ /// How large should the border be on each side?
+ pub border: Edges<DefinedLength>,
+ /// How this node's children aligned in the cross/block axis?
+ pub align_items: Option<AlignItems>,
+ /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
+ pub align_self: Option<AlignSelf>,
+ /// How should content contained within this item be aligned in the cross/block axis
+ pub align_content: Option<AlignContent>,
+ /// How should contained within this item be aligned in the main/inline axis
+ pub justify_content: Option<JustifyContent>,
+ /// How large should the gaps between items in a flex container be?
+ pub gap: Size<DefinedLength>,
+ /// Which direction does the main axis flow in?
+ pub flex_direction: FlexDirection,
+ /// Should elements wrap, or stay in a single line?
+ pub flex_wrap: FlexWrap,
+ /// Sets the initial main axis size of the item
+ pub flex_basis: Length,
+ /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
+ pub flex_grow: f32,
+ /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
+ pub flex_shrink: f32,
+ /// The fill color of this element
+ pub fill: Option<Fill>,
+ /// The color of text within this element. Cascades to children unless overridden.
+ pub text_color: Option<Hsla>,
+ }
+ #[automatically_derived]
+ impl ::core::clone::Clone for Style {
+ #[inline]
+ fn clone(&self) -> Style {
+ Style {
+ display: ::core::clone::Clone::clone(&self.display),
+ overflow: ::core::clone::Clone::clone(&self.overflow),
+ scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
+ position: ::core::clone::Clone::clone(&self.position),
+ inset: ::core::clone::Clone::clone(&self.inset),
+ size: ::core::clone::Clone::clone(&self.size),
+ min_size: ::core::clone::Clone::clone(&self.min_size),
+ max_size: ::core::clone::Clone::clone(&self.max_size),
+ aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
+ margin: ::core::clone::Clone::clone(&self.margin),
+ padding: ::core::clone::Clone::clone(&self.padding),
+ border: ::core::clone::Clone::clone(&self.border),
+ align_items: ::core::clone::Clone::clone(&self.align_items),
+ align_self: ::core::clone::Clone::clone(&self.align_self),
+ align_content: ::core::clone::Clone::clone(&self.align_content),
+ justify_content: ::core::clone::Clone::clone(&self.justify_content),
+ gap: ::core::clone::Clone::clone(&self.gap),
+ flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
+ flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
+ flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
+ flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
+ flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
+ fill: ::core::clone::Clone::clone(&self.fill),
+ text_color: ::core::clone::Clone::clone(&self.text_color),
+ }
+ }
+ }
+ pub struct OptionalStyle {
+ pub display: Option<Display>,
+ pub overflow: OptionalPoint<Overflow>,
+ pub scrollbar_width: Option<f32>,
+ pub position: Option<Position>,
+ pub inset: Option<Edges<Length>>,
+ pub size: OptionalSize<Length>,
+ pub min_size: OptionalSize<Length>,
+ pub max_size: OptionalSize<Length>,
+ pub aspect_ratio: Option<Option<f32>>,
+ pub margin: OptionalEdges<Length>,
+ pub padding: Option<Edges<DefinedLength>>,
+ pub border: Option<Edges<DefinedLength>>,
+ pub align_items: Option<Option<AlignItems>>,
+ pub align_self: Option<Option<AlignSelf>>,
+ pub align_content: Option<Option<AlignContent>>,
+ pub justify_content: Option<Option<JustifyContent>>,
+ pub gap: Option<Size<DefinedLength>>,
+ pub flex_direction: Option<FlexDirection>,
+ pub flex_wrap: Option<FlexWrap>,
+ pub flex_basis: Option<Length>,
+ pub flex_grow: Option<f32>,
+ pub flex_shrink: Option<f32>,
+ pub fill: Option<Option<Fill>>,
+ pub text_color: Option<Option<Hsla>>,
+ }
+ #[automatically_derived]
+ impl ::core::default::Default for OptionalStyle {
+ #[inline]
+ fn default() -> OptionalStyle {
+ OptionalStyle {
+ display: ::core::default::Default::default(),
+ overflow: ::core::default::Default::default(),
+ scrollbar_width: ::core::default::Default::default(),
+ position: ::core::default::Default::default(),
+ inset: ::core::default::Default::default(),
+ size: ::core::default::Default::default(),
+ min_size: ::core::default::Default::default(),
+ max_size: ::core::default::Default::default(),
+ aspect_ratio: ::core::default::Default::default(),
+ margin: ::core::default::Default::default(),
+ padding: ::core::default::Default::default(),
+ border: ::core::default::Default::default(),
+ align_items: ::core::default::Default::default(),
+ align_self: ::core::default::Default::default(),
+ align_content: ::core::default::Default::default(),
+ justify_content: ::core::default::Default::default(),
+ gap: ::core::default::Default::default(),
+ flex_direction: ::core::default::Default::default(),
+ flex_wrap: ::core::default::Default::default(),
+ flex_basis: ::core::default::Default::default(),
+ flex_grow: ::core::default::Default::default(),
+ flex_shrink: ::core::default::Default::default(),
+ fill: ::core::default::Default::default(),
+ text_color: ::core::default::Default::default(),
+ }
+ }
+ }
+ #[automatically_derived]
+ impl ::core::clone::Clone for OptionalStyle {
+ #[inline]
+ fn clone(&self) -> OptionalStyle {
+ OptionalStyle {
+ display: ::core::clone::Clone::clone(&self.display),
+ overflow: ::core::clone::Clone::clone(&self.overflow),
+ scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
+ position: ::core::clone::Clone::clone(&self.position),
+ inset: ::core::clone::Clone::clone(&self.inset),
+ size: ::core::clone::Clone::clone(&self.size),
+ min_size: ::core::clone::Clone::clone(&self.min_size),
+ max_size: ::core::clone::Clone::clone(&self.max_size),
+ aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
+ margin: ::core::clone::Clone::clone(&self.margin),
+ padding: ::core::clone::Clone::clone(&self.padding),
+ border: ::core::clone::Clone::clone(&self.border),
+ align_items: ::core::clone::Clone::clone(&self.align_items),
+ align_self: ::core::clone::Clone::clone(&self.align_self),
+ align_content: ::core::clone::Clone::clone(&self.align_content),
+ justify_content: ::core::clone::Clone::clone(&self.justify_content),
+ gap: ::core::clone::Clone::clone(&self.gap),
+ flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
+ flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
+ flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
+ flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
+ flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
+ fill: ::core::clone::Clone::clone(&self.fill),
+ text_color: ::core::clone::Clone::clone(&self.text_color),
+ }
+ }
+ }
+ impl Optional for OptionalStyle {
+ type Base = Style;
+ fn assign(&self, base: &mut Self::Base) {
+ if let Some(value) = self.display.clone() {
+ base.display = value;
+ }
+ if let Some(value) = self.overflow.clone() {
+ base.overflow = value;
+ }
+ if let Some(value) = self.scrollbar_width.clone() {
+ base.scrollbar_width = value;
+ }
+ if let Some(value) = self.position.clone() {
+ base.position = value;
+ }
+ if let Some(value) = self.inset.clone() {
+ base.inset = value;
+ }
+ if let Some(value) = self.size.clone() {
+ base.size = value;
+ }
+ if let Some(value) = self.min_size.clone() {
+ base.min_size = value;
+ }
+ if let Some(value) = self.max_size.clone() {
+ base.max_size = value;
+ }
+ if let Some(value) = self.aspect_ratio.clone() {
+ base.aspect_ratio = value;
+ }
+ if let Some(value) = self.margin.clone() {
+ base.margin = value;
+ }
+ if let Some(value) = self.padding.clone() {
+ base.padding = value;
+ }
+ if let Some(value) = self.border.clone() {
+ base.border = value;
+ }
+ if let Some(value) = self.align_items.clone() {
+ base.align_items = value;
+ }
+ if let Some(value) = self.align_self.clone() {
+ base.align_self = value;
+ }
+ if let Some(value) = self.align_content.clone() {
+ base.align_content = value;
+ }
+ if let Some(value) = self.justify_content.clone() {
+ base.justify_content = value;
+ }
+ if let Some(value) = self.gap.clone() {
+ base.gap = value;
+ }
+ if let Some(value) = self.flex_direction.clone() {
+ base.flex_direction = value;
+ }
+ if let Some(value) = self.flex_wrap.clone() {
+ base.flex_wrap = value;
+ }
+ if let Some(value) = self.flex_basis.clone() {
+ base.flex_basis = value;
+ }
+ if let Some(value) = self.flex_grow.clone() {
+ base.flex_grow = value;
+ }
+ if let Some(value) = self.flex_shrink.clone() {
+ base.flex_shrink = value;
+ }
+ if let Some(value) = self.fill.clone() {
+ base.fill = value;
+ }
+ if let Some(value) = self.text_color.clone() {
+ base.text_color = value;
+ }
+ }
+ }
+ impl From<OptionalStyle> for Style
+ where
+ Style: Default,
+ {
+ fn from(wrapper: OptionalStyle) -> Self {
+ let mut base = Self::default();
+ wrapper.assign(&mut base);
+ base
+ }
+ }
+ impl Style {
+ pub const DEFAULT: Style = Style {
+ display: Display::DEFAULT,
+ overflow: Point {
+ x: Overflow::Visible,
+ y: Overflow::Visible,
+ },
+ scrollbar_width: 0.0,
+ position: Position::Relative,
+ inset: Edges::auto(),
+ margin: Edges::<Length>::zero(),
+ padding: Edges::<DefinedLength>::zero(),
+ border: Edges::<DefinedLength>::zero(),
+ size: Size::auto(),
+ min_size: Size::auto(),
+ max_size: Size::auto(),
+ aspect_ratio: None,
+ gap: Size::zero(),
+ align_items: None,
+ align_self: None,
+ align_content: None,
+ justify_content: None,
+ flex_direction: FlexDirection::Row,
+ flex_wrap: FlexWrap::NoWrap,
+ flex_grow: 0.0,
+ flex_shrink: 1.0,
+ flex_basis: Length::Auto,
+ fill: None,
+ text_color: None,
+ };
+ pub fn new() -> Self {
+ Self::DEFAULT.clone()
+ }
+ pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
+ taffy::style::Style {
+ display: self.display,
+ overflow: self.overflow.clone().into(),
+ scrollbar_width: self.scrollbar_width,
+ position: self.position,
+ inset: self.inset.to_taffy(rem_size),
+ size: self.size.to_taffy(rem_size),
+ min_size: self.min_size.to_taffy(rem_size),
+ max_size: self.max_size.to_taffy(rem_size),
+ aspect_ratio: self.aspect_ratio,
+ margin: self.margin.to_taffy(rem_size),
+ padding: self.padding.to_taffy(rem_size),
+ border: self.border.to_taffy(rem_size),
+ align_items: self.align_items,
+ align_self: self.align_self,
+ align_content: self.align_content,
+ justify_content: self.justify_content,
+ gap: self.gap.to_taffy(rem_size),
+ flex_direction: self.flex_direction,
+ flex_wrap: self.flex_wrap,
+ flex_basis: self.flex_basis.to_taffy(rem_size).into(),
+ flex_grow: self.flex_grow,
+ flex_shrink: self.flex_shrink,
+ ..Default::default()
+ }
+ }
+ }
+ impl Default for Style {
+ fn default() -> Self {
+ Self::DEFAULT.clone()
+ }
+ }
+ impl OptionalStyle {
+ pub fn text_style(&self) -> Option<OptionalTextStyle> {
+ self.text_color.map(|color| OptionalTextStyle { color })
+ }
+ }
+ pub struct OptionalTextStyle {
+ color: Option<Hsla>,
+ }
+ impl OptionalTextStyle {
+ pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
+ if let Some(color) = self.color {
+ style.color = color.into();
+ }
+ }
+ }
+ pub enum Fill {
+ Color(Hsla),
+ }
+ #[automatically_derived]
+ impl ::core::clone::Clone for Fill {
+ #[inline]
+ fn clone(&self) -> Fill {
+ match self {
+ Fill::Color(__self_0) => {
+ Fill::Color(::core::clone::Clone::clone(__self_0))
+ }
+ }
+ }
+ }
+ impl Fill {
+ pub fn color(&self) -> Option<Hsla> {
+ match self {
+ Fill::Color(color) => Some(*color),
+ }
+ }
+ }
+ impl Default for Fill {
+ fn default() -> Self {
+ Self::Color(Hsla::default())
+ }
+ }
+ impl From<Hsla> for Fill {
+ fn from(color: Hsla) -> Self {
+ Self::Color(color)
+ }
+ }
+}
+mod text {
+ use crate::{
+ element::{Element, ElementMetadata, EventHandler, IntoElement},
+ style::Style,
+ };
+ use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
+ use parking_lot::Mutex;
+ use std::sync::Arc;
+ impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
+ type Element = Text<V>;
+ fn into_element(self) -> Self::Element {
+ Text {
+ text: self.into(),
+ metadata: Default::default(),
+ }
+ }
+ }
+ pub struct Text<V> {
+ text: ArcCow<'static, str>,
+ metadata: ElementMetadata<V>,
+ }
+ impl<V: 'static> Element<V> for Text<V> {
+ type Layout = Arc<Mutex<Option<TextLayout>>>;
+ fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
+ &mut self.metadata.style
+ }
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut gpui::LayoutContext<V>,
+ ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+ let rem_size = cx.rem_pixels();
+ let fonts = cx.platform().fonts();
+ let text_style = cx.text_style();
+ let line_height = cx.font_cache().line_height(text_style.font_size);
+ let layout_engine = cx.layout_engine().expect("no layout engine present");
+ let text = self.text.clone();
+ let layout = Arc::new(Mutex::new(None));
+ let style: Style = self.metadata.style.into();
+ let node_id = layout_engine
+ .add_measured_node(
+ style.to_taffy(rem_size),
+ {
+ let layout = layout.clone();
+ move |params| {
+ let line_layout = fonts
+ .layout_line(
+ text.as_ref(),
+ text_style.font_size,
+ &[(text.len(), text_style.to_run())],
+ );
+ let size = Size {
+ width: line_layout.width,
+ height: line_height,
+ };
+ layout
+ .lock()
+ .replace(TextLayout {
+ line_layout: Arc::new(line_layout),
+ line_height,
+ });
+ size
+ }
+ },
+ )?;
+ Ok((node_id, layout))
+ }
+ fn paint<'a>(
+ &mut self,
+ layout: crate::element::Layout<Arc<Mutex<Option<TextLayout>>>>,
+ view: &mut V,
+ cx: &mut crate::element::PaintContext<V>,
+ ) -> anyhow::Result<()> {
+ let element_layout_lock = layout.from_element.lock();
+ let element_layout = element_layout_lock
+ .as_ref()
+ .expect("layout has not been performed");
+ let line_layout = element_layout.line_layout.clone();
+ let line_height = element_layout.line_height;
+ drop(element_layout_lock);
+ let text_style = cx.text_style();
+ let line = gpui::text_layout::Line::new(
+ line_layout,
+ &[(self.text.len(), text_style.to_run())],
+ );
+ line.paint(
+ cx.scene,
+ layout.from_engine.bounds.origin(),
+ layout.from_engine.bounds,
+ line_height,
+ cx.legacy_cx,
+ );
+ Ok(())
+ }
+ fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+ &mut self.metadata.handlers
+ }
+ }
+ pub struct TextLayout {
+ line_layout: Arc<LineLayout>,
+ line_height: f32,
+ }
+ pub enum ArcCow<'a, T: ?Sized> {
+ Borrowed(&'a T),
+ Owned(Arc<T>),
+ }
+ impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
+ fn clone(&self) -> Self {
+ match self {
+ Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
+ Self::Owned(owned) => Self::Owned(owned.clone()),
+ }
+ }
+ }
+ impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
+ fn from(s: &'a T) -> Self {
+ Self::Borrowed(s)
+ }
+ }
+ impl<T> From<Arc<T>> for ArcCow<'_, T> {
+ fn from(s: Arc<T>) -> Self {
+ Self::Owned(s)
+ }
+ }
+ impl From<String> for ArcCow<'_, str> {
+ fn from(value: String) -> Self {
+ Self::Owned(value.into())
+ }
+ }
+ impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ match self {
+ ArcCow::Borrowed(s) => s,
+ ArcCow::Owned(s) => s.as_ref(),
+ }
+ }
+ }
+ impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
+ fn as_ref(&self) -> &T {
+ match self {
+ ArcCow::Borrowed(borrowed) => borrowed,
+ ArcCow::Owned(owned) => owned.as_ref(),
+ }
+ }
+ }
+}
+mod themes {
+ use crate::color::{Hsla, Lerp};
+ use std::ops::Range;
+ pub mod rose_pine {
+ use std::ops::Range;
+ use crate::{
+ color::{hsla, rgb, Hsla},
+ ThemeColors,
+ };
+ pub struct RosePineThemes {
+ pub default: RosePinePalette,
+ pub dawn: RosePinePalette,
+ pub moon: RosePinePalette,
+ }
+ pub struct RosePinePalette {
+ pub base: Hsla,
+ pub surface: Hsla,
+ pub overlay: Hsla,
+ pub muted: Hsla,
+ pub subtle: Hsla,
+ pub text: Hsla,
+ pub love: Hsla,
+ pub gold: Hsla,
+ pub rose: Hsla,
+ pub pine: Hsla,
+ pub foam: Hsla,
+ pub iris: Hsla,
+ pub highlight_low: Hsla,
+ pub highlight_med: Hsla,
+ pub highlight_high: Hsla,
+ }
+ #[automatically_derived]
+ impl ::core::clone::Clone for RosePinePalette {
+ #[inline]
+ fn clone(&self) -> RosePinePalette {
+ let _: ::core::clone::AssertParamIsClone<Hsla>;
+ *self
+ }
+ }
+ #[automatically_derived]
+ impl ::core::marker::Copy for RosePinePalette {}
+ #[automatically_derived]
+ impl ::core::fmt::Debug for RosePinePalette {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ let names: &'static _ = &[
+ "base",
+ "surface",
+ "overlay",
+ "muted",
+ "subtle",
+ "text",
+ "love",
+ "gold",
+ "rose",
+ "pine",
+ "foam",
+ "iris",
+ "highlight_low",
+ "highlight_med",
+ "highlight_high",
+ ];
+ let values: &[&dyn ::core::fmt::Debug] = &[
+ &self.base,
+ &self.surface,
+ &self.overlay,
+ &self.muted,
+ &self.subtle,
+ &self.text,
+ &self.love,
+ &self.gold,
+ &self.rose,
+ &self.pine,
+ &self.foam,
+ &self.iris,
+ &self.highlight_low,
+ &self.highlight_med,
+ &&self.highlight_high,
+ ];
+ ::core::fmt::Formatter::debug_struct_fields_finish(
+ f,
+ "RosePinePalette",
+ names,
+ values,
+ )
+ }
+ }
+ impl RosePinePalette {
+ pub fn default() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0x191724),
+ surface: rgb(0x1f1d2e),
+ overlay: rgb(0x26233a),
+ muted: rgb(0x6e6a86),
+ subtle: rgb(0x908caa),
+ text: rgb(0xe0def4),
+ love: rgb(0xeb6f92),
+ gold: rgb(0xf6c177),
+ rose: rgb(0xebbcba),
+ pine: rgb(0x31748f),
+ foam: rgb(0x9ccfd8),
+ iris: rgb(0xc4a7e7),
+ highlight_low: rgb(0x21202e),
+ highlight_med: rgb(0x403d52),
+ highlight_high: rgb(0x524f67),
+ }
+ }
+ pub fn moon() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0x232136),
+ surface: rgb(0x2a273f),
+ overlay: rgb(0x393552),
+ muted: rgb(0x6e6a86),
+ subtle: rgb(0x908caa),
+ text: rgb(0xe0def4),
+ love: rgb(0xeb6f92),
+ gold: rgb(0xf6c177),
+ rose: rgb(0xea9a97),
+ pine: rgb(0x3e8fb0),
+ foam: rgb(0x9ccfd8),
+ iris: rgb(0xc4a7e7),
+ highlight_low: rgb(0x2a283e),
+ highlight_med: rgb(0x44415a),
+ highlight_high: rgb(0x56526e),
+ }
+ }
+ pub fn dawn() -> RosePinePalette {
+ RosePinePalette {
+ base: rgb(0xfaf4ed),
+ surface: rgb(0xfffaf3),
+ overlay: rgb(0xf2e9e1),
+ muted: rgb(0x9893a5),
+ subtle: rgb(0x797593),
+ text: rgb(0x575279),
+ love: rgb(0xb4637a),
+ gold: rgb(0xea9d34),
+ rose: rgb(0xd7827e),
+ pine: rgb(0x286983),
+ foam: rgb(0x56949f),
+ iris: rgb(0x907aa9),
+ highlight_low: rgb(0xf4ede8),
+ highlight_med: rgb(0xdfdad9),
+ highlight_high: rgb(0xcecacd),
+ }
+ }
+ }
+ pub fn default() -> ThemeColors {
+ theme_colors(&RosePinePalette::default())
+ }
+ pub fn moon() -> ThemeColors {
+ theme_colors(&RosePinePalette::moon())
+ }
+ pub fn dawn() -> ThemeColors {
+ theme_colors(&RosePinePalette::dawn())
+ }
+ fn theme_colors(p: &RosePinePalette) -> ThemeColors {
+ ThemeColors {
+ base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
+ surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
+ overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
+ muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
+ subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
+ text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
+ highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
+ highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
+ highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
+ success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+ warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
+ error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+ inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+ deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+ modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
+ }
+ }
+ /// Produces a range by multiplying the saturation and lightness of the base color by the given
+ /// start and end factors.
+ fn scale_sl(
+ base: Hsla,
+ (start_s, start_l): (f32, f32),
+ (end_s, end_l): (f32, f32),
+ ) -> Range<Hsla> {
+ let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
+ let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
+ Range { start, end }
+ }
+ }
+ pub struct ThemeColors {
+ pub base: Range<Hsla>,
+ pub surface: Range<Hsla>,
+ pub overlay: Range<Hsla>,
+ pub muted: Range<Hsla>,
+ pub subtle: Range<Hsla>,
+ pub text: Range<Hsla>,
+ pub highlight_low: Range<Hsla>,
+ pub highlight_med: Range<Hsla>,
+ pub highlight_high: Range<Hsla>,
+ pub success: Range<Hsla>,
+ pub warning: Range<Hsla>,
+ pub error: Range<Hsla>,
+ pub inserted: Range<Hsla>,
+ pub deleted: Range<Hsla>,
+ pub modified: Range<Hsla>,
+ }
+ impl ThemeColors {
+ pub fn base(&self, level: f32) -> Hsla {
+ self.base.lerp(level)
+ }
+ pub fn surface(&self, level: f32) -> Hsla {
+ self.surface.lerp(level)
+ }
+ pub fn overlay(&self, level: f32) -> Hsla {
+ self.overlay.lerp(level)
+ }
+ pub fn muted(&self, level: f32) -> Hsla {
+ self.muted.lerp(level)
+ }
+ pub fn subtle(&self, level: f32) -> Hsla {
+ self.subtle.lerp(level)
+ }
+ pub fn text(&self, level: f32) -> Hsla {
+ self.text.lerp(level)
+ }
+ pub fn highlight_low(&self, level: f32) -> Hsla {
+ self.highlight_low.lerp(level)
+ }
+ pub fn highlight_med(&self, level: f32) -> Hsla {
+ self.highlight_med.lerp(level)
+ }
+ pub fn highlight_high(&self, level: f32) -> Hsla {
+ self.highlight_high.lerp(level)
+ }
+ pub fn success(&self, level: f32) -> Hsla {
+ self.success.lerp(level)
+ }
+ pub fn warning(&self, level: f32) -> Hsla {
+ self.warning.lerp(level)
+ }
+ pub fn error(&self, level: f32) -> Hsla {
+ self.error.lerp(level)
+ }
+ pub fn inserted(&self, level: f32) -> Hsla {
+ self.inserted.lerp(level)
+ }
+ pub fn deleted(&self, level: f32) -> Hsla {
+ self.deleted.lerp(level)
+ }
+ pub fn modified(&self, level: f32) -> Hsla {
+ self.modified.lerp(level)
+ }
+ }
+}
+mod view {
+ use crate::element::{AnyElement, Element};
+ use gpui::{Element as _, ViewContext};
+ pub fn view<F, E>(mut render: F) -> ViewFn
+ where
+ F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
+ E: Element<ViewFn>,
+ {
+ ViewFn(Box::new(move |cx| (render)(cx).into_any()))
+ }
+ pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
+ impl gpui::Entity for ViewFn {
+ type Event = ();
+ }
+ impl gpui::View for ViewFn {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
+ (self.0)(cx).adapt().into_any()
+ }
+ }
+}
+fn main() {
+ SimpleLogger::init(LevelFilter::Info, Default::default())
+ .expect("could not initialize logger");
+ gpui::App::new(())
+ .unwrap()
+ .run(|cx| {
+ cx.add_window(
+ WindowOptions {
+ bounds: gpui::platform::WindowBounds::Fixed(
+ RectF::new(vec2f(0., 0.), vec2f(400., 300.)),
+ ),
+ center: true,
+ ..Default::default()
+ },
+ |_| view(|_| playground(&rose_pine::moon())),
+ );
+ cx.platform().activate(true);
+ });
+}
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+ frame()
+ .text_color(black())
+ .h_full()
+ .w_half()
+ .fill(theme.success(0.5))
+ .child(
+ button()
+ .label("Hello")
+ .click(|_, _, _| {
+ ::std::io::_print(format_args!("click!\n"));
+ }),
+ )
+}