diff --git a/Cargo.lock b/Cargo.lock index 39b348c43a..84fd279fa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2161,6 +2161,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" @@ -3149,6 +3158,7 @@ dependencies = [ "png", "postage", "rand 0.8.5", + "refineable", "resvg", "schemars", "seahash", @@ -4964,34 +4974,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "optional_struct" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e60da57c6a9d057c07f1a90ca7abed9d104fca0d0db1a7d7e3304e4567d977fd" -dependencies = [ - "optional_struct_internal", - "optional_struct_macro_impl", -] - -[[package]] -name = "optional_struct_internal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e389cec0df3c934737dadc7b927a8e05b8c8ef792cd1af06a524bd129e9f4d" - -[[package]] -name = "optional_struct_macro_impl" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286db11c92049709d5fbbe89eecaa2febc0efe6c18d94d9ebf942e592ac80f9f" -dependencies = [ - "optional_struct_internal", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "orbclient" version = "0.3.45" @@ -5303,9 +5285,9 @@ dependencies = [ "derive_more", "gpui", "log", - "optional_struct", "parking_lot 0.11.2", "playground_macros", + "refineable", "serde", "simplelog", "smallvec", @@ -5988,6 +5970,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" diff --git a/Cargo.toml b/Cargo.toml index a82d5daa2d..3cc697fb03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", @@ -94,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" } diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index d9fcb168c4..24397f6193 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -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" diff --git a/crates/gpui/playground/Cargo.toml b/crates/gpui/playground/Cargo.toml index f9b2d5e110..b837e7d994 100644 --- a/crates/gpui/playground/Cargo.toml +++ b/crates/gpui/playground/Cargo.toml @@ -12,9 +12,9 @@ anyhow.workspace = true derive_more.workspace = true gpui = { path = ".." } log.workspace = true -optional_struct = "0.3.1" playground_macros = { path = "../playground_macros" } parking_lot.workspace = true +refineable.workspace = true serde.workspace = true simplelog = "0.9" smallvec.workspace = true diff --git a/crates/gpui/playground/src/element.rs b/crates/gpui/playground/src/element.rs index ff1e6412b8..2d59b7a48b 100644 --- a/crates/gpui/playground/src/element.rs +++ b/crates/gpui/playground/src/element.rs @@ -1,12 +1,13 @@ use crate::{ adapter::Adapter, color::Hsla, - style::{Display, ElementStyle, Fill, Overflow, Position}, + hoverable::Hoverable, + style::{Display, Fill, Overflow, Position, StyleRefinement}, }; use anyhow::Result; pub use gpui::LayoutContext; use gpui::{ - geometry::{DefinedLength, Length}, + geometry::{DefinedLength, Length, PointRefinement}, platform::{MouseButton, MouseButtonEvent}, EngineLayout, EventContext, RenderContext, ViewContext, }; @@ -26,7 +27,7 @@ pub struct Layout<'a, E: ?Sized> { } pub struct ElementMetadata { - pub style: ElementStyle, + pub style: StyleRefinement, pub handlers: Vec>, } @@ -49,7 +50,7 @@ impl Clone for EventHandler { impl Default for ElementMetadata { fn default() -> Self { Self { - style: ElementStyle::default(), + style: StyleRefinement::default(), handlers: Vec::new(), } } @@ -58,11 +59,17 @@ impl Default for ElementMetadata { pub trait Element: 'static { type Layout: 'static; - fn style_mut(&mut self) -> &mut ElementStyle; + fn declared_style(&mut self) -> &mut StyleRefinement; + + fn computed_style(&mut self) -> &StyleRefinement { + self.declared_style() + } + fn handlers_mut(&mut self) -> &mut Vec>; fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result<(NodeId, Self::Layout)>; + fn paint<'a>( &mut self, layout: Layout, @@ -208,7 +215,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().display = Display::Block; + self.declared_style().display = Some(Display::Block); self } @@ -216,7 +223,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().display = Display::Flex; + self.declared_style().display = Some(Display::Flex); self } @@ -224,7 +231,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().display = Display::Grid; + self.declared_style().display = Some(Display::Grid); self } @@ -234,8 +241,10 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Visible; - self.style_mut().overflow.y = Overflow::Visible; + self.declared_style().overflow = PointRefinement { + x: Some(Overflow::Visible), + y: Some(Overflow::Visible), + }; self } @@ -243,8 +252,10 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Hidden; - self.style_mut().overflow.y = Overflow::Hidden; + self.declared_style().overflow = PointRefinement { + x: Some(Overflow::Hidden), + y: Some(Overflow::Hidden), + }; self } @@ -252,8 +263,10 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Scroll; - self.style_mut().overflow.y = Overflow::Scroll; + self.declared_style().overflow = PointRefinement { + x: Some(Overflow::Scroll), + y: Some(Overflow::Scroll), + }; self } @@ -261,7 +274,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Visible; + self.declared_style().overflow.x = Some(Overflow::Visible); self } @@ -269,7 +282,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Hidden; + self.declared_style().overflow.x = Some(Overflow::Hidden); self } @@ -277,7 +290,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.x = Overflow::Scroll; + self.declared_style().overflow.x = Some(Overflow::Scroll); self } @@ -285,7 +298,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.y = Overflow::Visible; + self.declared_style().overflow.y = Some(Overflow::Visible); self } @@ -293,7 +306,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.y = Overflow::Hidden; + self.declared_style().overflow.y = Some(Overflow::Hidden); self } @@ -301,7 +314,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().overflow.y = Overflow::Scroll; + self.declared_style().overflow.y = Some(Overflow::Scroll); self } @@ -311,7 +324,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().position = Position::Relative; + self.declared_style().position = Some(Position::Relative); self } @@ -319,7 +332,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().position = Position::Absolute; + self.declared_style().position = Some(Position::Absolute); self } @@ -329,10 +342,11 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().inset.top = length; - self.style_mut().inset.right = length; - self.style_mut().inset.bottom = length; - self.style_mut().inset.left = length; + let inset = &mut self.declared_style().inset; + inset.top = Some(length); + inset.right = Some(length); + inset.bottom = Some(length); + inset.left = Some(length); self } @@ -340,7 +354,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.width = width.into(); + self.declared_style().size.width = Some(width.into()); self } @@ -348,7 +362,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.width = Length::Auto; + self.declared_style().size.width = Some(Length::Auto); self } @@ -357,7 +371,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.width = length; + self.declared_style().size.width = Some(length); self } @@ -366,7 +380,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().min_size.width = length; + self.declared_style().min_size.width = Some(length); self } @@ -374,7 +388,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.height = height.into(); + self.declared_style().size.height = Some(height.into()); self } @@ -382,7 +396,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.height = Length::Auto; + self.declared_style().size.height = Some(Length::Auto); self } @@ -391,7 +405,7 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().size.height = height; + self.declared_style().size.height = Some(height); self } @@ -400,23 +414,22 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().min_size.height = length; + self.declared_style().min_size.height = Some(length); self } + fn hoverable(self) -> Hoverable + where + Self: Sized, + { + Hoverable::new(self) + } + fn fill(mut self, fill: impl Into) -> Self where Self: Sized, { - self.style_mut().fill = Some(fill.into()); - self - } - - fn hover_fill(mut self, fill: impl Into) -> Self - where - Self: Sized, - { - self.style_mut().hover_fill = Some(fill.into()); + self.declared_style().fill = Some(fill.into()); self } @@ -424,14 +437,14 @@ pub trait Element: 'static { where Self: Sized, { - self.style_mut().text_color = Some(color.into()); + self.declared_style().text_color = Some(color.into()); self } } // Object-safe counterpart of Element used by AnyElement to store elements as trait objects. trait ElementObject { - fn style_mut(&mut self) -> &mut ElementStyle; + fn style(&mut self) -> &mut StyleRefinement; fn handlers_mut(&mut self) -> &mut Vec>; fn layout(&mut self, view: &mut V, cx: &mut LayoutContext) -> Result<(NodeId, Box)>; @@ -444,8 +457,8 @@ trait ElementObject { } impl> ElementObject for E { - fn style_mut(&mut self) -> &mut ElementStyle { - Element::style_mut(self) + fn style(&mut self) -> &mut StyleRefinement { + Element::declared_style(self) } fn handlers_mut(&mut self) -> &mut Vec> { @@ -498,11 +511,9 @@ impl AnyElement { } pub fn push_text_style(&mut self, cx: &mut impl RenderContext) -> bool { - let text_style = self.element.style_mut().text_style(); + 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); + cx.push_text_style(cx.text_style().refine(text_style)); true } else { false @@ -524,20 +535,17 @@ impl AnyElement { from_element: element_layout.as_mut(), }; - let fill_color = self - .element - .style_mut() - .fill - .as_ref() - .and_then(Fill::color) - .map(Into::into); + let style = self.element.style(); - cx.scene.push_quad(gpui::scene::Quad { - bounds: layout.from_engine.bounds, - background: fill_color, - border: Default::default(), - corner_radii: Default::default(), - }); + let fill_color = style.fill.as_ref().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; @@ -574,8 +582,8 @@ impl AnyElement { impl Element for AnyElement { type Layout = (); - fn style_mut(&mut self) -> &mut ElementStyle { - self.element.style_mut() + fn declared_style(&mut self) -> &mut StyleRefinement { + self.element.style() } fn handlers_mut(&mut self) -> &mut Vec> { diff --git a/crates/gpui/playground/src/frame.rs b/crates/gpui/playground/src/frame.rs index 56201c331b..0b4bdbb1c1 100644 --- a/crates/gpui/playground/src/frame.rs +++ b/crates/gpui/playground/src/frame.rs @@ -2,23 +2,24 @@ use crate::{ element::{ AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId, PaintContext, }, - style::ElementStyle, + style::{Style, StyleRefinement}, }; use anyhow::{anyhow, Result}; use gpui::LayoutNodeId; use playground_macros::IntoElement; +use refineable::Refineable; #[derive(IntoElement)] #[element_crate = "crate"] pub struct Frame { - style: ElementStyle, + style: StyleRefinement, handlers: Vec>, children: Vec>, } pub fn frame() -> Frame { Frame { - style: ElementStyle::default(), + style: StyleRefinement::default(), handlers: Vec::new(), children: Vec::new(), } @@ -27,7 +28,7 @@ pub fn frame() -> Frame { impl Element for Frame { type Layout = (); - fn style_mut(&mut self) -> &mut ElementStyle { + fn declared_style(&mut self) -> &mut StyleRefinement { &mut self.style } @@ -47,10 +48,11 @@ impl Element for Frame { .collect::>>()?; let rem_size = cx.rem_pixels(); + let style = Style::default().refine(&self.style); let node_id = cx .layout_engine() .ok_or_else(|| anyhow!("no layout engine"))? - .add_node(self.style.to_taffy(rem_size), child_layout_node_ids)?; + .add_node(style.to_taffy(rem_size), child_layout_node_ids)?; Ok((node_id, ())) } diff --git a/crates/gpui/playground/src/hoverable.rs b/crates/gpui/playground/src/hoverable.rs new file mode 100644 index 0000000000..1cbfdc283f --- /dev/null +++ b/crates/gpui/playground/src/hoverable.rs @@ -0,0 +1,80 @@ +use std::{cell::Cell, marker::PhantomData, rc::Rc}; + +use gpui::{ + geometry::{rect::RectF, vector::Vector2F}, + scene::MouseMove, + EngineLayout, +}; + +use crate::{ + element::Element, + style::{Style, StyleRefinement}, +}; + +pub struct Hoverable { + hover_style: StyleRefinement, + computed_style: Option