diff --git a/crates/gpui/examples/components.rs b/crates/gpui/examples/components.rs index 50ace5eee0..cf695ea834 100644 --- a/crates/gpui/examples/components.rs +++ b/crates/gpui/examples/components.rs @@ -1,9 +1,8 @@ use button_component::Button; -use component::AdaptComponent; use gpui::{ color::Color, - elements::{ContainerStyle, Flex, Label, ParentElement}, + elements::{Component, ContainerStyle, Flex, Label, ParentElement}, fonts::{self, TextStyle}, platform::WindowOptions, AnyElement, App, Element, Entity, View, ViewContext, @@ -14,6 +13,8 @@ use simplelog::SimpleLogger; use theme::Toggleable; use toggleable_button::ToggleableButton; +// cargo run -p gpui --example components + fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); @@ -113,12 +114,12 @@ mod theme { // Component creation: mod toggleable_button { use gpui::{ - elements::{ContainerStyle, LabelStyle}, + elements::{Component, ContainerStyle, LabelStyle}, scene::MouseClick, EventContext, View, }; - use crate::{button_component::Button, component::Component, theme::Toggleable}; + use crate::{button_component::Button, theme::Toggleable}; pub struct ToggleableButton { active: bool, @@ -155,14 +156,8 @@ mod toggleable_button { } } - impl Component for ToggleableButton { - type View = V; - - fn render( - self, - v: &mut Self::View, - cx: &mut gpui::ViewContext, - ) -> gpui::AnyElement { + impl Component for ToggleableButton { + fn render(self, v: &mut V, cx: &mut gpui::ViewContext) -> gpui::AnyElement { let button = if let Some(style) = self.style { self.button.with_style(*style.style_for(self.active)) } else { @@ -176,14 +171,12 @@ mod toggleable_button { mod button_component { use gpui::{ - elements::{ContainerStyle, Label, LabelStyle, MouseEventHandler}, + elements::{Component, ContainerStyle, Label, LabelStyle, MouseEventHandler}, platform::MouseButton, scene::MouseClick, AnyElement, Element, EventContext, TypeTag, View, ViewContext, }; - use crate::component::Component; - type ClickHandler = Box)>; pub struct Button { @@ -219,10 +212,8 @@ mod button_component { } } - impl Component for Button { - type View = V; - - fn render(self, _: &mut Self::View, cx: &mut ViewContext) -> AnyElement { + impl Component for Button { + fn render(self, _: &mut V, cx: &mut ViewContext) -> AnyElement { let click_handler = self.click_handler; let result = MouseEventHandler::new_dynamic(self.tag, 0, cx, |_, _| { @@ -244,92 +235,3 @@ mod button_component { } } } - -mod component { - - use gpui::{AnyElement, Element, View, ViewContext}; - use pathfinder_geometry::vector::Vector2F; - - // Public API: - pub trait Component { - type View: View; - - fn render( - self, - v: &mut Self::View, - cx: &mut ViewContext, - ) -> AnyElement; - } - - pub struct ComponentAdapter { - component: Option, - } - - impl ComponentAdapter { - pub fn new(e: E) -> Self { - Self { component: Some(e) } - } - } - - pub trait AdaptComponent: Sized { - fn into_element(self) -> ComponentAdapter { - ComponentAdapter::new(self) - } - } - - impl AdaptComponent for C {} - - impl Element for ComponentAdapter { - type LayoutState = AnyElement; - - type PaintState = (); - - fn layout( - &mut self, - constraint: gpui::SizeConstraint, - view: &mut C::View, - cx: &mut gpui::LayoutContext, - ) -> (Vector2F, Self::LayoutState) { - let component = self.component.take().unwrap(); - let mut element = component.render(view, cx.view_context()); - let constraint = element.layout(constraint, view, cx); - (constraint, element) - } - - fn paint( - &mut self, - scene: &mut gpui::SceneBuilder, - bounds: gpui::geometry::rect::RectF, - visible_bounds: gpui::geometry::rect::RectF, - layout: &mut Self::LayoutState, - view: &mut C::View, - cx: &mut gpui::PaintContext, - ) -> Self::PaintState { - layout.paint(scene, bounds.origin(), visible_bounds, view, cx) - } - - fn rect_for_text_range( - &self, - _: std::ops::Range, - _: gpui::geometry::rect::RectF, - _: gpui::geometry::rect::RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &C::View, - _: &ViewContext, - ) -> Option { - todo!() - } - - fn debug( - &self, - _: gpui::geometry::rect::RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &C::View, - _: &ViewContext, - ) -> serde_json::Value { - todo!() - } - } -} diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 16c750ea8e..35ecf0545a 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -1,6 +1,7 @@ mod align; mod canvas; mod clipped; +mod component; mod constrained_box; mod container; mod empty; @@ -21,9 +22,9 @@ mod tooltip; mod uniform_list; pub use self::{ - align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*, - keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*, - stack::*, svg::*, text::*, tooltip::*, uniform_list::*, + align::*, canvas::*, component::*, constrained_box::*, container::*, empty::*, flex::*, + hook::*, image::*, keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, + resizable::*, stack::*, svg::*, text::*, tooltip::*, uniform_list::*, }; pub use crate::window::ChildView; diff --git a/crates/gpui/src/elements/component.rs b/crates/gpui/src/elements/component.rs new file mode 100644 index 0000000000..1c4359e2c3 --- /dev/null +++ b/crates/gpui/src/elements/component.rs @@ -0,0 +1,87 @@ +use std::marker::PhantomData; + +use pathfinder_geometry::{rect::RectF, vector::Vector2F}; + +use crate::{ + AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, +}; + +pub trait Component { + fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement; + + fn into_element(self) -> ComponentAdapter + where + Self: Sized, + { + ComponentAdapter::new(self) + } +} + +pub struct ComponentAdapter { + component: Option, + phantom: PhantomData, +} + +impl ComponentAdapter { + pub fn new(e: E) -> Self { + Self { + component: Some(e), + phantom: PhantomData, + } + } +} + +impl + 'static> Element for ComponentAdapter { + type LayoutState = AnyElement; + + type PaintState = (); + + fn layout( + &mut self, + constraint: SizeConstraint, + view: &mut V, + cx: &mut LayoutContext, + ) -> (Vector2F, Self::LayoutState) { + let component = self.component.take().unwrap(); + let mut element = component.render(view, cx.view_context()); + let constraint = element.layout(constraint, view, cx); + (constraint, element) + } + + fn paint( + &mut self, + scene: &mut SceneBuilder, + bounds: RectF, + visible_bounds: RectF, + layout: &mut Self::LayoutState, + view: &mut V, + cx: &mut PaintContext, + ) -> Self::PaintState { + layout.paint(scene, bounds.origin(), visible_bounds, view, cx) + } + + fn rect_for_text_range( + &self, + range_utf16: std::ops::Range, + _: RectF, + _: RectF, + element: &Self::LayoutState, + _: &Self::PaintState, + view: &V, + cx: &ViewContext, + ) -> Option { + element.rect_for_text_range(range_utf16, view, cx) + } + + fn debug( + &self, + _: RectF, + element: &Self::LayoutState, + _: &Self::PaintState, + view: &V, + cx: &ViewContext, + ) -> serde_json::Value { + element.debug(view, cx) + } +}