WIP

Nathan Sobo created

Change summary

crates/gpui/playground/src/playground.rs       |   4 
crates/gpui/playground/ui/src/playground_ui.rs |  47 +++----
crates/gpui/src/elements/node.rs               | 108 ++++++++++---------
3 files changed, 80 insertions(+), 79 deletions(-)

Detailed changes

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

@@ -14,10 +14,10 @@ fn main() {
 }
 
 #[derive(Clone, Default)]
-struct Playground(playground_ui::Playground);
+struct Playground(playground_ui::Playground<Self>);
 
 impl Deref for Playground {
-    type Target = playground_ui::Playground;
+    type Target = playground_ui::Playground<Self>;
 
     fn deref(&self) -> &Self::Target {
         &self.0

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

@@ -2,16 +2,16 @@ use gpui::{
     elements::node::{column, length::auto, row, text},
     AnyElement, Element, LayoutContext, View, ViewContext,
 };
-use std::{borrow::Cow, marker::PhantomData};
+use std::{borrow::Cow, cell::RefCell, marker::PhantomData, rc::Rc};
 use tokens::{margin::m4, text::lg};
 
 mod tokens;
 
 #[derive(Element, Clone, Default)]
-pub struct Playground;
+pub struct Playground<V: View>(PhantomData<V>);
 
-impl Playground {
-    pub fn render<V: View>(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
+impl<V: View> Playground<V> {
+    pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
         column()
             .width(auto())
             .child(
@@ -22,30 +22,25 @@ impl Playground {
             .into_any()
     }
 
-    fn action_1(&mut self, data: &usize, _: &mut ViewContext<Self>) {
+    fn action_1(_: &mut V, data: &usize, _: &mut ViewContext<V>) {
         println!("action 1: data is {}", *data);
     }
 
-    fn action_2(&mut self, data: &usize, _: &mut ViewContext<Self>) {
+    fn action_2(_: &mut V, data: &usize, _: &mut ViewContext<V>) {
         println!("action 1: data is {}", *data);
     }
 }
 
-pub trait DialogDelegate<V: View>: 'static {
-    fn handle_confirm<B>(&mut self, view: &mut V, button: B);
-}
+pub trait DialogDelegate<V: View>: 'static {}
 
-impl<V: View> DialogDelegate<V> for () {
-    fn handle_cancel<B>(&mut self, view: &mut V, button: B) {}
-    fn handle_confirm<B>(&mut self, _: &mut V, _: B) {}
-}
+impl<V: View> DialogDelegate<V> for () {}
 
 #[derive(Element)]
 pub struct Dialog<V: View, D: DialogDelegate<V>> {
     title: Cow<'static, str>,
     description: Cow<'static, str>,
     delegate: Option<Rc<RefCell<D>>>,
-    buttons: Vec<Box<dyn Fn() -> Button>>,
+    buttons: Vec<Box<dyn FnOnce() -> AnyElement<V>>>,
     view_type: PhantomData<V>,
 }
 
@@ -64,19 +59,21 @@ pub fn dialog<V: View>(
 
 impl<V: View, D: DialogDelegate<V>> Dialog<V, D> {
     pub fn delegate(mut self, delegate: D) -> Dialog<V, D> {
-        let old_delegate = self.delegate.replace(delegate);
+        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, D, H>(mut self, label: L, data: D, handler: H) -> Self
+    pub fn button<L, Data, H>(mut self, label: L, data: Data, handler: H) -> Self
     where
-        L: Into<Cow<'static, str>>,
-        D: 'static,
-        H: ClickHandler<V, D>,
+        L: 'static + Into<Cow<'static, str>>,
+        Data: 'static + Clone,
+        H: ClickHandler<V, Data>,
     {
-        self.buttons
-            .push(|| button(label).data(data).click(handler));
+        let label = label.into();
+        self.buttons.push(Box::new(move || {
+            button(label).data(data).click(handler).into_any()
+        }));
         self
     }
 }
@@ -128,7 +125,7 @@ where
 impl<V: View> Button<V, (), ()> {
     fn data<D>(self, data: D) -> Button<V, D, ()>
     where
-        D: 'static + FnOnce(&mut V, &D, &mut ViewContext<V>),
+        D: 'static,
     {
         Button {
             label: self.label,
@@ -142,7 +139,7 @@ impl<V: View> Button<V, (), ()> {
 impl<V: View, D> Button<V, D, ()> {
     fn click<H>(self, handler: H) -> Button<V, D, H>
     where
-        H: 'static + Fn(&mut V, &D, &mut ViewContext<V>),
+        H: 'static + ClickHandler<V, D>,
     {
         Button {
             label: self.label,
@@ -155,12 +152,10 @@ impl<V: View, D> Button<V, D, ()> {
 
 impl<V: View, D: DialogDelegate<V>> Dialog<V, D> {
     pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
-        let delegate = self.delegate.clone();
-
         column()
             .child(text(self.title.clone()).text_size(lg()))
             .child(text(self.description.clone()).margins(m4(), auto()))
-            .child(row().children(self.buttons.iter().map(|button| (button)())))
+            .child(row().children(self.buttons.drain(..).map(|button| (button)())))
             .into_any()
     }
 }

crates/gpui/src/elements/node.rs 🔗

@@ -273,7 +273,7 @@ impl<V: View> Node<V> {
         }
     }
 
-    fn paint_2d_children(
+    fn paint_children_xy(
         &mut self,
         scene: &mut SceneBuilder,
         axis: Axis2d,
@@ -395,15 +395,7 @@ impl<V: View> Element<V> for Node<V> {
         let mut layout = NodeLayout::default();
 
         let size = if let Some(axis) = self.style.axis.to_2d() {
-            self.layout_xy(
-                axis,
-                constraint.max,
-                cx.rem_pixels(),
-                &mut layout,
-                &mut layout.padding,
-                view,
-                cx,
-            )
+            self.layout_xy(axis, constraint.max, cx.rem_pixels(), &mut layout, view, cx)
         } else {
             todo!()
         };
@@ -421,11 +413,6 @@ impl<V: View> Element<V> for Node<V> {
         cx: &mut PaintContext<V>,
     ) -> Self::PaintState {
         let rem_pixels = cx.rem_pixels();
-        // let margin: Edges<f32> = todo!(); // &self.style.margin.to_pixels(rem_size);
-        //
-
-        let size = bounds.size();
-
         let margined_bounds = RectF::from_points(
             bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
             bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
@@ -434,7 +421,7 @@ impl<V: View> Element<V> for Node<V> {
         // Paint drop shadow
         for shadow in &self.style.shadows {
             scene.push_shadow(scene::Shadow {
-                bounds: margin_bounds + shadow.offset,
+                bounds: margined_bounds + shadow.offset,
                 corner_radius: self.style.corner_radius,
                 sigma: shadow.blur,
                 color: shadow.color,
@@ -451,7 +438,7 @@ impl<V: View> Element<V> for Node<V> {
         //     }
         // }
 
-        // Render the background and/or the border (if it not an overlay border).
+        // Render the background and/or the border.
         let Fill::Color(fill_color) = self.style.fill;
         let is_fill_visible = !fill_color.is_fully_transparent();
         if is_fill_visible || self.style.borders.is_visible() {
@@ -472,38 +459,25 @@ impl<V: View> Element<V> for Node<V> {
         }
 
         if !self.children.is_empty() {
-            let padded_bounds = RectF::from_points(
-                margined_bounds.origin() + vec2f(layout.padding.left, layout.padding.top),
-                margined_bounds.lower_right() - vec2f(layout.padding.right, layout.padding.bottom),
-            );
-
             // Account for padding first.
-            let padding: Edges<f32> = todo!(); // &self.style.padding.to_pixels(rem_size);
+            let borders = &self.style.borders;
             let padded_bounds = RectF::from_points(
-                margined_bounds.origin() + vec2f(padding.left, padding.top),
-                margined_bounds.lower_right() - vec2f(padding.right, padding.top),
+                margined_bounds.origin()
+                    + vec2f(
+                        borders.left_width() + layout.padding.left,
+                        borders.top_width() + layout.padding.top,
+                    ),
+                margined_bounds.lower_right()
+                    - vec2f(
+                        layout.padding.right + borders.right_width(),
+                        layout.padding.bottom + borders.bottom_width(),
+                    ),
             );
 
-            match self.style.axis {
-                Axis3d::X => self.paint_2d_children(
-                    scene,
-                    Axis2d::X,
-                    padded_bounds,
-                    visible_bounds,
-                    layout,
-                    view,
-                    cx,
-                ),
-                Axis3d::Y => self.paint_2d_children(
-                    scene,
-                    Axis2d::Y,
-                    padded_bounds,
-                    visible_bounds,
-                    layout,
-                    view,
-                    cx,
-                ),
-                Axis3d::Z => todo!(),
+            if let Some(axis) = self.style.axis.to_2d() {
+                self.paint_children_xy(scene, axis, padded_bounds, visible_bounds, layout, view, cx)
+            } else {
+                todo!();
             }
         }
     }
@@ -631,7 +605,7 @@ pub struct NodeStyle {
     text: OptionalTextStyle,
     opacity: f32,
     fill: Fill,
-    borders: Border,
+    borders: Borders,
     corner_radius: f32,
     shadows: Vec<Shadow>,
 }
@@ -673,7 +647,7 @@ struct Size<T> {
     height: T,
 }
 
-impl<T> Size<T> {
+impl<T: Copy> Size<T> {
     fn get(&self, axis: Axis2d) -> T {
         match axis {
             Axis2d::X => self.width,
@@ -682,7 +656,7 @@ impl<T> Size<T> {
     }
 }
 
-impl<T: Add<Output = T>> Size<Option<T>> {
+impl<T: Copy + Add<Output = T>> Size<Option<T>> {
     fn add_assign_optional(&mut self, rhs: Size<Option<T>>) {
         self.width = optional_add(self.width, rhs.width);
         self.height = optional_add(self.height, rhs.height);
@@ -843,7 +817,7 @@ impl Default for Fill {
 }
 
 #[derive(Clone, Default)]
-struct Border {
+struct Borders {
     color: Color,
     width: f32,
     top: bool,
@@ -852,13 +826,45 @@ struct Border {
     right: bool,
 }
 
-impl Border {
+impl Borders {
     fn is_visible(&self) -> bool {
         self.width > 0.
             && !self.color.is_fully_transparent()
             && (self.top || self.bottom || self.left || self.right)
     }
 
+    fn top_width(&self) -> f32 {
+        if self.top {
+            self.width
+        } else {
+            0.
+        }
+    }
+
+    fn bottom_width(&self) -> f32 {
+        if self.bottom {
+            self.width
+        } else {
+            0.
+        }
+    }
+
+    fn left_width(&self) -> f32 {
+        if self.left {
+            self.width
+        } else {
+            0.
+        }
+    }
+
+    fn right_width(&self) -> f32 {
+        if self.right {
+            self.width
+        } else {
+            0.
+        }
+    }
+
     fn size(&self) -> Vector2F {
         let width =
             if self.left { self.width } else { 0. } + if self.right { self.width } else { 0. };
@@ -1078,7 +1084,7 @@ pub fn text<V: View>(text: impl Into<Cow<'static, str>>) -> Node<V> {
 }
 
 #[derive(Default)]
-struct NodeLayout {
+pub struct NodeLayout {
     content_size: Vector2F,
     margins: Edges<f32>,
     padding: Edges<f32>,