Ditch the hot reloading approach

Nathan Sobo created

Change summary

Cargo.lock                                       |  13 
Cargo.toml                                       |   1 
crates/gpui/playground/Cargo.toml                |   6 
crates/gpui/playground/src/color.rs              |  21 +
crates/gpui/playground/src/editor_layout_demo.rs |   0 
crates/gpui/playground/src/frame.rs              | 113 ++++++++
crates/gpui/playground/src/playground.rs         | 221 +++++++++++++++--
crates/gpui/playground/src/themes.rs             |   0 
crates/gpui/playground/src/themes/rose_pine.rs   |   0 
crates/gpui/playground/src/tokens.rs             |   0 
crates/gpui/playground/ui/Cargo.toml             |  22 -
crates/gpui/playground/ui/src/playground_ui.rs   | 200 ----------------
crates/gpui/src/app.rs                           |  12 
crates/gpui/src/color.rs                         |   2 
14 files changed, 338 insertions(+), 273 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5291,24 +5291,13 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 name = "playground"
 version = "0.1.0"
 dependencies = [
- "gpui",
- "log",
- "playground_ui",
- "simplelog",
-]
-
-[[package]]
-name = "playground_ui"
-version = "0.1.0"
-dependencies = [
- "collections",
  "derive_more",
  "gpui",
  "log",
  "optional_struct",
  "serde",
+ "simplelog",
  "smallvec",
- "util",
 ]
 
 [[package]]

Cargo.toml 🔗

@@ -29,7 +29,6 @@ members = [
     "crates/go_to_line",
     "crates/gpui",
     "crates/gpui/playground",
-    "crates/gpui/playground/ui",
     "crates/gpui_macros",
     "crates/install_cli",
     "crates/journal",

crates/gpui/playground/Cargo.toml 🔗

@@ -8,11 +8,13 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-playground_ui = { path = "ui" }
-
+derive_more.workspace = true
 gpui = { path = ".." }
 log.workspace = true
+optional_struct = "0.3.1"
+serde.workspace = true
 simplelog = "0.9"
+smallvec.workspace = true
 
 [dev-dependencies]
 gpui = { path = "..", features = ["test-support"] }

crates/gpui/playground/ui/src/color.rs → crates/gpui/playground/src/color.rs 🔗

@@ -35,6 +35,12 @@ impl Lerp for Range<Hsla> {
     }
 }
 
+impl From<gpui::color::Color> for Rgba {
+    fn from(value: gpui::color::Color) -> Self {
+        todo!()
+    }
+}
+
 impl From<Hsla> for Rgba {
     fn from(color: Hsla) -> Self {
         let h = color.h;
@@ -88,7 +94,7 @@ impl Into<gpui::color::Color> for Rgba {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Hsla {
     pub h: f32,
     pub s: f32,
@@ -97,7 +103,12 @@ pub struct Hsla {
 }
 
 pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
-    Hsla { h, s, l, a }
+    Hsla {
+        h: h.clamp(0., 1.),
+        s: s.clamp(0., 1.),
+        l: l.clamp(0., 1.),
+        a: a.clamp(0., 1.),
+    }
 }
 
 impl From<Rgba> for Hsla {
@@ -182,6 +193,12 @@ impl Hsla {
     }
 }
 
+impl From<gpui::color::Color> for Hsla {
+    fn from(value: gpui::color::Color) -> Self {
+        Rgba::from(value).into()
+    }
+}
+
 pub struct ColorScale {
     colors: SmallVec<[Hsla; 2]>,
     positions: SmallVec<[f32; 2]>,

crates/gpui/playground/ui/src/frame.rs → crates/gpui/playground/src/frame.rs 🔗

@@ -1,10 +1,9 @@
 #![allow(unused_variables, dead_code)]
 
 use derive_more::{Add, Deref, DerefMut};
-use gpui::elements::layout_highlighted_chunks;
-use gpui::Entity;
 use gpui::{
     color::Color,
+    elements::layout_highlighted_chunks,
     fonts::HighlightStyle,
     geometry::{
         rect::RectF,
@@ -14,15 +13,17 @@ use gpui::{
     scene,
     serde_json::Value,
     text_layout::{Line, ShapedBoundary},
-    AnyElement, AppContext, Element, LayoutContext, PaintContext, Quad, SceneBuilder,
-    SizeConstraint, View, ViewContext,
+    AnyElement, AppContext, Element, Entity, LayoutContext, PaintContext, Quad, SceneBuilder,
+    SizeConstraint, View, ViewContext, WindowContext,
 };
 use length::{Length, Rems};
 use log::warn;
 use optional_struct::*;
 use std::{any::Any, borrow::Cow, f32, ops::Range, sync::Arc};
 
-use crate::color::Rgba;
+use crate::color::{Hsla, Rgba};
+
+use self::length::rems;
 
 pub struct Frame<V> {
     style: FrameStyle,
@@ -76,6 +77,12 @@ impl<V: 'static> Element<V> for Frame<V> {
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
+        if self.style.text.is_some() {
+            let mut style = TextStyle::from_legacy(&cx.text_style(), cx);
+            self.style.text.clone().apply_to(&mut style);
+            cx.push_text_style(style.to_legacy());
+        }
+
         let layout = if let Some(axis) = self.style.axis.to_2d() {
             self.layout_xy(axis, constraint, cx.rem_pixels(), view, cx)
         } else {
@@ -263,6 +270,11 @@ impl<V: 'static> Frame<V> {
         self
     }
 
+    pub fn text_color(mut self, color: Hsla) -> Self {
+        self.style.text.color = Some(color);
+        self
+    }
+
     pub fn margins(mut self, margins: impl Into<Edges<Length>>) -> Self {
         self.style.margins = margins.into();
         self
@@ -327,6 +339,8 @@ impl<V: 'static> Frame<V> {
         view: &mut V,
         cx: &mut LayoutContext<V>,
     ) -> FrameLayout {
+        self.style.text.is_some();
+
         let cross_axis = primary_axis.rotate();
         let total_flex = self.style.flex();
         let mut layout = FrameLayout {
@@ -608,8 +622,67 @@ struct TextStyle {
     font_family: Arc<str>,
     weight: FontWeight,
     style: FontStyle,
+    color: Hsla,
 }
 
+impl TextStyle {
+    fn from_legacy(text_style: &gpui::fonts::TextStyle, _cx: &WindowContext) -> Self {
+        Self {
+            size: rems(text_style.font_size / 16.), // TODO: Get this from the context!
+            font_family: text_style.font_family_name.clone(),
+            weight: text_style.font_properties.weight.into(),
+            style: text_style.font_properties.style.into(),
+            color: text_style.color.into(),
+        }
+    }
+
+    fn to_legacy(&self, cx: &WindowContext) -> Result<gpui::fonts::TextStyle> {
+        let font_family_id = cx.font_cache().load_family(
+            &[self.font_family.as_ref()],
+            &gpui::fonts::Features::default(),
+        )?;
+        let font_properties = gpui::fonts::Properties {
+            style: self.style.into(),
+            weight: self.weight.into(),
+            stretch: Default::default(),
+        };
+        let font_id = cx
+            .font_cache()
+            .select_font(font_family_id, &font_properties);
+
+        Ok(gpui::fonts::TextStyle {
+            color: self.color.into(),
+            font_family_name: self.font_family.clone(),
+            font_family_id,
+            font_id,
+            font_size: todo!(),
+            font_properties,
+            underline: todo!(),
+            soft_wrap: true,
+        })
+    }
+}
+
+impl OptionalTextStyle {
+    pub fn is_some(&self) -> bool {
+        self.size.is_some()
+            && self.font_family.is_some()
+            && self.weight.is_some()
+            && self.style.is_some()
+            && self.color.is_some()
+    }
+}
+
+// pub color: Color,
+// pub font_family_name: Arc<str>,
+// pub font_family_id: FamilyId,
+// pub font_id: FontId,
+// pub font_size: f32,
+// #[schemars(with = "PropertiesDef")]
+// pub font_properties: Properties,
+// pub underline: Underline,
+// pub soft_wrap: bool,
+
 #[derive(Add, Default, Clone)]
 pub struct Size<T> {
     width: T,
@@ -1126,6 +1199,18 @@ enum FontStyle {
     Oblique,
 }
 
+impl From<gpui::fonts::Style> for FontStyle {
+    fn from(value: gpui::fonts::Style) -> Self {
+        use gpui::fonts::Style;
+
+        match value {
+            Style::Normal => FontStyle::Normal,
+            Style::Italic => FontStyle::Italic,
+            Style::Oblique => FontStyle::Oblique,
+        }
+    }
+}
+
 #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
 enum FontWeight {
     Thin,
@@ -1140,6 +1225,24 @@ enum FontWeight {
     Black,
 }
 
+impl From<gpui::fonts::Weight> for FontWeight {
+    fn from(value: gpui::fonts::Weight) -> Self {
+        use gpui::fonts::Weight;
+
+        match value {
+            Weight::THIN => FontWeight::Thin,
+            Weight::EXTRA_LIGHT => FontWeight::ExtraLight,
+            Weight::LIGHT => FontWeight::Light,
+            Weight::NORMAL => FontWeight::Normal,
+            Weight::MEDIUM => FontWeight::Medium,
+            Weight::SEMIBOLD => FontWeight::Semibold,
+            Weight::BOLD => FontWeight::Bold,
+            Weight::EXTRA_BOLD => FontWeight::ExtraBold,
+            Weight::BLACK => FontWeight::Black,
+        }
+    }
+}
+
 #[derive(Default)]
 pub struct Text {
     text: Cow<'static, str>,

crates/gpui/playground/src/playground.rs 🔗

@@ -1,17 +1,11 @@
+#![allow(dead_code, unused_variables)]
+
 use gpui::{
     platform::{TitlebarOptions, WindowOptions},
-    AnyElement, Element, Entity, View,
+    AnyElement, Element,
 };
 use log::LevelFilter;
 use simplelog::SimpleLogger;
-use std::ops::{Deref, DerefMut};
-
-// dymod! {
-//     #[path = "../ui/src/playground_ui.rs"]
-//     pub mod ui {
-//         // fn workspace<V>(theme: &ThemeColors) -> impl Element<V>;
-//     }
-// }
 
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
@@ -26,38 +20,213 @@ fn main() {
                 }),
                 ..Default::default()
             },
-            |_| Playground::default(),
+            |_| view(|_| Playground::new()),
         );
     });
 }
 
-#[derive(Clone, Default)]
-struct Playground(playground_ui::Playground<Self>);
+use frame::{length::auto, *};
+use gpui::{LayoutContext, ViewContext};
+use std::{borrow::Cow, cell::RefCell, marker::PhantomData, rc::Rc};
+use themes::{rose_pine, ThemeColors};
+use tokens::{margin::m4, text::lg};
 
-impl Deref for Playground {
-    type Target = playground_ui::Playground<Self>;
+mod color;
+mod frame;
+mod themes;
+mod tokens;
+
+#[derive(Element, Clone)]
+pub struct Playground<V: 'static>(PhantomData<V>);
+
+impl<V> Playground<V> {
+    pub fn new() -> Self {
+        Self(PhantomData)
+    }
 
-    fn deref(&self) -> &Self::Target {
-        &self.0
+    pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> impl Element<V> {
+        workspace(&rose_pine::dawn())
     }
 }
 
-impl DerefMut for Playground {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
+fn workspace<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+    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))
+}
+
+pub trait DialogDelegate<V>: 'static {}
+
+impl<V> DialogDelegate<V> for () {}
+
+#[derive(Element)]
+pub struct Dialog<V: 'static, D: DialogDelegate<V>> {
+    title: Cow<'static, str>,
+    description: Cow<'static, str>,
+    delegate: Option<Rc<RefCell<D>>>,
+    buttons: Vec<Box<dyn FnOnce() -> AnyElement<V>>>,
+    view_type: PhantomData<V>,
+}
+
+pub fn dialog<V>(
+    title: impl Into<Cow<'static, str>>,
+    description: impl Into<Cow<'static, str>>,
+) -> Dialog<V, ()> {
+    Dialog {
+        title: title.into(),
+        description: description.into(),
+        delegate: None,
+        buttons: Vec::new(),
+        view_type: PhantomData,
     }
 }
 
-impl Entity for Playground {
-    type Event = ();
+impl<V, D: DialogDelegate<V>> Dialog<V, D> {
+    pub fn delegate(mut self, delegate: D) -> Dialog<V, D> {
+        let old_delegate = self.delegate.replace(Rc::new(RefCell::new(delegate)));
+        debug_assert!(old_delegate.is_none(), "delegate already set");
+        self
+    }
+
+    pub fn button<L, Data, H>(mut self, label: L, data: Data, handler: H) -> Self
+    where
+        L: 'static + Into<Cow<'static, str>>,
+        Data: 'static + Clone,
+        H: ClickHandler<V, Data>,
+    {
+        let label = label.into();
+        self.buttons.push(Box::new(move || {
+            button(label).data(data).click(handler).into_any()
+        }));
+        self
+    }
 }
 
-impl View for Playground {
-    fn ui_name() -> &'static str {
-        "PlaygroundView"
+#[derive(Element)]
+struct Button<V: 'static, D: 'static, H: ClickHandler<V, D>> {
+    label: Cow<'static, str>,
+    click_handler: Option<H>,
+    data: Option<D>,
+    view_type: PhantomData<V>,
+}
+
+pub trait ClickHandler<V, D>: 'static {
+    fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>);
+}
+
+impl<V, M, F: 'static + Fn(&mut V, &M, &mut ViewContext<V>)> ClickHandler<V, M> for F {
+    fn handle(&self, view: &mut V, data: &M, cx: &mut ViewContext<V>) {
+        self(view, data, cx)
+    }
+}
+
+impl<V, D> ClickHandler<V, D> for () {
+    fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>) {}
+}
+
+fn button<V>(label: impl Into<Cow<'static, str>>) -> Button<V, (), ()> {
+    Button {
+        label: label.into(),
+        click_handler: None,
+        data: None,
+        view_type: PhantomData,
+    }
+}
+
+impl<V, D, F> Button<V, D, F>
+where
+    F: ClickHandler<V, D>,
+{
+    fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> AnyElement<V> {
+        // TODO! Handle click etc
+        row().child(text(self.label.clone())).into_any()
+    }
+}
+
+// impl<V, D, F> Button<V, D, F>
+// where
+//     V,
+//     F: ClickHandler<V, D>,
+// {
+//     fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
+//         // TODO! Handle click etc
+//         row()
+//             .fill(theme.colors.primary(5))
+//             .child(text(self.label.clone()).text_color(theme.colors.on_primary()))
+//     }
+// }
+
+// struct Tab<V> {
+//     active: bool,
+// }
+
+// impl<V> Tab<V>
+// where
+//     V,
+// {
+//     fn tab(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
+//         let theme = todo!();
+//         // TODO! Handle click etc
+//         row()
+//             .fill(theme.colors.neutral(6))
+//             .child(text(self.label.clone()).text_color(theme.colors.on_neutral()))
+//     }
+// }
+
+impl<V> Button<V, (), ()> {
+    fn data<D>(self, data: D) -> Button<V, D, ()>
+    where
+        D: 'static,
+    {
+        Button {
+            label: self.label,
+            click_handler: self.click_handler,
+            data: Some(data),
+            view_type: self.view_type,
+        }
+    }
+}
+
+impl<V, D> Button<V, D, ()> {
+    fn click<H>(self, handler: H) -> Button<V, D, H>
+    where
+        H: 'static + ClickHandler<V, D>,
+    {
+        Button {
+            label: self.label,
+            click_handler: Some(handler),
+            data: self.data,
+            view_type: self.view_type,
+        }
     }
+}
 
-    fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> AnyElement<Playground> {
-        self.0.clone().into_any()
+impl<V, D: DialogDelegate<V>> Dialog<V, D> {
+    pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
+        column()
+            .child(text(self.title.clone()).text_size(lg()))
+            .child(text(self.description.clone()).margins((m4(), auto())))
+            .child(row().children(self.buttons.drain(..).map(|button| (button)())))
+            .into_any()
     }
 }

crates/gpui/playground/ui/Cargo.toml 🔗

@@ -1,22 +0,0 @@
-[package]
-name = "playground_ui"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-name = "playground_ui"
-path = "src/playground_ui.rs"
-crate-type = ["dylib"]
-
-[dependencies]
-collections = { path = "../../../collections" }
-util = { path = "../../../util" }
-gpui = { path = "../.." }
-derive_more = "0.99.17"
-log.workspace = true
-optional_struct = "0.3.1"
-smallvec.workspace = true
-serde.workspace = true
-
-[dev-dependencies]
-gpui = { path = "../..", features = ["test-support"] }

crates/gpui/playground/ui/src/playground_ui.rs 🔗

@@ -1,200 +0,0 @@
-#![allow(dead_code, unused_variables)]
-
-use frame::{length::auto, *};
-use gpui::{AnyElement, Element, LayoutContext, ViewContext};
-use std::{borrow::Cow, cell::RefCell, marker::PhantomData, rc::Rc};
-use themes::{rose_pine, ThemeColors};
-use tokens::{margin::m4, text::lg};
-
-mod color;
-mod frame;
-mod themes;
-mod tokens;
-
-#[derive(Element, Clone, Default)]
-pub struct Playground<V: 'static>(PhantomData<V>);
-
-impl<V> Frame<V> {}
-
-impl<V> Playground<V> {
-    pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> impl Element<V> {
-        workspace(&rose_pine::dawn())
-    }
-}
-
-fn workspace<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-    column()
-        .size(auto())
-        .fill(theme.base(0.1))
-        .child(title_bar(theme))
-        .child(stage(theme))
-        .child(status_bar(theme))
-}
-
-fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-    row().fill(theme.surface(1.0))
-}
-
-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))
-}
-
-pub trait DialogDelegate<V>: 'static {}
-
-impl<V> DialogDelegate<V> for () {}
-
-#[derive(Element)]
-pub struct Dialog<V: 'static, D: DialogDelegate<V>> {
-    title: Cow<'static, str>,
-    description: Cow<'static, str>,
-    delegate: Option<Rc<RefCell<D>>>,
-    buttons: Vec<Box<dyn FnOnce() -> AnyElement<V>>>,
-    view_type: PhantomData<V>,
-}
-
-pub fn dialog<V>(
-    title: impl Into<Cow<'static, str>>,
-    description: impl Into<Cow<'static, str>>,
-) -> Dialog<V, ()> {
-    Dialog {
-        title: title.into(),
-        description: description.into(),
-        delegate: None,
-        buttons: Vec::new(),
-        view_type: PhantomData,
-    }
-}
-
-impl<V, D: DialogDelegate<V>> Dialog<V, D> {
-    pub fn delegate(mut self, delegate: D) -> Dialog<V, D> {
-        let old_delegate = self.delegate.replace(Rc::new(RefCell::new(delegate)));
-        debug_assert!(old_delegate.is_none(), "delegate already set");
-        self
-    }
-
-    pub fn button<L, Data, H>(mut self, label: L, data: Data, handler: H) -> Self
-    where
-        L: 'static + Into<Cow<'static, str>>,
-        Data: 'static + Clone,
-        H: ClickHandler<V, Data>,
-    {
-        let label = label.into();
-        self.buttons.push(Box::new(move || {
-            button(label).data(data).click(handler).into_any()
-        }));
-        self
-    }
-}
-
-#[derive(Element)]
-struct Button<V: 'static, D: 'static, H: ClickHandler<V, D>> {
-    label: Cow<'static, str>,
-    click_handler: Option<H>,
-    data: Option<D>,
-    view_type: PhantomData<V>,
-}
-
-pub trait ClickHandler<V, D>: 'static {
-    fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>);
-}
-
-impl<V, M, F: 'static + Fn(&mut V, &M, &mut ViewContext<V>)> ClickHandler<V, M> for F {
-    fn handle(&self, view: &mut V, data: &M, cx: &mut ViewContext<V>) {
-        self(view, data, cx)
-    }
-}
-
-impl<V, D> ClickHandler<V, D> for () {
-    fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>) {}
-}
-
-fn button<V>(label: impl Into<Cow<'static, str>>) -> Button<V, (), ()> {
-    Button {
-        label: label.into(),
-        click_handler: None,
-        data: None,
-        view_type: PhantomData,
-    }
-}
-
-impl<V, D, F> Button<V, D, F>
-where
-    F: ClickHandler<V, D>,
-{
-    fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> AnyElement<V> {
-        // TODO! Handle click etc
-        row().child(text(self.label.clone())).into_any()
-    }
-}
-
-// impl<V, D, F> Button<V, D, F>
-// where
-//     V,
-//     F: ClickHandler<V, D>,
-// {
-//     fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
-//         // TODO! Handle click etc
-//         row()
-//             .fill(theme.colors.primary(5))
-//             .child(text(self.label.clone()).text_color(theme.colors.on_primary()))
-//     }
-// }
-
-// struct Tab<V> {
-//     active: bool,
-// }
-
-// impl<V> Tab<V>
-// where
-//     V,
-// {
-//     fn tab(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
-//         let theme = todo!();
-//         // TODO! Handle click etc
-//         row()
-//             .fill(theme.colors.neutral(6))
-//             .child(text(self.label.clone()).text_color(theme.colors.on_neutral()))
-//     }
-// }
-
-impl<V> Button<V, (), ()> {
-    fn data<D>(self, data: D) -> Button<V, D, ()>
-    where
-        D: 'static,
-    {
-        Button {
-            label: self.label,
-            click_handler: self.click_handler,
-            data: Some(data),
-            view_type: self.view_type,
-        }
-    }
-}
-
-impl<V, D> Button<V, D, ()> {
-    fn click<H>(self, handler: H) -> Button<V, D, H>
-    where
-        H: 'static + ClickHandler<V, D>,
-    {
-        Button {
-            label: self.label,
-            click_handler: Some(handler),
-            data: self.data,
-            view_type: self.view_type,
-        }
-    }
-}
-
-impl<V, D: DialogDelegate<V>> Dialog<V, D> {
-    pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
-        column()
-            .child(text(self.title.clone()).text_size(lg()))
-            .child(text(self.description.clone()).margins((m4(), auto())))
-            .child(row().children(self.buttons.drain(..).map(|button| (button)())))
-            .into_any()
-    }
-}

crates/gpui/src/app.rs 🔗

@@ -3440,14 +3440,22 @@ impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
             .unwrap_or(Arc::new(TextStyle::default(&self.font_cache)))
     }
 
+    pub fn push_text_style<S: Into<Arc<TextStyle>>>(&mut self, style: S) {
+        self.text_style_stack.push(style.into());
+    }
+
+    pub fn pop_text_style(&mut self) {
+        self.text_style_stack.pop();
+    }
+
     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());
+        self.push_text_style(style);
         let result = f(self);
-        self.text_style_stack.pop();
+        self.pop_text_style();
         result
     }
 }

crates/gpui/src/color.rs 🔗

@@ -15,7 +15,7 @@ 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)